From c5944b34349d405d716a3c968ec0f69cee15cdac Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Sun, 28 Jul 2013 20:22:13 +0100 Subject: [PATCH 01/47] update properties to dev branch properties. --- api-docs/apiLive.htm | 2 +- mifosng-provider/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index aa683f780..3805e793b 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -90,7 +90,7 @@
- +

Overview

diff --git a/mifosng-provider/gradle.properties b/mifosng-provider/gradle.properties index 414b7ebda..7e81526b5 100644 --- a/mifosng-provider/gradle.properties +++ b/mifosng-provider/gradle.properties @@ -1 +1 @@ -releaseVersion=1.7.0.RELEASE +releaseVersion=1.8.0.DEVELOP \ No newline at end of file From f6ba4cc93055c05e6e9803e2504099cffed26d0f Mon Sep 17 00:00:00 2001 From: Nayan Ambali Date: Mon, 29 Jul 2013 14:08:34 +0530 Subject: [PATCH 02/47] minor change --- .../org/mifosplatform/useradministration/domain/AppUser.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java index fa1a18743..9d6656bcd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java @@ -23,7 +23,6 @@ import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.UniqueConstraint; -import org.apache.commons.lang.StringUtils; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.security.domain.PlatformUser; @@ -95,7 +94,9 @@ public class AppUser extends AbstractPersistable implements PlatformUser { final String username = command.stringValueOfParameterNamed("username"); String password = command.stringValueOfParameterNamed("password"); - if (StringUtils.isBlank(password)) { + final Boolean sendPasswordToEmail = command.booleanObjectValueOfParameterNamed("sendPasswordToEmail"); + + if (sendPasswordToEmail.booleanValue()) { password = new RandomPasswordGenerator(13).generate(); } From 111175df91846db89b41c798630957593d863a59 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Mon, 29 Jul 2013 11:22:29 +0530 Subject: [PATCH 03/47] added condition to call scheduler's standby --- .../infrastructure/jobs/service/JobRegisterServiceImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java index 0c1b4db2c..c395cfd71 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java @@ -195,6 +195,9 @@ public class JobRegisterServiceImpl implements JobRegisterService { JobDetail jobDetail = createJobDetail(scheduledJobDetails); Trigger trigger = createTrigger(scheduledJobDetails, jobDetail); Scheduler scheduler = getScheduler(scheduledJobDetails); + if (!this.schedulerStatus.get(ThreadLocalContextUtil.getTenant().getId()) && !scheduler.isInStandbyMode()) { + scheduler.standby(); + } scheduler.scheduleJob(jobDetail, trigger); scheduledJobDetails.updateJobKey(getJobKeyAsString(jobDetail.getKey())); scheduledJobDetails.updateNextRunTime(trigger.getNextFireTime()); From 872d13a6ab85acdd7cb49760307be3cc49fb9e7e Mon Sep 17 00:00:00 2001 From: John Woodlock Date: Wed, 31 Jul 2013 17:33:40 +0100 Subject: [PATCH 04/47] mifosx-567 externalId updates (this for staff) --- api-docs/apiLive.htm | 17 +++++++++----- .../staff/api/StaffApiResource.java | 2 +- .../organisation/staff/data/StaffData.java | 12 +++++----- .../organisation/staff/domain/Staff.java | 22 +++++++++++++++---- .../StaffCommandFromApiJsonDeserializer.java | 2 +- .../service/StaffReadPlatformServiceImpl.java | 10 +++++---- ...WritePlatformServiceJpaRepositoryImpl.java | 9 +++++++- 7 files changed, 52 insertions(+), 22 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 3805e793b..ef2dc2294 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -861,7 +861,7 @@ Staff staff - Create a Staff + Create a Staff Member List Staff @@ -10029,7 +10029,8 @@ Request Body: "officeId": 1, "firstname": "John", "lastname": "Doe", - "isLoanOfficer": "true" + "isLoanOfficer": "true", + "externalId": "17H" } @@ -10062,7 +10063,8 @@ GET https://DomainName/api/v1/staff "displayName": "Doe, John", "officeId": 1, "officeName": "Head Office", - "isLoanOfficer": true + "isLoanOfficer": true, + "externalId": "17H" } ] @@ -10087,7 +10089,8 @@ GET https://DomainName/api/v1/staff "displayName": "Doe, John", "officeId": 1, "officeName": "Head Office", - "isLoanOfficer": true + "isLoanOfficer": true, + "externalId": "17H" }
@@ -10108,7 +10111,8 @@ PUT staff/1 Content-Type: application/json Request Body: { - "isLoanOfficer": "false" + "isLoanOfficer": "false", + "externalId": "17Hbb" } @@ -10116,7 +10120,8 @@ Request Body: "officeId": 1, "resourceId": 1, "changes": { - "isLoanOfficer": false + "isLoanOfficer": false, + "externalId": "17Hbb" } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/api/StaffApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/api/StaffApiResource.java index 22009a5e7..fe20fe593 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/api/StaffApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/api/StaffApiResource.java @@ -48,7 +48,7 @@ public class StaffApiResource { * {@link StaffData}. */ private final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "firstname", "lastname", "displayName", - "officeId", "officeName", "loanOfficerFlag", "allowedOffices")); + "officeId", "officeName", "loanOfficerFlag", "externalId", "allowedOffices")); private final String resourceNameForPermissions = "STAFF"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/data/StaffData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/data/StaffData.java index d2e0557d8..2356341e4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/data/StaffData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/data/StaffData.java @@ -21,32 +21,34 @@ public class StaffData { private final Long officeId; private final String officeName; private final Boolean isLoanOfficer; + private final String externalId; @SuppressWarnings("unused") private final Collection allowedOffices; public static StaffData templateData(final StaffData staff, final Collection allowedOffices) { return new StaffData(staff.id, staff.firstname, staff.lastname, staff.displayName, staff.officeId, staff.officeName, - staff.isLoanOfficer, allowedOffices); + staff.isLoanOfficer, staff.externalId, allowedOffices); } public static StaffData lookup(final Long id, final String displayName) { - return new StaffData(id, null, null, displayName, null, null, null, null); + return new StaffData(id, null, null, displayName, null, null, null, null, null); } public static StaffData instance(final Long id, final String firstname, final String lastname, final String displayName, - final Long officeId, final String officeName, final Boolean isLoanOfficer) { - return new StaffData(id, firstname, lastname, displayName, officeId, officeName, isLoanOfficer, null); + final Long officeId, final String officeName, final Boolean isLoanOfficer, final String externalId) { + return new StaffData(id, firstname, lastname, displayName, officeId, officeName, isLoanOfficer, externalId, null); } private StaffData(final Long id, final String firstname, final String lastname, final String displayName, final Long officeId, - final String officeName, final Boolean isLoanOfficer, final Collection allowedOffices) { + final String officeName, final Boolean isLoanOfficer, final String externalId, final Collection allowedOffices) { this.id = id; this.firstname = firstname; this.lastname = lastname; this.displayName = displayName; this.officeName = officeName; this.isLoanOfficer = isLoanOfficer; + this.externalId = externalId; this.officeId = officeId; this.allowedOffices = allowedOffices; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java index 22f7b2879..bc07e68d3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java @@ -34,6 +34,9 @@ public class Staff extends AbstractPersistable { @Column(name = "display_name", length = 100) private String displayName; + @Column(name = "external_id", length = 100, nullable = true, unique = true) + private String externalId; + // Office to which this employee belongs @ManyToOne @JoinColumn(name = "office_id", nullable = false) @@ -58,25 +61,29 @@ public class Staff extends AbstractPersistable { final String lastnameParamName = "lastname"; String lastname = command.stringValueOfParameterNamed(lastnameParamName); + + final String externalIdParamName = "externalId"; + String externalId = command.stringValueOfParameterNamed(externalIdParamName); final String isLoanOfficerParamName = "isLoanOfficer"; final boolean isLoanOfficer = command.booleanPrimitiveValueOfParameterNamed(isLoanOfficerParamName); - return new Staff(staffOffice, firstname, lastname, isLoanOfficer); + return new Staff(staffOffice, firstname, lastname, externalId, isLoanOfficer); } - public static Staff createNew(final Office staffOffice, final String firstname, final String lastname, final boolean isLoanOfficer) { - return new Staff(staffOffice, firstname, lastname, isLoanOfficer); + public static Staff createNew(final Office staffOffice, final String firstname, final String lastname, final String externalId, final boolean isLoanOfficer) { + return new Staff(staffOffice, firstname, lastname, externalId, isLoanOfficer); } protected Staff() { // } - private Staff(final Office staffOffice, final String firstname, final String lastname, final boolean isLoanOfficer) { + private Staff(final Office staffOffice, final String firstname, final String lastname, final String externalId, final boolean isLoanOfficer) { this.office = staffOffice; this.firstname = StringUtils.defaultIfEmpty(firstname, null); this.lastname = StringUtils.defaultIfEmpty(lastname, null); + this.externalId = externalId; this.loanOfficer = isLoanOfficer; deriveDisplayName(firstname); } @@ -125,6 +132,13 @@ public class Staff extends AbstractPersistable { deriveDisplayName(this.firstname); } + final String externalIdParamName = "externalId"; + if (command.isChangeInStringParameterNamed(externalIdParamName, this.externalId)) { + final String newValue = command.stringValueOfParameterNamed(externalIdParamName); + actualChanges.put(externalIdParamName, newValue); + this.externalId = newValue; + } + final String isLoanOfficerParamName = "isLoanOfficer"; if (command.isChangeInBooleanParameterNamed(isLoanOfficerParamName, this.loanOfficer)) { final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isLoanOfficerParamName); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/serialization/StaffCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/serialization/StaffCommandFromApiJsonDeserializer.java index c2cd8c018..c1562e58a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/serialization/StaffCommandFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/serialization/StaffCommandFromApiJsonDeserializer.java @@ -31,7 +31,7 @@ public final class StaffCommandFromApiJsonDeserializer { /** * The parameters supported for this command. */ - private final Set supportedParameters = new HashSet(Arrays.asList("firstname", "lastname", "officeId", "isLoanOfficer")); + private final Set supportedParameters = new HashSet(Arrays.asList("firstname", "lastname", "officeId", "externalId", "isLoanOfficer")); private final FromJsonHelper fromApiJsonHelper; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformServiceImpl.java index cbd0d1791..52422e9b0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformServiceImpl.java @@ -38,7 +38,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { public String schema() { return " s.id as id,s.office_id as officeId, o.name as officeName, s.firstname as firstname, s.lastname as lastname," - + " s.display_name as displayName, s.is_loan_officer as isLoanOfficer from m_staff s " + + " s.display_name as displayName, s.is_loan_officer as isLoanOfficer, s.external_id as externalId from m_staff s " + " join m_office o on o.id = s.office_id"; } @@ -52,8 +52,9 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { final Long officeId = rs.getLong("officeId"); final boolean isLoanOfficer = rs.getBoolean("isLoanOfficer"); final String officeName = rs.getString("officeName"); + final String externalId = rs.getString("externalId"); - return StaffData.instance(id, firstname, lastname, displayName, officeId, officeName, isLoanOfficer); + return StaffData.instance(id, firstname, lastname, displayName, officeId, officeName, isLoanOfficer, externalId); } } @@ -64,7 +65,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { StringBuilder sqlBuilder = new StringBuilder(200); sqlBuilder.append("s.id as id, s.office_id as officeId, o.name as officeName,"); sqlBuilder.append("s.firstname as firstname, s.lastname as lastname,"); - sqlBuilder.append("s.display_name as displayName, s.is_loan_officer as isLoanOfficer "); + sqlBuilder.append("s.display_name as displayName, s.is_loan_officer as isLoanOfficer, s.external_id as externalId "); sqlBuilder.append("from m_office o "); sqlBuilder.append("join m_office ohierarchy on o.hierarchy like concat(ohierarchy.hierarchy, '%') "); sqlBuilder.append("join m_staff s on s.office_id = ohierarchy.id "); @@ -88,8 +89,9 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { final Long officeId = rs.getLong("officeId"); final String officeName = rs.getString("officeName"); final boolean isLoanOfficer = rs.getBoolean("isLoanOfficer"); + final String externalId = rs.getString("externalId"); - return StaffData.instance(id, firstname, lastname, displayName, officeId, officeName, isLoanOfficer); + return StaffData.instance(id, firstname, lastname, displayName, officeId, officeName, isLoanOfficer, externalId); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffWritePlatformServiceJpaRepositoryImpl.java index 27f960e52..0524c6f05 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffWritePlatformServiceJpaRepositoryImpl.java @@ -115,7 +115,14 @@ public class StaffWritePlatformServiceJpaRepositoryImpl implements StaffWritePla */ private void handleStaffDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { Throwable realCause = dve.getMostSpecificCause(); - if (realCause.getMessage().contains("display_name")) { + + if (realCause.getMessage().contains("external_id")) { + + final String externalId = command.stringValueOfParameterNamed("externalId"); + throw new PlatformDataIntegrityException("error.msg.staff.duplicate.externalId", "Staff with externalId `" + externalId + + "` already exists", "externalId", externalId); + } + else if (realCause.getMessage().contains("display_name")) { final String lastname = command.stringValueOfParameterNamed("lastname"); String displayName = lastname; if (!StringUtils.isBlank(displayName)) { From fff9cba28f1b88a10b0a274f996b26d050b0c9ce Mon Sep 17 00:00:00 2001 From: Raman Kansal Date: Mon, 29 Jul 2013 23:08:56 +0530 Subject: [PATCH 05/47] Added Group Tests for Creation, Activation, Client Association, Updating & Deletion. --- .../integrationtests/GroupTest.java | 67 +++++++ .../integrationtests/common/GroupHelper.java | 167 ++++++++++++++++++ .../integrationtests/common/Utils.java | 57 +++++- 3 files changed, 282 insertions(+), 9 deletions(-) create mode 100644 mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java create mode 100644 mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java new file mode 100644 index 000000000..3dc206122 --- /dev/null +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java @@ -0,0 +1,67 @@ +package org.mifosplatform.integrationtests; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; +import org.mifosplatform.integrationtests.common.ClientHelper; +import org.mifosplatform.integrationtests.common.GroupHelper; +import org.mifosplatform.integrationtests.common.Utils; +import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder; +import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder; +import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper; + +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.builder.ResponseSpecBuilder; +import com.jayway.restassured.http.ContentType; +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; + +/** + * Group Test for checking Group: + * Creation, Activation, Client Association, Updating & Deletion + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class GroupTest { + + private ResponseSpecification responseSpec; + private RequestSpecification requestSpec; + private LoanTransactionHelper loanTransactionHelper; + + @Before + public void setup() { + Utils.initializeRESTAssured(); + requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + } + + @Test + public void checkGroupLoanCreateAndDisburseFlow() { + Integer clientID = ClientHelper.createClient(requestSpec, responseSpec); + Integer groupID = GroupHelper.createGroup(requestSpec, responseSpec); + GroupHelper.verifyGroupCreatedOnServer(requestSpec, responseSpec, groupID); + + groupID = GroupHelper.activateGroup(requestSpec, responseSpec, groupID.toString()); + GroupHelper.verifyGroupActivatedOnServer(requestSpec, responseSpec, groupID, true); + + groupID = GroupHelper.associateClient(requestSpec, responseSpec, groupID.toString(),clientID.toString()); + GroupHelper.verifyGroupMembers(requestSpec,responseSpec,groupID, clientID); + + groupID = GroupHelper.disAssociateClient(requestSpec, responseSpec, groupID.toString(),clientID.toString()); + GroupHelper.verifyEmptyGroupMembers(requestSpec,responseSpec,groupID); + + String updatedGroupName=GroupHelper.randomNameGenerator("Group-",5); + groupID = GroupHelper.updateGroup(requestSpec, responseSpec, updatedGroupName, groupID.toString()); + GroupHelper.verifyGroupDetails(requestSpec, responseSpec, groupID,"name",updatedGroupName); + + groupID = GroupHelper.createGroup(requestSpec, responseSpec); + groupID = GroupHelper.deleteGroup(requestSpec, responseSpec, groupID.toString()); + GroupHelper.verifyGroupCreatedOnServer(requestSpec, responseSpec, groupID); + + } +} \ No newline at end of file diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java new file mode 100644 index 000000000..4695352e8 --- /dev/null +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java @@ -0,0 +1,167 @@ +package org.mifosplatform.integrationtests.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; +import com.google.gson.Gson; +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; + +public class GroupHelper { + + private static final String CREATE_GROUP_URL = "/mifosng-provider/api/v1/groups?tenantIdentifier=default"; + + public static Integer createGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final boolean active) { + System.out.println("---------------------------------CREATING A GROUP---------------------------------------------"); + return createGroup(requestSpec, responseSpec, "04 March 2011"); + } + + public static Integer createGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final String activationDate) { + System.out.println("---------------------------------CREATING A GROUP---------------------------------------------"); + return Utils.performServerPost(requestSpec, responseSpec, CREATE_GROUP_URL, getTestGroupAsJSON(true,activationDate), "groupId"); + } + + public static Integer createGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) { + System.out.println("---------------------------------CREATING A GROUP---------------------------------------------"); + return Utils.performServerPost(requestSpec, responseSpec, CREATE_GROUP_URL, getTestGroupAsJSON(false,""), "groupId"); + } + + public static Integer associateClient(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final String groupId, final String clientMember) { + String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?command=associateClients&tenantIdentifier=default"; + System.out.println("---------------------------------Associate Client To A GROUP---------------------------------------------"); + return Utils.performServerPost(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, associateClientAsJSON(clientMember), "groupId"); + } + + public static Integer disAssociateClient(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final String groupId, final String clientMember) { + String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?command=disassociateClients&tenantIdentifier=default"; + System.out.println("---------------------------------Disassociate Client To A GROUP---------------------------------------------"); + return Utils.performServerPost(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, associateClientAsJSON(clientMember), "groupId"); + } + + public static Integer activateGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final String groupId) { + String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?command=activate&tenantIdentifier=default"; + System.out.println("---------------------------------Activate A GROUP---------------------------------------------"); + return Utils.performServerPost(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, activateGroupAsJSON(""), "groupId"); + } + + public static Integer updateGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + String name, final String groupId) { + String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?tenantIdentifier=default"; + System.out.println("---------------------------------UPDATE GROUP---------------------------------------------"); + return Utils.performServerPut(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, updateGroupAsJSON(name), "groupId"); + } + + public static Integer deleteGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final String groupId) { + String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?tenantIdentifier=default"; + System.out.println("---------------------------------DELETE GROUP---------------------------------------------"); + return Utils.performServerDelete(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, "groupId"); + } + + + public static String getTestGroupAsJSON(final boolean active, final String activationDate) { + HashMap map = new HashMap(); + map.put("officeId", "1"); + map.put("name", randomNameGenerator("Group_Name_", 5)); + map.put("externalId", randomIDGenerator("ID_", 7)); + if (active==true){ + map.put("active", "true"); + map.put("dateFormat", "dd MMMM yyyy"); + map.put("locale", "en"); + map.put("activationDate", activationDate); + } + else + map.put("active", "false"); + + System.out.println("map : " + map); + return new Gson().toJson(map); + } + + + public static String associateClientAsJSON(final String clientMember) { + HashMap> map = new HashMap>(); + List list = new ArrayList(); + list.add(clientMember); + map.put("clientMembers", list); + System.out.println("map : " + map); + return new Gson().toJson(map); + } + + public static String activateGroupAsJSON(final String activationDate) { + HashMap map = new HashMap(); + map.put("dateFormat", "dd MMMM yyyy"); + map.put("locale", "en"); + if (activationDate != "") + map.put("activationDate", activationDate); + else + map.put("activationDate", "04 March 2011"); + System.out.println("map : " + map); + return new Gson().toJson(map); + } + + public static String updateGroupAsJSON(final String name) { + HashMap map = new HashMap(); + map.put("name", name); + System.out.println("map : " + map); + return new Gson().toJson(map); + } + + public static void verifyGroupCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final Integer generatedGroupID) { + System.out.println("------------------------------CHECK GROUP DETAILS------------------------------------\n"); + String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default"; + Integer responseGroupID = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "id"); + assertEquals("ERROR IN CREATING THE GROUP",generatedGroupID, responseGroupID); + } + + public static void verifyGroupDetails(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final Integer generatedGroupID, final String field, final String expectedValue) { + System.out.println("------------------------------CHECK GROUP DETAILS------------------------------------\n"); + String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default"; + String responseValue = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, field); + assertEquals("ERROR IN CREATING THE GROUP",expectedValue, responseValue); + } + + public static void verifyGroupActivatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final Integer generatedGroupID, final boolean generatedGroupStatus) { + System.out.println("------------------------------CHECK GROUP STATUS------------------------------------\n"); + String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default"; + boolean responseGroupStatus = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "active"); + assertEquals("ERROR IN ACTIVATING THE GROUP",generatedGroupStatus, responseGroupStatus); + } + + public static void verifyGroupMembers(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final Integer generatedGroupID, final Integer groupMember) { + List list = new ArrayList(); + System.out.println("------------------------------CHECK GROUP MEMBERS------------------------------------\n"); + String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?associations=clientMembers&tenantIdentifier=default"; + list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "clientMembers"); + assertTrue("ERROR IN GROUP MEMBER",list.toString().contains("id=" + groupMember.toString())); + } + + public static void verifyEmptyGroupMembers(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final Integer generatedGroupID) { + List list = new ArrayList(); + System.out.println("------------------------------CHECK EMPTY GROUP MEMBER LIST------------------------------------\n"); + String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?associations=clientMembers&tenantIdentifier=default"; + list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "clientMembers"); + System.out.println(list); + assertEquals("GROUP MEMBER LIST NOT EMPTY",list, null); + } + + public static String randomNameGenerator(final String prefix, final int lenOfRandomSuffix) { + return Utils.randomStringGenerator(prefix, lenOfRandomSuffix); + } + + private static String randomIDGenerator(final String prefix, final int lenOfRandomSuffix) { + return Utils.randomStringGenerator(prefix, lenOfRandomSuffix, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/Utils.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/Utils.java index 9cffbaf9c..747fb7045 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/Utils.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/Utils.java @@ -6,8 +6,6 @@ import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import java.util.Random; - import org.apache.commons.lang.StringUtils; import org.apache.http.conn.HttpHostConnectException; @@ -16,13 +14,19 @@ import com.jayway.restassured.path.json.JsonPath; import com.jayway.restassured.specification.RequestSpecification; import com.jayway.restassured.specification.ResponseSpecification; +import java.lang.System; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Random; + @SuppressWarnings("unchecked") public class Utils { private static final String LOGIN_URL = "/mifosng-provider/api/v1/authentication?username=mifos&password=password&tenantIdentifier=default"; + public static void initializeRESTAssured() { - RestAssured.baseURI = "https://localhost"; + RestAssured.baseURI ="https://localhost"; RestAssured.port = 8443; RestAssured.keystore("../keystore.jks", "openmf"); } @@ -33,10 +37,11 @@ public class Utils { String json = RestAssured.post(LOGIN_URL).asString(); assertThat("Failed to login into mifosx platform", StringUtils.isBlank(json), is(false)); return JsonPath.with(json).get("base64EncodedAuthenticationKey"); - } catch (Exception e) { + } + catch (Exception e) { if (e instanceof HttpHostConnectException) { HttpHostConnectException hh = (HttpHostConnectException) e; - fail("Failed to connect to mifosx platform:" + hh.getMessage()); + fail("Failed to connect to mifosx platform:"+hh.getMessage()); } throw new RuntimeException(e); @@ -44,18 +49,52 @@ public class Utils { } public static T performServerGet(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final String getURL, final String jsonAttributeToGetBack) { - String json = given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().get(getURL).andReturn().asString(); + final String getURL, final String jsonAttributeToGetBack) { + String json = given().spec(requestSpec) + .expect().spec(responseSpec).log().ifError() + .when().get(getURL).andReturn().asString(); return (T) from(json).get(jsonAttributeToGetBack); } public static T performServerPost(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final String postURL, final String jsonBodyToSend, final String jsonAttributeToGetBack) { - String json = given().spec(requestSpec).body(jsonBodyToSend).expect().spec(responseSpec).log().ifError().when().post(postURL) + final String postURL, final String jsonBodyToSend, final String jsonAttributeToGetBack) { + String json = given().spec(requestSpec).body(jsonBodyToSend) + .expect().spec(responseSpec).log().ifError() + .when().post(postURL) .andReturn().asString(); return (T) from(json).get(jsonAttributeToGetBack); } + public static T performServerPut(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final String putURL, final String jsonBodyToSend, final String jsonAttributeToGetBack) { + String json = given().spec(requestSpec).body(jsonBodyToSend) + .expect().spec(responseSpec).log().ifError() + .when().put(putURL) + .andReturn().asString(); + return (T) from(json).get(jsonAttributeToGetBack); + } + + public static T performServerDelete(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final String deleteURL, final String jsonAttributeToGetBack) { + String json = given().spec(requestSpec) + .expect().spec(responseSpec).log().ifError() + .when().delete(deleteURL) + .andReturn().asString(); + return (T) from(json).get(jsonAttributeToGetBack); + } + + public static String convertDateToURLFormat(String dateToBeConvert){ + SimpleDateFormat oldFormat = new SimpleDateFormat("dd MMMMMM yyyy"); + SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd"); + String reformattedStr=""; + try { + reformattedStr = newFormat.format(oldFormat.parse(dateToBeConvert)); + } catch (ParseException e) { + e.printStackTrace(); + } + return reformattedStr; + } + public static String randomStringGenerator(final String prefix, final int len, final String sourceSetString) { int lengthOfSource = sourceSetString.length(); Random rnd = new Random(); From c0f780b398eec3d6959822db491b72e3431f3cfe Mon Sep 17 00:00:00 2001 From: Raman Kansal Date: Wed, 31 Jul 2013 10:58:32 +0530 Subject: [PATCH 06/47] Added Group Tests for Creation, Activation, Client Association, Updating & Deletion. --- .../mifosplatform/integrationtests/GroupTest.java | 7 +++---- .../integrationtests/common/GroupHelper.java | 12 +++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java index 3dc206122..877cbbba2 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java @@ -41,7 +41,7 @@ public class GroupTest { } @Test - public void checkGroupLoanCreateAndDisburseFlow() { + public void checkGroupFunctions() { Integer clientID = ClientHelper.createClient(requestSpec, responseSpec); Integer groupID = GroupHelper.createGroup(requestSpec, responseSpec); GroupHelper.verifyGroupCreatedOnServer(requestSpec, responseSpec, groupID); @@ -60,8 +60,7 @@ public class GroupTest { GroupHelper.verifyGroupDetails(requestSpec, responseSpec, groupID,"name",updatedGroupName); groupID = GroupHelper.createGroup(requestSpec, responseSpec); - groupID = GroupHelper.deleteGroup(requestSpec, responseSpec, groupID.toString()); - GroupHelper.verifyGroupCreatedOnServer(requestSpec, responseSpec, groupID); - + Integer groupID2 = GroupHelper.deleteGroup(requestSpec, responseSpec, groupID.toString()); + GroupHelper.verifyGroupDeleted(requestSpec, responseSpec, groupID); } } \ No newline at end of file diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java index 4695352e8..6ba7a0a35 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java @@ -2,6 +2,7 @@ package org.mifosplatform.integrationtests.common; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import java.util.HashMap; import java.util.ArrayList; @@ -153,10 +154,19 @@ public class GroupHelper { System.out.println("------------------------------CHECK EMPTY GROUP MEMBER LIST------------------------------------\n"); String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?associations=clientMembers&tenantIdentifier=default"; list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "clientMembers"); - System.out.println(list); assertEquals("GROUP MEMBER LIST NOT EMPTY",list, null); } + public static void verifyGroupDeleted(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, + final Integer generatedGroupID) { + List list = new ArrayList(); + System.out.println("------------------------------CHECK GROUP DELETED------------------------------------\n"); + String GROUP_URL = "/mifosng-provider/api/v1/groups/?tenantIdentifier=default"; + list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "pageItems"); + + assertFalse("GROUP NOT DELETED",list.toString().contains("id=" + generatedGroupID.toString())); + } + public static String randomNameGenerator(final String prefix, final int lenOfRandomSuffix) { return Utils.randomStringGenerator(prefix, lenOfRandomSuffix); } From 194b00665f7890c49688ade8dad9b537535a18b3 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Wed, 31 Jul 2013 18:33:31 +0530 Subject: [PATCH 07/47] Retrieve writeoff date for writtenoff loan --- .../data/LoanApplicationTimelineData.java | 18 ++++++++++++++++-- .../portfolio/loanaccount/domain/Loan.java | 4 +++- .../service/LoanReadPlatformServiceImpl.java | 6 ++++-- ...nWritePlatformServiceJpaRepositoryImpl.java | 4 ++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApplicationTimelineData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApplicationTimelineData.java index 61fcc6e98..15d922a38 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApplicationTimelineData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanApplicationTimelineData.java @@ -39,6 +39,10 @@ public class LoanApplicationTimelineData { private final String disbursedByFirstname; private final String disbursedByLastname; private final LocalDate expectedMaturityDate; + private final LocalDate writeOffOnDate; + private final String writeOffByUsername; + private final String writeOffByFirstname; + private final String writeOffByLastname; public static LoanApplicationTimelineData templateDefault(final LocalDate expectedDisbursementDate) { @@ -67,12 +71,17 @@ public class LoanApplicationTimelineData { final String closedByFirstname = null; final String closedByLastname = null; final LocalDate expectedMaturityDate = null; + final LocalDate writeOffOnDate = null; + final String writeOffByUsername = null; + final String writeOffByFirstname = null; + final String writeOffByLastname = null; return new LoanApplicationTimelineData(submittedOnDate, submittedByUsername, submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname, withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname, expectedDisbursementDate, actualDisbursementDate, disbursedByUsername, disbursedByFirstname, disbursedByLastname, - closedOnDate, closedByUsername, closedByFirstname, closedByLastname, expectedMaturityDate); + closedOnDate, closedByUsername, closedByFirstname, closedByLastname, expectedMaturityDate, writeOffOnDate, + writeOffByUsername, writeOffByFirstname, writeOffByLastname); } public LoanApplicationTimelineData(final LocalDate submittedOnDate, final String submittedByUsername, @@ -83,7 +92,8 @@ public class LoanApplicationTimelineData { final String approvedByFirstname, final String approvedByLastname, final LocalDate expectedDisbursementDate, final LocalDate actualDisbursementDate, final String disbursedByUsername, final String disbursedByFirstname, final String disbursedByLastname, final LocalDate closedOnDate, final String closedByUsername, final String closedByFirstname, - final String closedByLastname, final LocalDate expectedMaturityDate) { + final String closedByLastname, final LocalDate expectedMaturityDate, final LocalDate writeOffOnDate, + final String writeOffByUsername, final String writeOffByFirstname, final String writeOffByLastname) { this.submittedOnDate = submittedOnDate; this.submittedByUsername = submittedByUsername; this.submittedByFirstname = submittedByFirstname; @@ -106,6 +116,10 @@ public class LoanApplicationTimelineData { this.disbursedByFirstname = disbursedByFirstname; this.disbursedByLastname = disbursedByLastname; this.expectedMaturityDate = expectedMaturityDate; + this.writeOffOnDate = writeOffOnDate; + this.writeOffByUsername = writeOffByUsername; + this.writeOffByFirstname = writeOffByFirstname; + this.writeOffByLastname = writeOffByLastname; } public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData(final CurrencyData currency, final BigDecimal principal, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 0fa3cf8f3..c0a2ce8fc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1815,7 +1815,8 @@ public class Loan extends AbstractPersistable { } public LoanTransaction closeAsWrittenOff(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, - final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds) { + final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds, + final AppUser currentUser) { final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, LoanStatus.fromInt(this.loanStatus)); @@ -1832,6 +1833,7 @@ public class Loan extends AbstractPersistable { this.closedOnDate = writtenOffOnLocalDate.toDate(); this.writtenOffOnDate = writtenOffOnLocalDate.toDate(); + this.closedBy = currentUser; changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); changes.put("writtenOffOnDate", command.stringValueOfParameterNamed("transactionDate")); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 1d749797a..71301f921 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -398,7 +398,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " l.withdrawnon_date as withdrawnOnDate, wbu.username as withdrawnByUsername, wbu.firstname as withdrawnByFirstname, wbu.lastname as withdrawnByLastname," + " l.approvedon_date as approvedOnDate, abu.username as approvedByUsername, abu.firstname as approvedByFirstname, abu.lastname as approvedByLastname," + " l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, dbu.username as disbursedByUsername, dbu.firstname as disbursedByFirstname, dbu.lastname as disbursedByLastname," - + " l.closedon_date as closedOnDate, cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname," + + " l.closedon_date as closedOnDate, cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname, l.writtenoffon_date as writtenOffOnDate, " + " l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate, l.interest_calculated_from_date as interestChargedFromDate, l.expected_maturedon_date as expectedMaturityDate, " + " l.principal_amount as principal, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery," + " l.grace_on_principal_periods as graceOnPrincipalPayment, l.grace_on_interest_periods as graceOnInterestPayment, l.grace_interest_free_periods as graceOnInterestCharged," @@ -538,6 +538,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final String closedByFirstname = rs.getString("closedByFirstname"); final String closedByLastname = rs.getString("closedByLastname"); + final LocalDate writtenOffOnDate = JdbcSupport.getLocalDate(rs, "writtenOffOnDate"); + final LocalDate expectedMaturityDate = JdbcSupport.getLocalDate(rs, "expectedMaturityDate"); final LoanApplicationTimelineData timeline = new LoanApplicationTimelineData(submittedOnDate, submittedByUsername, @@ -545,7 +547,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname, expectedDisbursementDate, actualDisbursementDate, disbursedByUsername, disbursedByFirstname, disbursedByLastname, closedOnDate, closedByUsername, closedByFirstname, closedByLastname, - expectedMaturityDate); + expectedMaturityDate, writtenOffOnDate, closedByUsername, closedByFirstname, closedByLastname); final BigDecimal principal = rs.getBigDecimal("principal"); final BigDecimal totalOverpaid = rs.getBigDecimal("totalOverpaid"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 0376b2258..5e594427d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -607,7 +607,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Transactional @Override public CommandProcessingResult writeOff(final Long loanId, final JsonCommand command) { - context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); this.loanEventApiJsonValidator.validateTransactionWithNoAmount(command.json()); @@ -622,7 +622,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); final LoanTransaction writeoff = loan.closeAsWrittenOff(command, defaultLoanLifecycleStateMachine(), changes, - existingTransactionIds, existingReversedTransactionIds); + existingTransactionIds, existingReversedTransactionIds, currentUser); this.loanTransactionRepository.save(writeoff); this.loanRepository.save(loan); From 55af36dec0a0fc0bd0a25b87e0b70f80897e6f2b Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Thu, 1 Aug 2013 12:23:00 +0530 Subject: [PATCH 08/47] mifosx-538 global search functionality query fix --- .../portfolio/search/service/SearchReadPlatformServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformServiceImpl.java index cd80e94b0..fb6edb452 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformServiceImpl.java @@ -52,7 +52,7 @@ public class SearchReadPlatformServiceImpl implements SearchReadPlatformService public String searchSchema(final SearchConditions searchConditions) { - String union = " union all "; + String union = " union "; String clientExactMatchSql = " (select 'CLIENT' as entityType, c.id as entityId, c.display_name as entityName, c.external_id as entityExternalId, c.account_no as entityAccountNo " + " , c.office_id as parentId, o.name as parentName " + " from m_client c join m_office o on o.id = c.office_id where o.hierarchy like :hierarchy and (c.account_no like :search or c.display_name like :search or c.external_id like :search)) "; From ce93ec677e3868386a933f2f4796c632ce6ccc88 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Tue, 30 Jul 2013 16:13:31 +0530 Subject: [PATCH 09/47] Validating Expected disbursal date for holiday or nonworking day --- .../holiday/domain/HolidayRepository.java | 4 ++-- .../portfolio/loanaccount/domain/Loan.java | 8 ++++++++ ...icationWritePlatformServiceJpaRepositoryImpl.java | 6 ++++-- .../portfolio/loanaccount/service/LoanAssembler.java | 12 +++++++++++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/HolidayRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/HolidayRepository.java index 478b0cc53..11a0b84c7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/HolidayRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/HolidayRepository.java @@ -15,8 +15,8 @@ import org.springframework.data.repository.query.Param; public interface HolidayRepository extends JpaRepository, JpaSpecificationExecutor { - @Query("select holiday from Holiday holiday, IN(holiday.offices) office where holiday.fromDate >= :fromDate and office.id = :officeId") - List findByOfficeIdAndGreaterThanDate(@Param("officeId") Long officeId, @Param("fromDate") Date fromDate); + @Query("select holiday from Holiday holiday, IN(holiday.offices) office where (holiday.fromDate >= :date OR :date <= holiday.toDate) and office.id = :officeId") + List findByOfficeIdAndGreaterThanDate(@Param("officeId") Long officeId, @Param("date") Date date); @Query("from Holiday holiday where holiday.processed = false") List findUnprocessed(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index c0a2ce8fc..b30e73209 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -2511,4 +2511,12 @@ public class Loan extends AbstractPersistable { return this.termPeriodFrequencyType; } + public void validateExpectedDisbursementForHolidayAndNonWorkingDay(WorkingDays workingDays, boolean isHolidayEnabled, + List holidays) { + // validate if disbursement date is a holiday or a non-working day + validateDisbursementDateIsOnNonWorkingDay(workingDays); + validateDisbursementDateIsOnHoliday(isHolidayEnabled, holidays); + + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 2a9024112..b64a0b515 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -224,6 +224,10 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final Map changes = existingLoanApplication.loanApplicationModification(command, possiblyModifedLoanCharges, possiblyModifedLoanCollateralItems, this.aprCalculator); + if (changes.containsKey("expectedDisbursementDate")) { + this.loanAssembler.validateExpectedDisbursementForHolidayAndNonWorkingDay(existingLoanApplication); + } + final String clientIdParamName = "clientId"; if (changes.containsKey(clientIdParamName)) { final Long clientId = command.longValueOfParameterNamed(clientIdParamName); @@ -245,8 +249,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa } validateSubmittedOnDate(existingLoanApplication); - // validate min and maximum constraints - this.fromApiJsonDeserializer.validateForModify(command.json()); final LoanProductRelatedDetail productRelatedDetail = existingLoanApplication.repaymentScheduleDetail(); this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(existingLoanApplication.getTermFrequency(), existingLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(), productRelatedDetail.getRepayEvery(), diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java index cf04003f3..40c1c1e4d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java @@ -22,8 +22,8 @@ import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.staff.domain.StaffRepository; import org.mifosplatform.organisation.staff.exception.StaffNotFoundException; import org.mifosplatform.organisation.staff.exception.StaffRoleException; -import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; +import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.collateral.domain.LoanCollateral; @@ -238,4 +238,14 @@ public class LoanAssembler { } return strategy; } + + public void validateExpectedDisbursementForHolidayAndNonWorkingDay(final Loan loanApplication) { + + final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(), + loanApplication.getExpectedDisbursedOnLocalDate().toDate()); + final WorkingDays workingDays = this.workingDaysRepository.findOne(); + + loanApplication.validateExpectedDisbursementForHolidayAndNonWorkingDay(workingDays,isHolidayEnabled,holidays); + } } \ No newline at end of file From 425f5f382269130b875e4b9ca865fa9286f1026e Mon Sep 17 00:00:00 2001 From: goutham-M Date: Thu, 1 Aug 2013 17:51:39 +0530 Subject: [PATCH 10/47] assign staff for group/center(improvement) --- ...__group_center_assign_staff_permission.sql | 2 ++ .../commands/domain/CommandWrapper.java | 6 +++- .../service/CommandWrapperBuilder.java | 11 ++++++- .../SynchronousCommandProcessingService.java | 4 ++- .../group/api/GroupsApiResource.java | 6 +++- .../portfolio/group/domain/Group.java | 1 + .../AssignGroupStaffCommandHandler.java | 31 +++++++++++++++++++ .../GroupingTypesDataValidator.java | 22 +++++++++++++ .../GroupingTypesWritePlatformService.java | 2 ++ ...WritePlatformServiceJpaRepositoryImpl.java | 28 +++++++++++++++++ 10 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V92__group_center_assign_staff_permission.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/AssignGroupStaffCommandHandler.java diff --git a/mifosng-db/migrations/core_db/V92__group_center_assign_staff_permission.sql b/mifosng-db/migrations/core_db/V92__group_center_assign_staff_permission.sql new file mode 100644 index 000000000..2f8eaa718 --- /dev/null +++ b/mifosng-db/migrations/core_db/V92__group_center_assign_staff_permission.sql @@ -0,0 +1,2 @@ +INSERT INTO `m_permission` (grouping,code,`entity_name`,`action_name`,`can_maker_checker') +values('portfolio_group','ASSIGNSTAFF_GROUP','GROUP','ASSIGNSTAFF',0) \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 483bc88ff..3385bdee5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -413,7 +413,11 @@ public class CommandWrapper { public boolean isUnassignStaff() { return this.actionName.equalsIgnoreCase("UNASSIGNSTAFF") && this.entityName.equalsIgnoreCase("GROUP"); } - + + public boolean isAssignStaff() { + return this.actionName.equalsIgnoreCase("ASSIGNSTAFF"); + } + public String commandName() { return this.actionName + "_" + this.entityName; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 282259cd5..6da48c263 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -990,7 +990,16 @@ public class CommandWrapperBuilder { this.href = "/groups/" + groupId; return this; } - + + public CommandWrapperBuilder assignGroupStaff(final Long groupId) { + this.actionName = "ASSIGNSTAFF"; + this.entityName = "GROUP"; + this.entityId = groupId; + this.groupId = groupId; + this.href = "/groups/" + groupId +"?command=assignStaff"; + return this; + } + public CommandWrapperBuilder createCollateral(final Long loanId) { this.actionName = "CREATE"; this.entityName = "COLLATERAL"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index 80df9e704..f8f3614b2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -441,7 +441,9 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("unassignRoleCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isUpdateGroupRole()) { handler = this.applicationContext.getBean("updateGroupRoleCommandHandler", NewCommandSourceHandler.class); - } else { + } else if (wrapper.isAssignStaff()) { + handler = this.applicationContext.getBean("assignGroupStaffCommandHandler", NewCommandSourceHandler.class); + }else { throw new UnsupportedCommandException(wrapper.commandName()); } } else if (wrapper.isCenterResource()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index 465507b64..6d47d5812 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -222,7 +222,7 @@ public class GroupsApiResource { return this.toApiJsonSerializer.serialize(result); } - + @PUT @Path("{groupId}") @Consumes({ MediaType.APPLICATION_JSON }) @@ -287,6 +287,10 @@ public class GroupsApiResource { final CommandWrapper commandRequest = builder.unassignGroupStaff(groupId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return this.toApiJsonSerializer.serialize(result); + } else if (is(commandParam, "assignStaff")) { + final CommandWrapper commandRequest = builder.assignGroupStaff(groupId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); } else if (is(commandParam, "assignRole")) { final CommandWrapper commandRequest = builder.assignRole(groupId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java index 5270fedcf..79f6d0bd0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java @@ -380,4 +380,5 @@ public final class Group extends AbstractPersistable { public Office getOffice() { return this.office; } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/AssignGroupStaffCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/AssignGroupStaffCommandHandler.java new file mode 100644 index 000000000..1896e641b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/AssignGroupStaffCommandHandler.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.group.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.group.service.GroupingTypesWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class AssignGroupStaffCommandHandler implements NewCommandSourceHandler { + + private final GroupingTypesWritePlatformService groupWritePlatformService; + + @Autowired + public AssignGroupStaffCommandHandler(final GroupingTypesWritePlatformService groupWritePlatformService) { + this.groupWritePlatformService = groupWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.groupWritePlatformService.assignGroupOrCenterStaff(command.entityId(), command); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java index bd0b242c5..60813c17a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java @@ -345,6 +345,28 @@ public final class GroupingTypesDataValidator { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + public void validateForAssignStaff(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + + final Set supportedParametersAssignStaff = new HashSet(Arrays.asList(GroupingTypesApiConstants.staffIdParamName)); + + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersAssignStaff); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(GroupingTypesApiConstants.GROUP_RESOURCE_NAME); + + final String staffIdParameterName = GroupingTypesApiConstants.staffIdParamName; + final Long staffId = this.fromApiJsonHelper.extractLongNamed(staffIdParameterName, element); + baseDataValidator.reset().parameter(staffIdParameterName).value(staffId).notNull().longGreaterThanZero(); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + public void validateForAssociateClients(final String json){ if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformService.java index 351f4a3d7..928379deb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformService.java @@ -24,6 +24,8 @@ public interface GroupingTypesWritePlatformService { CommandProcessingResult unassignGroupOrCenterStaff(Long groupId, JsonCommand command); + CommandProcessingResult assignGroupOrCenterStaff(Long groupId, JsonCommand command); + CommandProcessingResult associateClientsToGroup(Long groupId, JsonCommand command); CommandProcessingResult disassociateClientsFromGroup(Long groupId, JsonCommand command); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java index 2dd5288d2..e5b2ce415 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java @@ -350,7 +350,35 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group .build(); } + + @Override + public CommandProcessingResult assignGroupOrCenterStaff(final Long grouptId, final JsonCommand command) { + this.context.authenticatedUser(); + + final Map actualChanges = new LinkedHashMap(5); + + this.fromApiJsonDeserializer.validateForAssignStaff(command.json()); + + final Group groupForUpdate = this.groupRepository.findOneWithNotFoundDetection(grouptId); + + Staff staff = null; + final Long staffId = command.longValueOfParameterNamed(GroupingTypesApiConstants.staffIdParamName); + staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, groupForUpdate.getOffice().getHierarchy()); + groupForUpdate.updateStaff(staff); + + this.groupRepository.saveAndFlush(groupForUpdate); + + actualChanges.put(GroupingTypesApiConstants.staffIdParamName, staffId); + + return new CommandProcessingResultBuilder() // + .withOfficeId(groupForUpdate.officeId()) // + .withEntityId(groupForUpdate.getId()) // + .withGroupId(grouptId) // + .with(actualChanges) // + .build(); + } + @Transactional @Override public CommandProcessingResult deleteGroup(final Long groupId) { From 0da7dabf0cb8865d1e38c6b543752b034c472992 Mon Sep 17 00:00:00 2001 From: John Woodlock Date: Thu, 1 Aug 2013 14:33:37 +0100 Subject: [PATCH 11/47] mifosx-567 ensure duplicate externalId for staff and loan general valid json error response --- .../StaffWritePlatformServiceJpaRepositoryImpl.java | 2 +- ...licationWritePlatformServiceJpaRepositoryImpl.java | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffWritePlatformServiceJpaRepositoryImpl.java index 0524c6f05..ad66f77c6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffWritePlatformServiceJpaRepositoryImpl.java @@ -98,7 +98,7 @@ public class StaffWritePlatformServiceJpaRepositoryImpl implements StaffWritePla } if (!changesOnly.isEmpty()) { - this.staffRepository.save(staffForUpdate); + this.staffRepository.saveAndFlush(staffForUpdate); } return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(staffId) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index b64a0b515..b4842467a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -140,6 +140,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa @Override public CommandProcessingResult submitApplication(final JsonCommand command) { + try { final AppUser currentUser = context.authenticatedUser(); this.fromApiJsonDeserializer.validateForCreate(command.json()); @@ -201,8 +202,12 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa .withGroupId(newLoanApplication.getGroupId()) // .withLoanId(newLoanApplication.getId()) // .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve); + return CommandProcessingResult.empty(); + } } - + @Transactional @Override public CommandProcessingResult modifyApplication(final Long loanId, final JsonCommand command) { @@ -379,8 +384,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa logAsErrorUnexpectedDataIntegrityException(dve); throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); - } - + } + private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) { logger.error(dve.getMessage(), dve); } From b6a164c8c2287c767ba0d73fc08dbdbefb8936f9 Mon Sep 17 00:00:00 2001 From: John Woodlock Date: Thu, 1 Aug 2013 19:15:28 +0100 Subject: [PATCH 12/47] mifosx-567 allow post and put for externalId for loanproduct --- ...__group_center_assign_staff_permission.sql | 4 +-- .../organisation/staff/domain/Staff.java | 3 ++- .../api/LoanProductsApiResource.java | 2 +- .../loanproduct/data/LoanProductData.java | 14 ++++++++--- .../loanproduct/domain/LoanProduct.java | 25 ++++++++++++++++--- .../LoanProductDataValidator.java | 2 +- .../LoanProductReadPlatformServiceImpl.java | 5 ++-- ...WritePlatformServiceJpaRepositoryImpl.java | 8 +++++- 8 files changed, 48 insertions(+), 15 deletions(-) diff --git a/mifosng-db/migrations/core_db/V92__group_center_assign_staff_permission.sql b/mifosng-db/migrations/core_db/V92__group_center_assign_staff_permission.sql index 2f8eaa718..d47106776 100644 --- a/mifosng-db/migrations/core_db/V92__group_center_assign_staff_permission.sql +++ b/mifosng-db/migrations/core_db/V92__group_center_assign_staff_permission.sql @@ -1,2 +1,2 @@ -INSERT INTO `m_permission` (grouping,code,`entity_name`,`action_name`,`can_maker_checker') -values('portfolio_group','ASSIGNSTAFF_GROUP','GROUP','ASSIGNSTAFF',0) \ No newline at end of file +INSERT INTO `m_permission` (grouping,code,`entity_name`,`action_name`,`can_maker_checker`) +values('portfolio_group','ASSIGNSTAFF_GROUP','GROUP','ASSIGNSTAFF',0); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java index bc07e68d3..d23c69a45 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java @@ -22,7 +22,8 @@ import org.mifosplatform.organisation.office.domain.Office; import org.springframework.data.jpa.domain.AbstractPersistable; @Entity -@Table(name = "m_staff", uniqueConstraints = { @UniqueConstraint(columnNames = { "display_name" }, name = "display_name") }) +@Table(name = "m_staff", uniqueConstraints = { @UniqueConstraint(columnNames = { "display_name" }, name = "display_name") , +@UniqueConstraint(columnNames = { "external_id" }, name = "external_id_UNIQUE") }) public class Staff extends AbstractPersistable { @Column(name = "firstname", length = 50) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java index 6231b8454..167fc2c69 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java @@ -69,7 +69,7 @@ public class LoanProductsApiResource { "graceOnInterestPayment", "graceOnInterestCharged", "interestRatePerPeriod", "minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "annualInterestRate", "amortizationType", "interestType", "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName", - "charges", "accountingRule", "accountingMappings", "paymentChannelToFundSourceMappings", "fundOptions", "paymentTypeOptions", + "charges", "accountingRule", "externalId", "accountingMappings", "paymentChannelToFundSourceMappings", "fundOptions", "paymentTypeOptions", "currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "accountingRuleOptions", "accountingMappingOptions")); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java index c1d28c0a5..57380472a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java @@ -42,6 +42,7 @@ public class LoanProductData { private final LocalDate startDate; private final LocalDate closeDate; private final String status; + private final String externalId; // terms private final CurrencyData currency; private final BigDecimal principal; @@ -135,12 +136,13 @@ public class LoanProductData { final LocalDate startDate = null; final LocalDate closeDate = null; final String status = null; + final String externalId = null; return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId, transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, - accountingType, includeInBorrowerCycle, startDate, closeDate, status); + accountingType, includeInBorrowerCycle, startDate, closeDate, status, externalId); } public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) { @@ -176,13 +178,14 @@ public class LoanProductData { final LocalDate startDate = null; final LocalDate closeDate = null; final String status = null; + final String externalId = null; return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId, transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, - accountingType, includeInBorrowerCycle, startDate, closeDate, status); + accountingType, includeInBorrowerCycle, startDate, closeDate, status, externalId); } public static LoanProductData sensibleDefaultsForNewLoanProductCreation() { @@ -224,13 +227,14 @@ public class LoanProductData { final LocalDate startDate = null; final LocalDate closeDate = null; final String status = null; + final String externalId = null; return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId, transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, - accountingType, includeInBorrowerCycle, startDate, closeDate, status); + accountingType, includeInBorrowerCycle, startDate, closeDate, status, externalId); } public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map accountingMappings, @@ -254,7 +258,7 @@ public class LoanProductData { final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName, final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final Collection charges, final EnumOptionData accountingType, final boolean includeInBorrowerCycle, - final LocalDate startDate, final LocalDate closeDate, final String status) { + final LocalDate startDate, final LocalDate closeDate, final String status, final String externalId) { this.id = id; this.name = name; this.description = description; @@ -289,6 +293,7 @@ public class LoanProductData { this.startDate = startDate; this.closeDate = closeDate; this.status = status; + this.externalId = externalId; this.chargeOptions = null; this.penaltyOptions = null; @@ -343,6 +348,7 @@ public class LoanProductData { this.startDate = productData.startDate; this.closeDate = productData.closeDate; this.status = productData.status; + this.externalId = productData.externalId; this.charges = nullIfEmpty(productData.charges()); this.accountingRule = productData.accountingRule; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index 2d4164b1e..ec000bbea 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -47,7 +47,8 @@ import com.google.gson.JsonArray; * They allow for constraints to be added at product level. */ @Entity -@Table(name = "m_product_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name") }) +@Table(name = "m_product_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name"), +@UniqueConstraint(columnNames = { "external_id" }, name = "external_id_UNIQUE") }) public class LoanProduct extends AbstractPersistable { @ManyToOne @@ -87,6 +88,9 @@ public class LoanProduct extends AbstractPersistable { @Column(name = "close_date", nullable = true) @Temporal(TemporalType.DATE) private Date closeDate; + + @Column(name = "external_id", length = 100, nullable = true, unique = true) + private String externalId; public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy, final List productCharges, final JsonCommand command, final AprCalculator aprCalculator) { @@ -130,12 +134,13 @@ public class LoanProduct extends AbstractPersistable { final LocalDate startDate = command.localDateValueOfParameterNamed("startDate"); final LocalDate closeDate = command.localDateValueOfParameterNamed("closeDate"); + final String externalId = command.stringValueOfParameterNamed("externalId"); return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, - amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate); + amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate, externalId); } protected LoanProduct() { @@ -155,7 +160,7 @@ public class LoanProduct extends AbstractPersistable { final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List charges, final AccountingRuleType accountingRuleType, final boolean includeInBorrowerCycle, final LocalDate startDate, - final LocalDate closeDate) { + final LocalDate closeDate, final String externalId) { this.fund = fund; this.transactionProcessingStrategy = transactionProcessingStrategy; this.name = name.trim(); @@ -192,6 +197,8 @@ public class LoanProduct extends AbstractPersistable { if (closeDate != null) { this.closeDate = closeDate.toDateMidnight().toDate(); } + + this.externalId = externalId; } public MonetaryCurrency getCurrency() { @@ -329,6 +336,14 @@ public class LoanProduct extends AbstractPersistable { this.closeDate = null; } } + + final String externalIdTypeParamName = "externalId"; + if (command.isChangeInStringParameterNamed(externalIdTypeParamName, this.externalId)) { + final String newValue = command.stringValueOfParameterNamed(externalIdTypeParamName); + actualChanges.put(accountingTypeParamName, newValue); + this.externalId = newValue; + } + return actualChanges; } @@ -412,5 +427,9 @@ public class LoanProduct extends AbstractPersistable { public String productName() { return this.name; } + + public String getExternalId() { + return externalId; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java index d57fd8b16..3f0177aba 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -43,7 +43,7 @@ public final class LoanProductDataValidator { "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod", "minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment", - "graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule","includeInBorrowerCycle", "startDate", "closeDate", + "graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule","includeInBorrowerCycle", "startDate", "closeDate", "externalId", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index 5ba844d04..7610b6db1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -106,7 +106,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo + "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged," + "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, " + "lp.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate, " - + "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol " + + "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId " + " from m_product_loan lp " + " left join m_fund f on f.id = lp.fund_id" + " left join ref_loan_transaction_processing_strategy ltps on ltps.id = lp.loan_transaction_strategy_id" @@ -180,13 +180,14 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo } else { status = "loanProduct.active"; } + final String externalId = rs.getString("externalId"); return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionStrategyId, transactionStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, - graceOnInterestCharged, this.charges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate, status); + graceOnInterestCharged, this.charges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate, status, externalId); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java index a0709ed0e..197ab221b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java @@ -226,7 +226,13 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { Throwable realCause = dve.getMostSpecificCause(); - if (realCause.getMessage().contains("unq_name")) { + + if (realCause.getMessage().contains("external_id")) { + + final String externalId = command.stringValueOfParameterNamed("externalId"); + throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.externalId", "Loan Product with externalId `" + externalId + + "` already exists", "externalId", externalId); + } else if (realCause.getMessage().contains("unq_name")) { final String name = command.stringValueOfParameterNamed("name"); throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.name", "Loan product with name `" + name From b3f11233cd2245d61f8e051dd6f80efede4dc77a Mon Sep 17 00:00:00 2001 From: John Woodlock Date: Fri, 2 Aug 2013 20:03:48 +0100 Subject: [PATCH 13/47] mifosx-567 add support for externalId for loan transactions --- .../V93__loan_transaction_external_id.sql | 3 ++ .../api/LoanTransactionsApiResource.java | 2 +- .../loanaccount/data/LoanTransactionData.java | 10 +++-- .../portfolio/loanaccount/domain/Loan.java | 19 +++++---- .../loanaccount/domain/LoanTransaction.java | 42 ++++++++++++------- .../LoanEventApiJsonValidator.java | 6 +-- .../service/LoanReadPlatformServiceImpl.java | 13 +++--- ...WritePlatformServiceJpaRepositoryImpl.java | 12 +++--- 8 files changed, 64 insertions(+), 43 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V93__loan_transaction_external_id.sql diff --git a/mifosng-db/migrations/core_db/V93__loan_transaction_external_id.sql b/mifosng-db/migrations/core_db/V93__loan_transaction_external_id.sql new file mode 100644 index 000000000..1e5cfdfc6 --- /dev/null +++ b/mifosng-db/migrations/core_db/V93__loan_transaction_external_id.sql @@ -0,0 +1,3 @@ +ALTER TABLE m_loan_transaction +ADD COLUMN `external_id` VARCHAR(100) NULL DEFAULT NULL AFTER `is_reversed` , +ADD UNIQUE INDEX `external_id_UNIQUE` (`external_id` ASC) ; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java index 0c59ccc06..5a487002d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java @@ -45,7 +45,7 @@ import org.springframework.stereotype.Component; @Scope("singleton") public class LoanTransactionsApiResource { - private final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "type", "date", "currency", "amount")); + private final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "type", "date", "currency", "amount", "externalId")); private final String resourceNameForPermissions = "LOAN"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java index a79bb13d9..f7d69538c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java @@ -32,6 +32,7 @@ public class LoanTransactionData { private final BigDecimal interestPortion; private final BigDecimal feeChargesPortion; private final BigDecimal penaltyChargesPortion; + private final String externalId; // templates final Collection paymentTypeOptions; @@ -40,20 +41,20 @@ public class LoanTransactionData { return new LoanTransactionData(loanTransactionData.id, loanTransactionData.type, loanTransactionData.paymentDetailData, loanTransactionData.currency, loanTransactionData.date, loanTransactionData.amount, loanTransactionData.principalPortion, loanTransactionData.interestPortion, loanTransactionData.feeChargesPortion, loanTransactionData.penaltyChargesPortion, - paymentTypeOptions); + paymentTypeOptions, loanTransactionData.externalId); } public LoanTransactionData(final Long id, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion, - final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion) { + final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final String externalId) { this(id, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion, feeChargesPortion, - penaltyChargesPortion, null); + penaltyChargesPortion, null, externalId); } public LoanTransactionData(final Long id, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, - final Collection paymentTypeOptions) { + final Collection paymentTypeOptions, final String externalId) { this.id = id; this.type = transactionType; this.paymentDetailData = paymentDetailData; @@ -65,6 +66,7 @@ public class LoanTransactionData { this.feeChargesPortion = feeChargesPortion; this.penaltyChargesPortion = penaltyChargesPortion; this.paymentTypeOptions = paymentTypeOptions; + this.externalId = externalId; } public LocalDate dateOf() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index b30e73209..e94d31b05 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1331,7 +1331,8 @@ public class Loan extends AbstractPersistable { actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); - + final String txnExternalId = command.stringValueOfParameterNamed("externalId"); + this.actualDisbursementDate = actualDisbursementDate.toDate(); this.disbursedBy = currentUser; updateLoanScheduleDependentDerivedFields(); @@ -1344,7 +1345,7 @@ public class Loan extends AbstractPersistable { validateDisbursementDateIsOnNonWorkingDay(workingDays); validateDisbursementDateIsOnHoliday(isHolidayEnabled, holidays); - handleDisbursementTransaction(paymentDetail, actualDisbursementDate); + handleDisbursementTransaction(paymentDetail, actualDisbursementDate, txnExternalId); if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) { regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays); @@ -1394,10 +1395,10 @@ public class Loan extends AbstractPersistable { updateLoanSchedule(loanSchedule); } - private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn) { + private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn, final String txnExternalId) { // track disbursement transaction final LoanTransaction disbursementTransaction = LoanTransaction.disbursement(this.loanRepaymentScheduleDetail.getPrincipal(), - paymentDetail, disbursedOn); + paymentDetail, disbursedOn, txnExternalId); disbursementTransaction.updateLoan(this); this.loanTransactions.add(disbursementTransaction); @@ -1411,7 +1412,7 @@ public class Loan extends AbstractPersistable { final Money totalFeeChargesDueAtDisbursement = this.summary.getTotalFeeChargesDueAtDisbursement(loanCurrency()); if (totalFeeChargesDueAtDisbursement.isGreaterThanZero()) { - LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(totalFeeChargesDueAtDisbursement, null, disbursedOn); + LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(totalFeeChargesDueAtDisbursement, null, disbursedOn, txnExternalId); Money zero = Money.zero(getCurrency()); chargesPayment.updateComponents(zero, zero, totalFeeChargesDueAtDisbursement, zero); chargesPayment.updateLoan(this); @@ -1830,6 +1831,7 @@ public class Loan extends AbstractPersistable { existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed("transactionDate"); + final String txnExternalId = command.stringValueOfParameterNamed("externalId"); this.closedOnDate = writtenOffOnLocalDate.toDate(); this.writtenOffOnDate = writtenOffOnLocalDate.toDate(); @@ -1849,7 +1851,7 @@ public class Loan extends AbstractPersistable { throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.a.future.date", errorMessage, writtenOffOnLocalDate); } - loanTransaction = LoanTransaction.writeoff(this, writtenOffOnLocalDate); + loanTransaction = LoanTransaction.writeoff(this, writtenOffOnLocalDate, txnExternalId); final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, loanTransactions); if (!isLastTransaction) { final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions."; @@ -1876,7 +1878,8 @@ public class Loan extends AbstractPersistable { existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); final LocalDate closureDate = command.localDateValueOfParameterNamed("transactionDate"); - + final String txnExternalId = command.stringValueOfParameterNamed("externalId"); + this.closedOnDate = closureDate.toDate(); changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); @@ -1905,7 +1908,7 @@ public class Loan extends AbstractPersistable { } this.closedOnDate = closureDate.toDate(); - loanTransaction = LoanTransaction.writeoff(this, closureDate); + loanTransaction = LoanTransaction.writeoff(this, closureDate, txnExternalId); boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, loanTransactions); if (!isLastTransaction) { final String errorMessage = "The closing date of the loan must be on or after latest transaction date."; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java index 56b549ea0..c39b04fa1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java @@ -23,6 +23,7 @@ import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.persistence.UniqueConstraint; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; @@ -42,7 +43,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable; * Disbursements, Repayments, Waivers, Write-off etc */ @Entity -@Table(name = "m_loan_transaction") +@Table(name = "m_loan_transaction", uniqueConstraints = {@UniqueConstraint(columnNames = { "external_id" }, name = "external_id_UNIQUE") }) public final class LoanTransaction extends AbstractPersistable { @ManyToOne(optional = false) @@ -78,6 +79,9 @@ public final class LoanTransaction extends AbstractPersistable { @Column(name = "is_reversed", nullable = false) private boolean reversed; + @Column(name = "external_id", length = 100, nullable = true, unique = true) + private String externalId; + @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction", orphanRemoval = true) private Set loanChargesPaid = new HashSet(); @@ -88,24 +92,24 @@ public final class LoanTransaction extends AbstractPersistable { this.typeOf = null; } - public static LoanTransaction disbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate disbursementDate) { - return new LoanTransaction(null, LoanTransactionType.DISBURSEMENT, paymentDetail, amount.getAmount(), disbursementDate); + public static LoanTransaction disbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate disbursementDate, final String externalId) { + return new LoanTransaction(null, LoanTransactionType.DISBURSEMENT, paymentDetail, amount.getAmount(), disbursementDate, externalId); } - public static LoanTransaction repayment(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate) { - return new LoanTransaction(null, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate); + public static LoanTransaction repayment(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId) { + return new LoanTransaction(null, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId); } - public static LoanTransaction repaymentAtDisbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate) { - return new LoanTransaction(null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT, paymentDetail, amount.getAmount(), paymentDate); + public static LoanTransaction repaymentAtDisbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId) { + return new LoanTransaction(null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT, paymentDetail, amount.getAmount(), paymentDate, externalId); } public static LoanTransaction waiver(final Loan loan, final Money waived, final LocalDate waiveDate) { - return new LoanTransaction(loan, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), waiveDate); + return new LoanTransaction(loan, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), waiveDate, null); } public static LoanTransaction applyInterest(final Loan loan, final Money amount, final LocalDate interestAppliedDate) { - return new LoanTransaction(loan, LoanTransactionType.APPLY_INTEREST, amount.getAmount(), interestAppliedDate); + return new LoanTransaction(loan, LoanTransactionType.APPLY_INTEREST, amount.getAmount(), interestAppliedDate, null); } public static LoanTransaction copyTransactionProperties(LoanTransaction loanTransaction) { @@ -116,7 +120,7 @@ public final class LoanTransaction extends AbstractPersistable { public static LoanTransaction applyLoanCharge(final Loan loan, final Money amount, final LocalDate applyDate, final Money feeCharges, final Money penaltyCharges) { - LoanTransaction applyCharge = new LoanTransaction(loan, LoanTransactionType.APPLY_CHARGES, amount.getAmount(), applyDate); + LoanTransaction applyCharge = new LoanTransaction(loan, LoanTransactionType.APPLY_CHARGES, amount.getAmount(), applyDate, null); applyCharge.updateChargesComponents(feeCharges, penaltyCharges); return applyCharge; } @@ -149,30 +153,32 @@ public final class LoanTransaction extends AbstractPersistable { public static LoanTransaction waiveLoanCharge(final Loan loan, final Money waived, final LocalDate waiveDate, final Money feeChargesWaived, final Money penaltyChargesWaived) { - LoanTransaction waiver = new LoanTransaction(loan, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate); + LoanTransaction waiver = new LoanTransaction(loan, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate, null); waiver.updateChargesComponents(feeChargesWaived, penaltyChargesWaived); return waiver; } - public static LoanTransaction writeoff(final Loan loan, final LocalDate writeOffDate) { - return new LoanTransaction(loan, LoanTransactionType.WRITEOFF, null, writeOffDate); + public static LoanTransaction writeoff(final Loan loan, final LocalDate writeOffDate, final String externalId) { + return new LoanTransaction(loan, LoanTransactionType.WRITEOFF, null, writeOffDate, externalId); } - private LoanTransaction(final Loan loan, final LoanTransactionType type, final BigDecimal amount, final LocalDate date) { + private LoanTransaction(final Loan loan, final LoanTransactionType type, final BigDecimal amount, final LocalDate date, final String externalId) { this.loan = loan; this.typeOf = type.getValue(); this.amount = amount; this.dateOf = date.toDateMidnight().toDate(); + this.externalId = externalId; } private LoanTransaction(final Loan loan, final LoanTransactionType type, final PaymentDetail paymentDetail, final BigDecimal amount, - final LocalDate date) { + final LocalDate date, final String externalId) { this.loan = loan; this.typeOf = type.getValue(); this.paymentDetail = paymentDetail; this.amount = amount; this.dateOf = date.toDateMidnight().toDate(); + this.externalId = externalId; } public void reverse() { @@ -353,7 +359,7 @@ public final class LoanTransaction extends AbstractPersistable { paymentDetailData = paymentDetail.toData(); } return new LoanTransactionData(this.getId(), transactionType, paymentDetailData, currencyData, getTransactionDate(), this.amount, - this.principalPortion, this.interestPortion, this.feeChargesPortion, this.penaltyChargesPortion); + this.principalPortion, this.interestPortion, this.feeChargesPortion, this.penaltyChargesPortion, this.externalId); } public Map toMapData(final CurrencyData currencyData) { @@ -405,4 +411,8 @@ public final class LoanTransaction extends AbstractPersistable { this.loanChargesPaid = loanChargesPaid; } + public String getExternalId() { + return externalId; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index 65d983a2b..a5a9149fd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -50,7 +50,7 @@ public final class LoanEventApiJsonValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } - final Set disbursementParameters = new HashSet(Arrays.asList("actualDisbursementDate", "note", "locale", + final Set disbursementParameters = new HashSet(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber")); final Type typeOfMap = new TypeToken>() {}.getType(); @@ -92,7 +92,7 @@ public final class LoanEventApiJsonValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } - final Set transactionParameters = new HashSet(Arrays.asList("transactionDate", "transactionAmount", "note", + final Set transactionParameters = new HashSet(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note", "locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber")); final Type typeOfMap = new TypeToken>() {}.getType(); @@ -119,7 +119,7 @@ public final class LoanEventApiJsonValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } - final Set transactionParameters = new HashSet(Arrays.asList("transactionDate", "transactionAmount", "note", + final Set transactionParameters = new HashSet(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note", "locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber")); final Type typeOfMap = new TypeToken>() {}.getType(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 71301f921..11e15721a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -315,7 +315,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Collection paymentOptions = codeValueReadPlatformService .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); return new LoanTransactionData(null, transactionType, null, currencyData, earliestUnpaidInstallmentDate, - possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions); + possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions, null); } @Override @@ -339,7 +339,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal amount = waiveOfInterest.getAmount(currency).getAmount(); return new LoanTransactionData(null, transactionType, null, currencyData, waiveOfInterest.getTransactionDate(), amount, null, null, - null, null); + null, null, null); } @Override @@ -348,7 +348,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { context.authenticatedUser(); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); - return new LoanTransactionData(null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, null, null); + return new LoanTransactionData(null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, null, null, null); } @Override @@ -359,7 +359,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Collection paymentOptions = codeValueReadPlatformService .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); return new LoanTransactionData(null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDate(), null, null, null, null, - null, paymentOptions); + null, paymentOptions, null); } @Override @@ -844,7 +844,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, " + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, " + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " - + " cv.code_value as paymentTypeName " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id" + + " cv.code_value as paymentTypeName, tr.external_id as externalId " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id" + " join m_currency rc on rc.`code` = l.currency_code " + " left JOIN m_payment_detail pd ON tr.payment_detail_id = pd.id" + " left join m_code_value cv on pd.payment_type_cv_id = cv.id"; @@ -887,9 +887,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal interestPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interest"); final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees"); final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties"); + final String externalId = rs.getString("externalId"); return new LoanTransactionData(id, transactionType, paymentDetailData, currencyData, date, totalAmount, principalPortion, - interestPortion, feeChargesPortion, penaltyChargesPortion); + interestPortion, feeChargesPortion, penaltyChargesPortion, externalId); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 5e594427d..c8ea47a3d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -349,6 +349,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); + final String txnExternalId = command.stringValueOfParameterNamed("externalId"); final Map changes = new LinkedHashMap(); changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); @@ -364,7 +365,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); CommandProcessingResultBuilder commandProcessingResultBuilder = saveLoanRepayment(loanId, paymentDetail, transactionAmount, - transactionDate, noteText); + transactionDate, noteText, txnExternalId); return commandProcessingResultBuilder.withCommandId(command.commandId()) // .withLoanId(loanId) // @@ -373,7 +374,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } private CommandProcessingResultBuilder saveLoanRepayment(final Long loanId, final PaymentDetail paymentDetail, - final BigDecimal transactionAmount, final LocalDate transactionDate, final String noteText) { + final BigDecimal transactionAmount, final LocalDate transactionDate, final String noteText, final String txnExternalId) { final Loan loan = retrieveLoanBy(loanId); // TODO: Is it required to validate transaction date with meeting dates @@ -393,7 +394,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingReversedTransactionIds = new ArrayList(); final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount); - final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, paymentDetail, transactionDate); + final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, paymentDetail, transactionDate, txnExternalId); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); @@ -448,7 +449,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf ***/ PaymentDetail paymentDetail = null; saveLoanRepayment(singleLoanRepaymentCommand.getLoanId(), paymentDetail, singleLoanRepaymentCommand.getTransactionAmount(), - bulkRepaymentCommand.getTransactionDate(), bulkRepaymentCommand.getNote()); + bulkRepaymentCommand.getTransactionDate(), bulkRepaymentCommand.getNote(), null); changes.put("bulkTransations", singleLoanRepaymentCommand); } return changes; @@ -469,6 +470,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); + final String txnExternalId = command.stringValueOfParameterNamed("externalId"); final Map changes = new LinkedHashMap(); changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); @@ -481,7 +483,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount); PaymentDetail paymentDetail = paymentDetailWritePlatformService.createPaymentDetail(command, changes); - LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate); + LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate, txnExternalId); if (transactionToAdjust.isInterestWaiver()) { newTransactionDetail = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate); } From e2a76bbd0e3ba673ab6229f66df7bca96c01771f Mon Sep 17 00:00:00 2001 From: Raman Kansal Date: Fri, 2 Aug 2013 23:18:57 +0530 Subject: [PATCH 14/47] Raman - Fixed defect for java 1.6. --- .../org/mifosplatform/integrationtests/common/GroupHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java index 6ba7a0a35..374aa9779 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java @@ -4,6 +4,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; +import java.lang.Boolean; import java.util.HashMap; import java.util.ArrayList; import java.util.List; @@ -135,7 +136,7 @@ public class GroupHelper { final Integer generatedGroupID, final boolean generatedGroupStatus) { System.out.println("------------------------------CHECK GROUP STATUS------------------------------------\n"); String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default"; - boolean responseGroupStatus = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "active"); + Boolean responseGroupStatus = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "active"); assertEquals("ERROR IN ACTIVATING THE GROUP",generatedGroupStatus, responseGroupStatus); } From 9d69ed11a9cbd939bae00f33c7a5b6a26af42ad0 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Sat, 3 Aug 2013 19:21:41 +0530 Subject: [PATCH 15/47] Null checks for collection sheet get data --- .../CollectionSheetReadPlatformServiceImpl.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java index 60e57cf00..d17ecb7fb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java @@ -101,8 +101,10 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe for (final JLGCollectionSheetFlatData collectionSheetFlatData : jlgCollectionSheetFlatData) { - loanProducts.add(LoanProductData.lookupWithCurrency(collectionSheetFlatData.getProductId(), + if(collectionSheetFlatData.getProductId() !=null){ + loanProducts.add(LoanProductData.lookupWithCurrency(collectionSheetFlatData.getProductId(), collectionSheetFlatData.getProductShortName(), collectionSheetFlatData.getCurrency())); + } corrCollectioSheetFlatData = collectionSheetFlatData; if (firstTime || collectionSheetFlatData.getGroupId().equals(prevGroupId)) { @@ -239,8 +241,11 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + CurrencyData currencyData = null; + if(currencyCode != null){ + currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, + currencyNameCode); + } final BigDecimal disbursementAmount = rs.getBigDecimal("disbursementAmount"); final BigDecimal principalDue = rs.getBigDecimal("principalDue"); From d193fcf35f9c36d4ec82c0380102a07bfcb3f4f4 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Mon, 5 Aug 2013 11:24:59 +0530 Subject: [PATCH 16/47] group retrive accountdetails API changes --- .../StaffAccountSummaryCollectionData.java | 47 +---- .../data/AccountSummaryCollectionData.java | 57 ++++++ .../data/LoanAccountSummaryData.java} | 8 +- .../data/SavingsAccountSummaryData.java} | 8 +- .../accountdetails/domain/AccountType.java | 81 ++++++++ .../AccountDetailsReadPlatformService.java | 18 ++ ...sReadPlatformServiceJpaRepositoryImpl.java | 178 ++++++++++++++++++ .../service/AccountEnumerations.java | 38 ++++ .../client/api/ClientsApiResource.java | 14 +- .../ClientAccountSummaryCollectionData.java | 33 ---- .../service/ClientReadPlatformService.java | 8 +- .../ClientReadPlatformServiceImpl.java | 131 +------------ .../group/api/CentersApiResource.java | 43 ++--- .../group/api/GroupsApiResource.java | 22 ++- .../GroupAccountSummaryCollectionData.java | 101 ---------- .../group/data/GroupAccountSummaryData.java | 50 ----- .../service/GroupReadPlatformService.java | 9 - .../service/GroupReadPlatformServiceImpl.java | 119 ------------ .../portfolio/loanaccount/domain/Loan.java | 3 +- .../loanaccount/domain/LoanType.java | 81 -------- ...anApplicationCommandFromApiJsonHelper.java | 10 +- .../BulkLoansReadPlatformServiceImpl.java | 38 ++-- .../loanaccount/service/LoanAssembler.java | 4 +- .../service/LoanReadPlatformServiceImpl.java | 3 +- ...WritePlatformServiceJpaRepositoryImpl.java | 14 +- .../loanproduct/service/LoanEnumerations.java | 34 +--- .../domain/SavingsAccountAssembler.java | 5 + 27 files changed, 478 insertions(+), 679 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/AccountSummaryCollectionData.java rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{client/data/ClientLoanAccountSummaryData.java => accountdetails/data/LoanAccountSummaryData.java} (80%) rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{client/data/ClientSavingsAccountSummaryData.java => accountdetails/data/SavingsAccountSummaryData.java} (80%) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/domain/AccountType.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountEnumerations.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientAccountSummaryCollectionData.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupAccountSummaryCollectionData.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupAccountSummaryData.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanType.java diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/data/StaffAccountSummaryCollectionData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/data/StaffAccountSummaryCollectionData.java index bb4fbb40d..0fd820705 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/data/StaffAccountSummaryCollectionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/data/StaffAccountSummaryCollectionData.java @@ -8,8 +8,7 @@ package org.mifosplatform.organisation.staff.data; import java.util.Collection; import java.util.List; -import org.mifosplatform.portfolio.client.data.ClientLoanAccountSummaryData; -import org.mifosplatform.portfolio.group.data.GroupAccountSummaryData; +import org.mifosplatform.portfolio.accountdetails.data.LoanAccountSummaryData; /** * @@ -17,23 +16,23 @@ import org.mifosplatform.portfolio.group.data.GroupAccountSummaryData; public class StaffAccountSummaryCollectionData { @SuppressWarnings("unused") - private final List clients; + private final List clients; @SuppressWarnings("unused") - private final List groups; + private final List groups; - public StaffAccountSummaryCollectionData(final List clients, final List groups) { + public StaffAccountSummaryCollectionData(final List clients, final List groups) { this.clients = clients; this.groups = groups; } - public static final class ClientSummary { + public static final class LoanAccountSummary { private final Long id; private final String displayName; - private Collection loans; + private Collection loans; - public ClientSummary(Long id, String displayName) { + public LoanAccountSummary(Long id, String displayName) { this.id = id; this.displayName = displayName; } @@ -46,41 +45,13 @@ public class StaffAccountSummaryCollectionData { return displayName; } - public Collection getLoans() { + public Collection getLoans() { return loans; } - public void setLoans(Collection loans) { + public void setLoans(Collection loans) { this.loans = loans; } } - public static final class GroupSummary { - - private final Long id; - private final String name; - - private Collection loans; - - public GroupSummary(Long id, String name) { - this.id = id; - this.name = name; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public Collection getLoans() { - return loans; - } - - public void setLoans(Collection loans) { - this.loans = loans; - } - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/AccountSummaryCollectionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/AccountSummaryCollectionData.java new file mode 100644 index 000000000..dd4b59650 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/AccountSummaryCollectionData.java @@ -0,0 +1,57 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accountdetails.data; + +import java.util.Collection; + +/** + * Immutable data object representing a summary of various accounts. + */ +public class AccountSummaryCollectionData { + + @SuppressWarnings("unused") + private final Collection loanAccounts; + @SuppressWarnings("unused") + private final Collection savingsAccounts; + @SuppressWarnings("unused") + private final Collection memberLoanAccounts; + @SuppressWarnings("unused") + private final Collection memberSavingsAccounts; + + public AccountSummaryCollectionData(final Collection loanAccounts, + final Collection savingsAccounts) { + this.loanAccounts = defaultLoanAccountsIfEmpty(loanAccounts); + this.savingsAccounts = defaultSavingsAccountsIfEmpty(savingsAccounts); + this.memberLoanAccounts = null; + this.memberSavingsAccounts = null; + } + + public AccountSummaryCollectionData(final Collection loanAccounts, + final Collection savingsAccounts, final Collection memberLoanAccounts, + Collection memberSavingsAccounts) { + this.loanAccounts = defaultLoanAccountsIfEmpty(loanAccounts); + this.savingsAccounts = defaultSavingsAccountsIfEmpty(savingsAccounts); + this.memberLoanAccounts = defaultLoanAccountsIfEmpty(memberLoanAccounts); + this.memberSavingsAccounts = defaultSavingsAccountsIfEmpty(memberSavingsAccounts); + } + + private Collection defaultLoanAccountsIfEmpty(final Collection collection) { + Collection returnCollection = null; + if (collection != null && !collection.isEmpty()) { + returnCollection = collection; + } + return returnCollection; + } + + private Collection defaultSavingsAccountsIfEmpty(final Collection collection) { + Collection returnCollection = null; + if (collection != null && !collection.isEmpty()) { + returnCollection = collection; + } + return returnCollection; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientLoanAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/LoanAccountSummaryData.java similarity index 80% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientLoanAccountSummaryData.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/LoanAccountSummaryData.java index 990de0c62..15a64d472 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientLoanAccountSummaryData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/LoanAccountSummaryData.java @@ -3,16 +3,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.client.data; +package org.mifosplatform.portfolio.accountdetails.data; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData; /** - * Immutable data object for client loan accounts. + * Immutable data object for loan accounts. */ @SuppressWarnings("unused") -public class ClientLoanAccountSummaryData { +public class LoanAccountSummaryData { private final Long id; private final String accountNo; @@ -23,7 +23,7 @@ public class ClientLoanAccountSummaryData { private final EnumOptionData loanType; private final Integer loanCycle; - public ClientLoanAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId, + public LoanAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId, final String loanProductName, final LoanStatusEnumData loanStatus, final EnumOptionData loanType, final Integer loanCycle) { this.id = id; this.accountNo = accountNo; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientSavingsAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java similarity index 80% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientSavingsAccountSummaryData.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java index 711969e04..dc5cde49b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientSavingsAccountSummaryData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.client.data; +package org.mifosplatform.portfolio.accountdetails.data; import java.math.BigDecimal; @@ -11,10 +11,10 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData; /** - * Immutable data object for client savings accounts. + * Immutable data object for savings accounts. */ @SuppressWarnings("unused") -public class ClientSavingsAccountSummaryData { +public class SavingsAccountSummaryData { private final Long id; private final String accountNo; @@ -25,7 +25,7 @@ public class ClientSavingsAccountSummaryData { private final CurrencyData currency; private final BigDecimal accountBalance; - public ClientSavingsAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId, + public SavingsAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId, final String productName, final SavingsAccountStatusEnumData status, final CurrencyData currency, final BigDecimal accountBalance) { this.id = id; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/domain/AccountType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/domain/AccountType.java new file mode 100644 index 000000000..02ba6955e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/domain/AccountType.java @@ -0,0 +1,81 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accountdetails.domain; + +/** + * Enum representation of account types . + */ +public enum AccountType { + + INVALID(0, "accountType.invalid"), // + INDIVIDUAL(1, "accountType.individual"), // + GROUP(2, "accountType.group"), // + JLG(3, "accountType.jlg");//JLG account given in group context + + private final Integer value; + private final String code; + + private AccountType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public static AccountType fromInt(final Integer accountTypeValue) { + + AccountType enumeration = AccountType.INVALID; + switch (accountTypeValue) { + case 1: + enumeration = AccountType.INDIVIDUAL; + break; + case 2: + enumeration = AccountType.GROUP; + break; + case 3: + enumeration = AccountType.JLG; + break; + } + return enumeration; + } + + public static AccountType fromName(final String name){ + AccountType accountType = AccountType.INVALID; + for (AccountType type : AccountType.values()) { + if(type.getName().equals(name)) { + accountType = type; + break; + } + } + return accountType; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return code; + } + + public String getName(){ + return name().toLowerCase(); + } + + public boolean isInvalid(){ + return this.value.equals(AccountType.INVALID.getValue()); + } + + public boolean isIndividualAccount(){ + return this.value.equals(AccountType.INDIVIDUAL.getValue()); + } + + public boolean isGroupAccount(){ + return this.value.equals(AccountType.GROUP.getValue()); + } + + public boolean isJLGAccount(){ + return this.value.equals(AccountType.JLG.getValue()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformService.java new file mode 100755 index 000000000..ad918f046 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformService.java @@ -0,0 +1,18 @@ +package org.mifosplatform.portfolio.accountdetails.service; + +import java.util.Collection; + +import org.mifosplatform.portfolio.accountdetails.data.AccountSummaryCollectionData; +import org.mifosplatform.portfolio.accountdetails.data.LoanAccountSummaryData; + + +public interface AccountDetailsReadPlatformService { + + public AccountSummaryCollectionData retrieveClientAccountDetails(final Long clientId); + + public AccountSummaryCollectionData retrieveGroupAccountDetails(final Long groupId); + + public Collection retrieveClientLoanAccountsByLoanOfficerId(final Long clientId, final Long loanOfficerId); + + public Collection retrieveGroupLoanAccountsByLoanOfficerId(final Long groupId,final Long loanOfficerId); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java new file mode 100755 index 000000000..3e99e27a5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,178 @@ +package org.mifosplatform.portfolio.accountdetails.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.accountdetails.data.AccountSummaryCollectionData; +import org.mifosplatform.portfolio.accountdetails.data.LoanAccountSummaryData; +import org.mifosplatform.portfolio.accountdetails.data.SavingsAccountSummaryData; +import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; +import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; +import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData; +import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; +import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData; +import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; +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 AccountDetailsReadPlatformServiceJpaRepositoryImpl implements AccountDetailsReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final ClientReadPlatformService clientReadPlatformService; + private final GroupReadPlatformService groupReadPlatformService; + + @Autowired + public AccountDetailsReadPlatformServiceJpaRepositoryImpl(final ClientReadPlatformService clientReadPlatformService, + final RoutingDataSource dataSource, final GroupReadPlatformService groupReadPlatformService) { + this.clientReadPlatformService = clientReadPlatformService; + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.groupReadPlatformService = groupReadPlatformService; + } + + @Override + public AccountSummaryCollectionData retrieveClientAccountDetails(Long clientId) { + // Check if client exists + clientReadPlatformService.retrieveOne(clientId); + String loanwhereClause = " where l.client_id = ?"; + String savingswhereClause = " where sa.client_id = ? order by sa.status_enum ASC, sa.account_no ASC"; + List loanAccounts = retrieveLoanAccountDetails(loanwhereClause, new Object[] { clientId }); + List savingsAccounts = retrieveAccountDetails(savingswhereClause, new Object[] { clientId }); + return new AccountSummaryCollectionData(loanAccounts, savingsAccounts); + } + + @Override + public AccountSummaryCollectionData retrieveGroupAccountDetails(Long groupId) { + // Check if group exists + groupReadPlatformService.retrieveOne(groupId); + String loanWhereClauseForGroup = " where l.group_id = ? and l.client_id is null"; + String loanWhereClauseForMembers = " where l.group_id = ? and l.client_id is not null"; + String savingswhereClauseForGroup = " where sa.group_id = ? and sa.client_id is null order by sa.status_enum ASC, sa.account_no ASC"; + String savingswhereClauseForMembers = " where sa.group_id = ? and sa.client_id is not null order by sa.status_enum ASC, sa.account_no ASC"; + List groupLoanAccounts = retrieveLoanAccountDetails(loanWhereClauseForGroup, new Object[] { groupId }); + List groupSavingsAccounts = retrieveAccountDetails(savingswhereClauseForGroup, new Object[] { groupId }); + List memberLoanAccounts = retrieveLoanAccountDetails(loanWhereClauseForMembers, new Object[] { groupId }); + List memberSavingsAccounts = retrieveAccountDetails(savingswhereClauseForMembers, + new Object[] { groupId }); + return new AccountSummaryCollectionData(groupLoanAccounts, groupSavingsAccounts,memberLoanAccounts, memberSavingsAccounts); + } + + @Override + public Collection retrieveClientLoanAccountsByLoanOfficerId(final Long clientId, final Long loanOfficerId) { + // Check if client exists + clientReadPlatformService.retrieveOne(clientId); + String loanWhereClause = " where l.client_id = ? and l.loan_officer_id = ?"; + return retrieveLoanAccountDetails(loanWhereClause, new Object[] { clientId, loanOfficerId }); + } + + @Override + public Collection retrieveGroupLoanAccountsByLoanOfficerId(final Long groupId, final Long loanOfficerId) { + // Check if group exists + groupReadPlatformService.retrieveOne(groupId); + String loanWhereClause = " where l.group_id = ? and l.client_id is null and l.loan_officer_id = ?"; + return retrieveLoanAccountDetails(loanWhereClause, new Object[] { groupId, loanOfficerId }); + } + + private List retrieveLoanAccountDetails(String loanwhereClause, Object[] inputs) { + LoanAccountSummaryDataMapper rm = new LoanAccountSummaryDataMapper(); + String sql = "select " + rm.loanAccountSummarySchema() + loanwhereClause; + return this.jdbcTemplate.query(sql, rm, inputs); + } + + /** + * @param entityId + * @return + */ + private List retrieveAccountDetails(String savingswhereClause, Object[] inputs) { + SavingsAccountSummaryDataMapper savingsAccountSummaryDataMapper = new SavingsAccountSummaryDataMapper(); + final String savingsSql = "select " + savingsAccountSummaryDataMapper.schema() + savingswhereClause; + return this.jdbcTemplate.query(savingsSql, savingsAccountSummaryDataMapper, inputs); + } + + private static final class SavingsAccountSummaryDataMapper implements RowMapper { + + final String schemaSql; + + public SavingsAccountSummaryDataMapper() { + StringBuilder accountsSummary = new StringBuilder(); + accountsSummary.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, sa.status_enum as statusEnum, "); + accountsSummary.append("sa.account_balance_derived as accountBalance, "); + accountsSummary.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, "); + accountsSummary.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + accountsSummary.append("curr.display_symbol as currencyDisplaySymbol, "); + accountsSummary.append("sa.product_id as productId, p.name as productName "); + accountsSummary.append("from m_savings_account sa "); + accountsSummary.append("join m_savings_product as p on p.id = sa.product_id "); + accountsSummary.append("join m_currency curr on curr.code = sa.currency_code "); + + this.schemaSql = accountsSummary.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public SavingsAccountSummaryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = JdbcSupport.getLong(rs, "id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + final Long productId = JdbcSupport.getLong(rs, "productId"); + final String productName = rs.getString("productName"); + final Integer statusId = JdbcSupport.getInteger(rs, "statusEnum"); + final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accountBalance"); + final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusId); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, + currencyNameCode); + + return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, status, currency, accountBalance); + } + } + + private static final class LoanAccountSummaryDataMapper implements RowMapper { + + public String loanAccountSummarySchema() { + + StringBuilder accountsSummary = new StringBuilder("l.id as id, l.account_no as accountNo, l.external_id as externalId,"); + accountsSummary.append("l.product_id as productId, lp.name as productName,") + .append("l.loan_status_id as statusId, l.loan_type_enum as loanType, ").append("l.loan_product_counter as loanCycle ") + .append(" from m_loan l ").append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id"); + + return accountsSummary.toString(); + } + + @Override + public LoanAccountSummaryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = JdbcSupport.getLong(rs, "id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + final Long productId = JdbcSupport.getLong(rs, "productId"); + final String loanProductName = rs.getString("productName"); + final Integer loanStatusId = JdbcSupport.getInteger(rs, "statusId"); + final LoanStatusEnumData loanStatus = LoanEnumerations.status(loanStatusId); + final Integer loanTypeId = JdbcSupport.getInteger(rs, "loanType"); + final EnumOptionData loanType = AccountEnumerations.loanType(loanTypeId); + final Integer loanCycle = JdbcSupport.getInteger(rs, "loanCycle"); + + return new LoanAccountSummaryData(id, accountNo, externalId, productId, loanProductName, loanStatus, loanType, loanCycle); + } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountEnumerations.java new file mode 100755 index 000000000..119d3c8a4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountEnumerations.java @@ -0,0 +1,38 @@ +package org.mifosplatform.portfolio.accountdetails.service; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; + + +public class AccountEnumerations { + + public static EnumOptionData loanType(final Integer loanTypeId) { + return loanType(AccountType.fromInt(loanTypeId)); + } + + public static EnumOptionData loanType(final String name) { + return loanType(AccountType.fromName(name)); + } + + public static EnumOptionData loanType(final AccountType type) { + EnumOptionData optionData = new EnumOptionData(AccountType.INVALID.getValue().longValue(), AccountType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + optionData = new EnumOptionData(AccountType.INVALID.getValue().longValue(), AccountType.INVALID.getCode(), "Invalid"); + break; + case INDIVIDUAL: + optionData = new EnumOptionData(AccountType.INDIVIDUAL.getValue().longValue(), AccountType.INDIVIDUAL.getCode(), "Individual"); + break; + case GROUP: + optionData = new EnumOptionData(AccountType.GROUP.getValue().longValue(), AccountType.GROUP.getCode(), "Group"); + break; + case JLG: + optionData = new EnumOptionData(AccountType.JLG.getValue().longValue(), AccountType.JLG.getCode(), "JLG"); + break; + } + + return optionData; + } + + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java index 9309e030d..a760e9e6d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java @@ -34,7 +34,8 @@ import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSeriali import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; -import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; +import org.mifosplatform.portfolio.accountdetails.data.AccountSummaryCollectionData; +import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlatformService; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; @@ -50,22 +51,25 @@ public class ClientsApiResource { private final PlatformSecurityContext context; private final ClientReadPlatformService clientReadPlatformService; private final ToApiJsonSerializer toApiJsonSerializer; - private final ToApiJsonSerializer clientAccountSummaryToApiJsonSerializer; + private final ToApiJsonSerializer clientAccountSummaryToApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; @Autowired public ClientsApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService, final ToApiJsonSerializer toApiJsonSerializer, - final ToApiJsonSerializer clientAccountSummaryToApiJsonSerializer, + final ToApiJsonSerializer clientAccountSummaryToApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, - final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final AccountDetailsReadPlatformService accountDetailsReadPlatformService) { this.context = context; this.clientReadPlatformService = readPlatformService; this.toApiJsonSerializer = toApiJsonSerializer; this.clientAccountSummaryToApiJsonSerializer = clientAccountSummaryToApiJsonSerializer; this.apiRequestParameterHelper = apiRequestParameterHelper; this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; } @GET @@ -223,7 +227,7 @@ public class ClientsApiResource { context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME); - final ClientAccountSummaryCollectionData clientAccount = this.clientReadPlatformService.retrieveClientAccountDetails(clientId); + final AccountSummaryCollectionData clientAccount = this.accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId); final Set CLIENT_ACCOUNTS_DATA_PARAMETERS = new HashSet(Arrays.asList("loanAccounts", "savingsAccounts")); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientAccountSummaryCollectionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientAccountSummaryCollectionData.java deleted file mode 100644 index 239b96947..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientAccountSummaryCollectionData.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.mifosplatform.portfolio.client.data; - -import java.util.Collection; - -/** - * Immutable data object representing a summary of a clients various accounts. - */ -public class ClientAccountSummaryCollectionData { - - @SuppressWarnings("unused") - private final Collection loanAccounts; - @SuppressWarnings("unused") - private final Collection savingsAccounts; - - public ClientAccountSummaryCollectionData(final Collection loanAccounts, - final Collection savingsAccounts) { - this.loanAccounts = defaultIfEmpty(loanAccounts); - this.savingsAccounts = savingsAccounts; - } - - private Collection defaultIfEmpty(final Collection collection) { - Collection returnCollection = null; - if (collection != null && !collection.isEmpty()) { - returnCollection = collection; - } - return returnCollection; - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java index 29b15bb45..4a60e8e86 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java @@ -8,8 +8,6 @@ package org.mifosplatform.portfolio.client.service; import java.util.Collection; import org.mifosplatform.infrastructure.core.service.Page; -import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; -import org.mifosplatform.portfolio.client.data.ClientLoanAccountSummaryData; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.group.service.SearchParameters; @@ -25,14 +23,10 @@ public interface ClientReadPlatformService { Collection retrieveAllForLookupByOfficeId(Long officeId); - ClientAccountSummaryCollectionData retrieveClientAccountDetails(Long clientId); - - Collection retrieveClientLoanAccountsByLoanOfficerId(Long clientId, Long loanOfficerId); - ClientData retrieveClientByIdentifier(Long identifierTypeId, String identifierKey); Collection retrieveClientMembersOfGroup(Long groupId); - + Collection retrieveActiveClientMembersOfGroup(Long groupId); ClientData retrieveAllClosureReasons(String clientClosureReason); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java index 6d9ea0746..d334b2ba8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -5,7 +5,6 @@ */ package org.mifosplatform.portfolio.client.service; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -23,24 +22,16 @@ import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.core.service.PaginationHelper; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; -import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; -import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; -import org.mifosplatform.portfolio.client.data.ClientLoanAccountSummaryData; import org.mifosplatform.portfolio.client.data.ClientData; -import org.mifosplatform.portfolio.client.data.ClientSavingsAccountSummaryData; import org.mifosplatform.portfolio.client.domain.ClientEnumerations; import org.mifosplatform.portfolio.client.domain.ClientStatus; import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.service.SearchParameters; -import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData; -import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; -import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData; -import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; @@ -63,7 +54,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService private final ClientLookupMapper lookupMapper = new ClientLookupMapper(); private final ClientMembersOfGroupMapper membersOfGroupMapper = new ClientMembersOfGroupMapper(); private final ParentGroupsMapper clientGroupsMapper = new ParentGroupsMapper(); - private final ClientSavingsAccountSummaryDataMapper savingsAccountSummaryDataMapper = new ClientSavingsAccountSummaryDataMapper(); @Autowired public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, @@ -255,7 +245,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService return this.jdbcTemplate.query(sql, this.membersOfGroupMapper, new Object[] { hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue() }); } - + private static final class ClientMembersOfGroupMapper implements RowMapper { private final String schema; @@ -407,125 +397,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService } } - @Override - public ClientAccountSummaryCollectionData retrieveClientAccountDetails(final Long clientId) { - - try { - // Check if client exists - retrieveOne(clientId); - - ClientLoanAccountSummaryDataMapper rm = new ClientLoanAccountSummaryDataMapper(); - - String sql = "select " + rm.loanAccountSummarySchema() + " where l.client_id = ?"; - - List allLoanAccounts = this.jdbcTemplate.query(sql, rm, new Object[] { clientId }); - - final String savingsSql = "select " + this.savingsAccountSummaryDataMapper.schema() - + " where sa.client_id = ? order by sa.status_enum ASC, sa.account_no ASC"; - - final List allSavingsAccounts = this.jdbcTemplate.query(savingsSql, - savingsAccountSummaryDataMapper, new Object[] { clientId }); - - return new ClientAccountSummaryCollectionData(allLoanAccounts, allSavingsAccounts); - } catch (EmptyResultDataAccessException e) { - throw new ClientNotFoundException(clientId); - } - } - - @Override - public Collection retrieveClientLoanAccountsByLoanOfficerId(final Long clientId, final Long loanOfficerId) { - - this.context.authenticatedUser(); - - // Check if client exists - retrieveOne(clientId); - - ClientLoanAccountSummaryDataMapper rm = new ClientLoanAccountSummaryDataMapper(); - - String sql = "select " + rm.loanAccountSummarySchema() + " where l.client_id = ? and l.loan_officer_id = ?"; - - List loanAccounts = this.jdbcTemplate.query(sql, rm, new Object[] { clientId, loanOfficerId }); - - return loanAccounts; - } - - private static final class ClientSavingsAccountSummaryDataMapper implements RowMapper { - - final String schemaSql; - - public ClientSavingsAccountSummaryDataMapper() { - StringBuilder accountsSummary = new StringBuilder(); - accountsSummary.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, sa.status_enum as statusEnum, "); - accountsSummary.append("sa.account_balance_derived as accountBalance, "); - accountsSummary.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, "); - accountsSummary.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); - accountsSummary.append("curr.display_symbol as currencyDisplaySymbol, "); - accountsSummary.append("sa.product_id as productId, p.name as productName "); - accountsSummary.append("from m_savings_account sa "); - accountsSummary.append("join m_savings_product as p on p.id = sa.product_id "); - accountsSummary.append("join m_currency curr on curr.code = sa.currency_code "); - - this.schemaSql = accountsSummary.toString(); - } - - public String schema() { - return this.schemaSql; - } - - @Override - public ClientSavingsAccountSummaryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final Long id = JdbcSupport.getLong(rs, "id"); - final String accountNo = rs.getString("accountNo"); - final String externalId = rs.getString("externalId"); - final Long productId = JdbcSupport.getLong(rs, "productId"); - final String productName = rs.getString("productName"); - final Integer statusId = JdbcSupport.getInteger(rs, "statusEnum"); - final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accountBalance"); - final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusId); - - final String currencyCode = rs.getString("currencyCode"); - final String currencyName = rs.getString("currencyName"); - final String currencyNameCode = rs.getString("currencyNameCode"); - final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); - final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); - - return new ClientSavingsAccountSummaryData(id, accountNo, externalId, productId, productName, status, currency, accountBalance); - } - } - - private static final class ClientLoanAccountSummaryDataMapper implements RowMapper { - - public String loanAccountSummarySchema() { - - StringBuilder accountsSummary = new StringBuilder("l.id as id, l.account_no as accountNo, l.external_id as externalId,"); - accountsSummary.append("l.product_id as productId, lp.name as productName,") - .append("l.loan_status_id as statusId, l.loan_type_enum as loanType, ").append("l.loan_product_counter as loanCycle ") - .append(" from m_loan l ").append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id"); - - return accountsSummary.toString(); - } - - @Override - public ClientLoanAccountSummaryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final Long id = JdbcSupport.getLong(rs, "id"); - final String accountNo = rs.getString("accountNo"); - final String externalId = rs.getString("externalId"); - final Long productId = JdbcSupport.getLong(rs, "productId"); - final String loanProductName = rs.getString("productName"); - final Integer loanStatusId = JdbcSupport.getInteger(rs, "statusId"); - final LoanStatusEnumData loanStatus = LoanEnumerations.status(loanStatusId); - final Integer loanTypeId = JdbcSupport.getInteger(rs, "loanType"); - final EnumOptionData loanType = LoanEnumerations.loanType(loanTypeId); - final Integer loanCycle = JdbcSupport.getInteger(rs, "loanCycle"); - - return new ClientLoanAccountSummaryData(id, accountNo, externalId, productId, loanProductName, loanStatus, loanType, loanCycle); - } - } - @Override public ClientData retrieveClientByIdentifier(final Long identifierTypeId, final String identifierKey) { try { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java index 03bbec3ee..f363af10a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java @@ -36,12 +36,12 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.accountdetails.data.AccountSummaryCollectionData; +import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlatformService; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; import org.mifosplatform.portfolio.collectionsheet.service.CollectionSheetReadPlatformService; import org.mifosplatform.portfolio.group.data.CenterData; -import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData; import org.mifosplatform.portfolio.group.service.CenterReadPlatformService; -import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; @@ -56,26 +56,25 @@ public class CentersApiResource { private final PlatformSecurityContext context; private final CenterReadPlatformService centerReadPlatformService; - private final GroupReadPlatformService groupReadPlatformService; private final ToApiJsonSerializer centerApiJsonSerializer; private final ToApiJsonSerializer toApiJsonSerializer; - private final ToApiJsonSerializer groupSummaryToApiJsonSerializer; + private final ToApiJsonSerializer groupSummaryToApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final CollectionSheetReadPlatformService collectionSheetReadPlatformService; private final FromJsonHelper fromJsonHelper; + private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; @Autowired public CentersApiResource(final PlatformSecurityContext context, final CenterReadPlatformService centerReadPlatformService, - final GroupReadPlatformService groupReadPlatformService, final ToApiJsonSerializer centerApiJsonSerializer, - final ToApiJsonSerializer toApiJsonSerializer, - final ToApiJsonSerializer groupSummaryToApiJsonSerializer, + final ToApiJsonSerializer centerApiJsonSerializer, final ToApiJsonSerializer toApiJsonSerializer, + final ToApiJsonSerializer groupSummaryToApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, - final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper) { + final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper, + final AccountDetailsReadPlatformService accountDetailsReadPlatformService) { this.context = context; this.centerReadPlatformService = centerReadPlatformService; - this.groupReadPlatformService = groupReadPlatformService; this.centerApiJsonSerializer = centerApiJsonSerializer; this.toApiJsonSerializer = toApiJsonSerializer; this.groupSummaryToApiJsonSerializer = groupSummaryToApiJsonSerializer; @@ -83,6 +82,7 @@ public class CentersApiResource { this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; this.collectionSheetReadPlatformService = collectionSheetReadPlatformService; this.fromJsonHelper = fromJsonHelper; + this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; } @GET @@ -126,7 +126,7 @@ public class CentersApiResource { this.context.authenticatedUser().validateHasReadPermission(GroupingTypesApiConstants.CENTER_RESOURCE_NAME); final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); - + CenterData center = this.centerReadPlatformService.retrieveOne(centerId); final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); @@ -134,7 +134,7 @@ public class CentersApiResource { final CenterData templateCenter = this.centerReadPlatformService.retrieveTemplate(center.officeId()); center = CenterData.withTemplate(templateCenter, center); } - + if (!associationParameters.isEmpty()) { if (associationParameters.contains("groupMembers")) { center = CenterData.setGroups(center, this.centerReadPlatformService.retrieveAssociatedGroups(centerId)); @@ -200,18 +200,20 @@ public class CentersApiResource { final CommandWrapper commandRequest = builder.activateCenter(centerId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return this.toApiJsonSerializer.serialize(result); - }else if (is(commandParam, "generateCollectionSheet")) { + } else if (is(commandParam, "generateCollectionSheet")) { final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson); final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper); - final JLGCollectionSheetData collectionSheet = this.collectionSheetReadPlatformService.generateCenterCollectionSheet(centerId, query); + final JLGCollectionSheetData collectionSheet = this.collectionSheetReadPlatformService.generateCenterCollectionSheet(centerId, + query); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, collectionSheet, GroupingTypesApiConstants.COLLECTIONSHEET_DATA_PARAMETERS); - }else if (is(commandParam, "saveCollectionSheet")) { + } else if (is(commandParam, "saveCollectionSheet")) { final CommandWrapper commandRequest = builder.saveCenterCollectionSheet(centerId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return this.toApiJsonSerializer.serialize(result); - } else { - throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet", "createRole" }); + } else { + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet", + "createRole" }); } } @@ -221,18 +223,17 @@ public class CentersApiResource { } @GET - @Path("{centerId}/loans") + @Path("{centerId}/accounts") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) public String retrieveGroupAccount(@PathParam("centerId") final Long centerId, @Context final UriInfo uriInfo) { this.context.authenticatedUser().validateHasReadPermission(GroupingTypesApiConstants.CENTER_RESOURCE_NAME); - final GroupAccountSummaryCollectionData groupAccount = this.groupReadPlatformService.retrieveGroupAccountDetails(centerId); + final AccountSummaryCollectionData groupAccount = this.accountDetailsReadPlatformService.retrieveGroupAccountDetails(centerId); - final Set GROUP_ACCOUNTS_DATA_PARAMETERS = new HashSet(Arrays.asList("pendingApprovalLoans", - "awaitingDisbursalLoans", "openLoans", "closedLoans", "anyLoanCount", "pendingApprovalLoanCount", - "awaitingDisbursalLoanCount", "activeLoanCount", "closedLoanCount")); + final Set GROUP_ACCOUNTS_DATA_PARAMETERS = new HashSet(Arrays.asList("loanAccounts", "savingsAccounts", + "memberLoanAccounts", "memberSavingsAccounts")); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.groupSummaryToApiJsonSerializer.serialize(settings, groupAccount, GROUP_ACCOUNTS_DATA_PARAMETERS); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index 6d47d5812..3e008d920 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -38,11 +38,12 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.accountdetails.data.AccountSummaryCollectionData; +import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlatformService; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; import org.mifosplatform.portfolio.collectionsheet.service.CollectionSheetReadPlatformService; -import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.data.GroupRoleData; import org.mifosplatform.portfolio.group.service.CenterReadPlatformService; @@ -67,23 +68,25 @@ public class GroupsApiResource { private final ClientReadPlatformService clientReadPlatformService; private final ToApiJsonSerializer toApiJsonSerializer; private final ToApiJsonSerializer groupGeneralApiJsonSerializer; - private final ToApiJsonSerializer groupSummaryToApiJsonSerializer; + private final ToApiJsonSerializer groupSummaryToApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final CollectionSheetReadPlatformService collectionSheetReadPlatformService; private final FromJsonHelper fromJsonHelper; private final GroupRolesReadPlatformService groupRolesReadPlatformService; + private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; @Autowired public GroupsApiResource(final PlatformSecurityContext context, final GroupReadPlatformService groupReadPlatformService, final CenterReadPlatformService centerReadPlatformService, final ClientReadPlatformService clientReadPlatformService, final ToApiJsonSerializer toApiJsonSerializer, final ToApiJsonSerializer groupTopOfHierarchyApiJsonSerializer, - final ToApiJsonSerializer groupSummaryToApiJsonSerializer, + final ToApiJsonSerializer groupSummaryToApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper, - final GroupRolesReadPlatformService groupRolesReadPlatformService) { + final GroupRolesReadPlatformService groupRolesReadPlatformService, + final AccountDetailsReadPlatformService accountDetailsReadPlatformService) { this.context = context; this.groupReadPlatformService = groupReadPlatformService; this.centerReadPlatformService = centerReadPlatformService; @@ -96,6 +99,7 @@ public class GroupsApiResource { this.collectionSheetReadPlatformService = collectionSheetReadPlatformService; this.fromJsonHelper = fromJsonHelper; this.groupRolesReadPlatformService = groupRolesReadPlatformService; + this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; } @GET @@ -315,20 +319,20 @@ public class GroupsApiResource { } @GET - @Path("{groupId}/loans") + @Path("{groupId}/accounts") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) public String retrieveLoans(@PathParam("groupId") final Long groupId, @Context final UriInfo uriInfo) { this.context.authenticatedUser().validateHasReadPermission("GROUP"); - final GroupAccountSummaryCollectionData groupAccount = this.groupReadPlatformService.retrieveGroupAccountDetails(groupId); + final AccountSummaryCollectionData groupAccount = this.accountDetailsReadPlatformService.retrieveGroupAccountDetails(groupId); - final Set GROUP_ACCOUNTS_DATA_PARAMETERS = new HashSet(Arrays.asList("pendingApprovalLoans", - "awaitingDisbursalLoans", "openLoans", "closedLoans", "anyLoanCount", "pendingApprovalLoanCount", - "awaitingDisbursalLoanCount", "activeLoanCount", "closedLoanCount")); + final Set GROUP_ACCOUNTS_DATA_PARAMETERS = new HashSet(Arrays.asList("loanAccounts", "savingsAccounts", + "memberLoanAccounts", "memberSavingsAccounts")); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.groupSummaryToApiJsonSerializer.serialize(settings, groupAccount, GROUP_ACCOUNTS_DATA_PARAMETERS); } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupAccountSummaryCollectionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupAccountSummaryCollectionData.java deleted file mode 100644 index c5f29e3e9..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupAccountSummaryCollectionData.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.mifosplatform.portfolio.group.data; - -import java.util.List; - -public class GroupAccountSummaryCollectionData { - - private final int anyLoanCount; - private final int pendingApprovalLoanCount; - private final List pendingApprovalLoans; - private final int awaitingDisbursalLoanCount; - private final List awaitingDisbursalLoans; - private final int activeLoanCount; - private final List openLoans; - private final int closedLoanCount; - private final List closedLoans; - - @SuppressWarnings("unused") - private final int anyIndividualLoanCount; - private final int pendingApprovalIndividualLoanCount; - @SuppressWarnings("unused") - private final List pendingApprovalIndividualLoans; - private final int awaitingDisbursalIndividualLoanCount; - @SuppressWarnings("unused") - private final List awaitingDisbursalIndividualLoans; - private final int activeIndividualLoanCount; - @SuppressWarnings("unused") - private final List openIndividualLoans; - private final int closedIndividualLoanCount; - @SuppressWarnings("unused") - private final List closedIndividualLoans; - - public GroupAccountSummaryCollectionData(final List pendingApprovalLoans, - final List awaitingDisbursalLoans, final List openLoans, - final List closedLoans, final List pendingApprovalIndividualLoans, - final List awaitingDisbursalIndividualLoans, final List openIndividualLoans, - final List closedIndividualLoans) { - this.pendingApprovalLoans = pendingApprovalLoans; - this.pendingApprovalLoanCount = pendingApprovalLoans.size(); - this.awaitingDisbursalLoans = awaitingDisbursalLoans; - this.awaitingDisbursalLoanCount = awaitingDisbursalLoans.size(); - this.openLoans = openLoans; - this.activeLoanCount = openLoans.size(); - this.closedLoans = closedLoans; - this.closedLoanCount = closedLoans.size(); - - this.anyLoanCount = this.pendingApprovalLoanCount + this.awaitingDisbursalLoanCount + this.activeLoanCount + this.closedLoanCount; - - this.pendingApprovalIndividualLoans = pendingApprovalIndividualLoans; - this.pendingApprovalIndividualLoanCount = pendingApprovalIndividualLoans.size(); - this.awaitingDisbursalIndividualLoans = awaitingDisbursalIndividualLoans; - this.awaitingDisbursalIndividualLoanCount = awaitingDisbursalIndividualLoans.size(); - this.openIndividualLoans = openIndividualLoans; - this.activeIndividualLoanCount = openIndividualLoans.size(); - this.closedIndividualLoans = closedIndividualLoans; - this.closedIndividualLoanCount = closedIndividualLoans.size(); - - this.anyIndividualLoanCount = this.pendingApprovalIndividualLoanCount + this.awaitingDisbursalIndividualLoanCount - + this.activeIndividualLoanCount + this.closedIndividualLoanCount; - } - - public int getAnyLoanCount() { - return this.anyLoanCount; - } - - public int getPendingApprovalLoanCount() { - return this.pendingApprovalLoanCount; - } - - public List getPendingApprovalLoans() { - return this.pendingApprovalLoans; - } - - public int getAwaitingDisbursalLoanCount() { - return this.awaitingDisbursalLoanCount; - } - - public List getAwaitingDisbursalLoans() { - return this.awaitingDisbursalLoans; - } - - public int getActiveLoanCount() { - return this.activeLoanCount; - } - - public List getOpenLoans() { - return this.openLoans; - } - - public int getClosedLoanCount() { - return this.closedLoanCount; - } - - public List getClosedLoans() { - return this.closedLoans; - } -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupAccountSummaryData.java deleted file mode 100644 index d02c746fd..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupAccountSummaryData.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.mifosplatform.portfolio.group.data; - -/** - * Immutable data object for group loan accounts. - */ -public class GroupAccountSummaryData { - - private final Long id; - private final String externalId; - private final Long productId; - private final String productName; - private final Integer accountStatusId; - @SuppressWarnings("unused") - private final String accountNo; - - public GroupAccountSummaryData(final Long id, final String externalId, final Long productId, final String productName, - final Integer accountStatusId, final String accountNo) { - this.id = id; - this.externalId = externalId; - this.productId = productId; - this.productName = productName; - this.accountStatusId = accountStatusId; - this.accountNo = accountNo; - } - - public Long getId() { - return this.id; - } - - public String getExternalId() { - return this.externalId; - } - - public Long getProductId() { - return this.productId; - } - - public String getProductName() { - return this.productName; - } - - public Integer getAccountStatusId() { - return this.accountStatusId; - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java index 9a38c6aed..f850d8845 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java @@ -5,11 +5,7 @@ */ package org.mifosplatform.portfolio.group.service; -import java.util.Collection; - import org.mifosplatform.infrastructure.core.service.Page; -import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData; -import org.mifosplatform.portfolio.group.data.GroupAccountSummaryData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; public interface GroupReadPlatformService { @@ -19,9 +15,4 @@ public interface GroupReadPlatformService { Page retrieveAll(SearchParameters searchParameters); GroupGeneralData retrieveOne(Long groupId); - - // - GroupAccountSummaryCollectionData retrieveGroupAccountDetails(Long groupId); - - Collection retrieveGroupLoanAccountsByLoanOfficerId(Long groupId, Long loanOfficerId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java index d75be5e33..d10d84f35 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java @@ -5,17 +5,12 @@ */ package org.mifosplatform.portfolio.group.service; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import org.apache.commons.lang.StringUtils; 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.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.core.service.PaginationHelper; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; @@ -26,11 +21,8 @@ import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; -import org.mifosplatform.portfolio.client.service.LoanStatusMapper; import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants; import org.mifosplatform.portfolio.group.data.CenterData; -import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData; -import org.mifosplatform.portfolio.group.data.GroupAccountSummaryData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.domain.GroupTypes; import org.mifosplatform.portfolio.group.exception.GroupNotFoundException; @@ -38,7 +30,6 @@ import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -209,114 +200,4 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService { throw new GroupNotFoundException(groupId); } } - - @Override - public GroupAccountSummaryCollectionData retrieveGroupAccountDetails(final Long groupId) { - try { - this.context.authenticatedUser(); - - // Check if group exists - // retrieveGroup(groupId); - - final List pendingApprovalLoans = new ArrayList(); - final List awaitingDisbursalLoans = new ArrayList(); - final List openLoans = new ArrayList(); - final List closedLoans = new ArrayList(); - - final GroupLoanAccountSummaryDataMapper rm = new GroupLoanAccountSummaryDataMapper(); - - String sql = "select " + rm.loanAccountSummarySchema() + " where l.group_id = ? and l.client_id is null"; - - List results = this.jdbcTemplate.query(sql, rm, new Object[] { groupId }); - if (results != null) { - for (final GroupAccountSummaryData row : results) { - - final LoanStatusMapper statusMapper = new LoanStatusMapper(row.getAccountStatusId()); - - if (statusMapper.isOpen()) { - openLoans.add(row); - } else if (statusMapper.isAwaitingDisbursal()) { - awaitingDisbursalLoans.add(row); - } else if (statusMapper.isPendingApproval()) { - pendingApprovalLoans.add(row); - } else { - closedLoans.add(row); - } - } - } - - final List pendingApprovalIndividualLoans = new ArrayList(); - final List awaitingDisbursalIndividualLoans = new ArrayList(); - final List openIndividualLoans = new ArrayList(); - final List closedIndividualLoans = new ArrayList(); - - sql = "select " + rm.loanAccountSummarySchema() + " where l.group_id = ? and l.client_id is not null"; - - results = this.jdbcTemplate.query(sql, rm, new Object[] { groupId }); - if (results != null) { - for (final GroupAccountSummaryData row : results) { - - final LoanStatusMapper statusMapper = new LoanStatusMapper(row.getAccountStatusId()); - - if (statusMapper.isOpen()) { - openIndividualLoans.add(row); - } else if (statusMapper.isAwaitingDisbursal()) { - awaitingDisbursalIndividualLoans.add(row); - } else if (statusMapper.isPendingApproval()) { - pendingApprovalIndividualLoans.add(row); - } else { - closedIndividualLoans.add(row); - } - } - } - - return new GroupAccountSummaryCollectionData(pendingApprovalLoans, awaitingDisbursalLoans, openLoans, closedLoans, - pendingApprovalIndividualLoans, awaitingDisbursalIndividualLoans, openIndividualLoans, closedIndividualLoans); - } catch (final EmptyResultDataAccessException e) { - throw new GroupNotFoundException(groupId); - } - } - - @Override - public Collection retrieveGroupLoanAccountsByLoanOfficerId(final Long groupId, final Long loanOfficerId) { - - this.context.authenticatedUser(); - - // Check if group exists - // retrieveGroup(groupId); - - final GroupLoanAccountSummaryDataMapper rm = new GroupLoanAccountSummaryDataMapper(); - - final String sql = "select " + rm.loanAccountSummarySchema() - + " where l.group_id = ? and l.client_id is null and l.loan_officer_id = ?"; - - final List loanAccounts = this.jdbcTemplate.query(sql, rm, new Object[] { groupId, loanOfficerId }); - - return loanAccounts; - } - - private static final class GroupLoanAccountSummaryDataMapper implements RowMapper { - - public String loanAccountSummarySchema() { - - final StringBuilder accountsSummary = new StringBuilder("l.id as id, l.external_id as externalId,"); - accountsSummary.append("l.product_id as productId, lp.name as productName,").append("l.loan_status_id as statusId, ") - .append("l.account_no as accountNo ").append("from m_loan l ") - .append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id "); - - return accountsSummary.toString(); - } - - @Override - public GroupAccountSummaryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final Long id = JdbcSupport.getLong(rs, "id"); - final String externalId = rs.getString("externalId"); - final Long productId = JdbcSupport.getLong(rs, "productId"); - final String loanProductName = rs.getString("productName"); - final Integer loanStatusId = JdbcSupport.getInteger(rs, "statusId"); - final String accountNo = rs.getString("accountNo"); - return new GroupAccountSummaryData(id, externalId, productId, loanProductName, loanStatusId, accountNo); - } - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index e94d31b05..9b33ffb57 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -55,6 +55,7 @@ import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.calendar.service.CalendarHelper; import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException; import org.mifosplatform.portfolio.client.domain.Client; @@ -2499,7 +2500,7 @@ public class Loan extends AbstractPersistable { } public boolean isGroupLoan() { - return LoanType.fromInt(this.loanType).isGroupLoan(); + return AccountType.fromInt(this.loanType).isGroupAccount(); } public void updateInterestRateFrequencyType() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanType.java deleted file mode 100644 index 9a51a5cc6..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanType.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.mifosplatform.portfolio.loanaccount.domain; - -/** - * Enum representation of loan types . - */ -public enum LoanType { - - INVALID(0, "loanType.invalid"), // - INDIVIDUAL(1, "loanType.individual"), // - GROUP(2, "loanType.group"), // - JLG(3, "loanType.jlg");//JLG loans given in group context - - private final Integer value; - private final String code; - - private LoanType(final Integer value, final String code) { - this.value = value; - this.code = code; - } - - public static LoanType fromInt(final Integer loanTypeValue) { - - LoanType enumeration = LoanType.INVALID; - switch (loanTypeValue) { - case 1: - enumeration = LoanType.INDIVIDUAL; - break; - case 2: - enumeration = LoanType.GROUP; - break; - case 3: - enumeration = LoanType.JLG; - break; - } - return enumeration; - } - - public static LoanType fromName(final String name){ - LoanType loanType = LoanType.INVALID; - for (LoanType type : LoanType.values()) { - if(type.getName().equals(name)) { - loanType = type; - break; - } - } - return loanType; - } - - public Integer getValue() { - return this.value; - } - - public String getCode() { - return code; - } - - public String getName(){ - return name().toLowerCase(); - } - - public boolean isInvalid(){ - return this.value.equals(LoanType.INVALID.getValue()); - } - - public boolean isIndividualLoan(){ - return this.value.equals(LoanType.INDIVIDUAL.getValue()); - } - - public boolean isGroupLoan(){ - return this.value.equals(LoanType.GROUP.getValue()); - } - - public boolean isJLGLoan(){ - return this.value.equals(LoanType.JLG.getValue()); - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java index f826af108..48f44a7cc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java @@ -22,7 +22,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; -import org.mifosplatform.portfolio.loanaccount.domain.LoanType; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -77,22 +77,22 @@ public final class LoanApplicationCommandFromApiJsonHelper { baseDataValidator.reset().parameter(loanTypeParameterName).value(loanTypeStr).notNull(); if (!StringUtils.isBlank(loanTypeStr)) { - final LoanType loanType = LoanType.fromName(loanTypeStr); + final AccountType loanType = AccountType.fromName(loanTypeStr); baseDataValidator.reset().parameter(loanTypeParameterName).value(loanType.getValue()).inMinMaxRange(1, 3); final Long clientId = fromApiJsonHelper.extractLongNamed("clientId", element); final Long groupId = fromApiJsonHelper.extractLongNamed("groupId", element); - if (loanType.isIndividualLoan()) { + if (loanType.isIndividualAccount()) { baseDataValidator.reset().parameter("clientId").value(clientId).notNull().longGreaterThanZero(); baseDataValidator.reset().parameter("groupId").value(groupId).mustBeBlankWhenParameterProvided("clientId", clientId); } - if (loanType.isGroupLoan()) { + if (loanType.isGroupAccount()) { baseDataValidator.reset().parameter("groupId").value(groupId).notNull().longGreaterThanZero(); baseDataValidator.reset().parameter("clientId").value(clientId).mustBeBlankWhenParameterProvided("groupId", groupId); } - if (loanType.isJLGLoan()) { + if (loanType.isJLGAccount()) { baseDataValidator.reset().parameter("clientId").value(clientId).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter("groupId").value(groupId).notNull().longGreaterThanZero(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/BulkLoansReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/BulkLoansReadPlatformServiceImpl.java index b0409c981..8a6506259 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/BulkLoansReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/BulkLoansReadPlatformServiceImpl.java @@ -14,10 +14,8 @@ import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.staff.data.StaffAccountSummaryCollectionData; -import org.mifosplatform.portfolio.client.data.ClientLoanAccountSummaryData; -import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; -import org.mifosplatform.portfolio.group.data.GroupAccountSummaryData; -import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; +import org.mifosplatform.portfolio.accountdetails.data.LoanAccountSummaryData; +import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -28,16 +26,14 @@ public class BulkLoansReadPlatformServiceImpl implements BulkLoansReadPlatformSe private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; - private final ClientReadPlatformService clientReadPlatformService; - private final GroupReadPlatformService groupReadPlatformService; + private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; @Autowired public BulkLoansReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, - final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService) { + final AccountDetailsReadPlatformService accountDetailsReadPlatformService) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); - this.clientReadPlatformService = clientReadPlatformService; - this.groupReadPlatformService = groupReadPlatformService; + this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; } @Override @@ -51,23 +47,23 @@ public class BulkLoansReadPlatformServiceImpl implements BulkLoansReadPlatformSe final StaffGroupMapper staffGroupMapper = new StaffGroupMapper(); final String groupSql = "select distinct " + staffGroupMapper.schema(); - final List clientSummaryList = this.jdbcTemplate.query(clientSql, + final List clientSummaryList = this.jdbcTemplate.query(clientSql, staffClientMapper, new Object[] { loanOfficerId }); - for (StaffAccountSummaryCollectionData.ClientSummary clientSummary : clientSummaryList) { + for (StaffAccountSummaryCollectionData.LoanAccountSummary clientSummary : clientSummaryList) { - final Collection clientLoanAccounts = this.clientReadPlatformService + final Collection clientLoanAccounts = this.accountDetailsReadPlatformService .retrieveClientLoanAccountsByLoanOfficerId(clientSummary.getId(), loanOfficerId); clientSummary.setLoans(clientLoanAccounts); } - final List groupSummaryList = this.jdbcTemplate.query(groupSql, staffGroupMapper, + final List groupSummaryList = this.jdbcTemplate.query(groupSql, staffGroupMapper, new Object[] { loanOfficerId }); - for (StaffAccountSummaryCollectionData.GroupSummary groupSummary : groupSummaryList) { + for (StaffAccountSummaryCollectionData.LoanAccountSummary groupSummary : groupSummaryList) { - final Collection groupLoanAccounts = this.groupReadPlatformService + final Collection groupLoanAccounts = this.accountDetailsReadPlatformService .retrieveGroupLoanAccountsByLoanOfficerId(groupSummary.getId(), loanOfficerId); groupSummary.setLoans(groupLoanAccounts); @@ -76,7 +72,7 @@ public class BulkLoansReadPlatformServiceImpl implements BulkLoansReadPlatformSe return new StaffAccountSummaryCollectionData(clientSummaryList, groupSummaryList); } - private static final class StaffClientMapper implements RowMapper { + private static final class StaffClientMapper implements RowMapper { public String schema() { return " c.id as id, c.display_name as displayName from m_client c " @@ -84,16 +80,16 @@ public class BulkLoansReadPlatformServiceImpl implements BulkLoansReadPlatformSe } @Override - public StaffAccountSummaryCollectionData.ClientSummary mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) + public StaffAccountSummaryCollectionData.LoanAccountSummary mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { final Long id = JdbcSupport.getLong(rs, "id"); final String displayName = rs.getString("displayName"); - return new StaffAccountSummaryCollectionData.ClientSummary(id, displayName); + return new StaffAccountSummaryCollectionData.LoanAccountSummary(id, displayName); } } - private static final class StaffGroupMapper implements RowMapper { + private static final class StaffGroupMapper implements RowMapper { public String schema() { return " g.id as id, g.display_name as name from m_group g" @@ -101,12 +97,12 @@ public class BulkLoansReadPlatformServiceImpl implements BulkLoansReadPlatformSe } @Override - public StaffAccountSummaryCollectionData.GroupSummary mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) + public StaffAccountSummaryCollectionData.LoanAccountSummary mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { final Long id = JdbcSupport.getLong(rs, "id"); final String name = rs.getString("name"); - return new StaffAccountSummaryCollectionData.GroupSummary(id, name); + return new StaffAccountSummaryCollectionData.LoanAccountSummary(id, name); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java index 40c1c1e4d..1e55611b1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java @@ -24,6 +24,7 @@ import org.mifosplatform.organisation.staff.exception.StaffNotFoundException; import org.mifosplatform.organisation.staff.exception.StaffRoleException; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; +import org.mifosplatform.portfolio.accountdetails.service.AccountEnumerations; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.collateral.domain.LoanCollateral; @@ -53,7 +54,6 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository; import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy; import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException; import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException; -import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -154,7 +154,7 @@ public class LoanAssembler { final String loanTypeParameterName = "loanType"; final String loanTypeStr = fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element); - final EnumOptionData loanType = LoanEnumerations.loanType(loanTypeStr); + final EnumOptionData loanType = AccountEnumerations.loanType(loanTypeStr); if (clientId != null) { client = this.clientRepository.findOneWithNotFoundDetection(clientId); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 11e15721a..d348e94bc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -29,6 +29,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.portfolio.accountdetails.service.AccountEnumerations; import org.mifosplatform.portfolio.calendar.data.CalendarData; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService; @@ -492,7 +493,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final String groupHierarchy = rs.getString("groupHierarchy"); final Integer loanTypeId = JdbcSupport.getInteger(rs, "loanType"); - final EnumOptionData loanType = LoanEnumerations.loanType(loanTypeId); + final EnumOptionData loanType = AccountEnumerations.loanType(loanTypeId); final Long fundId = JdbcSupport.getLong(rs, "fundId"); final String fundName = rs.getString("fundName"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index c8ea47a3d..fb18ad442 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -34,6 +34,7 @@ import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; @@ -65,7 +66,6 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository; -import org.mifosplatform.portfolio.loanaccount.domain.LoanType; import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException; import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; @@ -1040,7 +1040,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final WorkingDays workingDays = this.workingDaysRepository.findOne(); final Collection loanStatuses = new ArrayList(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue())); - final Collection loanTypes = new ArrayList(Arrays.asList(LoanType.GROUP.getValue(), LoanType.JLG.getValue())); + final Collection loanTypes = new ArrayList(Arrays.asList(AccountType.GROUP.getValue(), AccountType.JLG.getValue())); final Collection loanIds = new ArrayList(loanCalendarInstances.size()); // loop through loanCalendarInstances to get loan ids for (CalendarInstance calendarInstance : loanCalendarInstances) { @@ -1064,10 +1064,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (loan.isGroupLoan()) { if (loan.loanProduct().isIncludeInBorrowerCycle()) { loansToUpdate = this.loanRepository.getGroupLoansToUpdateLoanCounter(loan.getCurrentLoanCounter(), loan.getGroupId(), - LoanType.GROUP.getValue()); + AccountType.GROUP.getValue()); } else { loansToUpdate = this.loanRepository.getGroupLoansToUpdateLoanProductCounter(loan.getLoanProductLoanCounter(), - loan.getGroupId(), LoanType.GROUP.getValue()); + loan.getGroupId(), AccountType.GROUP.getValue()); } } else { @@ -1092,7 +1092,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (loan.isGroupLoan()) { final List loansToUpdateForLoanCounter = this.loanRepository.getGroupLoansDisbursedAfter(actualDisbursementDate.toDate(), - loan.getGroupId(), LoanType.GROUP.getValue()); + loan.getGroupId(), AccountType.GROUP.getValue()); final Integer newLoanCounter = getNewGroupLoanCounter(loan); final Integer newLoanProductCounter = getNewGroupLoanProductCounter(loan); updateLoanCounter(loan, loansToUpdateForLoanCounter, newLoanCounter, newLoanProductCounter); @@ -1107,7 +1107,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private Integer getNewGroupLoanCounter(final Loan loan) { - Integer maxClientLoanCounter = this.loanRepository.getMaxGroupLoanCounter(loan.getGroupId(), LoanType.GROUP.getValue()); + Integer maxClientLoanCounter = this.loanRepository.getMaxGroupLoanCounter(loan.getGroupId(), AccountType.GROUP.getValue()); if (maxClientLoanCounter == null) { maxClientLoanCounter = 1; } else { @@ -1119,7 +1119,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private Integer getNewGroupLoanProductCounter(final Loan loan) { Integer maxLoanProductLoanCounter = this.loanRepository.getMaxGroupLoanProductCounter(loan.loanProduct().getId(), - loan.getGroupId(), LoanType.GROUP.getValue()); + loan.getGroupId(), AccountType.GROUP.getValue()); if (maxLoanProductLoanCounter == null) { maxLoanProductLoanCounter = 1; } else { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java index ee5ba9673..6ebdea8e9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java @@ -6,17 +6,17 @@ package org.mifosplatform.portfolio.loanproduct.service; import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.accountdetails.service.AccountEnumerations; import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData; import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType; -import org.mifosplatform.portfolio.loanaccount.domain.LoanType; import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod; import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType; -public class LoanEnumerations { +public class LoanEnumerations{ public static final String LOAN_TERM_FREQUENCY_TYPE = "loanTermFrequencyType"; public static final String TERM_FREQUENCY_TYPE = "termFrequencyType"; @@ -44,7 +44,7 @@ public class LoanEnumerations { return interestType(id); } else if (typeName.equals(INTEREST_CALCULATION_PERIOD_TYPE)) { return interestCalculationPeriodType(id); - } else if (typeName.equals(LOAN_TYPE)) { return loanType(id); } + } else if (typeName.equals(LOAN_TYPE)) { return AccountEnumerations.loanType(id); } return null; } @@ -333,32 +333,4 @@ public class LoanEnumerations { return optionData; } - - public static EnumOptionData loanType(final Integer loanTypeId) { - return loanType(LoanType.fromInt(loanTypeId)); - } - - public static EnumOptionData loanType(final String name) { - return loanType(LoanType.fromName(name)); - } - - public static EnumOptionData loanType(final LoanType type) { - EnumOptionData optionData = new EnumOptionData(LoanType.INVALID.getValue().longValue(), LoanType.INVALID.getCode(), "Invalid"); - switch (type) { - case INVALID: - optionData = new EnumOptionData(LoanType.INVALID.getValue().longValue(), LoanType.INVALID.getCode(), "Invalid"); - break; - case INDIVIDUAL: - optionData = new EnumOptionData(LoanType.INDIVIDUAL.getValue().longValue(), LoanType.INDIVIDUAL.getCode(), "Individual"); - break; - case GROUP: - optionData = new EnumOptionData(LoanType.GROUP.getValue().longValue(), LoanType.GROUP.getCode(), "Group"); - break; - case JLG: - optionData = new EnumOptionData(LoanType.JLG.getValue().longValue(), LoanType.JLG.getCode(), "JLG"); - break; - } - - return optionData; - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java index 0b0826f29..bc8ede232 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java @@ -38,6 +38,7 @@ import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper; +import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; @@ -106,6 +107,10 @@ public class SavingsAccountAssembler { if (groupId != null) { group = this.groupRepository.findOneWithNotFoundDetection(groupId); } + + if(group != null && client != null){ + if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); } + } final Long fieldOfficerId = fromApiJsonHelper.extractLongNamed(fieldOfficerIdParamName, element); if (fieldOfficerId != null) { From ea3662519b73a61342813df856bd1367ddf5ca4c Mon Sep 17 00:00:00 2001 From: John Woodlock Date: Mon, 5 Aug 2013 11:37:09 +0100 Subject: [PATCH 17/47] mifosx-567 fix externalId defaulting to blank string if not passed as a parameter --- .../mifosplatform/infrastructure/core/api/JsonCommand.java | 4 ++++ .../org/mifosplatform/organisation/staff/domain/Staff.java | 2 +- .../mifosplatform/portfolio/loanaccount/domain/Loan.java | 6 +++--- .../service/LoanWritePlatformServiceJpaRepositoryImpl.java | 4 ++-- .../portfolio/loanproduct/domain/LoanProduct.java | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java index eda5217d5..d6c81ed8a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/JsonCommand.java @@ -306,6 +306,10 @@ public final class JsonCommand { final String value = this.fromApiJsonHelper.extractStringNamed(parameterName, parsedCommand); return StringUtils.defaultIfEmpty(value, ""); } + + public String stringValueOfParameterNamedAllowingNull(final String parameterName) { + return this.fromApiJsonHelper.extractStringNamed(parameterName, parsedCommand); + } public boolean isChangeInBigDecimalParameterNamedDefaultingZeroToNull(final String parameterName, final BigDecimal existingValue) { boolean isChanged = false; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java index d23c69a45..2a82ef24a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java @@ -64,7 +64,7 @@ public class Staff extends AbstractPersistable { String lastname = command.stringValueOfParameterNamed(lastnameParamName); final String externalIdParamName = "externalId"; - String externalId = command.stringValueOfParameterNamed(externalIdParamName); + String externalId = command.stringValueOfParameterNamedAllowingNull(externalIdParamName); final String isLoanOfficerParamName = "isLoanOfficer"; final boolean isLoanOfficer = command.booleanPrimitiveValueOfParameterNamed(isLoanOfficerParamName); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 9b33ffb57..093a3579e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1332,7 +1332,7 @@ public class Loan extends AbstractPersistable { actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); - final String txnExternalId = command.stringValueOfParameterNamed("externalId"); + final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); this.actualDisbursementDate = actualDisbursementDate.toDate(); this.disbursedBy = currentUser; @@ -1832,7 +1832,7 @@ public class Loan extends AbstractPersistable { existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed("transactionDate"); - final String txnExternalId = command.stringValueOfParameterNamed("externalId"); + final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); this.closedOnDate = writtenOffOnLocalDate.toDate(); this.writtenOffOnDate = writtenOffOnLocalDate.toDate(); @@ -1879,7 +1879,7 @@ public class Loan extends AbstractPersistable { existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); final LocalDate closureDate = command.localDateValueOfParameterNamed("transactionDate"); - final String txnExternalId = command.stringValueOfParameterNamed("externalId"); + final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); this.closedOnDate = closureDate.toDate(); changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index fb18ad442..02abb94d1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -349,7 +349,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); - final String txnExternalId = command.stringValueOfParameterNamed("externalId"); + final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); final Map changes = new LinkedHashMap(); changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); @@ -470,7 +470,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); - final String txnExternalId = command.stringValueOfParameterNamed("externalId"); + final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); final Map changes = new LinkedHashMap(); changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index ec000bbea..95716c966 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -134,7 +134,7 @@ public class LoanProduct extends AbstractPersistable { final LocalDate startDate = command.localDateValueOfParameterNamed("startDate"); final LocalDate closeDate = command.localDateValueOfParameterNamed("closeDate"); - final String externalId = command.stringValueOfParameterNamed("externalId"); + final String externalId = command.stringValueOfParameterNamedAllowingNull("externalId"); return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, From 49c968da4affdc5775fd7aff865ac2fef2c40ee0 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Mon, 5 Aug 2013 13:03:14 +0530 Subject: [PATCH 18/47] MIFOSX-589 check edit transaction transactiondate for holiday or nonworking day --- .../mifosplatform/portfolio/loanaccount/domain/Loan.java | 6 +++++- .../service/LoanWritePlatformServiceJpaRepositoryImpl.java | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 093a3579e..2664268a2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1741,7 +1741,11 @@ public class Loan extends AbstractPersistable { public ChangedTransactionDetail adjustExistingTransaction(final LoanTransaction newTransactionDetail, final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment, - final List existingTransactionIds, final List existingReversedTransactionIds) { + final List existingTransactionIds, final List existingReversedTransactionIds, final boolean isHolidayEnabled, + final List holidays, final WorkingDays workingDays) { + + validateRepaymentDateIsOnHoliday(newTransactionDetail.getTransactionDate(), isHolidayEnabled, holidays); + validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(), workingDays); ChangedTransactionDetail changedTransactionDetail = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 02abb94d1..d07cf6886 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -488,8 +488,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf newTransactionDetail = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate); } + final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate()); + final WorkingDays workingDays = this.workingDaysRepository.findOne(); + final ChangedTransactionDetail changedTransactionDetail = loan.adjustExistingTransaction(newTransactionDetail, - defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds); + defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds, + isHolidayEnabled, holidays, workingDays); if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) { if (paymentDetail != null) { From 1320ef5080ab979ceb85b3397d77eceefee97748 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Mon, 5 Aug 2013 15:37:30 +0530 Subject: [PATCH 19/47] MIFSOX-428 Api fix for edit user --- .../domain/JpaUserDomainService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/JpaUserDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/JpaUserDomainService.java index 366e996e3..6ed40691a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/JpaUserDomainService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/JpaUserDomainService.java @@ -29,7 +29,7 @@ public class JpaUserDomainService implements UserDomainService { @Transactional @Override - public void create(final AppUser appUser , final Boolean sendPasswordToEmail) { + public void create(final AppUser appUser, final Boolean sendPasswordToEmail) { generateKeyUsedForPasswordSalting(appUser); @@ -40,11 +40,11 @@ public class JpaUserDomainService implements UserDomainService { this.userRepository.save(appUser); - if (sendPasswordToEmail.booleanValue()){ - final EmailDetail emailDetail = new EmailDetail(appUser.getFirstname(), appUser.getFirstname(), appUser.getEmail(), - appUser.getUsername()); + if (sendPasswordToEmail.booleanValue()) { + final EmailDetail emailDetail = new EmailDetail(appUser.getOffice().getName(), appUser.getFirstname(), appUser.getEmail(), + appUser.getUsername()); - this.emailService.sendToUserAccount(emailDetail, unencodedPassword); + this.emailService.sendToUserAccount(emailDetail, unencodedPassword); } } From 1fdf72679a11bb47501d321af3c4f91141fb7268 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Tue, 6 Aug 2013 12:13:52 +0530 Subject: [PATCH 20/47] added modifications for creating group and JLG savings account --- api-docs/apiLive.htm | 245 ++++++++++++++++++ .../V94__added_savings_accont type.sql | 2 + .../data/SavingsAccountSummaryData.java | 5 +- ...sReadPlatformServiceJpaRepositoryImpl.java | 8 +- .../group/api/GroupsApiResource.java | 2 +- .../portfolio/loanaccount/domain/Loan.java | 3 +- .../savings/domain/SavingsAccount.java | 37 ++- .../domain/SavingsAccountAssembler.java | 21 +- 8 files changed, 297 insertions(+), 26 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V94__added_savings_accont type.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index ef2dc2294..f982202d0 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -567,6 +567,15 @@ + + + groups/{groupId}/accounts + + Retrive Group accounts summary + + + + @@ -3888,6 +3897,233 @@ GET https://DomainName/mifosng-provider/api/v1/groups/1?associations=clientMembe + +   +
+
+

Retrieve Group accounts overview

+

+ An example of how a loan portfolio summary can be provided. This + is requested in a specific use case of the reference application.
It is quite reasonable to add resources like this to simplify User Interface development. +

+

Example Requests:

+
groups/1/accounts
+
+
+
groups/1/accounts?fields=loanAccounts,savingsAccounts,memberLoanAccounts,
memberSavingsAccounts
+
+
+ +GET https://DomainName/api/v1/groups/{groupId}/accounts + + +{ + "loanAccounts": [ + { + "id": 3, + "accountNo": "000000003", + "productId": 3, + "productName": "daily product", + "status": { + "id": 100, + "code": "loanStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "pendingApproval": true, + "waitingForDisbursal": false, + "active": false, + "closedObligationsMet": false, + "closedWrittenOff": false, + "closedRescheduled": false, + "closed": false, + "overpaid": false + }, + "loanType": { + "id": 2, + "code": "accountType.group", + "value": "Group" + } + } + ], + "savingsAccounts": [ + { + "id": 9, + "accountNo": "000000009", + "productId": 1, + "productName": "p_sav", + "status": { + "id": 100, + "code": "savingsAccountStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "submittedAndPendingApproval": true, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": false, + "closed": false + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "accountType": { + "id": 2, + "code": "accountType.group", + "value": "Group" + } + }, + { + "id": 4, + "accountNo": "000000004", + "productId": 1, + "productName": "p_sav", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "accountType": { + "id": 2, + "code": "accountType.group", + "value": "Group" + } + } + ], + "memberLoanAccounts": [ + { + "id": 4, + "accountNo": "000000004", + "productId": 1, + "productName": "testLoan", + "status": { + "id": 200, + "code": "loanStatusType.approved", + "value": "Approved", + "pendingApproval": false, + "waitingForDisbursal": true, + "active": false, + "closedObligationsMet": false, + "closedWrittenOff": false, + "closedRescheduled": false, + "closed": false, + "overpaid": false + }, + "loanType": { + "id": 3, + "code": "accountType.jlg", + "value": "JLG" + } + }, + { + "id": 7, + "accountNo": "000000007", + "productId": 2, + "productName": "weekly product", + "status": { + "id": 100, + "code": "loanStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "pendingApproval": true, + "waitingForDisbursal": false, + "active": false, + "closedObligationsMet": false, + "closedWrittenOff": false, + "closedRescheduled": false, + "closed": false, + "overpaid": false + }, + "loanType": { + "id": 3, + "code": "accountType.jlg", + "value": "JLG" + } + } + ], + "memberSavingsAccounts": [ + { + "id": 3, + "accountNo": "000000003", + "productId": 1, + "productName": "p_sav", + "status": { + "id": 100, + "code": "savingsAccountStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "submittedAndPendingApproval": true, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": false, + "closed": false + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "accountType": { + "id": 3, + "code": "accountType.jlg", + "value": "JLG" + } + }, + { + "id": 10, + "accountNo": "000000010", + "productId": 1, + "productName": "p_sav", + "status": { + "id": 100, + "code": "savingsAccountStatusType.submitted.and.pending.approval", + "value": "Submitted and pending approval", + "submittedAndPendingApproval": true, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": false, + "closed": false + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "accountType": { + "id": 3, + "code": "accountType.jlg", + "value": "JLG" + } + } + ] +} + +
+
+ +  
@@ -17110,6 +17346,15 @@ GET https://DomainName/api/v1/search?query=000000111 + + + groups/{groupId}/accounts + + Retrive Group accounts summary + + + + Loan Product loanproducts diff --git a/mifosng-db/migrations/core_db/V94__added_savings_accont type.sql b/mifosng-db/migrations/core_db/V94__added_savings_accont type.sql new file mode 100755 index 000000000..87985e710 --- /dev/null +++ b/mifosng-db/migrations/core_db/V94__added_savings_accont type.sql @@ -0,0 +1,2 @@ +ALTER TABLE `m_savings_account` + ADD COLUMN `account_type_enum` SMALLINT(5) NOT NULL DEFAULT '1' AFTER `status_enum`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java index dc5cde49b..ad8fee7fc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/data/SavingsAccountSummaryData.java @@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.accountdetails.data; import java.math.BigDecimal; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData; @@ -24,10 +25,11 @@ public class SavingsAccountSummaryData { private final SavingsAccountStatusEnumData status; private final CurrencyData currency; private final BigDecimal accountBalance; + private final EnumOptionData accountType; public SavingsAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId, final String productName, final SavingsAccountStatusEnumData status, final CurrencyData currency, - final BigDecimal accountBalance) { + final BigDecimal accountBalance, final EnumOptionData accountType) { this.id = id; this.accountNo = accountNo; this.externalId = externalId; @@ -36,5 +38,6 @@ public class SavingsAccountSummaryData { this.status = status; this.currency = currency; this.accountBalance = accountBalance; + this.accountType = accountType; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java index 3e99e27a5..f707853be 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java @@ -63,7 +63,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou List memberLoanAccounts = retrieveLoanAccountDetails(loanWhereClauseForMembers, new Object[] { groupId }); List memberSavingsAccounts = retrieveAccountDetails(savingswhereClauseForMembers, new Object[] { groupId }); - return new AccountSummaryCollectionData(groupLoanAccounts, groupSavingsAccounts,memberLoanAccounts, memberSavingsAccounts); + return new AccountSummaryCollectionData(groupLoanAccounts, groupSavingsAccounts, memberLoanAccounts, memberSavingsAccounts); } @Override @@ -105,6 +105,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou public SavingsAccountSummaryDataMapper() { StringBuilder accountsSummary = new StringBuilder(); accountsSummary.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, sa.status_enum as statusEnum, "); + accountsSummary.append("sa.account_type_enum as accountType, "); accountsSummary.append("sa.account_balance_derived as accountBalance, "); accountsSummary.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, "); accountsSummary.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); @@ -132,6 +133,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou final Integer statusId = JdbcSupport.getInteger(rs, "statusEnum"); final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accountBalance"); final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusId); + final Integer accountType = JdbcSupport.getInteger(rs, "accountType"); + final EnumOptionData accountTypeData = AccountEnumerations.loanType(accountType); final String currencyCode = rs.getString("currencyCode"); final String currencyName = rs.getString("currencyName"); @@ -141,7 +144,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, currencyNameCode); - return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, status, currency, accountBalance); + return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, status, currency, accountBalance, + accountTypeData); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index 3e008d920..5ccd9f420 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -322,7 +322,7 @@ public class GroupsApiResource { @Path("{groupId}/accounts") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveLoans(@PathParam("groupId") final Long groupId, @Context final UriInfo uriInfo) { + public String retrieveAccounts(@PathParam("groupId") final Long groupId, @Context final UriInfo uriInfo) { this.context.authenticatedUser().validateHasReadPermission("GROUP"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 2664268a2..f37368d2d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1108,7 +1108,8 @@ public class Loan extends AbstractPersistable { if (client != null && client.isActivatedAfter(submittedOn)) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date."; throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, submittedOn); - } else if (group != null && group.isActivatedAfter(submittedOn)) { + } + if (group != null && group.isActivatedAfter(submittedOn)) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date."; throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage, submittedOn); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 2691d21fc..3b4e39e2a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -58,6 +58,7 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType; @@ -105,6 +106,10 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "status_enum", nullable = false) private Integer status; + @SuppressWarnings("unused") + @Column(name = "account_type_enum", nullable = false) + private Integer accountType; + @Temporal(TemporalType.DATE) @Column(name = "submittedon_date", nullable = true) private Date submittedOnDate; @@ -254,8 +259,9 @@ public class SavingsAccount extends AbstractPersistable { } public static SavingsAccount createNewApplicationForSubmittal(final Client client, final Group group, final SavingsProduct product, - final Staff fieldOfficer, final String accountNo, final String externalId, final LocalDate submittedOnDate, - final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final Staff fieldOfficer, final String accountNo, final String externalId, final AccountType accountType, + final LocalDate submittedOnDate, final BigDecimal interestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, @@ -263,15 +269,16 @@ public class SavingsAccount extends AbstractPersistable { final MonthDay annualFeeOnMonthDay) { final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL; - return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, submittedOnDate, interestRate, - interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, - minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, - annualFeeAmount, annualFeeOnMonthDay); + return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, + interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, + interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, + withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay); } private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer, - final String accountNo, final String externalId, final SavingsAccountStatusType status, final LocalDate submittedOnDate, - final BigDecimal nominalAnnualInterestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final String accountNo, final String externalId, final SavingsAccountStatusType status, final AccountType accountType, + final LocalDate submittedOnDate, final BigDecimal nominalAnnualInterestRate, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, @@ -290,6 +297,7 @@ public class SavingsAccount extends AbstractPersistable { this.currency = product.currency(); this.externalId = externalId; this.status = status.getValue(); + this.accountType = accountType.getValue(); this.submittedOnDate = submittedOnDate.toDate(); this.nominalAnnualInterestRate = nominalAnnualInterestRate; this.interestCompoundingPeriodType = interestCompoundingPeriodType.getValue(); @@ -1612,14 +1620,19 @@ public class SavingsAccount extends AbstractPersistable { updateToNextAnnualFeeDueDateFrom(getActivationLocalDate()); } - if (this.client.isActivatedAfter(activationDate)) { - + if (this.client != null && this.client.isActivatedAfter(activationDate)) { final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); final String dateAsString = formatter.print(client.getActivationLocalDate()); - baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName).value(dateAsString) .failWithCodeNoParameterAddedToErrorCode("cannot.be.before.client.activation.date"); - + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (this.group != null && this.group.isActivatedAfter(activationDate)) { + final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); + final String dateAsString = formatter.print(client.getActivationLocalDate()); + baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName).value(dateAsString) + .failWithCodeNoParameterAddedToErrorCode("cannot.be.before.group.activation.date"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java index bc8ede232..0ede17f91 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java @@ -34,6 +34,7 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; +import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.group.domain.Group; @@ -67,8 +68,7 @@ public class SavingsAccountAssembler { public SavingsAccountAssembler(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper, final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository, final StaffRepositoryWrapper staffRepository, final SavingsProductRepository savingProductRepository, - final SavingsAccountRepositoryWrapper savingsAccountRepository, - final FromJsonHelper fromApiJsonHelper) { + final SavingsAccountRepositoryWrapper savingsAccountRepository, final FromJsonHelper fromApiJsonHelper) { this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper; this.clientRepository = clientRepository; this.groupRepository = groupRepository; @@ -97,19 +97,22 @@ public class SavingsAccountAssembler { Client client = null; Group group = null; Staff fieldOfficer = null; - + AccountType accountType = AccountType.INVALID; final Long clientId = fromApiJsonHelper.extractLongNamed(clientIdParamName, element); if (clientId != null) { client = this.clientRepository.findOneWithNotFoundDetection(clientId); + accountType = AccountType.INDIVIDUAL; } final Long groupId = fromApiJsonHelper.extractLongNamed(groupIdParamName, element); if (groupId != null) { group = this.groupRepository.findOneWithNotFoundDetection(groupId); + accountType = AccountType.GROUP; } - - if(group != null && client != null){ + + if (group != null && client != null) { if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); } + accountType = AccountType.JLG; } final Long fieldOfficerId = fromApiJsonHelper.extractLongNamed(fieldOfficerIdParamName, element); @@ -215,10 +218,10 @@ public class SavingsAccountAssembler { monthDayOfAnnualFee = product.monthDayOfAnnualFee(); } - final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNo, externalId, - submittedOnDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, - interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, - withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, monthDayOfAnnualFee); + final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNo, + externalId, accountType, submittedOnDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, + interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, + lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, monthDayOfAnnualFee); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.validateNewApplicationState(DateUtils.getLocalDateOfTenant()); From 63610b3c50750b5a6ffdb2618246eb3f4da2623a Mon Sep 17 00:00:00 2001 From: Raman Kansal Date: Tue, 6 Aug 2013 09:33:12 +0530 Subject: [PATCH 21/47] Raman - Added Group Loan test --- .../GroupLoanIntegrationTest.java | 118 ++++++++++++++++++ .../integrationtests/GroupTest.java | 2 +- .../loans/LoanApplicationTestBuilder.java | 7 +- 3 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupLoanIntegrationTest.java diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupLoanIntegrationTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupLoanIntegrationTest.java new file mode 100644 index 000000000..8e216d82b --- /dev/null +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupLoanIntegrationTest.java @@ -0,0 +1,118 @@ +package org.mifosplatform.integrationtests; + +import static org.junit.Assert.assertEquals; + +import java.lang.System; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; +import org.mifosplatform.integrationtests.common.ClientHelper; +import org.mifosplatform.integrationtests.common.GroupHelper; +import org.mifosplatform.integrationtests.common.Utils; +import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder; +import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder; +import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper; + +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.builder.ResponseSpecBuilder; +import com.jayway.restassured.http.ContentType; +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; + +/** + * Group Loan Integration Test for checking Loan Application Repayment + * Schedule. + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class GroupLoanIntegrationTest { + + private ResponseSpecification responseSpec; + private RequestSpecification requestSpec; + private LoanTransactionHelper loanTransactionHelper; + + @Before + public void setup() { + Utils.initializeRESTAssured(); + requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + } + + @Test + public void checkGroupLoanCreateAndDisburseFlow() { + loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec); + + Integer clientID = ClientHelper.createClient(requestSpec, responseSpec); + Integer groupID = GroupHelper.createGroup(requestSpec, responseSpec,true); + groupID = GroupHelper.associateClient(requestSpec, responseSpec, groupID.toString(),clientID.toString()); + + Integer loanProductID = createLoanProduct(); + Integer loanID = applyForLoanApplication(groupID, loanProductID); + ArrayList loanSchedule = loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID); + verifyLoanRepaymentSchedule(loanSchedule); + + } + + private Integer createLoanProduct() { + System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); + String loanProductJSON = new LoanProductTestBuilder() // + .withPrincipal("12,000.00") // + .withNumberOfRepayments("4") // + .withRepaymentAfterEvery("1") // + .withRepaymentTypeAsMonth() // + .withinterestRatePerPeriod("1") // + .withInterestRateFrequencyTypeAsMonths() // + .withAmortizationTypeAsEqualInstallments() // + .withInterestTypeAsDecliningBalance() // + .build(); + return loanTransactionHelper.getLoanProductId(loanProductJSON); + } + + private Integer applyForLoanApplication(final Integer groupID, final Integer loanProductID) { + System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------"); + String loanApplicationJSON = new LoanApplicationTestBuilder() // + .withPrincipal("12,000.00") // + .withLoanTermFrequency("4") // + .withLoanTermFrequencyAsMonths() // + .withNumberOfRepayments("4") // + .withRepaymentEveryAfter("1") // + .withRepaymentFrequencyTypeAsMonths() // + .withInterestRatePerPeriod("2") // + .withAmortizationTypeAsEqualInstallments() // + .withInterestTypeAsDecliningBalance() // + .withInterestCalculationPeriodTypeSameAsRepaymentPeriod() // + .withExpectedDisbursementDate("20 September 2011") // + .withSubmittedOnDate("20 September 2011") // + .withLoanType("group") + .build(groupID.toString(), loanProductID.toString()); + System.out.println(loanApplicationJSON); + return loanTransactionHelper.getLoanId(loanApplicationJSON); + } + + private void verifyLoanRepaymentSchedule(final ArrayList loanSchedule) { + System.out.println("--------------------VERIFYING THE PRINCIPAL DUES,INTEREST DUE AND DUE DATE--------------------------"); + + assertEquals("Checking for Due Date for 1st Month", new ArrayList(Arrays.asList(2011, 10, 20)), + loanSchedule.get(1).get("dueDate")); + assertEquals("Checking for Principal Due for 1st Month", new Float("2911.49"), loanSchedule.get(1).get("principalOriginalDue")); + assertEquals("Checking for Interest Due for 1st Month", new Float("240.00"), loanSchedule.get(1).get("interestOriginalDue")); + + assertEquals("Checking for Due Date for 2nd Month", new ArrayList(Arrays.asList(2011, 11, 20)), + loanSchedule.get(2).get("dueDate")); + assertEquals("Checking for Principal Due for 2nd Month", new Float("2969.71"), loanSchedule.get(2).get("principalDue")); + assertEquals("Checking for Interest Due for 2nd Month", new Float("181.77"), loanSchedule.get(2).get("interestOriginalDue")); + + assertEquals("Checking for Due Date for 3rd Month", new ArrayList(Arrays.asList(2011, 12, 20)), + loanSchedule.get(3).get("dueDate")); + assertEquals("Checking for Principal Due for 3rd Month", new Float("3029.1"), loanSchedule.get(3).get("principalDue")); + assertEquals("Checking for Interest Due for 3rd Month", new Float("122.38"), loanSchedule.get(3).get("interestOriginalDue")); + + assertEquals("Checking for Due Date for 4th Month", new ArrayList(Arrays.asList(2012, 1, 20)), + loanSchedule.get(4).get("dueDate")); + assertEquals("Checking for Principal Due for 4th Month", new Float("3089.7"), loanSchedule.get(4).get("principalDue")); + assertEquals("Checking for Interest Due for 4th Month", new Float("61.79"), loanSchedule.get(4).get("interestOriginalDue")); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java index 877cbbba2..8785a2b6a 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java @@ -60,7 +60,7 @@ public class GroupTest { GroupHelper.verifyGroupDetails(requestSpec, responseSpec, groupID,"name",updatedGroupName); groupID = GroupHelper.createGroup(requestSpec, responseSpec); - Integer groupID2 = GroupHelper.deleteGroup(requestSpec, responseSpec, groupID.toString()); + GroupHelper.deleteGroup(requestSpec, responseSpec, groupID.toString()); GroupHelper.verifyGroupDeleted(requestSpec, responseSpec, groupID); } } \ No newline at end of file diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java index b1b0de92a..f9bd1894b 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanApplicationTestBuilder.java @@ -33,12 +33,15 @@ public class LoanApplicationTestBuilder { private String submittedOnDate = ""; private String loanType = "individual"; - public String build(final String clientID, final String loanProductId) { + public String build(final String ID, final String loanProductId) { HashMap map = new HashMap(); map.put("dateFormat", "dd MMMM yyyy"); map.put("locale", "en_GB"); - map.put("clientId", clientID); + if (loanType == "group") + map.put("groupId", ID); + else + map.put("clientId", ID); map.put("productId", loanProductId); map.put("principal", principal); map.put("loanTermFrequency", loanTermFrequency); From dc38cb7e0b480244b2aecb2ee61b647cbe1d6ad8 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Tue, 6 Aug 2013 12:38:41 +0530 Subject: [PATCH 22/47] merging 327880473293c07fb82be9363f55d29547cdf9f9 into develop branch --- ...ettings_for_holiday_and_non_workingday.sql | 2 + .../domain/ConfigurationDomainService.java | 2 + .../domain/ConfigurationDomainServiceJpa.java | 19 ++++- .../portfolio/loanaccount/domain/Loan.java | 70 +++++++++++-------- .../loanaccount/service/LoanAssembler.java | 10 ++- ...WritePlatformServiceJpaRepositoryImpl.java | 24 +++++-- 6 files changed, 84 insertions(+), 43 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V92__configuration_settings_for_holiday_and_non_workingday.sql diff --git a/mifosng-db/migrations/core_db/V92__configuration_settings_for_holiday_and_non_workingday.sql b/mifosng-db/migrations/core_db/V92__configuration_settings_for_holiday_and_non_workingday.sql new file mode 100644 index 000000000..0b84ea2b0 --- /dev/null +++ b/mifosng-db/migrations/core_db/V92__configuration_settings_for_holiday_and_non_workingday.sql @@ -0,0 +1,2 @@ +INSERT INTO `c_configuration` (`name`, `enabled`) VALUES ('allow-transactions-on-holiday', 0); +INSERT INTO `c_configuration` (`name`, `enabled`) VALUES ('allow-transactions-on-non_workingday', 0); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java index 7bd6f8cf6..a343385c4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java @@ -11,4 +11,6 @@ public interface ConfigurationDomainService { boolean isAmazonS3Enabled(); boolean isRescheduleFutureRepaymentsEnabled(); boolean isRescheduleRepaymentsOnHolidaysEnabled(); + boolean allowTransactionsOnHolidayEnabled(); + boolean allowTransactionsOnNonWorkingDayEnabled(); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java index 8adcffe12..c32cf223c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java @@ -63,6 +63,21 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(holidaysConfigurationProperty); } return property.isEnabled(); } - - + + @Override + public boolean allowTransactionsOnHolidayEnabled() { + final String allowTransactionsOnHolidayProperty = "allow-transactions-on-holiday"; + final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByName(allowTransactionsOnHolidayProperty); + if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(allowTransactionsOnHolidayProperty); } + return property.isEnabled(); + } + + @Override + public boolean allowTransactionsOnNonWorkingDayEnabled() { + final String allowTransactionsOnNonWorkingDayProperty = "allow-transactions-on-non_workingday"; + final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByName(allowTransactionsOnNonWorkingDayProperty); + if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(allowTransactionsOnNonWorkingDayProperty); } + return property.isEnabled(); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index f37368d2d..6d93fb163 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1076,8 +1076,8 @@ public class Loan extends AbstractPersistable { public void loanApplicationSubmittal(final AppUser currentUser, final LoanScheduleModel loanSchedule, final LoanApplicationTerms loanApplicationTerms, final LoanLifecycleStateMachine lifecycleStateMachine, - final LocalDate submittedOn, final String externalId, final boolean isHolidayEnabled, final List holidays, - final WorkingDays workingDays) { + final LocalDate submittedOn, final String externalId, final boolean allowTransactionsOnHoliday, final List holidays, + final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) { updateLoanSchedule(loanSchedule); @@ -1127,8 +1127,8 @@ public class Loan extends AbstractPersistable { } //validate if disbursement date is a holiday or a non-working day - validateDisbursementDateIsOnNonWorkingDay(workingDays); - validateDisbursementDateIsOnHoliday(isHolidayEnabled, holidays); + validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay); + validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays); } private LocalDate determineExpectedMaturityDate() { @@ -1319,7 +1319,8 @@ public class Loan extends AbstractPersistable { public Map disburse(final LoanScheduleGeneratorFactory loanScheduleFactory, final AppUser currentUser, final JsonCommand command, final ApplicationCurrency currency, final List existingTransactionIds, final List existingReversedTransactionIds, final Map actualChanges, final PaymentDetail paymentDetail, - final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List holidays, final WorkingDays workingDays) { + final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List holidays, + final WorkingDays workingDays, final boolean allowTransactionsOnHoliday, final boolean allowTransactionsOnNonWorkingDay) { updateLoanToPreDisbursalState(); @@ -1344,8 +1345,8 @@ public class Loan extends AbstractPersistable { actualChanges.put("actualDisbursementDate", command.stringValueOfParameterNamed("actualDisbursementDate")); //validate if disbursement date is a holiday or a non-working day - validateDisbursementDateIsOnNonWorkingDay(workingDays); - validateDisbursementDateIsOnHoliday(isHolidayEnabled, holidays); + validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay); + validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays); handleDisbursementTransaction(paymentDetail, actualDisbursementDate, txnExternalId); @@ -1517,11 +1518,11 @@ public class Loan extends AbstractPersistable { public ChangedTransactionDetail makeRepayment(final LoanTransaction repaymentTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine, final List existingTransactionIds, - final List existingReversedTransactionIds, final boolean isHolidayEnabled, final List holidays, - final WorkingDays workingDays) { + final List existingReversedTransactionIds, final boolean allowTransactionsOnHoliday, final List holidays, + final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) { - validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), isHolidayEnabled, holidays); - validateRepaymentDateIsOnNonWorkingDay(repaymentTransaction.getTransactionDate(), workingDays); + validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays); + validateRepaymentDateIsOnNonWorkingDay(repaymentTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay); existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); @@ -1742,11 +1743,12 @@ public class Loan extends AbstractPersistable { public ChangedTransactionDetail adjustExistingTransaction(final LoanTransaction newTransactionDetail, final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment, - final List existingTransactionIds, final List existingReversedTransactionIds, final boolean isHolidayEnabled, - final List holidays, final WorkingDays workingDays) { + final List existingTransactionIds, final List existingReversedTransactionIds, + final boolean allowTransactionsOnHoliday, final List holidays, final WorkingDays workingDays, + final boolean allowTransactionsOnNonWorkingDay) { - validateRepaymentDateIsOnHoliday(newTransactionDetail.getTransactionDate(), isHolidayEnabled, holidays); - validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(), workingDays); + validateRepaymentDateIsOnHoliday(newTransactionDetail.getTransactionDate(), allowTransactionsOnHoliday, holidays); + validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay); ChangedTransactionDetail changedTransactionDetail = null; @@ -2451,15 +2453,17 @@ public class Loan extends AbstractPersistable { } } - private void validateDisbursementDateIsOnNonWorkingDay(final WorkingDays workingDays) { - if(!WorkingDaysUtil.isWorkingDay(workingDays, getDisbursementDate())){ - final String errorMessage = "Expected disbursement date cannot be on a non working day"; - throw new LoanApplicationDateException("disbursement.date.on.non.working.day", errorMessage, getExpectedDisbursedOnLocalDate()); + private void validateDisbursementDateIsOnNonWorkingDay(final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) { + if (!allowTransactionsOnNonWorkingDay) { + if(!WorkingDaysUtil.isWorkingDay(workingDays, getDisbursementDate())){ + final String errorMessage = "Expected disbursement date cannot be on a non working day"; + throw new LoanApplicationDateException("disbursement.date.on.non.working.day", errorMessage, getExpectedDisbursedOnLocalDate()); + } } } - private void validateDisbursementDateIsOnHoliday(final boolean isHolidayEnabled, final List holidays) { - if (isHolidayEnabled) { + private void validateDisbursementDateIsOnHoliday(final boolean allowTransactionsOnHoliday, final List holidays) { + if (!allowTransactionsOnHoliday) { if (HolidayUtil.isHoliday(getDisbursementDate(), holidays)) { final String errorMessage = "Expected disbursement date cannot be on a holiday"; throw new LoanApplicationDateException("disbursement.date.on.holiday", errorMessage, @@ -2468,15 +2472,19 @@ public class Loan extends AbstractPersistable { } } - private void validateRepaymentDateIsOnNonWorkingDay(final LocalDate repaymentDate, final WorkingDays workingDays) { - if(!WorkingDaysUtil.isWorkingDay(workingDays, repaymentDate)){ - final String errorMessage = "Repayment date cannot be on a non working day"; - throw new LoanApplicationDateException("repayment.date.on.non.working.day", errorMessage, repaymentDate); + private void validateRepaymentDateIsOnNonWorkingDay(final LocalDate repaymentDate, final WorkingDays workingDays, + final boolean allowTransactionsOnNonWorkingDay) { + if (!allowTransactionsOnNonWorkingDay) { + if (!WorkingDaysUtil.isWorkingDay(workingDays, repaymentDate)) { + final String errorMessage = "Repayment date cannot be on a non working day"; + throw new LoanApplicationDateException("repayment.date.on.non.working.day", errorMessage, repaymentDate); + } } } - private void validateRepaymentDateIsOnHoliday(final LocalDate repaymentDate, final boolean isHolidayEnabled, final List holidays) { - if (isHolidayEnabled) { + private void validateRepaymentDateIsOnHoliday(final LocalDate repaymentDate, final boolean allowTransactionsOnHoliday, + final List holidays) { + if (!allowTransactionsOnHoliday) { if (HolidayUtil.isHoliday(repaymentDate, holidays)) { final String errorMessage = "Repayment date cannot be on a holiday"; throw new LoanApplicationDateException("repayment.date.on.holiday", errorMessage, repaymentDate); @@ -2520,11 +2528,11 @@ public class Loan extends AbstractPersistable { return this.termPeriodFrequencyType; } - public void validateExpectedDisbursementForHolidayAndNonWorkingDay(WorkingDays workingDays, boolean isHolidayEnabled, - List holidays) { + public void validateExpectedDisbursementForHolidayAndNonWorkingDay(final WorkingDays workingDays, + final boolean allowTransactionsOnHoliday, final List holidays, final boolean allowTransactionsOnNonWorkingDay) { // validate if disbursement date is a holiday or a non-working day - validateDisbursementDateIsOnNonWorkingDay(workingDays); - validateDisbursementDateIsOnHoliday(isHolidayEnabled, holidays); + validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay); + validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java index 1e55611b1..7d69bda71 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java @@ -190,9 +190,11 @@ public class LoanAssembler { final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(), loanApplicationTerms.getExpectedDisbursementDate().toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); + final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); + final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms, isHolidayEnabled, holidays, workingDays, element); loanApplication.loanApplicationSubmittal(currentUser, loanScheduleModel, loanApplicationTerms, defaultLoanLifecycleStateMachine(), - submittedOnDate, externalId, isHolidayEnabled, holidays, workingDays); + submittedOnDate, externalId, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay); return loanApplication; } @@ -241,11 +243,13 @@ public class LoanAssembler { public void validateExpectedDisbursementForHolidayAndNonWorkingDay(final Loan loanApplication) { - final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); + final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(), loanApplication.getExpectedDisbursedOnLocalDate().toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); + final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); - loanApplication.validateExpectedDisbursementForHolidayAndNonWorkingDay(workingDays,isHolidayEnabled,holidays); + loanApplication.validateExpectedDisbursementForHolidayAndNonWorkingDay(workingDays, allowTransactionsOnHoliday, holidays, + allowTransactionsOnNonWorkingDay); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index d07cf6886..62debdee2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -191,10 +191,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), actualDisbursementDate.toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); + final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); + final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); updateLoanCounters(loan, actualDisbursementDate); loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, existingTransactionIds, - existingReversedTransactionIds, changes, paymentDetail, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays); + existingReversedTransactionIds, changes, paymentDetail, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, + workingDays, allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay); if (!changes.isEmpty()) { this.loanRepository.save(loan); @@ -281,9 +284,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), actualDisbursementDate.toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); + final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); + final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); + updateLoanCounters(loan, actualDisbursementDate); loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, existingTransactionIds, - existingReversedTransactionIds, changes, paymentDetail, firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays); + existingReversedTransactionIds, changes, paymentDetail, firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays, + allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay); if (!changes.isEmpty()) { this.loanRepository.save(loan); @@ -395,12 +402,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount); final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, paymentDetail, transactionDate, txnExternalId); - final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); + final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); - + final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); + final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction, - defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, isHolidayEnabled, holidays, workingDays); + defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, allowTransactionsOnHoliday, + holidays, workingDays, allowTransactionsOnNonWorkingDay); this.loanTransactionRepository.save(newRepaymentTransaction); @@ -488,13 +497,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf newTransactionDetail = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate); } - final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); + final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); + final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); final ChangedTransactionDetail changedTransactionDetail = loan.adjustExistingTransaction(newTransactionDetail, defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds, - isHolidayEnabled, holidays, workingDays); + allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay); if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) { if (paymentDetail != null) { From 62ef2338f0e0f8593e9aba5e6fd2b04b7cacaa9e Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Tue, 6 Aug 2013 21:08:11 +0530 Subject: [PATCH 23/47] incrementing version number of sql fix pulled in from bug fix branch to develop branch --- ...95__configuration_settings_for_holiday_and_non_workingday.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mifosng-db/migrations/core_db/{V92__configuration_settings_for_holiday_and_non_workingday.sql => V95__configuration_settings_for_holiday_and_non_workingday.sql} (100%) diff --git a/mifosng-db/migrations/core_db/V92__configuration_settings_for_holiday_and_non_workingday.sql b/mifosng-db/migrations/core_db/V95__configuration_settings_for_holiday_and_non_workingday.sql similarity index 100% rename from mifosng-db/migrations/core_db/V92__configuration_settings_for_holiday_and_non_workingday.sql rename to mifosng-db/migrations/core_db/V95__configuration_settings_for_holiday_and_non_workingday.sql From 610efe12d3ade2a28cf5c8506ad9fae78f181c31 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Tue, 6 Aug 2013 21:57:24 +0530 Subject: [PATCH 24/47] batch job for post Interest rebase corrections --- .../core_db/V96__batch_job_postInterest.sql | 1 + .../infrastructure/jobs/service/JobName.java | 2 +- .../domain/SavingsAccountAssembler.java | 4 ++ .../domain/SavingsAccountRepository.java | 4 ++ .../SavingsAccountWritePlatformService.java | 2 + ...WritePlatformServiceJpaRepositoryImpl.java | 46 +++++++++++++------ 6 files changed, 43 insertions(+), 16 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V96__batch_job_postInterest.sql diff --git a/mifosng-db/migrations/core_db/V96__batch_job_postInterest.sql b/mifosng-db/migrations/core_db/V96__batch_job_postInterest.sql new file mode 100755 index 000000000..58fda9d28 --- /dev/null +++ b/mifosng-db/migrations/core_db/V96__batch_job_postInterest.sql @@ -0,0 +1 @@ +INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`) VALUES ('Post Interest For Savings', 'Post Interest For Savings', '0 0 0 1/1 * ? *', now(), 5, NULL, NULL, NULL, NULL, NULL, 1, 0, 1, 0); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java index 8052140a7..cca8704cd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java @@ -4,7 +4,7 @@ public enum JobName { UPDATE_LOAN_SUMMARY("Update loan Summary"), UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), UPDATE_LOAN_PAID_IN_ADVANCE( "Update Loan Paid In Advance"), APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), APPLY_HOLIDAYS_TO_LOANS( - "Apply Holidays To Loans"); + "Apply Holidays To Loans"),POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"); private final String name; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java index 0ede17f91..1fb17245a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java @@ -235,4 +235,8 @@ public class SavingsAccountAssembler { return account; } + + public void assignSavingAccountHelpers(SavingsAccount savingsAccount){ + savingsAccount.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java index 67539b0cb..51e7b042d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java @@ -16,4 +16,8 @@ public interface SavingsAccountRepository extends JpaRepository findSavingAccountByClientId(@Param("clientId") Long clientId); + + @Query("from SavingsAccount s_acc where s_acc.status = :status") + List findSavingAccountByStatus(@Param("status") Integer status); + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java index 42ffbcc9b..cf8570b32 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java @@ -24,4 +24,6 @@ public interface SavingsAccountWritePlatformService { CommandProcessingResult postInterest(Long savingsId); CommandProcessingResult undoTransaction(Long savingsId, Long transactionId); + + void postInterestForAccounts(); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index 27cc1959b..d53241b28 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -22,6 +22,8 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; +import org.mifosplatform.infrastructure.jobs.service.JobName; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; @@ -31,7 +33,8 @@ import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatf import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDataValidator; import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; -import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepositoryWrapper; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository; import org.mifosplatform.useradministration.domain.AppUser; @@ -43,7 +46,7 @@ import org.springframework.transaction.annotation.Transactional; public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements SavingsAccountWritePlatformService { private final PlatformSecurityContext context; - private final SavingsAccountRepositoryWrapper savingAccountRepository; + private final SavingsAccountRepository savingAccountRepository; private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; private final SavingsAccountAssembler savingAccountAssembler; private final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator; @@ -53,7 +56,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi @Autowired public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, - final SavingsAccountRepositoryWrapper savingAccountRepository, + final SavingsAccountRepository savingAccountRepository, final SavingsAccountTransactionRepository savingsAccountTransactionRepository, final SavingsAccountAssembler savingAccountAssembler, final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator, @@ -259,19 +262,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi @Override public CommandProcessingResult postInterest(final Long savingsId) { - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); - final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); - - final LocalDate today = DateUtils.getLocalDateOfTenant(); - final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); - - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); - this.savingAccountRepository.save(account); - - postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); - + postInterest(account); return new CommandProcessingResultBuilder() // .withEntityId(savingsId) // .withOfficeId(account.officeId()) // @@ -281,6 +273,30 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .build(); } + + @Transactional + private void postInterest(final SavingsAccount account) { + final List existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + final LocalDate today = DateUtils.getLocalDateOfTenant(); + final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); + + account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + this.savingAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + } + + @CronTarget(jobName = JobName.POST_INTEREST_FOR_SAVINGS) + @Override + public void postInterestForAccounts() { + final List savingsAccounts = this.savingAccountRepository.findSavingAccountByStatus(SavingsAccountStatusType.ACTIVE.getValue()); + for (final SavingsAccount savingsAccount : savingsAccounts) { + this.savingAccountAssembler.assignSavingAccountHelpers(savingsAccount); + postInterest(savingsAccount); + } + } + @Override public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId) { From 6d46b4a3835c768288e96228caee9feb531168c7 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Tue, 6 Aug 2013 22:37:00 +0530 Subject: [PATCH 25/47] fixing issue with query for savings account annual fee mapper --- .../savings/service/SavingsAccountReadPlatformServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index c71b96f72..d38a8f0d2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -753,7 +753,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead public SavingsAccountAnnualFeeMapper() { final StringBuilder sqlBuilder = new StringBuilder(200); sqlBuilder.append("sa.id as id, sa.account_no as accountNo, "); - sqlBuilder.append("sa.annual_fee_next_due_date as annualFeeNextDueDate, "); + sqlBuilder.append("sa.annual_fee_next_due_date as annualFeeNextDueDate "); sqlBuilder.append("from m_savings_account sa "); this.schemaSql = sqlBuilder.toString(); From d0cbf417cf39035b04554f94ba66e1188e935ba5 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Tue, 6 Aug 2013 19:26:03 +0100 Subject: [PATCH 26/47] removed unused warnings. --- .../GroupLoanIntegrationTest.java | 3 +- .../integrationtests/GroupTest.java | 27 +++------ .../integrationtests/common/GroupHelper.java | 60 +++++++++---------- 3 files changed, 37 insertions(+), 53 deletions(-) diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupLoanIntegrationTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupLoanIntegrationTest.java index 8e216d82b..b309d9852 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupLoanIntegrationTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupLoanIntegrationTest.java @@ -2,7 +2,6 @@ package org.mifosplatform.integrationtests; import static org.junit.Assert.assertEquals; -import java.lang.System; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -46,7 +45,7 @@ public class GroupLoanIntegrationTest { loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec); Integer clientID = ClientHelper.createClient(requestSpec, responseSpec); - Integer groupID = GroupHelper.createGroup(requestSpec, responseSpec,true); + Integer groupID = GroupHelper.createGroup(requestSpec, responseSpec, true); groupID = GroupHelper.associateClient(requestSpec, responseSpec, groupID.toString(),clientID.toString()); Integer loanProductID = createLoanProduct(); diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java index 8785a2b6a..4b8374474 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/GroupTest.java @@ -1,19 +1,10 @@ package org.mifosplatform.integrationtests; -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; - import org.junit.Before; import org.junit.Test; import org.mifosplatform.integrationtests.common.ClientHelper; import org.mifosplatform.integrationtests.common.GroupHelper; import org.mifosplatform.integrationtests.common.Utils; -import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder; -import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder; -import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper; import com.jayway.restassured.builder.RequestSpecBuilder; import com.jayway.restassured.builder.ResponseSpecBuilder; @@ -22,15 +13,13 @@ import com.jayway.restassured.specification.RequestSpecification; import com.jayway.restassured.specification.ResponseSpecification; /** - * Group Test for checking Group: - * Creation, Activation, Client Association, Updating & Deletion + * Group Test for checking Group: Creation, Activation, Client Association, + * Updating & Deletion */ -@SuppressWarnings({ "rawtypes", "unchecked" }) public class GroupTest { private ResponseSpecification responseSpec; private RequestSpecification requestSpec; - private LoanTransactionHelper loanTransactionHelper; @Before public void setup() { @@ -49,15 +38,15 @@ public class GroupTest { groupID = GroupHelper.activateGroup(requestSpec, responseSpec, groupID.toString()); GroupHelper.verifyGroupActivatedOnServer(requestSpec, responseSpec, groupID, true); - groupID = GroupHelper.associateClient(requestSpec, responseSpec, groupID.toString(),clientID.toString()); - GroupHelper.verifyGroupMembers(requestSpec,responseSpec,groupID, clientID); + groupID = GroupHelper.associateClient(requestSpec, responseSpec, groupID.toString(), clientID.toString()); + GroupHelper.verifyGroupMembers(requestSpec, responseSpec, groupID, clientID); - groupID = GroupHelper.disAssociateClient(requestSpec, responseSpec, groupID.toString(),clientID.toString()); - GroupHelper.verifyEmptyGroupMembers(requestSpec,responseSpec,groupID); + groupID = GroupHelper.disAssociateClient(requestSpec, responseSpec, groupID.toString(), clientID.toString()); + GroupHelper.verifyEmptyGroupMembers(requestSpec, responseSpec, groupID); - String updatedGroupName=GroupHelper.randomNameGenerator("Group-",5); + String updatedGroupName = GroupHelper.randomNameGenerator("Group-", 5); groupID = GroupHelper.updateGroup(requestSpec, responseSpec, updatedGroupName, groupID.toString()); - GroupHelper.verifyGroupDetails(requestSpec, responseSpec, groupID,"name",updatedGroupName); + GroupHelper.verifyGroupDetails(requestSpec, responseSpec, groupID, "name", updatedGroupName); groupID = GroupHelper.createGroup(requestSpec, responseSpec); GroupHelper.deleteGroup(requestSpec, responseSpec, groupID.toString()); diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java index 374aa9779..973da206c 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/GroupHelper.java @@ -1,13 +1,13 @@ package org.mifosplatform.integrationtests.common; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; -import java.lang.Boolean; -import java.util.HashMap; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; + import com.google.gson.Gson; import com.jayway.restassured.specification.RequestSpecification; import com.jayway.restassured.specification.ResponseSpecification; @@ -17,77 +17,73 @@ public class GroupHelper { private static final String CREATE_GROUP_URL = "/mifosng-provider/api/v1/groups?tenantIdentifier=default"; public static Integer createGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final boolean active) { + @SuppressWarnings("unused") final boolean active) { System.out.println("---------------------------------CREATING A GROUP---------------------------------------------"); return createGroup(requestSpec, responseSpec, "04 March 2011"); } public static Integer createGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final String activationDate) { + final String activationDate) { System.out.println("---------------------------------CREATING A GROUP---------------------------------------------"); - return Utils.performServerPost(requestSpec, responseSpec, CREATE_GROUP_URL, getTestGroupAsJSON(true,activationDate), "groupId"); + return Utils.performServerPost(requestSpec, responseSpec, CREATE_GROUP_URL, getTestGroupAsJSON(true, activationDate), "groupId"); } public static Integer createGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) { System.out.println("---------------------------------CREATING A GROUP---------------------------------------------"); - return Utils.performServerPost(requestSpec, responseSpec, CREATE_GROUP_URL, getTestGroupAsJSON(false,""), "groupId"); + return Utils.performServerPost(requestSpec, responseSpec, CREATE_GROUP_URL, getTestGroupAsJSON(false, ""), "groupId"); } public static Integer associateClient(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final String groupId, final String clientMember) { + final String groupId, final String clientMember) { String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?command=associateClients&tenantIdentifier=default"; System.out.println("---------------------------------Associate Client To A GROUP---------------------------------------------"); return Utils.performServerPost(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, associateClientAsJSON(clientMember), "groupId"); } public static Integer disAssociateClient(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final String groupId, final String clientMember) { + final String groupId, final String clientMember) { String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?command=disassociateClients&tenantIdentifier=default"; System.out.println("---------------------------------Disassociate Client To A GROUP---------------------------------------------"); return Utils.performServerPost(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, associateClientAsJSON(clientMember), "groupId"); } public static Integer activateGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final String groupId) { + final String groupId) { String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?command=activate&tenantIdentifier=default"; System.out.println("---------------------------------Activate A GROUP---------------------------------------------"); return Utils.performServerPost(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, activateGroupAsJSON(""), "groupId"); } - public static Integer updateGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - String name, final String groupId) { + public static Integer updateGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final String name, + final String groupId) { String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?tenantIdentifier=default"; System.out.println("---------------------------------UPDATE GROUP---------------------------------------------"); return Utils.performServerPut(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, updateGroupAsJSON(name), "groupId"); } - public static Integer deleteGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final String groupId) { + public static Integer deleteGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final String groupId) { String GROUP_ASSOCIATE_URL = "/mifosng-provider/api/v1/groups/" + groupId + "?tenantIdentifier=default"; System.out.println("---------------------------------DELETE GROUP---------------------------------------------"); return Utils.performServerDelete(requestSpec, responseSpec, GROUP_ASSOCIATE_URL, "groupId"); } - public static String getTestGroupAsJSON(final boolean active, final String activationDate) { HashMap map = new HashMap(); map.put("officeId", "1"); map.put("name", randomNameGenerator("Group_Name_", 5)); map.put("externalId", randomIDGenerator("ID_", 7)); - if (active==true){ + if (active == true) { map.put("active", "true"); map.put("dateFormat", "dd MMMM yyyy"); map.put("locale", "en"); map.put("activationDate", activationDate); - } - else + } else map.put("active", "false"); System.out.println("map : " + map); return new Gson().toJson(map); } - public static String associateClientAsJSON(final String clientMember) { HashMap> map = new HashMap>(); List list = new ArrayList(); @@ -117,55 +113,55 @@ public class GroupHelper { } public static void verifyGroupCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final Integer generatedGroupID) { + final Integer generatedGroupID) { System.out.println("------------------------------CHECK GROUP DETAILS------------------------------------\n"); String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default"; Integer responseGroupID = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "id"); - assertEquals("ERROR IN CREATING THE GROUP",generatedGroupID, responseGroupID); + assertEquals("ERROR IN CREATING THE GROUP", generatedGroupID, responseGroupID); } public static void verifyGroupDetails(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final Integer generatedGroupID, final String field, final String expectedValue) { + final Integer generatedGroupID, final String field, final String expectedValue) { System.out.println("------------------------------CHECK GROUP DETAILS------------------------------------\n"); String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default"; String responseValue = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, field); - assertEquals("ERROR IN CREATING THE GROUP",expectedValue, responseValue); + assertEquals("ERROR IN CREATING THE GROUP", expectedValue, responseValue); } public static void verifyGroupActivatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final Integer generatedGroupID, final boolean generatedGroupStatus) { + final Integer generatedGroupID, final boolean generatedGroupStatus) { System.out.println("------------------------------CHECK GROUP STATUS------------------------------------\n"); String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?tenantIdentifier=default"; Boolean responseGroupStatus = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "active"); - assertEquals("ERROR IN ACTIVATING THE GROUP",generatedGroupStatus, responseGroupStatus); + assertEquals("ERROR IN ACTIVATING THE GROUP", generatedGroupStatus, responseGroupStatus); } public static void verifyGroupMembers(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final Integer generatedGroupID, final Integer groupMember) { + final Integer generatedGroupID, final Integer groupMember) { List list = new ArrayList(); System.out.println("------------------------------CHECK GROUP MEMBERS------------------------------------\n"); String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?associations=clientMembers&tenantIdentifier=default"; list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "clientMembers"); - assertTrue("ERROR IN GROUP MEMBER",list.toString().contains("id=" + groupMember.toString())); + assertTrue("ERROR IN GROUP MEMBER", list.toString().contains("id=" + groupMember.toString())); } public static void verifyEmptyGroupMembers(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final Integer generatedGroupID) { + final Integer generatedGroupID) { List list = new ArrayList(); System.out.println("------------------------------CHECK EMPTY GROUP MEMBER LIST------------------------------------\n"); String GROUP_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupID + "?associations=clientMembers&tenantIdentifier=default"; list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "clientMembers"); - assertEquals("GROUP MEMBER LIST NOT EMPTY",list, null); + assertEquals("GROUP MEMBER LIST NOT EMPTY", list, null); } public static void verifyGroupDeleted(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - final Integer generatedGroupID) { + final Integer generatedGroupID) { List list = new ArrayList(); System.out.println("------------------------------CHECK GROUP DELETED------------------------------------\n"); String GROUP_URL = "/mifosng-provider/api/v1/groups/?tenantIdentifier=default"; list = Utils.performServerGet(requestSpec, responseSpec, GROUP_URL, "pageItems"); - - assertFalse("GROUP NOT DELETED",list.toString().contains("id=" + generatedGroupID.toString())); + + assertFalse("GROUP NOT DELETED", list.toString().contains("id=" + generatedGroupID.toString())); } public static String randomNameGenerator(final String prefix, final int lenOfRandomSuffix) { From cc684e02c377571afbf53b51683096e19b0a836b Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Wed, 7 Aug 2013 23:58:30 +0100 Subject: [PATCH 27/47] MIFOSX-432: ability to transfer from savings account to savings account. --- api-docs/apiLive.htm | 1550 ++++++++++++++++- .../V97__savings_accounts_transfers_table.sql | 56 + .../commands/domain/CommandWrapper.java | 4 + .../service/CommandWrapperBuilder.java | 8 + .../SynchronousCommandProcessingService.java | 9 +- .../infrastructure/jobs/service/JobName.java | 2 +- .../AccountTransfersApiConstants.java | 50 + .../api/AccountTransfersApiResource.java | 122 ++ .../data/AccountTransferData.java | 108 ++ .../data/AccountTransfersDataValidator.java | 108 ++ .../domain/AccountTransfer.java | 111 ++ .../domain/AccountTransferAssembler.java | 85 + .../domain/AccountTransferRepository.java | 13 + .../AccountTransferNotFoundException.java | 15 + .../CreateAccountTransferCommandHandler.java | 32 + .../AccountTransfersReadPlatformService.java | 20 + ...countTransfersReadPlatformServiceImpl.java | 291 ++++ .../AccountTransfersWritePlatformService.java | 14 + ...ountTransfersWritePlatformServiceImpl.java | 91 + .../portfolio/client/data/ClientData.java | 23 +- .../savings/data/SavingsAccountData.java | 41 + .../data/SavingsAccountTransactionData.java | 14 +- .../savings/domain/SavingsAccount.java | 2 +- .../domain/SavingsAccountDomainService.java | 21 + .../SavingsAccountDomainServiceJpa.java | 117 ++ .../SavingsAccountReadPlatformService.java | 2 + ...SavingsAccountReadPlatformServiceImpl.java | 50 +- ...WritePlatformServiceJpaRepositoryImpl.java | 61 +- 28 files changed, 2881 insertions(+), 139 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V97__savings_accounts_transfers_table.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/api/AccountTransfersApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransfer.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index f982202d0..05feee8c6 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -581,61 +581,6 @@
- - -

Accounting

@@ -12892,7 +12837,6 @@ No Request Body:
- savingsaccounts_transactions_template  
@@ -12944,7 +12888,6 @@ No Request Body:
-  
@@ -13052,6 +12995,1457 @@ No Request Body:
+ + +  +
+
+

Account Transfers:

+

Ability to be able to transfer monetary funds from one account to another.

+

Note: At present only savings account to savings account transfers are supported.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Mandatory Parameters
fromOfficeId
The id of the office/branch from which the transfer is made.
fromClientId
The id of the client from which the transfer is made.
fromAccountType
The type of account from which the transfer is made. 1=Loan Account, 2=Savings Account
fromAccountId
The id of the account from which the transfer is made.
toOfficeId
The id of the office/branch to which the transfer is made.
toClientId
The id of the client to which the transfer is made.
toAccountType
The type of account to which the transfer is made. 1=Loan Account, 2=Savings Account
toAccountId
The id of the account to which the transfer is made.
transferDate
The date of the transfer. Requires dateFormat and locale parameters.
transferAmount
The amount of the transfer. Requires locale parameter.
transferDescription
Description of the transfer itself.
+
+
+ +   +
+
+

Retrieve Account Transfer Template:

+

This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of: +

    +
  • Field Defaults
  • +
  • Allowed Value Lists
  • +
+

+

Example Requests:

+
accounttransfers/template?fromAccountType=2&fromOfficeId=1
+
+
accounttransfers/template?fromAccountType=2&fromOfficeId=1&fromClientId=1
+
+
accounttransfers/template?fromClientId=1&fromAccountType=2&fromAccountId=1
+
+
+
+ GET https://Domain Name/api/v1/accounttransfers/template?fromAccountType=2&fromOfficeId=1 + + +{ + "transferAmount": 0, + "transferDate": [ + 2013, + 8, + 7 + ], + "fromOffice": { + "id": 1, + "name": "HO", + "nameDecorated": "HO", + "externalId": "1", + "openingDate": [ + 2009, + 1, + 1 + ], + "hierarchy": "." + }, + "toOffice": { + "id": 1, + "name": "HO", + "nameDecorated": "HO", + "externalId": "1", + "openingDate": [ + 2009, + 1, + 1 + ], + "hierarchy": "." + }, + "fromOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "fromClientOptions": [ + { + "id": 2, + "displayName": "headoffice-business", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + } + ], + "toOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "toClientOptions": [ + { + "id": 2, + "displayName": "headoffice-business", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + } + ] +} + +
+ +
+ GET https://Domain Name/api/v1/accounttransfers/template?fromAccountType=2&fromOfficeId=1&fromClientId=1 + + +{ + "transferAmount": 0, + "transferDate": [ + 2013, + 8, + 7 + ], + "fromOffice": { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1", + "externalId": "2", + "openingDate": [ + 2011, + 7, + 1 + ], + "hierarchy": ".3.", + "parentId": 1, + "parentName": "HO" + }, + "fromClient": { + "id": 1, + "accountNo": "000000001", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2012, + 3, + 1 + ], + "fullname": "Small shop", + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false, + "groups": [] + }, + "toOffice": { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1", + "externalId": "2", + "openingDate": [ + 2011, + 7, + 1 + ], + "hierarchy": ".3.", + "parentId": 1, + "parentName": "HO" + }, + "toClient": { + "id": 1, + "accountNo": "000000001", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2012, + 3, + 1 + ], + "fullname": "Small shop", + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false, + "groups": [] + }, + "fromOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "fromClientOptions": [ + { + "id": 1, + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false + } + ], + "fromAccountOptions": [ + { + "id": 1, + "accountNo": "000000001", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 2, + "fieldOfficerName": "Clare, Claire", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 3, + 1 + ], + "approvedOnDate": [ + 2013, + 3, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 3, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 1242, + "totalWithdrawals": 101, + "totalInterestEarned": 26.03, + "accountBalance": 1141 + } + }, + { + "id": 2, + "accountNo": "000000002", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 3, + "fieldOfficerName": "George, John", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 6, + 1 + ], + "approvedOnDate": [ + 2013, + 6, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 6, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 101, + "totalWithdrawals": 42, + "totalInterestEarned": 0.58, + "accountBalance": 59 + } + } + ], + "toOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "toClientOptions": [ + { + "id": 1, + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false + } + ], + "toAccountOptions": [ + { + "id": 1, + "accountNo": "000000001", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 2, + "fieldOfficerName": "Clare, Claire", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 3, + 1 + ], + "approvedOnDate": [ + 2013, + 3, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 3, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 1242, + "totalWithdrawals": 101, + "totalInterestEarned": 26.03, + "accountBalance": 1141 + } + }, + { + "id": 2, + "accountNo": "000000002", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 3, + "fieldOfficerName": "George, John", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 6, + 1 + ], + "approvedOnDate": [ + 2013, + 6, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 6, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 101, + "totalWithdrawals": 42, + "totalInterestEarned": 0.58, + "accountBalance": 59 + } + } + ] +} + +
+ +
+ GET https://Domain Name/api/v1/accounttransfers/template?fromClientId=1&fromAccountType=2&fromAccountId=1 + + +{ + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 0, + "transferDate": [ + 2013, + 8, + 7 + ], + "fromOffice": { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1", + "externalId": "2", + "openingDate": [ + 2011, + 7, + 1 + ], + "hierarchy": ".3.", + "parentId": 1, + "parentName": "HO" + }, + "fromClient": { + "id": 1, + "accountNo": "000000001", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2012, + 3, + 1 + ], + "fullname": "Small shop", + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false, + "groups": [] + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 2, + "fieldOfficerName": "Clare, Claire", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 3, + 1 + ], + "approvedOnDate": [ + 2013, + 3, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 3, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 1242, + "totalWithdrawals": 101, + "totalInterestEarned": 26.03, + "accountBalance": 1141 + } + }, + "toOffice": { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1", + "externalId": "2", + "openingDate": [ + 2011, + 7, + 1 + ], + "hierarchy": ".3.", + "parentId": 1, + "parentName": "HO" + }, + "toClient": { + "id": 1, + "accountNo": "000000001", + "status": { + "id": 300, + "code": "clientStatusType.active", + "value": "Active" + }, + "active": true, + "activationDate": [ + 2012, + 3, + 1 + ], + "fullname": "Small shop", + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false, + "groups": [] + }, + "fromOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "fromClientOptions": [ + { + "id": 1, + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false + } + ], + "fromAccountOptions": [ + { + "id": 1, + "accountNo": "000000001", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 2, + "fieldOfficerName": "Clare, Claire", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 3, + 1 + ], + "approvedOnDate": [ + 2013, + 3, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 3, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 1242, + "totalWithdrawals": 101, + "totalInterestEarned": 26.03, + "accountBalance": 1141 + } + }, + { + "id": 2, + "accountNo": "000000002", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 3, + "fieldOfficerName": "George, John", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 6, + 1 + ], + "approvedOnDate": [ + 2013, + 6, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 6, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 101, + "totalWithdrawals": 42, + "totalInterestEarned": 0.58, + "accountBalance": 59 + } + } + ], + "toOfficeOptions": [ + { + "id": 1, + "name": "HO", + "nameDecorated": "HO" + }, + { + "id": 3, + "name": "branch 1", + "nameDecorated": "....branch 1" + } + ], + "toClientOptions": [ + { + "id": 1, + "displayName": "Small shop", + "officeId": 3, + "officeName": "branch 1", + "imagePresent": false + } + ], + "toAccountOptions": [ + { + "id": 2, + "accountNo": "000000002", + "clientId": 1, + "clientName": "Small shop", + "savingsProductId": 1, + "savingsProductName": "Passbook", + "fieldOfficerId": 3, + "fieldOfficerName": "George, John", + "status": { + "id": 300, + "code": "savingsAccountStatusType.active", + "value": "Active", + "submittedAndPendingApproval": false, + "approved": false, + "rejected": false, + "withdrawnByApplicant": false, + "active": true, + "closed": false + }, + "timeline": { + "submittedOnDate": [ + 2013, + 6, + 1 + ], + "approvedOnDate": [ + 2013, + 6, + 1 + ], + "approvedByUsername": "mifos", + "approvedByFirstname": "App", + "approvedByLastname": "Administrator", + "activatedOnDate": [ + 2013, + 6, + 1 + ] + }, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "nominalAnnualInterestRate": 5, + "interestCompoundingPeriodType": { + "id": 1, + "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", + "value": "Daily" + }, + "interestPostingPeriodType": { + "id": 4, + "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", + "value": "Monthly" + }, + "interestCalculationType": { + "id": 1, + "code": "savingsInterestCalculationType.dailybalance", + "value": "Daily Balance" + }, + "interestCalculationDaysInYearType": { + "id": 365, + "code": "savingsInterestCalculationDaysInYearType.days365", + "value": "365 Days" + }, + "summary": { + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "totalDeposits": 101, + "totalWithdrawals": 42, + "totalInterestEarned": 0.58, + "accountBalance": 59 + } + } + ] +} + +
+ +
+ +   +
+
+

Create new Transfer

+

Ability to create new transfer of monetary funds from one account to another.

+
+
+ POST https://Domain Name/api/v1/accounttransfers + POST accounttransfers/ +Content-Type: application/json +No Request Body: +{ +"fromOfficeId": 1, +"fromClientId": 1, +"fromAccountType": 2, +"fromAccountId": 1, +"toOfficeId": 1, +"toClientId": 1, +"toAccountType": 2, +"toAccountId": 2, +"dateFormat": "dd MMMM yyyy", +"locale": "en", +"transferDate": "01 August 2011", +"transferAmount": "112.45", +"transferDescription": "A description of the transfer" +} + + + +
+
+ +   +
+
+

List account transfers

+

Example Requests:

+
accounttransfers
+
+
+
+ GET https://Domain Name/api/v1/accounttransfers + +{ + "totalFilteredRecords": 5, + "pageItems": [ + { + "id": 1, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 25, + "transferDate": [ + 2013, + 6, + 8 + ], + "transferDescription": "another transfer", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } + }, + { + "id": 2, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 13, + "transferDate": [ + 2013, + 6, + 15 + ], + "transferDescription": "T3", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } + }, + { + "id": 3, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 17, + "transferDate": [ + 2013, + 6, + 22 + ], + "transferDescription": "T4", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } + }, + { + "id": 4, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 21, + "transferDate": [ + 2013, + 6, + 29 + ], + "transferDescription": "T5", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } + }, + { + "id": 5, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 42, + "transferDate": [ + 2013, + 7, + 5 + ], + "transferDescription": "pay back loan", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 2, + "accountNo": "000000002" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 1, + "accountNo": "000000001" + } + } + ] +} + +
+
+ +   +
+
+

Retrieve account transfer:

+

Example Requests :

+
accounttransfers/1
+
+
+
+ GET https://DomainName/api/v1/accounttransfers/{transferId} + +{ + "id": 1, + "reversed": false, + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "transferAmount": 25, + "transferDate": [ + 2013, + 6, + 8 + ], + "transferDescription": "another transfer", + "fromOffice": { + "id": 1, + "name": "HO" + }, + "fromClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "fromAccount": { + "id": 1, + "accountNo": "000000001" + }, + "toOffice": { + "id": 1, + "name": "HO" + }, + "toClient": { + "id": 1, + "displayName": "Small shop", + "officeId": 1, + "officeName": "HO", + "imagePresent": false + }, + "toAccount": { + "id": 2, + "accountNo": "000000002" + } +} + +
+
+   @@ -18281,14 +19675,6 @@ GET https://DomainName/api/v1/search?query=000000111 - - - savingsaccounts/{accountId}/transactions/template - - Retrieve savings account transaction template - - - Savings Transactions savingsaccounts/{accountId}/transactions?command=deposit @@ -18312,7 +19698,39 @@ GET https://DomainName/api/v1/search?query=000000111 - + + + + savingsaccounts/{accountId}/transactions/template + + Retrieve savings account transaction template + + + + + Account Transfers + accounttransfers/ + Create new Transfer + List Transfers + + + + + + accounttransfers/template + + Retrieve Template + + + + + + accounttransfers/{transferId} + + Retrieve Transfer + + + diff --git a/mifosng-db/migrations/core_db/V97__savings_accounts_transfers_table.sql b/mifosng-db/migrations/core_db/V97__savings_accounts_transfers_table.sql new file mode 100644 index 000000000..425b17b4a --- /dev/null +++ b/mifosng-db/migrations/core_db/V97__savings_accounts_transfers_table.sql @@ -0,0 +1,56 @@ +INSERT INTO `m_permission` +(`grouping`,`code`,`entity_name`,`action_name`,`can_maker_checker`) +VALUES +('transaction_savings', 'READ_ACCOUNTTRANSFER', 'ACCOUNTTRANSFER', 'READ', 0), +('transaction_savings', 'CREATE_ACCOUNTTRANSFER', 'ACCOUNTTRANSFER', 'CREATE', 1), +('transaction_savings', 'CREATE_ACCOUNTTRANSFER_CHECKER', 'ACCOUNTTRANSFER', 'CREATE', 0); + + +DROP TABLE IF EXISTS `m_savings_account_transfer`; + +CREATE TABLE `m_savings_account_transfer` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `from_office_id` bigint(20) NOT NULL, + `to_office_id` bigint(20) NOT NULL, + `from_client_id` bigint(20) NOT NULL, + `to_client_id` bigint(20) NOT NULL, + `from_savings_account_id` bigint(20) DEFAULT NULL, + `to_savings_account_id` bigint(20) DEFAULT NULL, + `from_loan_account_id` bigint(20) DEFAULT NULL, + `to_loan_account_id` bigint(20) DEFAULT NULL, + `from_savings_transaction_id` bigint(20) DEFAULT NULL, + `from_loan_transaction_id` bigint(20) DEFAULT NULL, + `to_savings_transaction_id` bigint(20) DEFAULT NULL, + `to_loan_transaction_id` bigint(20) DEFAULT NULL, + `is_reversed` tinyint(1) NOT NULL, + `transaction_date` date NOT NULL, + `currency_code` varchar(3) NOT NULL, + `currency_digits` smallint(5) NOT NULL, + `amount` decimal(19,6) NOT NULL, + `description` varchar(200) NOT NULL, + PRIMARY KEY (`id`), + KEY `FKTRAN000000001` (`from_office_id`), + KEY `FKTRAN000000002` (`from_client_id`), + KEY `FKTRAN000000003` (`from_savings_account_id`), + KEY `FKTRAN000000004` (`to_office_id`), + KEY `FKTRAN000000005` (`to_client_id`), + KEY `FKTRAN000000006` (`to_savings_account_id`), + KEY `FKTRAN000000007` (`to_loan_account_id`), + KEY `FKTRAN000000008` (`from_savings_transaction_id`), + KEY `FKTRAN000000009` (`to_savings_transaction_id`), + KEY `FKTRAN000000010` (`to_loan_transaction_id`), + KEY `FKTRAN000000011` (`from_loan_account_id`), + KEY `FKTRAN000000012` (`from_loan_transaction_id`), + CONSTRAINT `FKTRAN000000001` FOREIGN KEY (`from_office_id`) REFERENCES `m_office` (`id`), + CONSTRAINT `FKTRAN000000002` FOREIGN KEY (`from_client_id`) REFERENCES `m_client` (`id`), + CONSTRAINT `FKTRAN000000003` FOREIGN KEY (`from_savings_account_id`) REFERENCES `m_savings_account` (`id`), + CONSTRAINT `FKTRAN000000004` FOREIGN KEY (`to_office_id`) REFERENCES `m_office` (`id`), + CONSTRAINT `FKTRAN000000005` FOREIGN KEY (`to_client_id`) REFERENCES `m_client` (`id`), + CONSTRAINT `FKTRAN000000006` FOREIGN KEY (`to_savings_account_id`) REFERENCES `m_savings_account` (`id`), + CONSTRAINT `FKTRAN000000007` FOREIGN KEY (`to_loan_account_id`) REFERENCES `m_loan` (`id`), + CONSTRAINT `FKTRAN000000008` FOREIGN KEY (`from_savings_transaction_id`) REFERENCES `m_savings_account_transaction` (`id`), + CONSTRAINT `FKTRAN000000009` FOREIGN KEY (`to_savings_transaction_id`) REFERENCES `m_savings_account_transaction` (`id`), + CONSTRAINT `FKTRAN000000010` FOREIGN KEY (`to_loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`), + CONSTRAINT `FKTRAN000000011` FOREIGN KEY (`from_loan_account_id`) REFERENCES `m_loan` (`id`), + CONSTRAINT `FKTRAN000000012` FOREIGN KEY (`from_loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 3385bdee5..3e973eddc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -563,4 +563,8 @@ public class CommandWrapper { public boolean isSchedulerResource() { return this.entityName.equalsIgnoreCase("SCHEDULER"); } + + public boolean isAccountTransferResource() { + return this.entityName.equalsIgnoreCase("ACCOUNTTRANSFER"); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 6da48c263..445fd23c1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -808,6 +808,14 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder createAccountTransfer() { + this.actionName = "CREATE"; + this.entityName = "ACCOUNTTRANSFER"; + this.entityId = null; + this.href = "/accounttransfers"; + return this; + } + public CommandWrapperBuilder savingsAccountDeposit(final Long accountId) { this.actionName = "DEPOSIT"; this.entityName = "SAVINGSACCOUNT"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index f8f3614b2..eabf31970 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -105,7 +105,14 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer private NewCommandSourceHandler findCommandHandler(final CommandWrapper wrapper) { NewCommandSourceHandler handler = null; - if (wrapper.isConfigurationResource()) { + if (wrapper.isAccountTransferResource()) { + if (wrapper.isCreate()) { + handler = this.applicationContext.getBean("createAccountTransferCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } + } + else if (wrapper.isConfigurationResource()) { handler = this.applicationContext.getBean("updateGlobalConfigurationCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isDatatableResource()) { if (wrapper.isCreateDatatable()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java index cca8704cd..976003921 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java @@ -4,7 +4,7 @@ public enum JobName { UPDATE_LOAN_SUMMARY("Update loan Summary"), UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), UPDATE_LOAN_PAID_IN_ADVANCE( "Update Loan Paid In Advance"), APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), APPLY_HOLIDAYS_TO_LOANS( - "Apply Holidays To Loans"),POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"); + "Apply Holidays To Loans"), POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"); private final String name; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.java new file mode 100644 index 000000000..03959d511 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.java @@ -0,0 +1,50 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; + +public class AccountTransfersApiConstants { + + public static final String ACCOUNT_TRANSFER_RESOURCE_NAME = "accounttransfer"; + + // general + public static final String localeParamName = "locale"; + public static final String dateFormatParamName = "dateFormat"; + + // savings product and account parameters + public static final String idParamName = "id"; + public static final String fromOfficeIdParamName = "fromOfficeId"; + public static final String fromClientIdParamName = "fromClientId"; + public static final String fromAccountIdParamName = "fromAccountId"; + public static final String fromAccountTypeParamName = "fromAccountType"; + public static final String toOfficeIdParamName = "toOfficeId"; + public static final String toClientIdParamName = "toClientId"; + public static final String toAccountIdParamName = "toAccountId"; + public static final String toAccountTypeParamName = "toAccountType"; + + // transaction parameters + public static final String transferDateParamName = "transferDate"; + public static final String transferAmountParamName = "transferAmount"; + public static final String transferDescriptionParamName = "transferDescription"; + + public static final Set REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, + fromOfficeIdParamName, fromClientIdParamName, fromAccountTypeParamName, fromAccountIdParamName, toOfficeIdParamName, + toClientIdParamName, toAccountTypeParamName, toAccountIdParamName, transferDateParamName, transferAmountParamName, + transferDescriptionParamName)); + + /** + * These parameters will match the class level parameters of + * {@link AccountTransferData}. Where possible, we try to get response + * parameters to match those of request parameters. + */ + public static final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, transferDescriptionParamName, + "currency")); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/api/AccountTransfersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/api/AccountTransfersApiResource.java new file mode 100644 index 000000000..37772bf38 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/api/AccountTransfersApiResource.java @@ -0,0 +1,122 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.accounttransfers.service.AccountTransfersReadPlatformService; +import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/accounttransfers") +@Component +@Scope("singleton") +public class AccountTransfersApiResource { + + private final PlatformSecurityContext context; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; + + @Autowired + public AccountTransfersApiResource(final PlatformSecurityContext context, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final AccountTransfersReadPlatformService accountTransfersReadPlatformService) { + this.context = context; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.accountTransfersReadPlatformService = accountTransfersReadPlatformService; + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String template(@QueryParam("fromOfficeId") final Long fromOfficeId, @QueryParam("fromClientId") final Long fromClientId, + @QueryParam("fromAccountId") final Long fromAccountId, @QueryParam("fromAccountType") final Integer fromAccountType, + @QueryParam("toOfficeId") final Long toOfficeId, @QueryParam("toClientId") final Long toClientId, + @QueryParam("toAccountId") final Long toAccountId, @QueryParam("toAccountType") final Integer toAccountType, + @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + + AccountTransferData transferData = this.accountTransfersReadPlatformService.retrieveTemplate(fromOfficeId, fromClientId, + fromAccountId, fromAccountType, toOfficeId, toClientId, toAccountId, toAccountType); + + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, transferData, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String create(final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createAccountTransfer().withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("sqlSearch") final String sqlSearch, + @QueryParam("externalId") final String externalId, @QueryParam("offset") final Integer offset, + @QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy, + @QueryParam("sortOrder") final String sortOrder) { + + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + + final SearchParameters searchParameters = SearchParameters.forLoans(sqlSearch, externalId, offset, limit, orderBy, sortOrder); + + final Page transfers = this.accountTransfersReadPlatformService.retrieveAll(searchParameters); + + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, transfers, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{transferId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("transferId") final Long transferId, @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + + AccountTransferData transfer = this.accountTransfersReadPlatformService.retrieveOne(transferId); + + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, transfer, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java new file mode 100644 index 000000000..e2fd10334 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java @@ -0,0 +1,108 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.data; + +import java.math.BigDecimal; +import java.util.Collection; + +import org.joda.time.LocalDate; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.organisation.office.data.OfficeData; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountData; + +/** + * Immutable data object representing a savings account. + */ +@SuppressWarnings("unused") +public class AccountTransferData { + + private final Long id; + private final Boolean reversed; + private final CurrencyData currency; + private final BigDecimal transferAmount; + private final LocalDate transferDate; + private final String transferDescription; + private final OfficeData fromOffice; + private final ClientData fromClient; + private final SavingsAccountData fromAccount; + private final OfficeData toOffice; + private final ClientData toClient; + private final SavingsAccountData toAccount; + + // template + private final Collection fromOfficeOptions; + private final Collection fromClientOptions; + private final Collection fromAccountOptions; + private final Collection toOfficeOptions; + private final Collection toClientOptions; + private final Collection toAccountOptions; + + public static AccountTransferData template(final OfficeData fromOffice, final ClientData fromClient, + final SavingsAccountData fromAccount, final LocalDate transferDate, final OfficeData toOffice, final ClientData toClient, + final SavingsAccountData toAccount, final Collection fromOfficeOptions, + final Collection fromClientOptions, final Collection fromAccountOptions, + final Collection toOfficeOptions, final Collection toClientOptions, + final Collection toAccountOptions) { + final Long id = null; + CurrencyData currency = null; + if (fromAccount != null) { + currency = fromAccount.currency(); + } + + final BigDecimal transferAmount = BigDecimal.ZERO; + final String transferDescription = null; + final Boolean reversed = null; + return new AccountTransferData(id, reversed, fromOffice, fromClient, fromAccount, currency, transferAmount, transferDate, + transferDescription, toOffice, toClient, toAccount, fromOfficeOptions, fromClientOptions, fromAccountOptions, + toOfficeOptions, toClientOptions, toAccountOptions); + } + + public static AccountTransferData instance(final Long id, final Boolean reversed, final LocalDate transferDate, + final CurrencyData currency, final BigDecimal transferAmount, final String transferDescription, final OfficeData fromOffice, + final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, final SavingsAccountData fromSavingsAccount, + final SavingsAccountData toSavingsAccount) { + + return new AccountTransferData(id, reversed, fromOffice, fromClient, fromSavingsAccount, currency, transferAmount, transferDate, + transferDescription, toOffice, toClient, toSavingsAccount, null, null, null, null, null, null); + } + + public static AccountTransferData transferBasicDetails(final Long id, final CurrencyData currency, final BigDecimal transferAmount, + final LocalDate transferDate, final String description, final Boolean reversed) { + + return new AccountTransferData(id, reversed, null, null, null, currency, transferAmount, transferDate, description, null, null, + null, null, null, null, null, null, null); + } + + private AccountTransferData(final Long id, final Boolean reversed, final OfficeData fromOffice, final ClientData fromClient, + final SavingsAccountData fromAccount, final CurrencyData currency, final BigDecimal transferAmount, + final LocalDate transferDate, final String transferDescription, final OfficeData toOffice, final ClientData toClient, + final SavingsAccountData toAccount, final Collection fromOfficeOptions, + final Collection fromClientOptions, final Collection fromAccountOptions, + final Collection toOfficeOptions, final Collection toClientOptions, + final Collection toAccountOptions) { + this.id = id; + this.reversed = reversed; + this.fromOffice = fromOffice; + this.fromClient = fromClient; + this.fromAccount = fromAccount; + this.toOffice = toOffice; + this.toClient = toClient; + this.toAccount = toAccount; + + this.currency = currency; + this.transferAmount = transferAmount; + this.transferDate = transferDate; + this.transferDescription = transferDescription; + + this.fromOfficeOptions = fromOfficeOptions; + this.fromClientOptions = fromClientOptions; + this.fromAccountOptions = fromAccountOptions; + this.toOfficeOptions = toOfficeOptions; + this.toClientOptions = toClientOptions; + this.toAccountOptions = toAccountOptions; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java new file mode 100644 index 000000000..0de979eb2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java @@ -0,0 +1,108 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.data; + +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDescriptionParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountTypeParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromClientIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromOfficeIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountTypeParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toClientIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toOfficeIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +@Component +public class AccountTransfersDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public AccountTransfersDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validate(final JsonCommand command) { + + final String json = command.json(); + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ACCOUNT_TRANSFER_RESOURCE_NAME); + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero(); + + final Long fromClientId = fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero(); + + final Long fromAccountId = fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element); + baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero(); + + final Integer fromAccountType = fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element); + baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType) + .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); + + final Long toOfficeId = fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero(); + + final Long toClientId = fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero(); + + final Long toAccountId = fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element); + baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero(); + + final Integer toAccountType = fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element); + baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType) + .isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2)); + + final LocalDate transactionDate = fromApiJsonHelper.extractLocalDateNamed(transferDateParamName, element); + baseDataValidator.reset().parameter(transferDateParamName).value(transactionDate).notNull(); + + final BigDecimal transactionAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(transferAmountParamName, element); + baseDataValidator.reset().parameter(transferAmountParamName).value(transactionAmount).notNull().positiveAmount(); + + final String transactionDescription = fromApiJsonHelper.extractStringNamed(transferDescriptionParamName, element); + baseDataValidator.reset().parameter(transferDescriptionParamName).value(transactionDescription).notBlank().notExceedingLengthOf(200); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransfer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransfer.java new file mode 100644 index 000000000..b31ba4cbc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransfer.java @@ -0,0 +1,111 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.domain; + +import java.math.BigDecimal; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.joda.time.LocalDate; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_savings_account_transfer") +public class AccountTransfer extends AbstractPersistable { + + @ManyToOne + @JoinColumn(name = "from_office_id", nullable = false) + private Office fromOffice; + + @ManyToOne + @JoinColumn(name = "from_client_id", nullable = false) + private Client fromClient; + + @ManyToOne + @JoinColumn(name = "from_savings_account_id", nullable = true) + private SavingsAccount fromAccount; + + @ManyToOne + @JoinColumn(name = "to_office_id", nullable = false) + private Office toOffice; + + @ManyToOne + @JoinColumn(name = "to_client_id", nullable = false) + private Client toClient; + + @ManyToOne + @JoinColumn(name = "to_savings_account_id", nullable = true) + private SavingsAccount toAccount; + + @ManyToOne + @JoinColumn(name = "from_savings_transaction_id", nullable = true) + private SavingsAccountTransaction fromTransaction; + + @ManyToOne + @JoinColumn(name = "to_savings_transaction_id", nullable = true) + private SavingsAccountTransaction toSavingsTransaction; + + @Column(name = "is_reversed", nullable = false) + private final boolean reversed = false; + + @Temporal(TemporalType.DATE) + @Column(name = "transaction_date") + private Date date; + + @Embedded + private MonetaryCurrency currency; + + @Column(name = "amount", scale = 6, precision = 19, nullable = false) + private BigDecimal amount; + + @Column(name = "description", length = 100) + private String description; + + public static AccountTransfer savingsToSavingsTransfer(final Office fromOffice, final Client fromClient, + final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, + final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, final LocalDate transactionDate, + final Money transactionAmount, final String description) { + + return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toSavingsAccount, withdrawal, deposit, + transactionDate, transactionAmount, description); + } + + protected AccountTransfer() { + // + } + + private AccountTransfer(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount, + final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, + final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, final LocalDate transactionDate, + final Money transactionAmount, final String description) { + this.fromOffice = fromOffice; + this.fromClient = fromClient; + this.fromAccount = fromSavingsAccount; + this.toOffice = toOffice; + this.toClient = toClient; + this.toAccount = toSavingsAccount; + this.fromTransaction = withdrawal; + this.toSavingsTransaction = deposit; + this.date = transactionDate.toDate(); + this.currency = transactionAmount.getCurrency(); + this.amount = transactionAmount.getAmountDefaultedToNullIfZero(); + this.description = description; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java new file mode 100644 index 000000000..6ed7f155d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java @@ -0,0 +1,85 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.domain; + +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDescriptionParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromClientIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromOfficeIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toClientIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toOfficeIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.organisation.office.domain.OfficeRepository; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.google.gson.JsonElement; + +@Service +public class AccountTransferAssembler { + + private final ClientRepositoryWrapper clientRepository; + private final OfficeRepository officeRepository; + private final SavingsAccountAssembler savingsAccountAssembler; + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public AccountTransferAssembler(final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, + final SavingsAccountAssembler savingsAccountAssembler, final FromJsonHelper fromApiJsonHelper) { + this.clientRepository = clientRepository; + this.officeRepository = officeRepository; + this.savingsAccountAssembler = savingsAccountAssembler; + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public AccountTransfer assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccountTransaction withdrawal, + final SavingsAccountTransaction deposit) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepository.findOne(fromOfficeId); + + final Long fromClientId = fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + + final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId); + + final Long toOfficeId = fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepository.findOne(toOfficeId); + + final Long toClientId = fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + + final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId); + + final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); + final Money transactionMonetaryAmount = Money.of(fromSavingsAccount.getCurrency(), transactionAmount); + + final String description = command.stringValueOfParameterNamed(transferDescriptionParamName); + + return AccountTransfer.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toSavingsAccount, + withdrawal, deposit, transactionDate, transactionMonetaryAmount, description); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java new file mode 100644 index 000000000..652565cd9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java @@ -0,0 +1,13 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface AccountTransferRepository extends JpaRepository, JpaSpecificationExecutor { + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java new file mode 100644 index 000000000..e7168bfcc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java @@ -0,0 +1,15 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class AccountTransferNotFoundException extends AbstractPlatformResourceNotFoundException { + + public AccountTransferNotFoundException(final Long id) { + super("error.msg.accounttransfer.id.invalid", "Account transfer with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java new file mode 100644 index 000000000..10da9690d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.accounttransfers.service.AccountTransfersWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CreateAccountTransferCommandHandler implements NewCommandSourceHandler { + + private final AccountTransfersWritePlatformService writePlatformService; + + @Autowired + public CreateAccountTransferCommandHandler(final AccountTransfersWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return writePlatformService.create(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java new file mode 100644 index 000000000..945f09d9c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java @@ -0,0 +1,20 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.service; + +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.group.service.SearchParameters; + +public interface AccountTransfersReadPlatformService { + + AccountTransferData retrieveTemplate(Long fromOfficeId, Long fromClientId, Long fromAccountId, Integer fromAccountType, + Long toOfficeId, Long toClientId, Long toAccountId, Integer toAccountType); + + Page retrieveAll(SearchParameters searchParameters); + + AccountTransferData retrieveOne(Long transferId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java new file mode 100644 index 000000000..7a19cac0a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java @@ -0,0 +1,291 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import org.joda.time.LocalDate; +import org.mifosplatform.commands.exception.UnsupportedCommandException; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.infrastructure.core.service.PaginationHelper; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.organisation.office.data.OfficeData; +import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.accounttransfers.exception.AccountTransferNotFoundException; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; +import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.mifosplatform.portfolio.savings.data.SavingsAccountData; +import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class AccountTransfersReadPlatformServiceImpl implements AccountTransfersReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final ClientReadPlatformService clientReadPlatformService; + private final OfficeReadPlatformService officeReadPlatformService; + private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + + // mapper + private final AccountTransfersMapper accountTransfersMapper; + + // pagination + private final PaginationHelper paginationHelper = new PaginationHelper(); + + @Autowired + public AccountTransfersReadPlatformServiceImpl(final RoutingDataSource dataSource, + final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, + final SavingsAccountReadPlatformService savingsAccountReadPlatformService) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.clientReadPlatformService = clientReadPlatformService; + this.officeReadPlatformService = officeReadPlatformService; + this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; + + this.accountTransfersMapper = new AccountTransfersMapper(); + } + + @Override + public AccountTransferData retrieveTemplate(final Long fromOfficeId, final Long fromClientId, final Long fromAccountId, + final Integer fromAccountType, final Long toOfficeId, final Long toClientId, final Long toAccountId, final Integer toAccountType) { + + Integer mostRelevantFromAccountType = fromAccountType; + if (mostRelevantFromAccountType == null) { + mostRelevantFromAccountType = 2; // savings + } + + Integer mostRelevantToAccountType = toAccountType; + if (mostRelevantToAccountType == null) { + mostRelevantToAccountType = 2; // savings + } + // check for now that only savings account type is passed and used. + if (mostRelevantFromAccountType != 2) { throw new UnsupportedCommandException("transferFromLoanAccount"); } + + // from settings + OfficeData fromOffice = null; + ClientData fromClient = null; + SavingsAccountData fromAccount = null; + + OfficeData toOffice = null; + ClientData toClient = null; + SavingsAccountData toAccount = null; + + // template + Collection fromAccountOptions = null; + Collection toAccountOptions = null; + + Long mostRelevantFromOfficeId = fromOfficeId; + Long mostRelevantFromClientId = fromClientId; + + Long mostRelevantToOfficeId = toOfficeId; + Long mostRelevantToClientId = toClientId; + + if (fromAccountId != null && fromAccountType == 2) { + fromAccount = this.savingsAccountReadPlatformService.retrieveOne(fromAccountId); + + // override provided fromClient with client of account + mostRelevantFromClientId = fromAccount.clientId(); + } + + if (mostRelevantFromClientId != null) { + fromClient = this.clientReadPlatformService.retrieveOne(mostRelevantFromClientId); + mostRelevantFromOfficeId = fromClient.officeId(); + toClient = fromClient; + fromAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantFromClientId); + toAccountOptions = new ArrayList(fromAccountOptions); + toAccountOptions.remove(fromAccount); + } + + Collection fromOfficeOptions = null; + Collection fromClientOptions = null; + if (mostRelevantFromOfficeId != null) { + fromOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantFromOfficeId); + fromOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); + fromClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantFromOfficeId); + } + + // defaults + LocalDate transferDate = DateUtils.getLocalDateOfTenant(); + toOffice = fromOffice; + Collection toOfficeOptions = fromOfficeOptions; + Collection toClientOptions = fromClientOptions; + + if (toAccountId != null) { + toAccount = this.savingsAccountReadPlatformService.retrieveOne(toAccountId); + + // override provided toClient with client of account + mostRelevantToClientId = toAccount.clientId(); + } + + if (mostRelevantToClientId != null && mostRelevantToClientId != mostRelevantFromClientId) { + toClient = this.clientReadPlatformService.retrieveOne(mostRelevantToClientId); + mostRelevantToOfficeId = toClient.officeId(); + + toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); + + toAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantToClientId); + toAccountOptions.remove(fromAccount); + } + + if (mostRelevantToOfficeId != null && mostRelevantToOfficeId != mostRelevantFromOfficeId) { + toOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantToOfficeId); + toOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); + + toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); + if (toClientOptions != null && toClientOptions.size() == 1) { + toClient = new ArrayList(toClientOptions).get(0); + + toAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantToClientId); + toAccountOptions.remove(fromAccount); + } + } + + return AccountTransferData.template(fromOffice, fromClient, fromAccount, transferDate, toOffice, toClient, toAccount, + fromOfficeOptions, fromClientOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountOptions); + } + + @Override + public Page retrieveAll(final SearchParameters searchParameters) { + + StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append(accountTransfersMapper.schema()); + + if (searchParameters.isOrderByRequested()) { + sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); + + if (searchParameters.isSortOrderProvided()) { + sqlBuilder.append(' ').append(searchParameters.getSortOrder()); + } + } + + if (searchParameters.isLimited()) { + sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + if (searchParameters.isOffset()) { + sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + } + } + + final Object[] objectArray = new Object[2]; + int arrayPos = 0; + final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos); + final String sqlCountRows = "SELECT FOUND_ROWS()"; + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, + this.accountTransfersMapper); + } + + @Override + public AccountTransferData retrieveOne(final Long transferId) { + + try { + final String sql = "select " + this.accountTransfersMapper.schema() + " where sat.id = ?"; + + return this.jdbcTemplate.queryForObject(sql, this.accountTransfersMapper, new Object[] { transferId }); + } catch (EmptyResultDataAccessException e) { + throw new AccountTransferNotFoundException(transferId); + } + } + + private static final class AccountTransfersMapper implements RowMapper { + + private final String schemaSql; + + public AccountTransfersMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("sat.id as id, sat.is_reversed as isReversed,"); + sqlBuilder.append("sat.transaction_date as transferDate, sat.amount as transferAmount,"); + sqlBuilder.append("sat.description as transferDescription,"); + sqlBuilder.append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits,"); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); + sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); + sqlBuilder.append("tooff.id as toOfficeId, tooff.name as toOfficeName,"); + sqlBuilder.append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,"); + sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); + sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); + sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); + sqlBuilder.append("fromsavtran.id as fromSavingsAccountTransactionId,"); + sqlBuilder.append("fromsavtran.transaction_type_enum as fromSavingsAccountTransactionType,"); + sqlBuilder.append("tosavtran.id as toSavingsAccountTransactionId,"); + sqlBuilder.append("tosavtran.transaction_type_enum as toSavingsAccountTransactionType"); + sqlBuilder.append(" FROM m_savings_account_transfer sat "); + sqlBuilder.append("join m_currency curr on curr.code = sat.currency_code "); + sqlBuilder.append("join m_office fromoff on fromoff.id = sat.from_office_id "); + sqlBuilder.append("join m_office tooff on tooff.id = sat.to_office_id "); + sqlBuilder.append("join m_client fromclient on fromclient.id = sat.from_client_id "); + sqlBuilder.append("join m_client toclient on toclient.id = sat.to_client_id "); + sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = sat.from_savings_account_id "); + sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = sat.to_savings_account_id "); + sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = sat.from_savings_transaction_id "); + sqlBuilder.append("left join m_savings_account_transaction tosavtran on tosavtran.id = sat.to_savings_transaction_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public AccountTransferData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final boolean reversed = rs.getBoolean("isReversed"); + + final LocalDate transferDate = JdbcSupport.getLocalDate(rs, "transferDate"); + final BigDecimal transferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "transferAmount"); + final String transferDescription = rs.getString("transferDescription"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, + currencyNameCode); + + final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); + final String fromOfficeName = rs.getString("fromOfficeName"); + OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); + + final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); + final String toOfficeName = rs.getString("toOfficeName"); + OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); + + final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId"); + final String fromClientName = rs.getString("fromClientName"); + ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); + + final Long toClientId = JdbcSupport.getLong(rs, "toClientId"); + final String toClientName = rs.getString("toClientName"); + ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); + + final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); + final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); + SavingsAccountData fromSavingsAccount = SavingsAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); + + final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId"); + final String toSavingsAccountNo = rs.getString("toSavingsAccountNo"); + SavingsAccountData toSavingsAccount = SavingsAccountData.lookup(toSavingsAccountId, toSavingsAccountNo); + + return AccountTransferData.instance(id, reversed, transferDate, currency, transferAmount, transferDescription, fromOffice, + toOffice, fromClient, toClient, fromSavingsAccount, toSavingsAccount); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java new file mode 100644 index 000000000..d950863b1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java @@ -0,0 +1,14 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface AccountTransfersWritePlatformService { + + CommandProcessingResult create(JsonCommand command); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java new file mode 100644 index 000000000..6bc88c0ff --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java @@ -0,0 +1,91 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.accounttransfers.service; + +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName; + +import java.math.BigDecimal; +import java.util.Locale; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransfersDataValidator; +import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransfer; +import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransferAssembler; +import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransferRepository; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountDomainService; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class AccountTransfersWritePlatformServiceImpl implements AccountTransfersWritePlatformService { + + private final AccountTransfersDataValidator accountTransfersDataValidator; + private final AccountTransferAssembler accountTransferAssembler; + private final AccountTransferRepository accountTransferRepository; + private final SavingsAccountAssembler savingsAccountAssembler; + private final SavingsAccountDomainService savingsAccountDomainService; + + @Autowired + public AccountTransfersWritePlatformServiceImpl(final AccountTransfersDataValidator accountTransfersDataValidator, + final AccountTransferAssembler accountTransferAssembler, final AccountTransferRepository accountTransferRepository, + final SavingsAccountAssembler savingsAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService) { + this.accountTransfersDataValidator = accountTransfersDataValidator; + this.accountTransferAssembler = accountTransferAssembler; + this.accountTransferRepository = accountTransferRepository; + this.savingsAccountAssembler = savingsAccountAssembler; + this.savingsAccountDomainService = savingsAccountDomainService; + } + + @Transactional + @Override + public CommandProcessingResult create(final JsonCommand command) { + + this.accountTransfersDataValidator.validate(command); + + final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + + // check fromAccountType settings to see if its a savings account + // withdrawal or a loan account transfer + final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId); + + final PaymentDetail paymentDetail = null; + + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, + transactionDate, transactionAmount, paymentDetail); + + final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, + transactionAmount, paymentDetail); + + AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, withdrawal, deposit); + this.accountTransferRepository.saveAndFlush(transferTransaction); + + return new CommandProcessingResultBuilder() // + .withSavingsId(fromSavingsId) // + .withEntityId(transferTransaction.getId()) // + .build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java index 1fc10807d..41f0564e0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java @@ -23,7 +23,6 @@ import org.mifosplatform.portfolio.group.data.GroupGeneralData; */ final public class ClientData implements Comparable { - private final Long id; private final String accountNo; private final String externalId; @@ -49,16 +48,17 @@ final public class ClientData implements Comparable { // associations private final Collection groups; - + // template private final Collection officeOptions; private final Collection staffOptions; @SuppressWarnings("unused") private final Collection closureReasons; - - public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection officeOptions, + + public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection officeOptions, final Collection staffOptions, final Collection closureReasons) { - return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, null, null, officeOptions, null, staffOptions, closureReasons); + return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, null, null, + officeOptions, null, staffOptions, closureReasons); } public static ClientData templateOnTop(final ClientData clientData, final ClientData templateData) { @@ -81,11 +81,12 @@ final public class ClientData implements Comparable { final String officeName) { return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, null, - null, null, null, null, null, null,null, null); + null, null, null, null, null, null, null, null); } public static ClientData lookup(final Long id, final String displayName, final Long officeId, final String officeName) { - return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, null, null, null, null); + return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, + null, null, null, null); } public static ClientData instance(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, @@ -99,8 +100,8 @@ final public class ClientData implements Comparable { private ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, final String displayName, final String externalId, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName, - final Collection allowedOffices, final Collection groups, final Collection staffOptions, - final Collection closureReasons) { + final Collection allowedOffices, final Collection groups, + final Collection staffOptions, final Collection closureReasons) { this.accountNo = accountNo; this.status = status; if (status != null) { @@ -122,7 +123,7 @@ final public class ClientData implements Comparable { if (imageId != null) { this.imagePresent = Boolean.TRUE; } else { - this.imagePresent = Boolean.FALSE; + this.imagePresent = null; } this.staffId = staffId; this.staffName = staffName; @@ -189,7 +190,6 @@ final public class ClientData implements Comparable { .toHashCode(); } - // TODO - kw - look into removing usage of the getters below public String getExternalId() { return this.externalId; } @@ -205,5 +205,4 @@ final public class ClientData implements Comparable { public LocalDate getActivationDate() { return this.activationDate; } - } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java index e9be89150..e6da94bd1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java @@ -8,6 +8,8 @@ package org.mifosplatform.portfolio.savings.data; import java.math.BigDecimal; import java.util.Collection; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.joda.time.LocalDate; import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.data.EnumOptionData; @@ -93,6 +95,26 @@ public class SavingsAccountData { interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } + public static SavingsAccountData lookup(final Long accountId, final String accountNo) { + + final Collection productOptions = null; + final Collection fieldOfficerOptions = null; + final Collection interestCompoundingPeriodTypeOptions = null; + final Collection interestPostingPeriodTypeOptions = null; + final Collection interestCalculationTypeOptions = null; + final Collection interestCalculationDaysInYearTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + + final Collection transactions = null; + + return new SavingsAccountData(accountId, accountNo, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions); + } + public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template, final Collection transactions) { @@ -263,4 +285,23 @@ public class SavingsAccountData { public Long productId() { return this.savingsProductId; } + + public CurrencyData currency() { + return this.currency; + } + + @Override + public boolean equals(final Object obj) { + + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { return false; } + SavingsAccountData rhs = (SavingsAccountData) obj; + return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).toHashCode(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java index 28547379e..88d5324b3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java @@ -11,6 +11,7 @@ import java.util.Collection; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData; /** @@ -29,16 +30,18 @@ public class SavingsAccountTransactionData { private final BigDecimal amount; private final BigDecimal runningBalance; private final boolean reversed; + private final AccountTransferData transfer; // templates final Collection paymentTypeOptions; public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, - final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed) { + final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed, + final AccountTransferData transfer) { final Collection paymentTypeOptions = null; return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, - amount, runningBalance, reversed, paymentTypeOptions); + amount, runningBalance, reversed, transfer, paymentTypeOptions); } public static SavingsAccountTransactionData template(final Long savingsId, final String savingsAccountNo, @@ -51,7 +54,7 @@ public class SavingsAccountTransactionData { final PaymentDetailData paymentDetailData = null; final Collection paymentTypeOptions = null; return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, defaultLocalDate, - currency, amount, runningBalance, reversed, null); + currency, amount, runningBalance, reversed, null, null); } public static SavingsAccountTransactionData templateOnTop(final SavingsAccountTransactionData savingsAccountTransactionData, @@ -60,13 +63,13 @@ public class SavingsAccountTransactionData { savingsAccountTransactionData.paymentDetailData, savingsAccountTransactionData.accountId, savingsAccountTransactionData.accountNo, savingsAccountTransactionData.date, savingsAccountTransactionData.currency, savingsAccountTransactionData.amount, savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed, - paymentTypeOptions); + savingsAccountTransactionData.transfer, paymentTypeOptions); } private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, final CurrencyData currency, final BigDecimal amount, final BigDecimal runningBalance, final boolean reversed, - final Collection paymentTypeOptions) { + final AccountTransferData transfer, final Collection paymentTypeOptions) { this.id = id; this.transactionType = transactionType; this.paymentDetailData = paymentDetailData; @@ -77,6 +80,7 @@ public class SavingsAccountTransactionData { this.amount = amount; this.runningBalance = runningBalance; this.reversed = reversed; + this.transfer = transfer; this.paymentTypeOptions = paymentTypeOptions; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 3b4e39e2a..d4dcea6d7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -699,7 +699,7 @@ public class SavingsAccount extends AbstractPersistable { boolean transactionBeforeLastInterestPosting = false; for (SavingsAccountTransaction transaction : retreiveListOfTransactions()) { - if (transaction.isNotReversed() && transaction.isInterestPostingAndNotReversed() && transaction.isAfter(transactionDate)) { + if (transaction.isInterestPostingAndNotReversed() && transaction.isAfter(transactionDate)) { transactionBeforeLastInterestPosting = true; break; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java new file mode 100644 index 000000000..96ddfa376 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainService.java @@ -0,0 +1,21 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; + +public interface SavingsAccountDomainService { + + SavingsAccountTransaction handleWithdrawal(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate, + BigDecimal transactionAmount, PaymentDetail paymentDetail); + + SavingsAccountTransaction handleDeposit(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate, + BigDecimal transactionAmount, PaymentDetail paymentDetail); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java new file mode 100644 index 000000000..286a28125 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java @@ -0,0 +1,117 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainService { + + private final SavingsAccountRepositoryWrapper savingsAccountRepository; + private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; + private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; + private final JournalEntryWritePlatformService journalEntryWritePlatformService; + + @Autowired + public SavingsAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository, + final SavingsAccountTransactionRepository savingsAccountTransactionRepository, + final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, + final JournalEntryWritePlatformService journalEntryWritePlatformService) { + this.savingsAccountRepository = savingsAccountRepository; + this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; + this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; + this.journalEntryWritePlatformService = journalEntryWritePlatformService; + } + + @Transactional + @Override + public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account, final DateTimeFormatter fmt, + final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail) { + + final List existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + + final SavingsAccountTransaction withdrawal = account.withdraw(fmt, transactionDate, transactionAmount, existingTransactionIds, + existingReversedTransactionIds, paymentDetail); + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(transactionDate)) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + saveTransactionToGenerateTransactionId(withdrawal); + this.savingsAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return withdrawal; + } + + @Transactional + @Override + public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt, + final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail) { + + final List existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + + final SavingsAccountTransaction deposit = account.deposit(fmt, transactionDate, transactionAmount, existingTransactionIds, + existingReversedTransactionIds, paymentDetail); + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(transactionDate)) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + saveTransactionToGenerateTransactionId(deposit); + + this.savingsAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + + return deposit; + } + + private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) { + this.savingsAccountTransactionRepository.save(transaction); + return transaction.getId(); + } + + private void postJournalEntries(final SavingsAccount savingsAccount, final List existingTransactionIds, + final List existingReversedTransactionIds) { + + final MonetaryCurrency currency = savingsAccount.getCurrency(); + final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); + + final Map accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(), + existingTransactionIds, existingReversedTransactionIds); + journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java index 677d97f4b..684a5a487 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java @@ -17,6 +17,8 @@ public interface SavingsAccountReadPlatformService { Page retrieveAll(SearchParameters searchParameters); + Collection retrieveAllForLookup(Long clientId); + SavingsAccountData retrieveOne(Long savingsId); SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index d38a8f0d2..75c7a0561 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -26,6 +26,7 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; +import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.group.data.GroupGeneralData; @@ -74,7 +75,6 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead // pagination private final PaginationHelper paginationHelper = new PaginationHelper(); - @Autowired public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService, @@ -93,6 +93,16 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead this.annualFeeMapper = new SavingsAccountAnnualFeeMapper(); } + @Override + public Collection retrieveAllForLookup(final Long clientId) { + + StringBuilder sqlBuilder = new StringBuilder("select " + this.savingAccountMapper.schema()); + sqlBuilder.append(" where sa.client_id = ? and sa.status_enum = 300"); + + Object[] queryParameters = new Object[] { clientId }; + return this.jdbcTemplate.query(sqlBuilder.toString(), this.savingAccountMapper, queryParameters); + } + @Override public Page retrieveAll(final SearchParameters searchParameters) { @@ -148,12 +158,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead public SavingsAccountData retrieveOne(final Long accountId) { try { - this.context.authenticatedUser(); + final String sql = "select " + this.savingAccountMapper.schema() + " where sa.id = ?"; - final SavingAccountMapper mapper = new SavingAccountMapper(); - final String sql = "select " + mapper.schema() + " where sa.id = ?"; - - return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { accountId }); + return this.jdbcTemplate.queryForObject(sql, this.savingAccountMapper, new Object[] { accountId }); } catch (EmptyResultDataAccessException e) { throw new SavingsAccountNotFoundException(accountId); } @@ -518,6 +525,12 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("tr.id as transactionId, tr.transaction_type_enum as transactionType, "); sqlBuilder.append("tr.transaction_date as transactionDate, tr.amount as transactionAmount,"); sqlBuilder.append("tr.running_balance_derived as runningBalance, tr.is_reversed as reversed,"); + sqlBuilder.append("fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed,"); + sqlBuilder.append("fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount,"); + sqlBuilder.append("fromtran.description as fromTransferDescription,"); + sqlBuilder.append("totran.id as toTransferId, totran.is_reversed as toTransferReversed,"); + sqlBuilder.append("totran.transaction_date as toTransferDate, totran.amount as toTransferAmount,"); + sqlBuilder.append("totran.description as toTransferDescription,"); sqlBuilder.append("sa.id as savingsId, sa.account_no as accountNo,"); sqlBuilder.append("pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "); sqlBuilder.append("pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, "); @@ -528,6 +541,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("from m_savings_account sa "); sqlBuilder.append("join m_savings_account_transaction tr on tr.savings_account_id = sa.id "); sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); + sqlBuilder.append("left join m_savings_account_transfer fromtran on fromtran.from_savings_transaction_id = tr.id "); + sqlBuilder.append("left join m_savings_account_transfer totran on totran.to_savings_transaction_id = tr.id "); sqlBuilder.append("left join m_payment_detail pd on tr.payment_detail_id = pd.id "); sqlBuilder.append("left join m_code_value cv on pd.payment_type_cv_id = cv.id "); @@ -576,8 +591,29 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, currencyNameCode); + AccountTransferData transfer = null; + final Long fromTransferId = JdbcSupport.getLong(rs, "fromTransferId"); + final Long toTransferId = JdbcSupport.getLong(rs, "toTransferId"); + if (fromTransferId != null) { + final LocalDate fromTransferDate = JdbcSupport.getLocalDate(rs, "fromTransferDate"); + final BigDecimal fromTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fromTransferAmount"); + final boolean fromTransferReversed = rs.getBoolean("fromTransferReversed"); + final String fromTransferDescription = rs.getString("fromTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(fromTransferId, currency, fromTransferAmount, fromTransferDate, + fromTransferDescription, fromTransferReversed); + } else if (toTransferId != null) { + final LocalDate toTransferDate = JdbcSupport.getLocalDate(rs, "toTransferDate"); + final BigDecimal toTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "toTransferAmount"); + final boolean toTransferReversed = rs.getBoolean("toTransferReversed"); + final String toTransferDescription = rs.getString("toTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(toTransferId, currency, toTransferAmount, toTransferDate, + toTransferDescription, toTransferReversed); + } + return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency, - amount, runningBalance, reversed); + amount, runningBalance, reversed, transfer); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index d53241b28..119915d13 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -33,6 +33,7 @@ import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatf import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDataValidator; import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountDomainService; import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; @@ -53,6 +54,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; private final JournalEntryWritePlatformService journalEntryWritePlatformService; + private final SavingsAccountDomainService savingsAccountDomainService; @Autowired public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -62,7 +64,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator, final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, - final JournalEntryWritePlatformService journalEntryWritePlatformService) { + final JournalEntryWritePlatformService journalEntryWritePlatformService, + final SavingsAccountDomainService savingsAccountDomainService) { this.context = context; this.savingAccountRepository = savingAccountRepository; this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; @@ -71,6 +74,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi this.paymentDetailWritePlatformService = paymentDetailWritePlatformService; this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; this.journalEntryWritePlatformService = journalEntryWritePlatformService; + this.savingsAccountDomainService = savingsAccountDomainService; } @Transactional @@ -120,36 +124,19 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); final Map changes = new LinkedHashMap(); PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); - final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); - final SavingsAccountTransaction deposit = account.deposit(fmt, transactionDate, transactionAmount, existingTransactionIds, - existingReversedTransactionIds, paymentDetail); - - if (account.isBeforeLastPostingPeriod(transactionDate)) { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); - } else { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.calculateInterestUsing(mc, today); - } - - final Long transactionId = saveTransactionToGenerateTransactionId(deposit); - - this.savingAccountRepository.save(account); - - postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(account, fmt, transactionDate, + transactionAmount, paymentDetail); return new CommandProcessingResultBuilder() // - .withEntityId(transactionId) // + .withEntityId(deposit.getId()) // .withOfficeId(account.officeId()) // .withClientId(account.clientId()) // .withGroupId(account.groupId()) // .withSavingsId(savingsId) // - .with(changes)// + .with(changes) // .build(); } @@ -162,42 +149,24 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi @Override public CommandProcessingResult withdrawal(final Long savingsId, final JsonCommand command) { - this.context.authenticatedUser(); - this.savingsAccountTransactionDataValidator.validate(command); - final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); - final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); final Map changes = new LinkedHashMap(); PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); - final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); - final SavingsAccountTransaction withdrawal = account.withdraw(fmt, transactionDate, transactionAmount, existingTransactionIds, - existingReversedTransactionIds, paymentDetail); + final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); - if (account.isBeforeLastPostingPeriod(transactionDate)) { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); - } else { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.calculateInterestUsing(mc, today); - } - - final Long transactionId = saveTransactionToGenerateTransactionId(withdrawal); - this.savingAccountRepository.save(account); - - postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(account, fmt, transactionDate, + transactionAmount, paymentDetail); return new CommandProcessingResultBuilder() // - .withEntityId(transactionId) // + .withEntityId(withdrawal.getId()) // .withOfficeId(account.officeId()) // .withClientId(account.clientId()) // .withGroupId(account.groupId()) // @@ -273,7 +242,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .build(); } - @Transactional private void postInterest(final SavingsAccount account) { final List existingTransactionIds = new ArrayList(); @@ -290,7 +258,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi @CronTarget(jobName = JobName.POST_INTEREST_FOR_SAVINGS) @Override public void postInterestForAccounts() { - final List savingsAccounts = this.savingAccountRepository.findSavingAccountByStatus(SavingsAccountStatusType.ACTIVE.getValue()); + final List savingsAccounts = this.savingAccountRepository.findSavingAccountByStatus(SavingsAccountStatusType.ACTIVE + .getValue()); for (final SavingsAccount savingsAccount : savingsAccounts) { this.savingAccountAssembler.assignSavingAccountHelpers(savingsAccount); postInterest(savingsAccount); From f260591ea955700c82241619d92185377699ec0a Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 8 Aug 2013 00:17:54 +0100 Subject: [PATCH 28/47] remove invalid suppression of warnings which were shown as warnings in eclipse indigo but fixed in eclipse juno and kepler. --- .../accounting/closure/domain/GLClosure.java | 1 - .../journalentry/domain/JournalEntry.java | 7 - .../rule/domain/AccountingTagRule.java | 1 - .../commands/domain/CommandSource.java | 264 ++--- .../codes/domain/CodeValue.java | 123 +-- .../configuration/domain/ExternalService.java | 3 +- .../domain/GlobalConfigurationProperty.java | 1 - .../jobs/domain/ScheduledJobDetail.java | 9 +- .../jobs/domain/ScheduledJobRunHistory.java | 8 - .../organisation/holiday/domain/Holiday.java | 2 - .../office/domain/OfficeTransaction.java | 6 - .../office/domain/OrganisationCurrency.java | 5 - .../organisation/staff/domain/Staff.java | 7 +- .../portfolio/client/domain/Client.java | 4 +- .../client/domain/ClientIdentifier.java | 125 +-- .../collateral/domain/LoanCollateral.java | 11 +- .../portfolio/group/domain/GroupRole.java | 1 - .../portfolio/loanaccount/domain/Loan.java | 235 +++-- .../domain/LoanOfficerAssignmentHistory.java | 5 +- .../LoanRepaymentScheduleInstallment.java | 15 +- .../loanaccount/domain/LoanSummary.java | 8 +- .../domain/LoanSummaryArrearsAging.java | 2 - .../portfolio/note/api/NotesApiResource.java | 15 +- .../portfolio/note/domain/Note.java | 5 - .../savings/domain/SavingsAccount.java | 128 ++- .../savings/domain/SavingsAccountSummary.java | 3 +- .../domain/SavingsAccountTransaction.java | 32 +- .../useradministration/domain/AppUser.java | 928 +++++++++--------- .../useradministration/domain/Permission.java | 4 +- 29 files changed, 972 insertions(+), 986 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/closure/domain/GLClosure.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/closure/domain/GLClosure.java index 5d57e6024..4c141b170 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/closure/domain/GLClosure.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/closure/domain/GLClosure.java @@ -33,7 +33,6 @@ public class GLClosure extends AbstractAuditableCustom { @JoinColumn(name = "office_id", nullable = false) private Office office; - @SuppressWarnings("unused") @Column(name = "is_deleted", nullable = false) private boolean deleted = true; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java index e5298504c..294fea652 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java @@ -35,20 +35,16 @@ public class JournalEntry extends AbstractAuditableCustom { @JoinColumn(name = "account_id", nullable = false) private GLAccount glAccount; - @SuppressWarnings("unused") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "reversal_id") private JournalEntry reversalJournalEntry; - @SuppressWarnings("unused") @Column(name = "transaction_id", nullable = false, length = 50) private String transactionId; - @SuppressWarnings("unused") @Column(name = "reversed", nullable = false) private boolean reversed = false; - @SuppressWarnings("unused") @Column(name = "manual_entry", nullable = false) private boolean manualEntry = false; @@ -62,15 +58,12 @@ public class JournalEntry extends AbstractAuditableCustom { @Column(name = "amount", scale = 6, precision = 19, nullable = false) private BigDecimal amount; - @SuppressWarnings("unused") @Column(name = "description", length = 500) private String description; - @SuppressWarnings("unused") @Column(name = "entity_type_enum", length = 50) private Integer entityType; - @SuppressWarnings("unused") @Column(name = "entity_id") private Long entityId; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingTagRule.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingTagRule.java index 16b03e806..f43a99cef 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingTagRule.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingTagRule.java @@ -20,7 +20,6 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "acc_rule_tags", uniqueConstraints = { @UniqueConstraint(columnNames = { "acc_rule_id", "tag_id", "acc_type_enum" }, name = "UNIQUE_ACCOUNT_RULE_TAGS") }) public class AccountingTagRule extends AbstractPersistable { - @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "acc_rule_id", nullable = false) private AccountingRule accountingRule; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandSource.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandSource.java index b8c948061..3aa5ab685 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandSource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandSource.java @@ -25,171 +25,175 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_portfolio_command_source") public class CommandSource extends AbstractPersistable { - @Column(name = "action_name", nullable = true, length = 100) - private String actionName; + @Column(name = "action_name", nullable = true, length = 100) + private String actionName; - @Column(name = "entity_name", nullable = true, length = 100) - private String entityName; + @Column(name = "entity_name", nullable = true, length = 100) + private String entityName; - @SuppressWarnings("unused") - @Column(name = "office_id") - private Long officeId; + @Column(name = "office_id") + private Long officeId; - @SuppressWarnings("unused") - @Column(name = "group_id") - private Long groupId; + @Column(name = "group_id") + private Long groupId; - @SuppressWarnings("unused") - @Column(name = "client_id") - private Long clientId; + @Column(name = "client_id") + private Long clientId; - @SuppressWarnings("unused") - @Column(name = "loan_id") - private Long loanId; + @Column(name = "loan_id") + private Long loanId; - @SuppressWarnings("unused") - @Column(name = "savings_account_id") - private Long savingsId; + @Column(name = "savings_account_id") + private Long savingsId; - @Column(name = "api_get_url", length = 100) - private String resourceGetUrl; + @Column(name = "api_get_url", length = 100) + private String resourceGetUrl; - @Column(name = "resource_id") - private Long resourceId; + @Column(name = "resource_id") + private Long resourceId; - @Column(name = "subresource_id") - private Long subresourceId; + @Column(name = "subresource_id") + private Long subresourceId; - @Column(name = "command_as_json", length = 1000) - private String commandAsJson; + @Column(name = "command_as_json", length = 1000) + private String commandAsJson; - @SuppressWarnings("unused") - @ManyToOne - @JoinColumn(name = "maker_id", nullable = false) - private AppUser maker; + @ManyToOne + @JoinColumn(name = "maker_id", nullable = false) + private AppUser maker; - @SuppressWarnings("unused") - @Column(name = "made_on_date", nullable = false) - @Temporal(TemporalType.TIMESTAMP) - private Date madeOnDate; + @Column(name = "made_on_date", nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date madeOnDate; - @SuppressWarnings("unused") - @ManyToOne - @JoinColumn(name = "checker_id", nullable = true) - private AppUser checker; + @ManyToOne + @JoinColumn(name = "checker_id", nullable = true) + private AppUser checker; - @SuppressWarnings("unused") - @Column(name = "checked_on_date", nullable = true) - @Temporal(TemporalType.TIMESTAMP) - private Date checkedOnDate; + @Column(name = "checked_on_date", nullable = true) + @Temporal(TemporalType.TIMESTAMP) + private Date checkedOnDate; - @Column(name = "processing_result_enum", nullable = false) - private Integer processingResult; - - @Column(name = "product_id") - private Long productId; + @Column(name = "processing_result_enum", nullable = false) + private Integer processingResult; - public static CommandSource fullEntryFrom(final CommandWrapper wrapper, final JsonCommand command, final AppUser maker) { - return new CommandSource(wrapper.actionName(), wrapper.entityName(), wrapper.getHref(), command.entityId(), command.subentityId(), - command.json(), maker, DateTime.now()); - } + @Column(name = "product_id") + private Long productId; - protected CommandSource() { - // - } + public static CommandSource fullEntryFrom(final CommandWrapper wrapper, + final JsonCommand command, final AppUser maker) { + return new CommandSource(wrapper.actionName(), wrapper.entityName(), + wrapper.getHref(), command.entityId(), command.subentityId(), + command.json(), maker, DateTime.now()); + } - private CommandSource(final String actionName, final String entityName, final String href, final Long resourceId, - final Long subresourceId, final String commandSerializedAsJson, final AppUser maker, final DateTime madeOnDateTime) { - this.actionName = actionName; - this.entityName = entityName; - this.resourceGetUrl = href; - this.resourceId = resourceId; - this.subresourceId = subresourceId; - this.commandAsJson = commandSerializedAsJson; - this.maker = maker; - this.madeOnDate = madeOnDateTime.toDate(); - this.processingResult = CommandProcessingResultType.PROCESSED.getValue(); - } + protected CommandSource() { + // + } - public void markAsChecked(final AppUser checker, final DateTime checkedOnDate) { - this.checker = checker; - this.checkedOnDate = checkedOnDate.toDate(); - this.processingResult = CommandProcessingResultType.PROCESSED.getValue(); - } + private CommandSource(final String actionName, final String entityName, + final String href, final Long resourceId, final Long subresourceId, + final String commandSerializedAsJson, final AppUser maker, + final DateTime madeOnDateTime) { + this.actionName = actionName; + this.entityName = entityName; + this.resourceGetUrl = href; + this.resourceId = resourceId; + this.subresourceId = subresourceId; + this.commandAsJson = commandSerializedAsJson; + this.maker = maker; + this.madeOnDate = madeOnDateTime.toDate(); + this.processingResult = CommandProcessingResultType.PROCESSED + .getValue(); + } - public void updateResourceId(final Long resourceId) { - this.resourceId = resourceId; - } + public void markAsChecked(final AppUser checker, + final DateTime checkedOnDate) { + this.checker = checker; + this.checkedOnDate = checkedOnDate.toDate(); + this.processingResult = CommandProcessingResultType.PROCESSED + .getValue(); + } - public void updateSubresourceId(final Long subresourceId) { - this.subresourceId = subresourceId; - } + public void updateResourceId(final Long resourceId) { + this.resourceId = resourceId; + } - public void updateJsonTo(final String json) { - this.commandAsJson = json; - } + public void updateSubresourceId(final Long subresourceId) { + this.subresourceId = subresourceId; + } - public Long resourceId() { - return this.resourceId; - } + public void updateJsonTo(final String json) { + this.commandAsJson = json; + } - public Long subresourceId() { - return this.subresourceId; - } + public Long resourceId() { + return this.resourceId; + } - public boolean hasJson() { - return StringUtils.isNotBlank(this.commandAsJson); - } + public Long subresourceId() { + return this.subresourceId; + } - public String json() { - return this.commandAsJson; - } + public boolean hasJson() { + return StringUtils.isNotBlank(this.commandAsJson); + } - public String getActionName() { - return this.actionName; - } + public String json() { + return this.commandAsJson; + } - public String getEntityName() { - return this.entityName; - } + public String getActionName() { + return this.actionName; + } - public String getPermissionCode() { - return this.actionName + "_" + this.entityName; - } + public String getEntityName() { + return this.entityName; + } - public Long getResourceId() { - return this.resourceId; - } + public String getPermissionCode() { + return this.actionName + "_" + this.entityName; + } - public Long getSubresourceId() { - return this.subresourceId; - } + public Long getResourceId() { + return this.resourceId; + } - public void markAsAwaitingApproval() { - this.processingResult = CommandProcessingResultType.AWAITING_APPROVAL.getValue(); - } + public Long getSubresourceId() { + return this.subresourceId; + } - public boolean isMarkedAsAwaitingApproval() { - if (this.processingResult.equals(CommandProcessingResultType.AWAITING_APPROVAL.getValue())) return true; + public void markAsAwaitingApproval() { + this.processingResult = CommandProcessingResultType.AWAITING_APPROVAL + .getValue(); + } - return false; - } + public boolean isMarkedAsAwaitingApproval() { + if (this.processingResult + .equals(CommandProcessingResultType.AWAITING_APPROVAL + .getValue())) { + return true; + } - public void updateForAudit(final Long officeId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId, - final Long productId) { - this.officeId = officeId; - this.groupId = groupId; - this.clientId = clientId; - this.loanId = loanId; - this.savingsId = savingsId; - this.productId = productId; - } + return false; + } - public String getResourceGetUrl() { - return this.resourceGetUrl; - } + public void updateForAudit(final Long officeId, final Long groupId, + final Long clientId, final Long loanId, final Long savingsId, + final Long productId) { + this.officeId = officeId; + this.groupId = groupId; + this.clientId = clientId; + this.loanId = loanId; + this.savingsId = savingsId; + this.productId = productId; + } - public Long getProductId() { - return this.productId; - } + public String getResourceGetUrl() { + return this.resourceGetUrl; + } + + public Long getProductId() { + return this.productId; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/codes/domain/CodeValue.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/codes/domain/CodeValue.java index 7efc1b33b..549722db0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/codes/domain/CodeValue.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/codes/domain/CodeValue.java @@ -22,72 +22,85 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.springframework.data.jpa.domain.AbstractPersistable; @Entity -@Table(name = "m_code_value", uniqueConstraints = { @UniqueConstraint(columnNames = { "code_id", "code_value" }, name = "code_value_duplicate") }) +@Table(name = "m_code_value", uniqueConstraints = { @UniqueConstraint(columnNames = { + "code_id", "code_value" }, name = "code_value_duplicate") }) public class CodeValue extends AbstractPersistable { - @Column(name = "code_value", length = 100) - private String label; + @Column(name = "code_value", length = 100) + private String label; - @Column(name = "order_position") - private int position; + @Column(name = "order_position") + private int position; - @SuppressWarnings("unused") - @ManyToOne - @JoinColumn(name = "code_id", nullable = false) - private Code code; + @ManyToOne + @JoinColumn(name = "code_id", nullable = false) + private Code code; - public static CodeValue createNew(Code code, final String label, final int position) { - return new CodeValue(code, label, position); - } + public static CodeValue createNew(final Code code, final String label, + final int position) { + return new CodeValue(code, label, position); + } - protected CodeValue() { - // - } + protected CodeValue() { + // + } - private CodeValue(final Code code, final String label, final int position) { - this.code = code; - this.label = StringUtils.defaultIfEmpty(label, null); - this.position = position; - } + private CodeValue(final Code code, final String label, final int position) { + this.code = code; + this.label = StringUtils.defaultIfEmpty(label, null); + this.position = position; + } - public String label() { - return label; - } + public String label() { + return this.label; + } - public int position() { - return position; - } - - public static CodeValue fromJson(Code code, final JsonCommand command) { + public int position() { + return this.position; + } - final String label = command.stringValueOfParameterNamed(CODEVALUE_JSON_INPUT_PARAMS.NAME.getValue()); - Integer position = command.integerValueSansLocaleOfParameterNamed(CODEVALUE_JSON_INPUT_PARAMS.POSITION.getValue()); - if(position == null) position = new Integer(0); - return new CodeValue(code, label, position.intValue()); - } - - public Map update(final JsonCommand command) { + public static CodeValue fromJson(final Code code, final JsonCommand command) { - final Map actualChanges = new LinkedHashMap(2); + final String label = command + .stringValueOfParameterNamed(CODEVALUE_JSON_INPUT_PARAMS.NAME + .getValue()); + Integer position = command + .integerValueSansLocaleOfParameterNamed(CODEVALUE_JSON_INPUT_PARAMS.POSITION + .getValue()); + if (position == null) { + position = new Integer(0); + } + return new CodeValue(code, label, position.intValue()); + } - final String labelParamName = CODEVALUE_JSON_INPUT_PARAMS.NAME.getValue(); - if (command.isChangeInStringParameterNamed(labelParamName, this.label)) { - final String newValue = command.stringValueOfParameterNamed(labelParamName); - actualChanges.put(labelParamName, newValue); - this.label = StringUtils.defaultIfEmpty(newValue, null); - } - - final String positionParamName = CODEVALUE_JSON_INPUT_PARAMS.POSITION.getValue(); - if (command.isChangeInIntegerSansLocaleParameterNamed(positionParamName, this.position)) { - final Integer newValue = command.integerValueSansLocaleOfParameterNamed(positionParamName); - actualChanges.put(positionParamName, newValue); - this.position = newValue.intValue(); - } - - return actualChanges; - } + public Map update(final JsonCommand command) { - public CodeValueData toData() { - return CodeValueData.instance(this.getId(), this.label, this.position); - } + final Map actualChanges = new LinkedHashMap( + 2); + + final String labelParamName = CODEVALUE_JSON_INPUT_PARAMS.NAME + .getValue(); + if (command.isChangeInStringParameterNamed(labelParamName, this.label)) { + final String newValue = command + .stringValueOfParameterNamed(labelParamName); + actualChanges.put(labelParamName, newValue); + this.label = StringUtils.defaultIfEmpty(newValue, null); + } + + final String positionParamName = CODEVALUE_JSON_INPUT_PARAMS.POSITION + .getValue(); + if (command.isChangeInIntegerSansLocaleParameterNamed( + positionParamName, this.position)) { + final Integer newValue = command + .integerValueSansLocaleOfParameterNamed(positionParamName); + actualChanges.put(positionParamName, newValue); + this.position = newValue.intValue(); + } + + return actualChanges; + } + + public CodeValueData toData() { + return CodeValueData.instance(getId(), this.label, this.position); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalService.java index 1fca5d62d..ec8b30055 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalService.java @@ -14,10 +14,9 @@ import javax.persistence.UniqueConstraint; import org.springframework.data.jpa.domain.AbstractPersistable; @Entity -@Table(name = "c_external_service", uniqueConstraints = { @UniqueConstraint(columnNames = ("name")) }) +@Table(name = "c_external_service", uniqueConstraints = { @UniqueConstraint(columnNames = "name") }) public class ExternalService extends AbstractPersistable { - @SuppressWarnings("unused") @Column(name = "name", nullable = false) private String name; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/GlobalConfigurationProperty.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/GlobalConfigurationProperty.java index e9dbff130..f40370be7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/GlobalConfigurationProperty.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/GlobalConfigurationProperty.java @@ -15,7 +15,6 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "c_configuration") public class GlobalConfigurationProperty extends AbstractPersistable { - @SuppressWarnings("unused") @Column(name = "name", nullable = false) private final String name; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java index ac391a344..7664cc5bc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java @@ -28,7 +28,6 @@ public class ScheduledJobDetail extends AbstractPersistable { @Column(name = "cron_expression") private String cronExpression; - @SuppressWarnings("unused") @Column(name = "create_time") @Temporal(TemporalType.TIMESTAMP) private Date createTime; @@ -39,12 +38,10 @@ public class ScheduledJobDetail extends AbstractPersistable { @Column(name = "group_name") private String groupName; - @SuppressWarnings("unused") @Column(name = "previous_run_start_time") @Temporal(TemporalType.TIMESTAMP) private Date previousRunStartTime; - @SuppressWarnings("unused") @Column(name = "next_run_time") @Temporal(TemporalType.TIMESTAMP) private Date nextRunTime; @@ -52,7 +49,6 @@ public class ScheduledJobDetail extends AbstractPersistable { @Column(name = "job_key") private String jobKey; - @SuppressWarnings("unused") @Column(name = "initializing_errorlog") private String errorLog; @@ -62,7 +58,6 @@ public class ScheduledJobDetail extends AbstractPersistable { @Column(name = "currently_running") private boolean currentlyRunning; - @SuppressWarnings("unused") @Column(name = "updates_allowed") private boolean updatesAllowed; @@ -113,7 +108,7 @@ public class ScheduledJobDetail extends AbstractPersistable { this.nextRunTime = nextRunTime; } - public void updateJobKey(String jobKey) { + public void updateJobKey(final String jobKey) { this.jobKey = jobKey; } @@ -125,7 +120,7 @@ public class ScheduledJobDetail extends AbstractPersistable { return this.currentlyRunning; } - public void updateCurrentlyRunningStatus(boolean currentlyRunning) { + public void updateCurrentlyRunningStatus(final boolean currentlyRunning) { this.currentlyRunning = currentlyRunning; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobRunHistory.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobRunHistory.java index a355ecd4e..e7b5255cd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobRunHistory.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobRunHistory.java @@ -16,38 +16,30 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "job_run_history") public class ScheduledJobRunHistory extends AbstractPersistable { - @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "job_id") private ScheduledJobDetail scheduledJobDetail; - @SuppressWarnings("unused") @Column(name = "version") private Long version; - @SuppressWarnings("unused") @Column(name = "start_time") @Temporal(TemporalType.TIMESTAMP) private Date startTime; - @SuppressWarnings("unused") @Column(name = "end_time") @Temporal(TemporalType.TIMESTAMP) private Date endTime; - @SuppressWarnings("unused") @Column(name = "status") private String status; - @SuppressWarnings("unused") @Column(name = "error_message") private String errorMessage; - @SuppressWarnings("unused") @Column(name = "trigger_type") private String triggerType; - @SuppressWarnings("unused") @Column(name = "error_log") private String errorLog; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/Holiday.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/Holiday.java index 3fd2487e4..b1eba52b1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/Holiday.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/Holiday.java @@ -30,7 +30,6 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_holiday", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "holiday_name") }) public class Holiday extends AbstractPersistable { - @SuppressWarnings("unused") @Column(name = "name", unique = true, nullable = false, length = 100) private String name; @@ -49,7 +48,6 @@ public class Holiday extends AbstractPersistable { @Column(name = "processed", nullable = false) private boolean processed; - @SuppressWarnings("unused") @Column(name = "description", length = 100) private String description; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OfficeTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OfficeTransaction.java index b4cf3be75..8ce899019 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OfficeTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OfficeTransaction.java @@ -28,30 +28,24 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_office_transaction") public class OfficeTransaction extends AbstractPersistable { - @SuppressWarnings("unused") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "from_office_id") private Office from; - @SuppressWarnings("unused") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "to_office_id") private Office to; - @SuppressWarnings("unused") @Column(name = "transaction_date", nullable = false) @Temporal(TemporalType.DATE) private Date transactionDate; - @SuppressWarnings("unused") @Embedded private MonetaryCurrency currency; - @SuppressWarnings("unused") @Column(name = "transaction_amount", scale = 6, precision = 19, nullable = false) private BigDecimal transactionAmount; - @SuppressWarnings("unused") @Column(name = "description", nullable = true, length = 100) private String description; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java index c0a88dc58..d824ee431 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java @@ -18,23 +18,18 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_organisation_currency") public class OrganisationCurrency extends AbstractPersistable { - @SuppressWarnings("unused") @Column(name = "code", nullable = false, length = 3) private final String code; - @SuppressWarnings("unused") @Column(name = "decimal_places", nullable = false) private final Integer decimalPlaces; - @SuppressWarnings("unused") @Column(name = "name", nullable = false, length = 50) private final String name; - @SuppressWarnings("unused") @Column(name = "internationalized_name_code", nullable = false, length = 50) private final String nameCode; - @SuppressWarnings("unused") @Column(name = "display_symbol", nullable = true, length = 10) private final String displaySymbol; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java index 2a82ef24a..d4c9d7b36 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/domain/Staff.java @@ -50,7 +50,6 @@ public class Staff extends AbstractPersistable { @Column(name = "organisational_role_enum", nullable = true) private Integer organisationalRoleType; - @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "organisational_role_parent_staff_id", nullable = true) private Staff organisationalRoleParentStaff; @@ -58,13 +57,13 @@ public class Staff extends AbstractPersistable { public static Staff fromJson(final Office staffOffice, final JsonCommand command) { final String firstnameParamName = "firstname"; - String firstname = command.stringValueOfParameterNamed(firstnameParamName); + final String firstname = command.stringValueOfParameterNamed(firstnameParamName); final String lastnameParamName = "lastname"; - String lastname = command.stringValueOfParameterNamed(lastnameParamName); + final String lastname = command.stringValueOfParameterNamed(lastnameParamName); final String externalIdParamName = "externalId"; - String externalId = command.stringValueOfParameterNamedAllowingNull(externalIdParamName); + final String externalId = command.stringValueOfParameterNamedAllowingNull(externalIdParamName); final String isLoanOfficerParamName = "isLoanOfficer"; final boolean isLoanOfficer = command.booleanPrimitiveValueOfParameterNamed(isLoanOfficerParamName); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java index c1c958095..7b644b87f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java @@ -96,11 +96,9 @@ public final class Client extends AbstractPersistable { @Transient private boolean accountNumberRequiresAutoGeneration = false; - @SuppressWarnings("unused") @Column(name = "closure_reason_cv_id", nullable = true) private Long closureReasonId; - @SuppressWarnings("unused") @Column(name = "closedon_date", nullable = true) @Temporal(TemporalType.DATE) private Date closureDate; @@ -464,7 +462,7 @@ public final class Client extends AbstractPersistable { } public Integer getStatus() { - return status; + return this.status; } public boolean isActivatedAfter(final LocalDate submittedOn) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientIdentifier.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientIdentifier.java index 2b9bcc29d..276e1e4bf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientIdentifier.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientIdentifier.java @@ -23,78 +23,89 @@ import org.mifosplatform.useradministration.domain.AppUser; @Entity @Table(name = "m_client_identifier", uniqueConstraints = { - @UniqueConstraint(columnNames = { "document_type_id", "document_key" }, name = "unique_identifier_key"), - @UniqueConstraint(columnNames = { "client_id", "document_type_id" }, name = "unique_client_identifier") }) + @UniqueConstraint(columnNames = { "document_type_id", "document_key" }, name = "unique_identifier_key"), + @UniqueConstraint(columnNames = { "client_id", "document_type_id" }, name = "unique_client_identifier") }) public class ClientIdentifier extends AbstractAuditableCustom { - @SuppressWarnings("unused") - @ManyToOne - @JoinColumn(name = "client_id", nullable = false) - private Client client; + @ManyToOne + @JoinColumn(name = "client_id", nullable = false) + private Client client; - @ManyToOne - @JoinColumn(name = "document_type_id", nullable = false) - private CodeValue documentType; + @ManyToOne + @JoinColumn(name = "document_type_id", nullable = false) + private CodeValue documentType; - @Column(name = "document_key", length = 1000) - private String documentKey; + @Column(name = "document_key", length = 1000) + private String documentKey; - @Column(name = "description", length = 1000) - private String description; + @Column(name = "description", length = 1000) + private String description; - public static ClientIdentifier fromJson(final Client client, final CodeValue documentType, final JsonCommand command) { - final String documentKey = command.stringValueOfParameterNamed("documentKey"); - final String description = command.stringValueOfParameterNamed("description"); - return new ClientIdentifier(client, documentType, documentKey, description); - } + public static ClientIdentifier fromJson(final Client client, + final CodeValue documentType, final JsonCommand command) { + final String documentKey = command + .stringValueOfParameterNamed("documentKey"); + final String description = command + .stringValueOfParameterNamed("description"); + return new ClientIdentifier(client, documentType, documentKey, + description); + } - protected ClientIdentifier() { - // - } + protected ClientIdentifier() { + // + } - private ClientIdentifier(final Client client, final CodeValue documentType, final String documentKey, final String description) { - this.client = client; - this.documentType = documentType; - this.documentKey = StringUtils.defaultIfEmpty(documentKey, null); - this.description = StringUtils.defaultIfEmpty(description, null); - } + private ClientIdentifier(final Client client, final CodeValue documentType, + final String documentKey, final String description) { + this.client = client; + this.documentType = documentType; + this.documentKey = StringUtils.defaultIfEmpty(documentKey, null); + this.description = StringUtils.defaultIfEmpty(description, null); + } - public void update(final CodeValue documentType) { - this.documentType = documentType; - } + public void update(final CodeValue documentType) { + this.documentType = documentType; + } - public Map update(final JsonCommand command) { + public Map update(final JsonCommand command) { - final Map actualChanges = new LinkedHashMap(7); + final Map actualChanges = new LinkedHashMap( + 7); - final String documentTypeIdParamName = "documentTypeId"; - if (command.isChangeInLongParameterNamed(documentTypeIdParamName, this.documentType.getId())) { - final Long newValue = command.longValueOfParameterNamed(documentTypeIdParamName); - actualChanges.put(documentTypeIdParamName, newValue); - } + final String documentTypeIdParamName = "documentTypeId"; + if (command.isChangeInLongParameterNamed(documentTypeIdParamName, + this.documentType.getId())) { + final Long newValue = command + .longValueOfParameterNamed(documentTypeIdParamName); + actualChanges.put(documentTypeIdParamName, newValue); + } - final String documentKeyParamName = "documentKey"; - if (command.isChangeInStringParameterNamed(documentKeyParamName, this.documentKey)) { - final String newValue = command.stringValueOfParameterNamed(documentKeyParamName); - actualChanges.put(documentKeyParamName, newValue); - this.documentKey = StringUtils.defaultIfEmpty(newValue, null); - } + final String documentKeyParamName = "documentKey"; + if (command.isChangeInStringParameterNamed(documentKeyParamName, + this.documentKey)) { + final String newValue = command + .stringValueOfParameterNamed(documentKeyParamName); + actualChanges.put(documentKeyParamName, newValue); + this.documentKey = StringUtils.defaultIfEmpty(newValue, null); + } - 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); - } + 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; - } + return actualChanges; + } - public String documentKey() { - return this.documentKey; - } + public String documentKey() { + return this.documentKey; + } - public Long documentTypeId() { - return this.documentType.getId(); - } + public Long documentTypeId() { + return this.documentType.getId(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/domain/LoanCollateral.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/domain/LoanCollateral.java index 5ad056a76..8e6eb8edf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/domain/LoanCollateral.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/domain/LoanCollateral.java @@ -30,7 +30,6 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_loan_collateral") public class LoanCollateral extends AbstractPersistable { - @SuppressWarnings("unused") @ManyToOne(optional = false) @JoinColumn(name = "loan_id", nullable = false) private Loan loan; @@ -105,10 +104,10 @@ public class LoanCollateral extends AbstractPersistable { public CollateralData toData() { final CodeValueData typeData = this.type.toData(); - return CollateralData.instance(this.getId(), typeData, this.value, this.description, null); + return CollateralData.instance(getId(), typeData, this.value, this.description, null); } - public void setCollateralType(CodeValue type) { + public void setCollateralType(final CodeValue type) { this.type = type; } @@ -117,9 +116,9 @@ public class LoanCollateral extends AbstractPersistable { if (obj == null) { return false; } if (obj == this) { return true; } if (obj.getClass() != getClass()) { return false; } - LoanCollateral rhs = (LoanCollateral) obj; + final LoanCollateral rhs = (LoanCollateral) obj; return new EqualsBuilder().appendSuper(super.equals(obj)) // - .append(this.getId(), rhs.getId()) // + .append(getId(), rhs.getId()) // .append(this.type.getId(), rhs.type.getId()) // .append(this.description, rhs.description) // .append(this.value, this.value)// @@ -129,7 +128,7 @@ public class LoanCollateral extends AbstractPersistable { @Override public int hashCode() { return new HashCodeBuilder(3, 5) // - .append(this.getId()) // + .append(getId()) // .append(this.type.getId()) // .append(this.description) // .append(this.value)// diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupRole.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupRole.java index 07a68e612..045d52570 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupRole.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupRole.java @@ -19,7 +19,6 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_group_roles") public class GroupRole extends AbstractPersistable { - @SuppressWarnings("unused") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "group_id") private Group group; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 6d93fb163..ee0285858 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -149,27 +149,22 @@ public class Loan extends AbstractPersistable { @Column(name = "submittedon_date") private Date submittedOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "submittedon_userid", nullable = true) private AppUser submittedBy; - @SuppressWarnings("unused") @Temporal(TemporalType.DATE) @Column(name = "rejectedon_date") private Date rejectedOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "rejectedon_userid", nullable = true) private AppUser rejectedBy; - @SuppressWarnings("unused") @Temporal(TemporalType.DATE) @Column(name = "withdrawnon_date") private Date withdrawnOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "withdrawnon_userid", nullable = true) private AppUser withdrawnBy; @@ -178,7 +173,6 @@ public class Loan extends AbstractPersistable { @Column(name = "approvedon_date") private Date approvedOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "approvedon_userid", nullable = true) private AppUser approvedBy; @@ -191,7 +185,6 @@ public class Loan extends AbstractPersistable { @Column(name = "disbursedon_date") private Date actualDisbursementDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "disbursedon_userid", nullable = true) private AppUser disbursedBy; @@ -200,12 +193,10 @@ public class Loan extends AbstractPersistable { @Column(name = "closedon_date") private Date closedOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "closedon_userid", nullable = true) private AppUser closedBy; - @SuppressWarnings("unused") @Temporal(TemporalType.DATE) @Column(name = "writtenoffon_date") private Date writtenOffOnDate; @@ -214,12 +205,10 @@ public class Loan extends AbstractPersistable { @Column(name = "rescheduledon_date") private Date rescheduledOnDate; - @SuppressWarnings("unused") @Temporal(TemporalType.DATE) @Column(name = "expected_maturedon_date") private Date expectedMaturityDate; - @SuppressWarnings("unused") @Temporal(TemporalType.DATE) @Column(name = "maturedon_date") private Date actualMaturityDate; @@ -232,7 +221,6 @@ public class Loan extends AbstractPersistable { @Column(name = "interest_calculated_from_date") private Date interestChargedFromDate; - @SuppressWarnings("unused") @Column(name = "total_overpaid_derived", scale = 6, precision = 19) private BigDecimal totalOverpaid; @@ -382,7 +370,7 @@ public class Loan extends AbstractPersistable { Money chargesDue = Money.of(getCurrency(), BigDecimal.ZERO); - for (LoanCharge charge : setOfLoanCharges()) { + for (final LoanCharge charge : setOfLoanCharges()) { if (charge.isDueAtDisbursement()) { chargesDue = chargesDue.plus(charge.amount()); } @@ -392,14 +380,14 @@ public class Loan extends AbstractPersistable { } private Set associateChargesWithThisLoan(final Set loanCharges) { - for (LoanCharge loanCharge : loanCharges) { + for (final LoanCharge loanCharge : loanCharges) { loanCharge.update(this); } return loanCharges; } private Set associateWithThisLoan(final Set collateral) { - for (LoanCollateral item : collateral) { + for (final LoanCollateral item : collateral) { item.associateWith(this); } return collateral; @@ -455,13 +443,13 @@ public class Loan extends AbstractPersistable { final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); - for (LoanTransaction transaction : changedTransactionDetail.getNewTransactions()) { + for (final LoanTransaction transaction : changedTransactionDetail.getNewTransactions()) { transaction.updateLoan(this); } this.loanTransactions.addAll(changedTransactionDetail.getNewTransactions()); } else { // just reprocess the loan schedule only for now. - LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); + final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges()); } @@ -497,7 +485,7 @@ public class Loan extends AbstractPersistable { transactionDate = suppliedTransactionDate; } else { transactionDate = loanCharge.getDueLocalDate(); - LocalDate currentDate = DateUtils.getLocalDateOfTenant(); + final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); // if loan charge is to be applied on a future date, the loan // transaction would show todays date as applied date @@ -549,7 +537,7 @@ public class Loan extends AbstractPersistable { // if you want ability to remove loan charges that are waived. validateLoanChargeIsNotWaived(loanCharge); - boolean removed = setOfLoanCharges().remove(loanCharge); + final boolean removed = setOfLoanCharges().remove(loanCharge); if (removed) { updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); } @@ -578,7 +566,7 @@ public class Loan extends AbstractPersistable { private void removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(final LoanCharge loanCharge) { if (loanCharge.isDueAtDisbursement()) { LoanTransaction transactionToRemove = null; - for (LoanTransaction transaction : this.loanTransactions) { + for (final LoanTransaction transaction : this.loanTransactions) { if (transaction.isRepaymentAtDisbursement()) { final MonetaryCurrency currency = loanCurrency(); @@ -630,7 +618,7 @@ public class Loan extends AbstractPersistable { getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); } else { // reprocess loan schedule based on charge been waived. - LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); + final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges()); } @@ -687,7 +675,7 @@ public class Loan extends AbstractPersistable { getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); } else { // reprocess loan schedule based on charge been waived. - LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); + final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges()); } @@ -732,12 +720,12 @@ public class Loan extends AbstractPersistable { } public void updateLoanOfficerOnLoanApplication(final Staff newLoanOfficer) { - if (!this.isSubmittedAndPendingApproval()) { + if (!isSubmittedAndPendingApproval()) { Long loanOfficerId = null; if (this.loanOfficer != null) { loanOfficerId = this.loanOfficer.getId(); } - throw new LoanOfficerAssignmentException(this.getId(), loanOfficerId); + throw new LoanOfficerAssignmentException(getId(), loanOfficerId); } this.loanOfficer = newLoanOfficer; } @@ -763,15 +751,15 @@ public class Loan extends AbstractPersistable { public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) { this.repaymentScheduleInstallments.clear(); - for (LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) { + for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) { if (scheduledLoanInstallment.isRepaymentPeriod()) { - LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(this, + final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(this, scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(), scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(), scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(), scheduledLoanInstallment.penaltyChargesDue()); - this.addRepaymentScheduleInstallment(installment); + addRepaymentScheduleInstallment(installment); } } @@ -812,7 +800,7 @@ public class Loan extends AbstractPersistable { final Map actualChanges = this.loanRepaymentScheduleDetail.updateLoanApplicationAttributes(command, aprCalculator); if (!actualChanges.isEmpty()) { - boolean recalculateLoanSchedule = !(actualChanges.size() == 1 && actualChanges.containsKey("inArrearsTolerance")); + final boolean recalculateLoanSchedule = !(actualChanges.size() == 1 && actualChanges.containsKey("inArrearsTolerance")); actualChanges.put("recalculateLoanSchedule", recalculateLoanSchedule); } @@ -962,14 +950,14 @@ public class Loan extends AbstractPersistable { throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, getSubmittedOnDate()); } - if (!(client == null)) { - if (getSubmittedOnDate().isBefore(client.getActivationLocalDate())) { + if (!(this.client == null)) { + if (getSubmittedOnDate().isBefore(this.client.getActivationLocalDate())) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date."; throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, getSubmittedOnDate()); } - } else if (!(group == null)) { - if (getSubmittedOnDate().isBefore(group.getActivationLocalDate())) { + } else if (!(this.group == null)) { + if (getSubmittedOnDate().isBefore(this.group.getActivationLocalDate())) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date."; throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage, getSubmittedOnDate()); @@ -994,7 +982,7 @@ public class Loan extends AbstractPersistable { actualChanges.put(chargesParamName, getLoanCharges(possiblyModifedLoanCharges)); actualChanges.put("recalculateLoanSchedule", true); - for (LoanCharge loanCharge : possiblyModifedLoanCharges) { + for (final LoanCharge loanCharge : possiblyModifedLoanCharges) { validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); } } @@ -1038,10 +1026,10 @@ public class Loan extends AbstractPersistable { CollateralData[] existingLoanCollateral = null; - List loanCollateralList = new ArrayList(); - for (LoanCollateral loanCollateral : setOfLoanCollateral) { + final List loanCollateralList = new ArrayList(); + for (final LoanCollateral loanCollateral : setOfLoanCollateral) { - CollateralData data = loanCollateral.toData(); + final CollateralData data = loanCollateral.toData(); loanCollateralList.add(data); } @@ -1055,8 +1043,8 @@ public class Loan extends AbstractPersistable { LoanChargeCommand[] existingLoanCharges = null; - List loanChargesList = new ArrayList(); - for (LoanCharge loanCharge : setOfLoanCharges) { + final List loanChargesList = new ArrayList(); + for (final LoanCharge loanCharge : setOfLoanCharges) { loanChargesList.add(loanCharge.toCommand()); } @@ -1066,9 +1054,9 @@ public class Loan extends AbstractPersistable { } private void removeFirstDisbursementTransaction() { - for (LoanTransaction loanTransaction : loanTransactions) { + for (final LoanTransaction loanTransaction : this.loanTransactions) { if (loanTransaction.isDisbursement()) { - loanTransactions.remove(loanTransaction); + this.loanTransactions.remove(loanTransaction); break; } } @@ -1086,7 +1074,7 @@ public class Loan extends AbstractPersistable { from = LoanStatus.fromInt(this.loanStatus); } - LoanStatus statusEnum = lifecycleStateMachine.transition(LoanEvent.LOAN_CREATED, from); + final LoanStatus statusEnum = lifecycleStateMachine.transition(LoanEvent.LOAN_CREATED, from); this.loanStatus = statusEnum.getValue(); this.externalId = externalId; @@ -1105,11 +1093,11 @@ public class Loan extends AbstractPersistable { throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, submittedOn); } - if (client != null && client.isActivatedAfter(submittedOn)) { + if (this.client != null && this.client.isActivatedAfter(submittedOn)) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date."; throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, submittedOn); } - if (group != null && group.isActivatedAfter(submittedOn)) { + if (this.group != null && this.group.isActivatedAfter(submittedOn)) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date."; throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage, submittedOn); } @@ -1122,7 +1110,7 @@ public class Loan extends AbstractPersistable { } // charges are optional - for (LoanCharge loanCharge : setOfLoanCharges()) { + for (final LoanCharge loanCharge : setOfLoanCharges()) { validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); } @@ -1132,7 +1120,7 @@ public class Loan extends AbstractPersistable { } private LocalDate determineExpectedMaturityDate() { - int numberOfInstallments = this.repaymentScheduleInstallments.size(); + final int numberOfInstallments = this.repaymentScheduleInstallments.size(); return this.repaymentScheduleInstallments.get(numberOfInstallments - 1).getDueDate(); } @@ -1146,7 +1134,7 @@ public class Loan extends AbstractPersistable { this.loanStatus = statusEnum.getValue(); actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); - LocalDate rejectedOn = command.localDateValueOfParameterNamed("rejectedOnDate"); + final LocalDate rejectedOn = command.localDateValueOfParameterNamed("rejectedOnDate"); final Locale locale = new Locale(command.locale()); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); @@ -1294,9 +1282,9 @@ public class Loan extends AbstractPersistable { private Collection findExistingTransactionIds() { - Collection ids = new ArrayList(); + final Collection ids = new ArrayList(); - for (LoanTransaction transaction : this.loanTransactions) { + for (final LoanTransaction transaction : this.loanTransactions) { ids.add(transaction.getId()); } @@ -1307,7 +1295,7 @@ public class Loan extends AbstractPersistable { final Collection ids = new ArrayList(); - for (LoanTransaction transaction : this.loanTransactions) { + for (final LoanTransaction transaction : this.loanTransactions) { if (transaction.isReversed()) { ids.add(transaction.getId()); } @@ -1324,7 +1312,7 @@ public class Loan extends AbstractPersistable { updateLoanToPreDisbursalState(); - final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, LoanStatus.fromInt(this.loanStatus)); + final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, LoanStatus.fromInt(this.loanStatus)); if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { existingTransactionIds.addAll(findExistingTransactionIds()); @@ -1358,7 +1346,7 @@ public class Loan extends AbstractPersistable { updateLoanSummaryDerivedFields(); } - Money interestApplied = Money.of(this.getCurrency(), this.summary.getTotalInterestCharged()); + final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged()); final LoanTransaction interestAppliedTransaction = LoanTransaction.applyInterest(this, interestApplied, actualDisbursementDate); this.loanTransactions.add(interestAppliedTransaction); } @@ -1368,8 +1356,8 @@ public class Loan extends AbstractPersistable { private void updateLoanRepaymentPeriodsDerivedFields(final LocalDate actualDisbursementDate) { - for (LoanRepaymentScheduleInstallment repaymentPeriod : this.repaymentScheduleInstallments) { - repaymentPeriod.updateDerivedFields(this.loanCurrency(), actualDisbursementDate); + for (final LoanRepaymentScheduleInstallment repaymentPeriod : this.repaymentScheduleInstallments) { + repaymentPeriod.updateDerivedFields(loanCurrency(), actualDisbursementDate); } } @@ -1389,11 +1377,11 @@ public class Loan extends AbstractPersistable { final Integer loanTermFrequency = this.termFrequency; final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType); - LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, + final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail); - LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, this.charges, isHolidayEnabled, holidays, workingDays); + final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, this.charges, isHolidayEnabled, holidays, workingDays); updateLoanSchedule(loanSchedule); } @@ -1415,8 +1403,8 @@ public class Loan extends AbstractPersistable { final Money totalFeeChargesDueAtDisbursement = this.summary.getTotalFeeChargesDueAtDisbursement(loanCurrency()); if (totalFeeChargesDueAtDisbursement.isGreaterThanZero()) { - LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(totalFeeChargesDueAtDisbursement, null, disbursedOn, txnExternalId); - Money zero = Money.zero(getCurrency()); + final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(totalFeeChargesDueAtDisbursement, null, disbursedOn, txnExternalId); + final Money zero = Money.zero(getCurrency()); chargesPayment.updateComponents(zero, zero, totalFeeChargesDueAtDisbursement, zero); chargesPayment.updateLoan(this); this.loanTransactions.add(chargesPayment); @@ -1426,11 +1414,11 @@ public class Loan extends AbstractPersistable { * "APPLY Charge" transactions are created for all other fees ( * which are created during disbursal but not repaid) **/ - for (LoanCharge charge : setOfLoanCharges()) { + for (final LoanCharge charge : setOfLoanCharges()) { if (charge.isDueAtDisbursement()) { charge.markAsFullyPaid(); // Add "Loan Charge Paid By" details to this transaction - LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount()); + final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount()); chargesPayment.getLoanChargesPaid().add(loanChargePaidBy); } else { handleChargeAppliedTransaction(charge, disbursedOn); @@ -1481,7 +1469,7 @@ public class Loan extends AbstractPersistable { private final void reverseExistingTransactions() { - for (LoanTransaction transaction : this.loanTransactions) { + for (final LoanTransaction transaction : this.loanTransactions) { transaction.reverse(); } } @@ -1489,15 +1477,15 @@ public class Loan extends AbstractPersistable { private void updateLoanToPreDisbursalState() { this.actualDisbursementDate = null; - for (LoanCharge charge : setOfLoanCharges()) { + for (final LoanCharge charge : setOfLoanCharges()) { charge.resetToOriginal(loanCurrency()); } - for (LoanRepaymentScheduleInstallment currentInstallment : this.repaymentScheduleInstallments) { + for (final LoanRepaymentScheduleInstallment currentInstallment : this.repaymentScheduleInstallments) { currentInstallment.resetDerivedComponents(); } - LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); + final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges()); updateLoanSummaryDerivedFields(); @@ -1510,7 +1498,7 @@ public class Loan extends AbstractPersistable { existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrWaiverTransaction(waiveInterestTransaction, + final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrWaiverTransaction(waiveInterestTransaction, loanLifecycleStateMachine, null); return changedTransactionDetail; @@ -1527,7 +1515,7 @@ public class Loan extends AbstractPersistable { existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrWaiverTransaction(repaymentTransaction, + final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrWaiverTransaction(repaymentTransaction, loanLifecycleStateMachine, null); return changedTransactionDetail; @@ -1544,7 +1532,7 @@ public class Loan extends AbstractPersistable { loanTransaction.updateLoan(this); - boolean isTransactionChronologicallyLatest = isChronologicallyLatestRepaymentOrWaiver(loanTransaction, this.loanTransactions); + final boolean isTransactionChronologicallyLatest = isChronologicallyLatestRepaymentOrWaiver(loanTransaction, this.loanTransactions); if (loanTransaction.isNotZero(loanCurrency())) { this.loanTransactions.add(loanTransaction); @@ -1555,12 +1543,12 @@ public class Loan extends AbstractPersistable { throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.repayment.or.waiver.transaction", errorMessage); } - LocalDate loanTransactionDate = loanTransaction.getTransactionDate(); - if (loanTransactionDate.isBefore(this.getDisbursementDate())) { + final LocalDate loanTransactionDate = loanTransaction.getTransactionDate(); + if (loanTransactionDate.isBefore(getDisbursementDate())) { final String errorMessage = "The transaction date cannot be before the loan disbursement date: " + getApprovedOnDate().toString(); throw new InvalidLoanStateTransitionException("transaction", "cannot.be.before.disbursement.date", errorMessage, - loanTransactionDate, this.getDisbursementDate()); + loanTransactionDate, getDisbursementDate()); } if (loanTransactionDate.isAfter(DateUtils.getLocalDateOfTenant())) { @@ -1589,7 +1577,7 @@ public class Loan extends AbstractPersistable { final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); - for (LoanTransaction newLoanTransaction : changedTransactionDetail.getNewTransactions()) { + for (final LoanTransaction newLoanTransaction : changedTransactionDetail.getNewTransactions()) { newLoanTransaction.updateLoan(this); } this.loanTransactions.addAll(changedTransactionDetail.getNewTransactions()); @@ -1603,20 +1591,20 @@ public class Loan extends AbstractPersistable { } private List retreiveListOfTransactionsPostDisbursement() { - List repaymentsOrWaivers = new ArrayList(); - for (LoanTransaction transaction : this.loanTransactions) { + final List repaymentsOrWaivers = new ArrayList(); + for (final LoanTransaction transaction : this.loanTransactions) { if (!transaction.isDisbursement() && transaction.isNotReversed()) { repaymentsOrWaivers.add(transaction); } } - LoanTransactionComparator transactionComparator = new LoanTransactionComparator(); + final LoanTransactionComparator transactionComparator = new LoanTransactionComparator(); Collections.sort(repaymentsOrWaivers, transactionComparator); return repaymentsOrWaivers; } private void doPostLoanTransactionChecks(final LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) { - if (this.isOverPaid()) { + if (isOverPaid()) { // FIXME - kw - update account balance to negative amount. handleLoanOverpayment(loanLifecycleStateMachine); } else if (this.summary.isRepaidInFull(loanCurrency())) { @@ -1626,7 +1614,7 @@ public class Loan extends AbstractPersistable { private void handleLoanRepaymentInFull(final LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) { - LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, LoanStatus.fromInt(this.loanStatus)); + final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, LoanStatus.fromInt(this.loanStatus)); this.loanStatus = statusEnum.getValue(); this.closedOnDate = transactionDate.toDate(); @@ -1635,7 +1623,7 @@ public class Loan extends AbstractPersistable { private void handleLoanOverpayment(final LoanLifecycleStateMachine loanLifecycleStateMachine) { - LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, LoanStatus.fromInt(this.loanStatus)); + final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, LoanStatus.fromInt(this.loanStatus)); this.loanStatus = statusEnum.getValue(); this.closedOnDate = null; @@ -1647,8 +1635,8 @@ public class Loan extends AbstractPersistable { boolean isChronologicallyLatestRepaymentOrWaiver = true; - LocalDate currentTransactionDate = loanTransaction.getTransactionDate(); - for (LoanTransaction previousTransaction : loanTransactions) { + final LocalDate currentTransactionDate = loanTransaction.getTransactionDate(); + for (final LoanTransaction previousTransaction : loanTransactions) { if (!previousTransaction.isDisbursement() && previousTransaction.isNotReversed()) { if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate()) || currentTransactionDate.isEqual(previousTransaction.getTransactionDate())) { @@ -1665,8 +1653,8 @@ public class Loan extends AbstractPersistable { boolean isChronologicallyLatestRepaymentOrWaiver = true; - LocalDate currentTransactionDate = loanTransaction.getTransactionDate(); - for (LoanTransaction previousTransaction : loanTransactions) { + final LocalDate currentTransactionDate = loanTransaction.getTransactionDate(); + for (final LoanTransaction previousTransaction : loanTransactions) { if (previousTransaction.isNotReversed()) { if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate()) || currentTransactionDate.isEqual(previousTransaction.getTransactionDate())) { @@ -1681,7 +1669,7 @@ public class Loan extends AbstractPersistable { public LocalDate possibleNextRepaymentDate() { LocalDate earliestUnpaidInstallmentDate = new LocalDate(); - for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { + for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { if (installment.isNotFullyPaidOff()) { earliestUnpaidInstallmentDate = installment.getDueDate(); break; @@ -1689,7 +1677,7 @@ public class Loan extends AbstractPersistable { } LocalDate lastTransactionDate = null; - for (LoanTransaction transaction : this.loanTransactions) { + for (final LoanTransaction transaction : this.loanTransactions) { if (transaction.isRepayment() && transaction.isNonZero()) { lastTransactionDate = transaction.getTransactionDate(); } @@ -1704,10 +1692,10 @@ public class Loan extends AbstractPersistable { } public Money possibleNextRepaymentAmount() { - MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getPrincipal().getCurrency(); + final MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getPrincipal().getCurrency(); Money possibleNextRepaymentAmount = Money.zero(currency); - for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { + for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { if (installment.isNotFullyPaidOff()) { possibleNextRepaymentAmount = installment.getTotalOutstanding(currency); break; @@ -1726,7 +1714,7 @@ public class Loan extends AbstractPersistable { if (totalInterestOutstanding.isGreaterThanZero()) { // find earliest known instance of overdue interest and default to // that - for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) { + for (final LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) { final Money outstandingForPeriod = scheduledRepayment.getInterestOutstanding(loanCurrency()); if (scheduledRepayment.isOverdueOn(new LocalDate()) && scheduledRepayment.isNotFullyPaidOff() @@ -1765,7 +1753,7 @@ public class Loan extends AbstractPersistable { if (isClosedWrittenOff()) { // find write off transaction and reverse it - LoanTransaction writeOffTransaction = findWriteOffTransaction(); + final LoanTransaction writeOffTransaction = findWriteOffTransaction(); writeOffTransaction.reverse(); } @@ -1784,7 +1772,7 @@ public class Loan extends AbstractPersistable { private LoanTransaction findWriteOffTransaction() { LoanTransaction writeOff = null; - for (LoanTransaction transaction : this.loanTransactions) { + for (final LoanTransaction transaction : this.loanTransactions) { if (!transaction.isReversed() && transaction.isWriteOff()) { writeOff = transaction; } @@ -1805,7 +1793,7 @@ public class Loan extends AbstractPersistable { Money cumulativeTotalPaidOnInstallments = Money.zero(currency); Money cumulativeTotalWaivedOnInstallments = Money.zero(currency); - for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) { + for (final LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) { cumulativeTotalPaidOnInstallments = cumulativeTotalPaidOnInstallments .plus(scheduledRepayment.getPrincipalCompleted(currency).plus(scheduledRepayment.getInterestPaid(currency))) @@ -1847,7 +1835,7 @@ public class Loan extends AbstractPersistable { changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); changes.put("writtenOffOnDate", command.stringValueOfParameterNamed("transactionDate")); - if (writtenOffOnLocalDate.isBefore(this.getDisbursementDate())) { + if (writtenOffOnLocalDate.isBefore(getDisbursementDate())) { final String errorMessage = "The date on which a loan is written off cannot be before the loan disbursement date: " + getDisbursementDate().toString(); throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.before.submittal.date", errorMessage, @@ -1860,7 +1848,7 @@ public class Loan extends AbstractPersistable { } loanTransaction = LoanTransaction.writeoff(this, writtenOffOnLocalDate, txnExternalId); - final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, loanTransactions); + final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, this.loanTransactions); if (!isLastTransaction) { final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions."; throw new InvalidLoanStateTransitionException("writeoff", "must.occur.on.or.after.other.transaction.dates", errorMessage, @@ -1891,7 +1879,7 @@ public class Loan extends AbstractPersistable { this.closedOnDate = closureDate.toDate(); changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); - if (closureDate.isBefore(this.getDisbursementDate())) { + if (closureDate.isBefore(getDisbursementDate())) { final String errorMessage = "The date on which a loan is closed cannot be before the loan disbursement date: " + getDisbursementDate().toString(); throw new InvalidLoanStateTransitionException("close", "cannot.be.before.submittal.date", errorMessage, closureDate, @@ -1917,7 +1905,7 @@ public class Loan extends AbstractPersistable { this.closedOnDate = closureDate.toDate(); loanTransaction = LoanTransaction.writeoff(this, closureDate, txnExternalId); - boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, loanTransactions); + final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, this.loanTransactions); if (!isLastTransaction) { final String errorMessage = "The closing date of the loan must be on or after latest transaction date."; throw new InvalidLoanStateTransitionException("close.loan", "must.occur.on.or.after.latest.transaction.date", @@ -1981,8 +1969,8 @@ public class Loan extends AbstractPersistable { changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); changes.put("rescheduledOnDate", command.stringValueOfParameterNamed("transactionDate")); - LocalDate rescheduledOnLocalDate = new LocalDate(rescheduledOnDate); - if (rescheduledOnLocalDate.isBefore(this.getDisbursementDate())) { + final LocalDate rescheduledOnLocalDate = new LocalDate(this.rescheduledOnDate); + if (rescheduledOnLocalDate.isBefore(getDisbursementDate())) { final String errorMessage = "The date on which a loan is rescheduled cannot be before the loan disbursement date: " + getDisbursementDate().toString(); throw new InvalidLoanStateTransitionException("close.reschedule", "cannot.be.before.submittal.date", errorMessage, @@ -2009,7 +1997,7 @@ public class Loan extends AbstractPersistable { } private boolean isNotDisbursed() { - return !this.isDisbursed(); + return !isDisbursed(); } private boolean isDisbursed() { @@ -2017,7 +2005,7 @@ public class Loan extends AbstractPersistable { } private boolean isClosed() { - return status().isClosed() || this.isCancelled(); + return status().isClosed() || isCancelled(); } private boolean isClosedObligationsMet() { @@ -2033,7 +2021,7 @@ public class Loan extends AbstractPersistable { } private boolean isCancelled() { - return this.isRejected() || this.isWithdrawn(); + return isRejected() || isWithdrawn(); } private boolean isWithdrawn() { @@ -2050,7 +2038,7 @@ public class Loan extends AbstractPersistable { private boolean hasDisbursementTransaction() { boolean hasRepaymentTransaction = false; - for (LoanTransaction loanTransaction : this.loanTransactions) { + for (final LoanTransaction loanTransaction : this.loanTransactions) { if (loanTransaction.isDisbursement() && loanTransaction.isNotReversed()) { hasRepaymentTransaction = true; break; @@ -2060,7 +2048,7 @@ public class Loan extends AbstractPersistable { } public boolean isSubmittedOnDateAfter(final LocalDate compareDate) { - return this.submittedOnDate == null ? false : (new LocalDate(submittedOnDate)).isAfter(compareDate); + return this.submittedOnDate == null ? false : new LocalDate(this.submittedOnDate).isAfter(compareDate); } public LocalDate getSubmittedOnDate() { @@ -2115,7 +2103,7 @@ public class Loan extends AbstractPersistable { private Money getTotalPaidInRepayments() { Money cumulativePaid = Money.zero(loanCurrency()); - for (LoanTransaction repayment : this.loanTransactions) { + for (final LoanTransaction repayment : this.loanTransactions) { if (repayment.isRepayment()) { cumulativePaid = cumulativePaid.plus(repayment.getAmount(loanCurrency())); } @@ -2127,7 +2115,7 @@ public class Loan extends AbstractPersistable { private Money getTotalInterestOutstandingOnLoan() { Money cumulativeInterest = Money.zero(loanCurrency()); - for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) { + for (final LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) { cumulativeInterest = cumulativeInterest.plus(scheduledRepayment.getInterestOutstanding(loanCurrency())); } @@ -2138,7 +2126,7 @@ public class Loan extends AbstractPersistable { private Money getTotalInterestOverdueOnLoan() { Money cumulativeInterestOverdue = Money.zero(this.loanRepaymentScheduleDetail.getPrincipal().getCurrency()); - for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) { + for (final LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) { final Money interestOutstandingForPeriod = scheduledRepayment.getInterestOutstanding(loanCurrency()); if (scheduledRepayment.isOverdueOn(new LocalDate())) { @@ -2154,7 +2142,7 @@ public class Loan extends AbstractPersistable { } public boolean hasIdentifyOf(final Long loanId) { - return loanId.equals(this.getId()); + return loanId.equals(getId()); } public boolean hasLoanOfficer(final Staff fromLoanOfficer) { @@ -2199,7 +2187,7 @@ public class Loan extends AbstractPersistable { final LoanOfficerAssignmentHistory lastAssignmentRecord = findLastAssignmentHistoryRecord(newLoanOfficer); // assignment date should not be less than loan submitted date - if (this.isSubmittedOnDateAfter(assignmentDate)) { + if (isSubmittedOnDateAfter(assignmentDate)) { final String errorMessage = "The Loan Officer assignment date (" + assignmentDate.toString() + ") cannot be before loan submitted date (" + getSubmittedOnDate().toString() + ")."; @@ -2227,8 +2215,8 @@ public class Loan extends AbstractPersistable { latestHistoryRecord.updateLoanOfficer(newLoanOfficer); this.loanOfficer = newLoanOfficer; } else if (latestHistoryRecord != null && latestHistoryRecord.hasStartDateBefore(assignmentDate)) { - final String errorMessage = "Loan with identifier " + this.getId() + " was already assigned before date " + assignmentDate; - throw new LoanOfficerAssignmentDateException("is.before.last.assignment.date", errorMessage, this.getId(), assignmentDate); + final String errorMessage = "Loan with identifier " + getId() + " was already assigned before date " + assignmentDate; + throw new LoanOfficerAssignmentDateException("is.before.last.assignment.date", errorMessage, getId(), assignmentDate); } else { if (latestHistoryRecord != null) { // loan officer correctly changed from previous loan officer to @@ -2237,7 +2225,7 @@ public class Loan extends AbstractPersistable { } this.loanOfficer = newLoanOfficer; - if (this.isNotSubmittedAndPendingApproval()) { + if (isNotSubmittedAndPendingApproval()) { final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this, this.loanOfficer, assignmentDate); this.loanOfficerHistory.add(loanOfficerAssignmentHistory); @@ -2266,8 +2254,7 @@ public class Loan extends AbstractPersistable { final String errorMessage = "The Loan officer Unassign date(" + unassignDate + ") cannot be before its assignment date (" + latestHistoryRecord.getStartDate() + ")."; - throw new LoanOfficerUnassignmentDateException("cannot.be.before.assignment.date", errorMessage, this.getId(), this - .getLoanOfficer().getId(), latestHistoryRecord.getStartDate(), unassignDate); + throw new LoanOfficerUnassignmentDateException("cannot.be.before.assignment.date", errorMessage, getId(), getLoanOfficer().getId(), latestHistoryRecord.getStartDate(), unassignDate); } else if (unassignDate.isAfter(today)) { @@ -2280,7 +2267,7 @@ public class Loan extends AbstractPersistable { private LoanOfficerAssignmentHistory findLatestIncompleteHistoryRecord() { LoanOfficerAssignmentHistory latestRecordWithNoEndDate = null; - for (LoanOfficerAssignmentHistory historyRecord : this.loanOfficerHistory) { + for (final LoanOfficerAssignmentHistory historyRecord : this.loanOfficerHistory) { if (historyRecord.isCurrentRecord()) { latestRecordWithNoEndDate = historyRecord; break; @@ -2292,7 +2279,7 @@ public class Loan extends AbstractPersistable { private LoanOfficerAssignmentHistory findLastAssignmentHistoryRecord(final Staff newLoanOfficer) { LoanOfficerAssignmentHistory lastAssignmentRecordLatestEndDate = null; - for (LoanOfficerAssignmentHistory historyRecord : this.loanOfficerHistory) { + for (final LoanOfficerAssignmentHistory historyRecord : this.loanOfficerHistory) { if (historyRecord.isCurrentRecord() && !historyRecord.isSameLoanOfficer(newLoanOfficer)) { lastAssignmentRecordLatestEndDate = historyRecord; @@ -2363,15 +2350,15 @@ public class Loan extends AbstractPersistable { final List existingReversedTransactionIds) { final Map accountingBridgeData = new LinkedHashMap(); - accountingBridgeData.put("loanId", this.getId()); - accountingBridgeData.put("loanProductId", this.productId()); - accountingBridgeData.put("officeId", this.getOfficeId()); + accountingBridgeData.put("loanId", getId()); + accountingBridgeData.put("loanProductId", productId()); + accountingBridgeData.put("officeId", getOfficeId()); accountingBridgeData.put("calculatedInterest", this.summary.getTotalInterestCharged()); - accountingBridgeData.put("cashBasedAccountingEnabled", this.isCashBasedAccountingEnabledOnLoanProduct()); - accountingBridgeData.put("accrualBasedAccountingEnabled", this.isAccrualBasedAccountingEnabledOnLoanProduct()); + accountingBridgeData.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnLoanProduct()); + accountingBridgeData.put("accrualBasedAccountingEnabled", isAccrualBasedAccountingEnabledOnLoanProduct()); final List> newLoanTransactions = new ArrayList>(); - for (LoanTransaction transaction : this.loanTransactions) { + for (final LoanTransaction transaction : this.loanTransactions) { if (transaction.isReversed() && !existingReversedTransactionIds.contains(transaction.getId())) { newLoanTransactions.add(transaction.toMapData(currencyData)); } else if (!existingTransactionIds.contains(transaction.getId())) { @@ -2401,9 +2388,9 @@ public class Loan extends AbstractPersistable { public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule, final boolean isHolidayEnabled, final List holidays, final WorkingDays workingDays) { //update repayment dates of repayment schedule installaments - LocalDate tmpFromDate = this.getDisbursementDate();//first repayment's from date is same as disbursement date. + LocalDate tmpFromDate = getDisbursementDate();//first repayment's from date is same as disbursement date. LocalDate newRepaymentDate = null; - for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) { + for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) { final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate(); // FIXME: AA this won't update repayment dates before current date. if (oldDueDate.isAfter(meetingStartDate) && oldDueDate.isAfter(DateUtils.getLocalDateOfTenant())) { @@ -2430,9 +2417,9 @@ public class Loan extends AbstractPersistable { public void applyHolidayToRepaymentScheduleDates(final Holiday holiday) { // first repayment's from date is same as disbursement date. - LocalDate tmpFromDate = this.getDisbursementDate(); + LocalDate tmpFromDate = getDisbursementDate(); // Loop through all loanRepayments - for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) { + for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) { final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate(); // update from date if it's not same as previous installament's due @@ -2440,10 +2427,12 @@ public class Loan extends AbstractPersistable { if (!loanRepaymentScheduleInstallment.getFromDate().isEqual(tmpFromDate)) { loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate); } - if (oldDueDate.isAfter(holiday.getToDateLocalDate())) break; + if (oldDueDate.isAfter(holiday.getToDateLocalDate())) { + break; + } if (oldDueDate.equals(holiday.getFromDateLocalDate()) || oldDueDate.equals(holiday.getToDateLocalDate()) - || (oldDueDate.isAfter(holiday.getFromDateLocalDate()) && oldDueDate.isBefore(holiday.getToDateLocalDate()))) { + || oldDueDate.isAfter(holiday.getFromDateLocalDate()) && oldDueDate.isBefore(holiday.getToDateLocalDate())) { //FIXME: AA do we need to apply non-working days. //Assuming holiday's repayment reschedule to date cannot be created on a non-working day. final LocalDate newRepaymentDate = holiday.getRepaymentsRescheduledToLocalDate(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanOfficerAssignmentHistory.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanOfficerAssignmentHistory.java index e56956ade..bcad5845d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanOfficerAssignmentHistory.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanOfficerAssignmentHistory.java @@ -25,7 +25,6 @@ import org.mifosplatform.useradministration.domain.AppUser; @Table(name = "m_loan_officer_assignment_history") public class LoanOfficerAssignmentHistory extends AbstractAuditableCustom { - @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "loan_id", nullable = false) private Loan loan; @@ -74,7 +73,7 @@ public class LoanOfficerAssignmentHistory extends AbstractAuditableCustom { @Column(name = "total_overdue_derived", scale = 6, precision = 19) private BigDecimal totalOverdue; - @SuppressWarnings("unused") @Temporal(TemporalType.DATE) @Column(name = "overdue_since_date_derived") private Date overdueSinceDate; - @SuppressWarnings("unused") @OneToOne(optional = false) @PrimaryKeyJoinColumn private Loan loan; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/note/api/NotesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/note/api/NotesApiResource.java index 5fabdf8ff..65780aa82 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/note/api/NotesApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/note/api/NotesApiResource.java @@ -70,10 +70,9 @@ public class NotesApiResource { public String retrieveNotesByResource(@PathParam("resourceType") final String resourceType, @PathParam("resourceId") final Long resourceId, @Context final UriInfo uriInfo) { - NoteType noteType = NoteType.fromApiUrl(resourceType); + final NoteType noteType = NoteType.fromApiUrl(resourceType); if (noteType == null) { throw new NoteResourceNotSupportedException(resourceType); } - ; this.context.authenticatedUser().validateHasReadPermission(getResourceNameForPermissions(noteType)); @@ -92,10 +91,9 @@ public class NotesApiResource { public String retrieveNote(@PathParam("resourceType") final String resourceType, @PathParam("resourceId") final Long resourceId, @PathParam("noteId") final Long noteId, @Context final UriInfo uriInfo) { - NoteType noteType = NoteType.fromApiUrl(resourceType); + final NoteType noteType = NoteType.fromApiUrl(resourceType); if (noteType == null) { throw new NoteResourceNotSupportedException(resourceType); } - ; this.context.authenticatedUser().validateHasReadPermission(getResourceNameForPermissions(noteType)); @@ -113,10 +111,9 @@ public class NotesApiResource { public String addNewNote(@PathParam("resourceType") final String resourceType, @PathParam("resourceId") final Long resourceId, final String apiRequestBodyAsJson) { - NoteType noteType = NoteType.fromApiUrl(resourceType); + final NoteType noteType = NoteType.fromApiUrl(resourceType); if (noteType == null) { throw new NoteResourceNotSupportedException(resourceType); } - ; final String resourceNameForPermissions = getResourceNameForPermissions(noteType); final CommandWrapper commandRequest = new CommandWrapperBuilder().createNote(resourceNameForPermissions, resourceType, resourceId) @@ -134,10 +131,9 @@ public class NotesApiResource { public String updateNote(@PathParam("resourceType") final String resourceType, @PathParam("resourceId") final Long resourceId, @PathParam("noteId") final Long noteId, final String apiRequestBodyAsJson) { - NoteType noteType = NoteType.fromApiUrl(resourceType); + final NoteType noteType = NoteType.fromApiUrl(resourceType); if (noteType == null) { throw new NoteResourceNotSupportedException(resourceType); } - ; final String resourceNameForPermissions = getResourceNameForPermissions(noteType); @@ -156,10 +152,9 @@ public class NotesApiResource { public String deleteNote(@PathParam("resourceType") final String resourceType, @PathParam("resourceId") final Long resourceId, @PathParam("noteId") final Long noteId) { - NoteType noteType = NoteType.fromApiUrl(resourceType); + final NoteType noteType = NoteType.fromApiUrl(resourceType); if (noteType == null) { throw new NoteResourceNotSupportedException(resourceType); } - ; final String resourceNameForPermissions = getResourceNameForPermissions(noteType); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/note/domain/Note.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/note/domain/Note.java index 8e449d281..ead9f7cf8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/note/domain/Note.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/note/domain/Note.java @@ -32,17 +32,14 @@ public class Note extends AbstractAuditableCustom { @JoinColumn(name = "client_id", nullable = true) private final Client client; - @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "group_id", nullable = true) private Group group; - @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "loan_id", nullable = true) private Loan loan; - @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "loan_transaction_id", nullable = true) private LoanTransaction loanTransaction; @@ -50,11 +47,9 @@ public class Note extends AbstractAuditableCustom { @Column(name = "note", length = 1000) private String note; - @SuppressWarnings("unused") @Column(name = "note_type_enum") private final Integer noteTypeId; - @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "savings_account_id", nullable = true) private SavingsAccount savingsAccount; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index d4dcea6d7..7c1c11af3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -106,7 +106,6 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "status_enum", nullable = false) private Integer status; - @SuppressWarnings("unused") @Column(name = "account_type_enum", nullable = false) private Integer accountType; @@ -114,27 +113,22 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "submittedon_date", nullable = true) private Date submittedOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "submittedon_userid", nullable = true) private AppUser submittedBy; - @SuppressWarnings("unused") @Temporal(TemporalType.DATE) @Column(name = "rejectedon_date") private Date rejectedOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "rejectedon_userid", nullable = true) private AppUser rejectedBy; - @SuppressWarnings("unused") @Temporal(TemporalType.DATE) @Column(name = "withdrawnon_date") private Date withdrawnOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "withdrawnon_userid", nullable = true) private AppUser withdrawnBy; @@ -143,7 +137,6 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "approvedon_date") private Date approvedOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "approvedon_userid", nullable = true) private AppUser approvedBy; @@ -152,17 +145,14 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "activatedon_date", nullable = true) private Date activatedOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "activatedon_userid", nullable = true) private AppUser activatedBy; - @SuppressWarnings("unused") @Temporal(TemporalType.DATE) @Column(name = "closedon_date") private Date closedOnDate; - @SuppressWarnings("unused") @ManyToOne(optional = true) @JoinColumn(name = "closedon_userid", nullable = true) private AppUser closedBy; @@ -361,7 +351,7 @@ public class SavingsAccount extends AbstractPersistable { existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - for (PostingPeriod interestPostingPeriod : postingPeriods) { + for (final PostingPeriod interestPostingPeriod : postingPeriods) { final LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction(); final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned(); @@ -370,14 +360,14 @@ public class SavingsAccount extends AbstractPersistable { interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod); - SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate); + final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate); if (postingTransaction == null) { final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, interestPostingTransactionDate, interestEarnedToBePostedForPeriod); this.transactions.add(newPostingTransaction); recalucateDailyBalanceDetails = true; } else { - boolean correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod); + final boolean correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod); if (correctionRequired) { postingTransaction.reverse(); final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, @@ -407,7 +397,7 @@ public class SavingsAccount extends AbstractPersistable { SavingsAccountTransaction postingTransation = null; - for (SavingsAccountTransaction transaction : this.transactions) { + for (final SavingsAccountTransaction transaction : this.transactions) { if (transaction.isInterestPostingAndNotReversed() && transaction.occursOn(postingDate)) { postingTransation = transaction; break; @@ -454,19 +444,19 @@ public class SavingsAccount extends AbstractPersistable { final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType .fromInt(this.interestCalculationDaysInYearType); - List postingPeriodIntervals = savingsHelper.determineInterestPostingPeriods(getActivationLocalDate(), + final List postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(getActivationLocalDate(), upToInterestCalculationDate, postingPeriodType); - List allPostingPeriods = new ArrayList(); + final List allPostingPeriods = new ArrayList(); Money periodStartingBalance = Money.zero(this.currency); final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType); - BigDecimal interestRateAsFraction = nominalAnnualInterestRate.divide(BigDecimal.valueOf(100l), mc); - for (LocalDateInterval periodInterval : postingPeriodIntervals) { + final BigDecimal interestRateAsFraction = this.nominalAnnualInterestRate.divide(BigDecimal.valueOf(100l), mc); + for (final LocalDateInterval periodInterval : postingPeriodIntervals) { - PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, - retreiveOrderedListOfTransactions(), currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, + final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, + retreiveOrderedListOfTransactions(), this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue()); periodStartingBalance = postingPeriod.closingBalance(); @@ -474,20 +464,20 @@ public class SavingsAccount extends AbstractPersistable { allPostingPeriods.add(postingPeriod); } - savingsHelper.calculateInterestForAllPostingPeriods(currency, allPostingPeriods); + this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods); - this.summary.updateFromInterestPeriodSummaries(currency, allPostingPeriods); + this.summary.updateFromInterestPeriodSummaries(this.currency, allPostingPeriods); this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); return allPostingPeriods; } private List retreiveOrderedListOfTransactions() { - List listOfTransactionsSorted = retreiveListOfTransactions(); + final List listOfTransactionsSorted = retreiveListOfTransactions(); - List orderedNonInterestPostingTransactions = new ArrayList(); + final List orderedNonInterestPostingTransactions = new ArrayList(); - for (SavingsAccountTransaction transaction : listOfTransactionsSorted) { + for (final SavingsAccountTransaction transaction : listOfTransactionsSorted) { if (!transaction.isInterestPostingAndNotReversed() && transaction.isNotReversed()) { orderedNonInterestPostingTransactions.add(transaction); } @@ -497,12 +487,12 @@ public class SavingsAccount extends AbstractPersistable { } private List retreiveListOfTransactions() { - List listOfTransactionsSorted = new ArrayList(); - for (SavingsAccountTransaction transaction : this.transactions) { + final List listOfTransactionsSorted = new ArrayList(); + for (final SavingsAccountTransaction transaction : this.transactions) { listOfTransactionsSorted.add(transaction); } - SavingsAccountTransactionComparator transactionComparator = new SavingsAccountTransactionComparator(); + final SavingsAccountTransactionComparator transactionComparator = new SavingsAccountTransactionComparator(); Collections.sort(listOfTransactionsSorted, transactionComparator); return listOfTransactionsSorted; @@ -512,9 +502,9 @@ public class SavingsAccount extends AbstractPersistable { Money runningBalance = openingAccountBalance.copy(); - List accountTransactionsSorted = retreiveListOfTransactions(); + final List accountTransactionsSorted = retreiveListOfTransactions(); - for (SavingsAccountTransaction transaction : accountTransactionsSorted) { + for (final SavingsAccountTransaction transaction : accountTransactionsSorted) { if (transaction.isReversed()) { transaction.zeroBalanceFields(); } else { @@ -533,7 +523,7 @@ public class SavingsAccount extends AbstractPersistable { // loop over transactions in reverse LocalDate endOfBalanceDate = DateUtils.getLocalDateOfTenant(); for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) { - SavingsAccountTransaction transaction = accountTransactionsSorted.get(i); + final SavingsAccountTransaction transaction = accountTransactionsSorted.get(i); if (transaction.isNotReversed()) { transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate); @@ -698,7 +688,7 @@ public class SavingsAccount extends AbstractPersistable { boolean transactionBeforeLastInterestPosting = false; - for (SavingsAccountTransaction transaction : retreiveListOfTransactions()) { + for (final SavingsAccountTransaction transaction : retreiveListOfTransactions()) { if (transaction.isInterestPostingAndNotReversed() && transaction.isAfter(transactionDate)) { transactionBeforeLastInterestPosting = true; break; @@ -713,7 +703,7 @@ public class SavingsAccount extends AbstractPersistable { Money runningBalance = Money.zero(this.currency); - for (SavingsAccountTransaction transaction : transactionsSortedByDate) { + for (final SavingsAccountTransaction transaction : transactionsSortedByDate) { if (transaction.isNotReversed() && transaction.isCredit()) { runningBalance = runningBalance.plus(transaction.getAmount(this.currency)); } else if (transaction.isNotReversed() && transaction.isDebit()) { @@ -733,7 +723,7 @@ public class SavingsAccount extends AbstractPersistable { Money runningBalance = Money.zero(this.currency); - for (SavingsAccountTransaction transaction : transactionsSortedByDate) { + for (final SavingsAccountTransaction transaction : transactionsSortedByDate) { if (transaction.isNotReversed() && transaction.isCredit()) { runningBalance = runningBalance.plus(transaction.getAmount(this.currency)); } else if (transaction.isNotReversed() && transaction.isDebit()) { @@ -767,7 +757,7 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - LocalDate nextAnnualFeeDueDate = getNextAnnualFeeDueDate(); + final LocalDate nextAnnualFeeDueDate = getNextAnnualFeeDueDate(); if (nextAnnualFeeDueDate == null || annualFeeSettingsNotSet()) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.annualfee.settings"); @@ -806,7 +796,7 @@ public class SavingsAccount extends AbstractPersistable { existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); final Money annualFee = Money.of(this.currency, this.annualFeeAmount); - SavingsAccountTransaction annualFeeTransaction = SavingsAccountTransaction.annualFee(this, annualFeeTransactionDate, annualFee); + final SavingsAccountTransaction annualFeeTransaction = SavingsAccountTransaction.annualFee(this, annualFeeTransactionDate, annualFee); this.transactions.add(annualFeeTransaction); this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); @@ -815,7 +805,7 @@ public class SavingsAccount extends AbstractPersistable { currentAnnualFeeNextDueDate = findLatestAnnualFeeTransactionDueDate(); if (currentAnnualFeeNextDueDate != null) { - LocalDate newAnnualFeeNextDueDate = new LocalDate(currentAnnualFeeNextDueDate).withMonthOfYear(this.annualFeeOnMonth) + final LocalDate newAnnualFeeNextDueDate = new LocalDate(currentAnnualFeeNextDueDate).withMonthOfYear(this.annualFeeOnMonth) .withDayOfMonth(this.annualFeeOnDay).plusYears(1); this.annualFeeNextDueDate = newAnnualFeeNextDueDate.toDate(); } else { @@ -847,7 +837,7 @@ public class SavingsAccount extends AbstractPersistable { } private void updateToNextAnnualFeeDueDateFrom(final LocalDate startingDate) { - LocalDate nextDueLocalDate = startingDate.withMonthOfYear(this.annualFeeOnMonth).withDayOfMonth(this.annualFeeOnDay); + final LocalDate nextDueLocalDate = startingDate.withMonthOfYear(this.annualFeeOnMonth).withDayOfMonth(this.annualFeeOnDay); this.annualFeeNextDueDate = nextDueLocalDate.toDate(); } @@ -902,7 +892,7 @@ public class SavingsAccount extends AbstractPersistable { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.modifyApplicationAction); - SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); if (!SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.hasStateOf(currentStatus)) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.submittedandpendingapproval.state"); @@ -1124,16 +1114,16 @@ public class SavingsAccount extends AbstractPersistable { .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); if (this.lockinPeriodFrequency == null) { - baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).ignoreIfNull() + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(this.lockinPeriodFrequencyType).ignoreIfNull() .inMinMaxRange(0, 3); if (this.lockinPeriodFrequencyType != null) { - baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull() + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(this.lockinPeriodFrequency).notNull() .integerZeroOrGreater(); } } else { - baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequencyType).integerZeroOrGreater(); - baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull() + baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(this.lockinPeriodFrequencyType).integerZeroOrGreater(); + baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(this.lockinPeriodFrequencyType).notNull() .inMinMaxRange(0, 3); } @@ -1144,14 +1134,14 @@ public class SavingsAccount extends AbstractPersistable { final List existingReversedTransactionIds) { final Map accountingBridgeData = new LinkedHashMap(); - accountingBridgeData.put("savingsId", this.getId()); - accountingBridgeData.put("savingsProductId", this.productId()); - accountingBridgeData.put("officeId", this.officeId()); - accountingBridgeData.put("cashBasedAccountingEnabled", this.isCashBasedAccountingEnabledOnSavingsProduct()); - accountingBridgeData.put("accrualBasedAccountingEnabled", this.isAccrualBasedAccountingEnabledOnSavingsProduct()); + accountingBridgeData.put("savingsId", getId()); + accountingBridgeData.put("savingsProductId", productId()); + accountingBridgeData.put("officeId", officeId()); + accountingBridgeData.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnSavingsProduct()); + accountingBridgeData.put("accrualBasedAccountingEnabled", isAccrualBasedAccountingEnabledOnSavingsProduct()); final List> newLoanTransactions = new ArrayList>(); - for (SavingsAccountTransaction transaction : this.transactions) { + for (final SavingsAccountTransaction transaction : this.transactions) { if (transaction.isReversed() && !existingReversedTransactionIds.contains(transaction.getId())) { newLoanTransactions.add(transaction.toMapData(currencyData)); } else if (!existingTransactionIds.contains(transaction.getId())) { @@ -1165,9 +1155,9 @@ public class SavingsAccount extends AbstractPersistable { private Collection findExistingTransactionIds() { - Collection ids = new ArrayList(); + final Collection ids = new ArrayList(); - for (SavingsAccountTransaction transaction : this.transactions) { + for (final SavingsAccountTransaction transaction : this.transactions) { ids.add(transaction.getId()); } @@ -1178,7 +1168,7 @@ public class SavingsAccount extends AbstractPersistable { final Collection ids = new ArrayList(); - for (SavingsAccountTransaction transaction : this.transactions) { + for (final SavingsAccountTransaction transaction : this.transactions) { if (transaction.isReversed()) { ids.add(transaction.getId()); } @@ -1279,12 +1269,12 @@ public class SavingsAccount extends AbstractPersistable { .failWithCodeNoParameterAddedToErrorCode("cannot.be.a.future.date"); } - if (client != null && client.isActivatedAfter(submittedOn)) { - baseDataValidator.reset().parameter(SavingsApiConstants.submittedOnDateParamName).value(client.getActivationLocalDate()) + if (this.client != null && this.client.isActivatedAfter(submittedOn)) { + baseDataValidator.reset().parameter(SavingsApiConstants.submittedOnDateParamName).value(this.client.getActivationLocalDate()) .failWithCodeNoParameterAddedToErrorCode("cannot.be.before.client.activation.date"); - } else if (group != null && group.isActivatedAfter(submittedOn)) { + } else if (this.group != null && this.group.isActivatedAfter(submittedOn)) { - baseDataValidator.reset().parameter(SavingsApiConstants.submittedOnDateParamName).value(group.getActivationLocalDate()) + baseDataValidator.reset().parameter(SavingsApiConstants.submittedOnDateParamName).value(this.group.getActivationLocalDate()) .failWithCodeNoParameterAddedToErrorCode("cannot.be.before.client.activation.date"); } @@ -1319,7 +1309,7 @@ public class SavingsAccount extends AbstractPersistable { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.approvalAction); - SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); if (!SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.hasStateOf(currentStatus)) { baseDataValidator.reset().parameter(SavingsApiConstants.approvedOnDateParamName) .failWithCodeNoParameterAddedToErrorCode("not.in.submittedandpendingapproval.state"); @@ -1331,8 +1321,8 @@ public class SavingsAccount extends AbstractPersistable { actualChanges.put(SavingsApiConstants.statusParamName, SavingsEnumerations.status(this.status)); // only do below if status has changed in the 'approval' case - LocalDate approvedOn = command.localDateValueOfParameterNamed(SavingsApiConstants.approvedOnDateParamName); - String approvedOnDateChange = command.stringValueOfParameterNamed(SavingsApiConstants.approvedOnDateParamName); + final LocalDate approvedOn = command.localDateValueOfParameterNamed(SavingsApiConstants.approvedOnDateParamName); + final String approvedOnDateChange = command.stringValueOfParameterNamed(SavingsApiConstants.approvedOnDateParamName); this.approvedOnDate = approvedOn.toDate(); this.approvedBy = currentUser; @@ -1378,7 +1368,7 @@ public class SavingsAccount extends AbstractPersistable { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.undoApprovalAction); - SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); if (!SavingsAccountStatusType.APPROVED.hasStateOf(currentStatus)) { baseDataValidator.reset().parameter(SavingsApiConstants.approvedOnDateParamName) @@ -1410,7 +1400,7 @@ public class SavingsAccount extends AbstractPersistable { final LocalDate upToInterestCalculationDate) { SavingsAccountTransaction transactionToUndo = null; - for (SavingsAccountTransaction transaction : this.transactions) { + for (final SavingsAccountTransaction transaction : this.transactions) { if (transaction.isIdentifiedBy(transactionId)) { transactionToUndo = transaction; } @@ -1428,7 +1418,7 @@ public class SavingsAccount extends AbstractPersistable { if (this.annualFeeNextDueDate == null) { updateToNextAnnualFeeDueDateFrom(getActivationLocalDate()); } else { - LocalDate newAnnualFeeNextDueDate = new LocalDate(this.annualFeeNextDueDate).withMonthOfYear(this.annualFeeOnMonth) + final LocalDate newAnnualFeeNextDueDate = new LocalDate(this.annualFeeNextDueDate).withMonthOfYear(this.annualFeeOnMonth) .withDayOfMonth(this.annualFeeOnDay).plusYears(1); this.annualFeeNextDueDate = newAnnualFeeNextDueDate.toDate(); } @@ -1445,7 +1435,7 @@ public class SavingsAccount extends AbstractPersistable { Date nextDueDate = null; LocalDate lastAnnualFeeTransactionDate = null; - for (SavingsAccountTransaction transaction : retreiveOrderedListOfTransactions()) { + for (final SavingsAccountTransaction transaction : retreiveOrderedListOfTransactions()) { if (transaction.isAnnualFeeAndNotReversed()) { if (lastAnnualFeeTransactionDate == null) { lastAnnualFeeTransactionDate = transaction.transactionLocalDate(); @@ -1470,7 +1460,7 @@ public class SavingsAccount extends AbstractPersistable { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.rejectAction); - SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); if (!SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.hasStateOf(currentStatus)) { baseDataValidator.reset().parameter(SavingsApiConstants.rejectedOnDateParamName) @@ -1482,7 +1472,7 @@ public class SavingsAccount extends AbstractPersistable { this.status = SavingsAccountStatusType.REJECTED.getValue(); actualChanges.put(SavingsApiConstants.statusParamName, SavingsEnumerations.status(this.status)); - LocalDate rejectedOn = command.localDateValueOfParameterNamed(SavingsApiConstants.rejectedOnDateParamName); + final LocalDate rejectedOn = command.localDateValueOfParameterNamed(SavingsApiConstants.rejectedOnDateParamName); final String rejectedOnAsString = command.stringValueOfParameterNamed(SavingsApiConstants.rejectedOnDateParamName); this.rejectedOnDate = rejectedOn.toDate(); @@ -1528,7 +1518,7 @@ public class SavingsAccount extends AbstractPersistable { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.withdrawnByApplicantAction); - SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); if (!SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.hasStateOf(currentStatus)) { baseDataValidator.reset().parameter(SavingsApiConstants.withdrawnOnDateParamName) @@ -1587,7 +1577,7 @@ public class SavingsAccount extends AbstractPersistable { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.activateAction); - SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); + final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); if (!SavingsAccountStatusType.APPROVED.hasStateOf(currentStatus)) { baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName) @@ -1622,7 +1612,7 @@ public class SavingsAccount extends AbstractPersistable { if (this.client != null && this.client.isActivatedAfter(activationDate)) { final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); - final String dateAsString = formatter.print(client.getActivationLocalDate()); + final String dateAsString = formatter.print(this.client.getActivationLocalDate()); baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName).value(dateAsString) .failWithCodeNoParameterAddedToErrorCode("cannot.be.before.client.activation.date"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } @@ -1630,7 +1620,7 @@ public class SavingsAccount extends AbstractPersistable { if (this.group != null && this.group.isActivatedAfter(activationDate)) { final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); - final String dateAsString = formatter.print(client.getActivationLocalDate()); + final String dateAsString = formatter.print(this.client.getActivationLocalDate()); baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName).value(dateAsString) .failWithCodeNoParameterAddedToErrorCode("cannot.be.before.group.activation.date"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java index c04174883..986cee5fd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java @@ -28,7 +28,6 @@ public final class SavingsAccountSummary { @Column(name = "total_withdrawals_derived", scale = 6, precision = 19) private BigDecimal totalWithdrawals; - @SuppressWarnings("unused") @Column(name = "total_interest_earned_derived", scale = 6, precision = 19) private BigDecimal totalInterestEarned; @@ -65,7 +64,7 @@ public final class SavingsAccountSummary { Money totalEarned = Money.zero(currency); - for (PostingPeriod period : allPostingPeriods) { + for (final PostingPeriod period : allPostingPeriods) { totalEarned = totalEarned.plus(period.interest()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index e9a27c633..4dce66684 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -38,7 +38,6 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_savings_account_transaction") public final class SavingsAccountTransaction extends AbstractPersistable { - @SuppressWarnings("unused") @ManyToOne(optional = false) @JoinColumn(name = "savings_account_id", nullable = false) private SavingsAccount savingsAccount; @@ -63,7 +62,6 @@ public final class SavingsAccountTransaction extends AbstractPersistable { @Column(name = "running_balance_derived", scale = 6, precision = 19, nullable = false) private BigDecimal runningBalance; - @SuppressWarnings("unused") @Column(name = "cumulative_balance_derived", scale = 6, precision = 19, nullable = false) private BigDecimal cumulativeBalance; @@ -221,10 +219,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable { final SavingsAccountTransactionEnumData transactionType = SavingsEnumerations.transactionType(this.typeOf); - thisTransactionData.put("id", this.getId()); + thisTransactionData.put("id", getId()); thisTransactionData.put("type", transactionType); - thisTransactionData.put("reversed", Boolean.valueOf(this.isReversed())); - thisTransactionData.put("date", this.getTransactionLocalDate()); + thisTransactionData.put("reversed", Boolean.valueOf(isReversed())); + thisTransactionData.put("date", getTransactionLocalDate()); thisTransactionData.put("currency", currencyData); thisTransactionData.put("amount", this.amount); @@ -236,20 +234,20 @@ public final class SavingsAccountTransaction extends AbstractPersistable { } public boolean isAfter(final LocalDate transactionDate) { - return this.getTransactionLocalDate().isAfter(transactionDate); + return getTransactionLocalDate().isAfter(transactionDate); } public EndOfDayBalance toEndOfDayBalance(final LocalDateInterval periodInterval, final MonetaryCurrency currency) { - Money endOfDayBalance = Money.of(currency, this.runningBalance); - Money openingBalance = endOfDayBalance; + final Money endOfDayBalance = Money.of(currency, this.runningBalance); + final Money openingBalance = endOfDayBalance; LocalDate balanceDate = periodInterval.startDate(); int numberOfDays = periodInterval.daysInPeriodInclusiveOfEndDate(); if (periodInterval.contains(getTransactionLocalDate())) { balanceDate = getTransactionLocalDate(); - LocalDateInterval newInterval = LocalDateInterval.create(getTransactionLocalDate(), periodInterval.endDate()); + final LocalDateInterval newInterval = LocalDateInterval.create(getTransactionLocalDate(), periodInterval.endDate()); numberOfDays = newInterval.daysInPeriodInclusiveOfEndDate(); } @@ -258,7 +256,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { public EndOfDayBalance toEndOfDayBalance(final Money openingBalance, final LocalDate nextTransactionDate) { - MonetaryCurrency currency = openingBalance.getCurrency(); + final MonetaryCurrency currency = openingBalance.getCurrency(); Money endOfDayBalance = openingBalance.copy(); if (isDeposit()) { endOfDayBalance = openingBalance.plus(getAmount(currency)); @@ -274,7 +272,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { } public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) { - MonetaryCurrency currency = openingBalance.getCurrency(); + final MonetaryCurrency currency = openingBalance.getCurrency(); Money endOfDayBalance = openingBalance.copy(); if (isDeposit()) { endOfDayBalance = openingBalance.plus(getAmount(currency)); @@ -287,7 +285,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy) { - MonetaryCurrency currency = openingBalance.getCurrency(); + final MonetaryCurrency currency = openingBalance.getCurrency(); Money endOfDayBalance = openingBalance.copy(); int numberOfDaysOfBalance = this.balanceNumberOfDays; @@ -297,7 +295,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { if (boundedBy.startDate().isAfter(balanceStartDate)) { balanceStartDate = boundedBy.startDate(); - LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); + final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); } else { if (isDeposit()) { @@ -309,7 +307,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { if (balanceEndDate.isAfter(boundedBy.endDate())) { balanceEndDate = boundedBy.endDate(); - LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); + final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); } @@ -321,17 +319,17 @@ public final class SavingsAccountTransaction extends AbstractPersistable { } public boolean fallsWithin(final LocalDateInterval periodInterval) { - LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate()); + final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate()); return periodInterval.contains(balanceInterval); } public boolean spansAnyPortionOf(final LocalDateInterval periodInterval) { - LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate()); + final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate()); return balanceInterval.containsPortionOf(periodInterval); } public boolean isIdentifiedBy(final Long transactionId) { - return this.getId().equals(transactionId); + return getId().equals(transactionId); } public boolean isCredit() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java index 9d6656bcd..26feeae57 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/AppUser.java @@ -42,445 +42,491 @@ import org.springframework.security.core.userdetails.User; @Table(name = "m_appuser", uniqueConstraints = @UniqueConstraint(columnNames = { "username" }, name = "username_org")) public class AppUser extends AbstractPersistable implements PlatformUser { - private final static Logger logger = LoggerFactory.getLogger(AppUser.class); - - @Column(name = "email", nullable = false, length = 100) - private String email; - - @Column(name = "username", nullable = false, length = 100) - private String username; - - @Column(name = "firstname", nullable = false, length = 100) - private String firstname; - - @Column(name = "lastname", nullable = false, length = 100) - private String lastname; - - @Column(name = "password", nullable = false) - private String password; - - @Column(name = "nonexpired", nullable = false) - private boolean accountNonExpired; - - @Column(name = "nonlocked", nullable = false) - private final boolean accountNonLocked; - - @Column(name = "nonexpired_credentials", nullable = false) - private final boolean credentialsNonExpired; - - @Column(name = "enabled", nullable = false) - private boolean enabled; - - @SuppressWarnings("unused") - @Column(name = "firsttime_login_remaining", nullable = false) - private boolean firstTimeLoginRemaining; - - @Column(name = "is_deleted", nullable = false) - private boolean deleted; - - @ManyToOne - @JoinColumn(name = "office_id", nullable = false) - private Office office; - - @ManyToOne - @JoinColumn(name = "staff_id", nullable = true) - private Staff staff; - - @ManyToMany(fetch = FetchType.EAGER) - @JoinTable(name = "m_appuser_role", joinColumns = @JoinColumn(name = "appuser_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) - private Set roles; - - public static AppUser fromJson(final Office userOffice, final Set allRoles, final JsonCommand command) { - - final String username = command.stringValueOfParameterNamed("username"); - String password = command.stringValueOfParameterNamed("password"); - final Boolean sendPasswordToEmail = command.booleanObjectValueOfParameterNamed("sendPasswordToEmail"); - - if (sendPasswordToEmail.booleanValue()) { - password = new RandomPasswordGenerator(13).generate(); - } - - boolean userEnabled = true; - boolean userAccountNonExpired = true; - boolean userCredentialsNonExpired = true; - boolean userAccountNonLocked = true; - - Collection authorities = new ArrayList(); - authorities.add(new SimpleGrantedAuthority("DUMMY_ROLE_NOT_USED_OR_PERSISTED_TO_AVOID_EXCEPTION")); - - User user = new User(username, password, userEnabled, userAccountNonExpired, userCredentialsNonExpired, userAccountNonLocked, - authorities); - - final String email = command.stringValueOfParameterNamed("email"); - final String firstname = command.stringValueOfParameterNamed("firstname"); - final String lastname = command.stringValueOfParameterNamed("lastname"); - - return new AppUser(userOffice, user, allRoles, email, firstname, lastname); - } - - protected AppUser() { - this.accountNonLocked = false; - this.credentialsNonExpired = false; - this.roles = new HashSet(); - } - - public AppUser(final Office office, final User user, final Set roles, final String email, final String firstname, - final String lastname) { - this.office = office; - this.email = email.trim(); - this.username = user.getUsername().trim(); - this.firstname = firstname.trim(); - this.lastname = lastname.trim(); - this.password = user.getPassword().trim(); - this.accountNonExpired = user.isAccountNonExpired(); - this.accountNonLocked = user.isAccountNonLocked(); - this.credentialsNonExpired = user.isCredentialsNonExpired(); - this.enabled = user.isEnabled(); - this.roles = roles; - this.firstTimeLoginRemaining = true; - } - - public EnumOptionData organisationalRoleData() { - EnumOptionData organisationalRole = null; - if (this.staff != null) { - organisationalRole = this.staff.organisationalRoleData(); - } - return organisationalRole; - } - - public void updatePassword(final String encodePassword) { - this.password = encodePassword; - this.firstTimeLoginRemaining = false; - } - - public void changeOffice(final Office differentOffice) { - this.office = differentOffice; - } - - public void updateRoles(final Set allRoles) { - if (!allRoles.isEmpty()) { - this.roles.clear(); - this.roles = allRoles; - } - } - - public Map update(final JsonCommand command, final PlatformPasswordEncoder platformPasswordEncoder) { - - final Map actualChanges = new LinkedHashMap(7); - - // unencoded password provided - final String passwordParamName = "password"; - final String passwordEncodedParamName = "passwordEncoded"; - if (command.hasParameter(passwordParamName)) { - if (command.isChangeInPasswordParameterNamed(passwordParamName, this.password, platformPasswordEncoder, this.getId())) { - final String passwordEncodedValue = command.passwordValueOfParameterNamed(passwordParamName, platformPasswordEncoder, - this.getId()); - actualChanges.put(passwordEncodedParamName, passwordEncodedValue); - updatePassword(passwordEncodedValue); - } - } - - if (command.hasParameter(passwordEncodedParamName)) { - if (command.isChangeInStringParameterNamed(passwordEncodedParamName, this.password)) { - final String newValue = command.stringValueOfParameterNamed(passwordEncodedParamName); - actualChanges.put(passwordEncodedParamName, newValue); - updatePassword(newValue); - } - } - - final String officeIdParamName = "officeId"; - if (command.isChangeInLongParameterNamed(officeIdParamName, this.office.getId())) { - final Long newValue = command.longValueOfParameterNamed(officeIdParamName); - actualChanges.put(officeIdParamName, newValue); - } - - final String rolesParamName = "roles"; - if (command.isChangeInArrayParameterNamed(rolesParamName, getRolesAsIdStringArray())) { - final String[] newValue = command.arrayValueOfParameterNamed(rolesParamName); - actualChanges.put(rolesParamName, newValue); - } - - final String usernameParamName = "username"; - if (command.isChangeInStringParameterNamed(usernameParamName, this.username)) { - final String newValue = command.stringValueOfParameterNamed(usernameParamName); - actualChanges.put(usernameParamName, newValue); - this.username = newValue; - } - - final String firstnameParamName = "firstname"; - if (command.isChangeInStringParameterNamed(firstnameParamName, this.firstname)) { - final String newValue = command.stringValueOfParameterNamed(firstnameParamName); - actualChanges.put(firstnameParamName, newValue); - this.firstname = newValue; - } - - final String lastnameParamName = "lastname"; - if (command.isChangeInStringParameterNamed(lastnameParamName, this.lastname)) { - final String newValue = command.stringValueOfParameterNamed(lastnameParamName); - actualChanges.put(lastnameParamName, newValue); - this.lastname = newValue; - } - - final String emailParamName = "email"; - if (command.isChangeInStringParameterNamed(emailParamName, this.email)) { - final String newValue = command.stringValueOfParameterNamed(emailParamName); - actualChanges.put(emailParamName, newValue); - this.email = newValue; - } - - return actualChanges; - } - - private String[] getRolesAsIdStringArray() { - List roleIds = new ArrayList(); - - for (Role role : this.roles) { - roleIds.add(role.getId().toString()); - } - - return roleIds.toArray(new String[roleIds.size()]); - } - - /** - * Delete is a soft delete. Updates flag so it wont appear in - * query/report results. - * - * Any fields with unique constraints and prepended with id of record. - */ - public void delete() { - this.deleted = true; - this.enabled = false; - this.accountNonExpired = false; - this.firstTimeLoginRemaining = true; - this.username = this.getId() + "_DELETED_" + this.username; - } - - public boolean isDeleted() { - return deleted; - } - - @Override - public Collection getAuthorities() { - return this.populateGrantedAuthorities(); - } - - private List populateGrantedAuthorities() { - List grantedAuthorities = new ArrayList(); - for (Role role : this.roles) { - Collection permissions = role.getPermissions(); - for (Permission permission : permissions) { - grantedAuthorities.add(new SimpleGrantedAuthority(permission.getCode())); - } - } - return grantedAuthorities; - } - - @Override - public String getPassword() { - return this.password; - } - - @Override - public String getUsername() { - return this.username; - } - - @Override - public boolean isAccountNonExpired() { - return this.accountNonExpired; - } - - @Override - public boolean isAccountNonLocked() { - return this.accountNonLocked; - } - - @Override - public boolean isCredentialsNonExpired() { - return this.credentialsNonExpired; - } - - @Override - public boolean isEnabled() { - return this.enabled; - } - - public String getFirstname() { - return firstname; - } - - public String getLastname() { - return lastname; - } - - public String getEmail() { - return this.email; - } - - public Set getRoles() { - return this.roles; - } - - public Office getOffice() { - return this.office; - } - - public boolean canNotApproveLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "APPROVEINPAST_LOAN"); - } - - public boolean canNotRejectLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REJECTINPAST_LOAN"); - } - - public boolean canNotWithdrawByClientLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "WITHDRAWINPAST_LOAN"); - } - - public boolean canNotDisburseLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "DISBURSEINPAST_LOAN"); - } - - public boolean canNotMakeRepaymentOnLoanInPast() { - return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REPAYMENTINPAST_LOAN"); - } - - public boolean hasNotPermissionForReport(final String reportName) { - - if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "REPORTING_SUPER_USER", "READ_" + reportName)) return true; - - return false; - } - - public boolean hasNotPermissionForDatatable(final String datatable, final String accessType) { - - String matchPermission = accessType + "_" + datatable; - - if (accessType.equalsIgnoreCase("READ")) { - - if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", matchPermission)) return true; - - return false; - } - - if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", matchPermission)) return true; - - return false; - } - - public boolean hasNotPermissionForAnyOf(final String... permissionCodes) { - boolean hasNotPermission = true; - for (String permissionCode : permissionCodes) { - boolean checkPermission = this.hasPermissionTo(permissionCode); - if (checkPermission) { - hasNotPermission = false; - break; - } - } - return hasNotPermission; - } - - public void validateHasReadPermission(final String resourceType) { - - final String authorizationMessage = "User has no authority to view " + resourceType.toLowerCase() + "s"; - final String matchPermission = "READ_" + resourceType.toUpperCase(); - - if (!(hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", matchPermission))) return; - - throw new NoAuthorizationException(authorizationMessage); - } - - private boolean hasNotPermissionTo(final String permissionCode) { - return !hasPermissionTo(permissionCode); - } - - private boolean hasPermissionTo(final String permissionCode) { - boolean hasPermission = hasAllFunctionsPermission(); - if (!hasPermission) { - for (Role role : this.roles) { - if (role.hasPermissionTo(permissionCode)) { - hasPermission = true; - break; - } - } - } - return hasPermission; - } - - private boolean hasAllFunctionsPermission() { - boolean match = false; - for (Role role : this.roles) { - if (role.hasPermissionTo("ALL_FUNCTIONS")) { - match = true; - break; - } - } - return match; - } - - public boolean hasIdOf(final Long userId) { - return getId().equals(userId); - } - - private boolean hasNotAnyPermission(final List permissions) { - return !hasAnyPermission(permissions); - } - - private boolean hasAnyPermission(final List permissions) { - boolean hasAtLeastOneOf = false; - - for (final String permissionCode : permissions) { - if (hasPermissionTo(permissionCode)) { - hasAtLeastOneOf = true; - break; - } - } - - return hasAtLeastOneOf; - } - - public void validateHasPermissionTo(final String function, final List allowedPermissions) { - if (hasNotAnyPermission(allowedPermissions)) { - final String authorizationMessage = "User has no authority to: " + function; - throw new NoAuthorizationException(authorizationMessage); - } - } - - public void validateHasPermissionTo(final String function) { - if (hasNotPermissionTo(function)) { - final String authorizationMessage = "User has no authority to: " + function; - logger.info("Unauthorized access: userId: " + this.getId() + " action: " + function + " allowed: " + getAuthorities()); - throw new NoAuthorizationException(authorizationMessage); - } - } - - public void validateHasCheckerPermissionTo(final String function) { - - final String checkerPermissionName = function.toUpperCase() + "_CHECKER"; - if (hasNotPermissionTo("CHECKER_SUPER_USER") && hasNotPermissionTo(checkerPermissionName)) { - final String authorizationMessage = "User has no authority to be a checker for: " + function; - throw new NoAuthorizationException(authorizationMessage); - } - } - - public void validateHasDatatableReadPermission(final String datatable) { - if (hasNotPermissionForDatatable(datatable, "READ")) { throw new NoAuthorizationException("Not authorised to read datatable: " - + datatable); } - } - - public Long getStaffId() { - Long staffId = null; - if (this.staff != null) { - staffId = this.staff.getId(); - } - return staffId; - } - - public String getStaffDisplayName() { - String staffDisplayName = null; - if (this.staff != null) { - staffDisplayName = this.staff.displayName(); - } - return staffDisplayName; - } + private final static Logger logger = LoggerFactory.getLogger(AppUser.class); + + @Column(name = "email", nullable = false, length = 100) + private String email; + + @Column(name = "username", nullable = false, length = 100) + private String username; + + @Column(name = "firstname", nullable = false, length = 100) + private String firstname; + + @Column(name = "lastname", nullable = false, length = 100) + private String lastname; + + @Column(name = "password", nullable = false) + private String password; + + @Column(name = "nonexpired", nullable = false) + private boolean accountNonExpired; + + @Column(name = "nonlocked", nullable = false) + private final boolean accountNonLocked; + + @Column(name = "nonexpired_credentials", nullable = false) + private final boolean credentialsNonExpired; + + @Column(name = "enabled", nullable = false) + private boolean enabled; + + @Column(name = "firsttime_login_remaining", nullable = false) + private boolean firstTimeLoginRemaining; + + @Column(name = "is_deleted", nullable = false) + private boolean deleted; + + @ManyToOne + @JoinColumn(name = "office_id", nullable = false) + private Office office; + + @ManyToOne + @JoinColumn(name = "staff_id", nullable = true) + private Staff staff; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "m_appuser_role", joinColumns = @JoinColumn(name = "appuser_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) + private Set roles; + + public static AppUser fromJson(final Office userOffice, + final Set allRoles, final JsonCommand command) { + + final String username = command.stringValueOfParameterNamed("username"); + String password = command.stringValueOfParameterNamed("password"); + final Boolean sendPasswordToEmail = command + .booleanObjectValueOfParameterNamed("sendPasswordToEmail"); + + if (sendPasswordToEmail.booleanValue()) { + password = new RandomPasswordGenerator(13).generate(); + } + + final boolean userEnabled = true; + final boolean userAccountNonExpired = true; + final boolean userCredentialsNonExpired = true; + final boolean userAccountNonLocked = true; + + final Collection authorities = new ArrayList(); + authorities.add(new SimpleGrantedAuthority( + "DUMMY_ROLE_NOT_USED_OR_PERSISTED_TO_AVOID_EXCEPTION")); + + final User user = new User(username, password, userEnabled, + userAccountNonExpired, userCredentialsNonExpired, + userAccountNonLocked, authorities); + + final String email = command.stringValueOfParameterNamed("email"); + final String firstname = command + .stringValueOfParameterNamed("firstname"); + final String lastname = command.stringValueOfParameterNamed("lastname"); + + return new AppUser(userOffice, user, allRoles, email, firstname, + lastname); + } + + protected AppUser() { + this.accountNonLocked = false; + this.credentialsNonExpired = false; + this.roles = new HashSet(); + } + + public AppUser(final Office office, final User user, final Set roles, + final String email, final String firstname, final String lastname) { + this.office = office; + this.email = email.trim(); + this.username = user.getUsername().trim(); + this.firstname = firstname.trim(); + this.lastname = lastname.trim(); + this.password = user.getPassword().trim(); + this.accountNonExpired = user.isAccountNonExpired(); + this.accountNonLocked = user.isAccountNonLocked(); + this.credentialsNonExpired = user.isCredentialsNonExpired(); + this.enabled = user.isEnabled(); + this.roles = roles; + this.firstTimeLoginRemaining = true; + } + + public EnumOptionData organisationalRoleData() { + EnumOptionData organisationalRole = null; + if (this.staff != null) { + organisationalRole = this.staff.organisationalRoleData(); + } + return organisationalRole; + } + + public void updatePassword(final String encodePassword) { + this.password = encodePassword; + this.firstTimeLoginRemaining = false; + } + + public void changeOffice(final Office differentOffice) { + this.office = differentOffice; + } + + public void updateRoles(final Set allRoles) { + if (!allRoles.isEmpty()) { + this.roles.clear(); + this.roles = allRoles; + } + } + + public Map update(final JsonCommand command, + final PlatformPasswordEncoder platformPasswordEncoder) { + + final Map actualChanges = new LinkedHashMap( + 7); + + // unencoded password provided + final String passwordParamName = "password"; + final String passwordEncodedParamName = "passwordEncoded"; + if (command.hasParameter(passwordParamName)) { + if (command.isChangeInPasswordParameterNamed(passwordParamName, + this.password, platformPasswordEncoder, getId())) { + final String passwordEncodedValue = command + .passwordValueOfParameterNamed(passwordParamName, + platformPasswordEncoder, getId()); + actualChanges.put(passwordEncodedParamName, + passwordEncodedValue); + updatePassword(passwordEncodedValue); + } + } + + if (command.hasParameter(passwordEncodedParamName)) { + if (command.isChangeInStringParameterNamed( + passwordEncodedParamName, this.password)) { + final String newValue = command + .stringValueOfParameterNamed(passwordEncodedParamName); + actualChanges.put(passwordEncodedParamName, newValue); + updatePassword(newValue); + } + } + + final String officeIdParamName = "officeId"; + if (command.isChangeInLongParameterNamed(officeIdParamName, + this.office.getId())) { + final Long newValue = command + .longValueOfParameterNamed(officeIdParamName); + actualChanges.put(officeIdParamName, newValue); + } + + final String rolesParamName = "roles"; + if (command.isChangeInArrayParameterNamed(rolesParamName, + getRolesAsIdStringArray())) { + final String[] newValue = command + .arrayValueOfParameterNamed(rolesParamName); + actualChanges.put(rolesParamName, newValue); + } + + final String usernameParamName = "username"; + if (command.isChangeInStringParameterNamed(usernameParamName, + this.username)) { + final String newValue = command + .stringValueOfParameterNamed(usernameParamName); + actualChanges.put(usernameParamName, newValue); + this.username = newValue; + } + + final String firstnameParamName = "firstname"; + if (command.isChangeInStringParameterNamed(firstnameParamName, + this.firstname)) { + final String newValue = command + .stringValueOfParameterNamed(firstnameParamName); + actualChanges.put(firstnameParamName, newValue); + this.firstname = newValue; + } + + final String lastnameParamName = "lastname"; + if (command.isChangeInStringParameterNamed(lastnameParamName, + this.lastname)) { + final String newValue = command + .stringValueOfParameterNamed(lastnameParamName); + actualChanges.put(lastnameParamName, newValue); + this.lastname = newValue; + } + + final String emailParamName = "email"; + if (command.isChangeInStringParameterNamed(emailParamName, this.email)) { + final String newValue = command + .stringValueOfParameterNamed(emailParamName); + actualChanges.put(emailParamName, newValue); + this.email = newValue; + } + + return actualChanges; + } + + private String[] getRolesAsIdStringArray() { + final List roleIds = new ArrayList(); + + for (final Role role : this.roles) { + roleIds.add(role.getId().toString()); + } + + return roleIds.toArray(new String[roleIds.size()]); + } + + /** + * Delete is a soft delete. Updates flag so it wont appear in + * query/report results. + * + * Any fields with unique constraints and prepended with id of record. + */ + public void delete() { + this.deleted = true; + this.enabled = false; + this.accountNonExpired = false; + this.firstTimeLoginRemaining = true; + this.username = getId() + "_DELETED_" + this.username; + } + + public boolean isDeleted() { + return this.deleted; + } + + @Override + public Collection getAuthorities() { + return populateGrantedAuthorities(); + } + + private List populateGrantedAuthorities() { + final List grantedAuthorities = new ArrayList(); + for (final Role role : this.roles) { + final Collection permissions = role.getPermissions(); + for (final Permission permission : permissions) { + grantedAuthorities.add(new SimpleGrantedAuthority(permission + .getCode())); + } + } + return grantedAuthorities; + } + + @Override + public String getPassword() { + return this.password; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public boolean isAccountNonExpired() { + return this.accountNonExpired; + } + + @Override + public boolean isAccountNonLocked() { + return this.accountNonLocked; + } + + @Override + public boolean isCredentialsNonExpired() { + return this.credentialsNonExpired; + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + public String getFirstname() { + return this.firstname; + } + + public String getLastname() { + return this.lastname; + } + + public String getEmail() { + return this.email; + } + + public Set getRoles() { + return this.roles; + } + + public Office getOffice() { + return this.office; + } + + public boolean canNotApproveLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "APPROVEINPAST_LOAN"); + } + + public boolean canNotRejectLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REJECTINPAST_LOAN"); + } + + public boolean canNotWithdrawByClientLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "WITHDRAWINPAST_LOAN"); + } + + public boolean canNotDisburseLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "DISBURSEINPAST_LOAN"); + } + + public boolean canNotMakeRepaymentOnLoanInPast() { + return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "REPAYMENTINPAST_LOAN"); + } + + public boolean hasNotPermissionForReport(final String reportName) { + + if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", + "REPORTING_SUPER_USER", "READ_" + reportName)) { + return true; + } + + return false; + } + + public boolean hasNotPermissionForDatatable(final String datatable, + final String accessType) { + + final String matchPermission = accessType + "_" + datatable; + + if (accessType.equalsIgnoreCase("READ")) { + + if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", + matchPermission)) { + return true; + } + + return false; + } + + if (hasNotPermissionForAnyOf("ALL_FUNCTIONS", matchPermission)) { + return true; + } + + return false; + } + + public boolean hasNotPermissionForAnyOf(final String... permissionCodes) { + boolean hasNotPermission = true; + for (final String permissionCode : permissionCodes) { + final boolean checkPermission = hasPermissionTo(permissionCode); + if (checkPermission) { + hasNotPermission = false; + break; + } + } + return hasNotPermission; + } + + public void validateHasReadPermission(final String resourceType) { + + final String authorizationMessage = "User has no authority to view " + + resourceType.toLowerCase() + "s"; + final String matchPermission = "READ_" + resourceType.toUpperCase(); + + if (!hasNotPermissionForAnyOf("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", + matchPermission)) { + return; + } + + throw new NoAuthorizationException(authorizationMessage); + } + + private boolean hasNotPermissionTo(final String permissionCode) { + return !hasPermissionTo(permissionCode); + } + + private boolean hasPermissionTo(final String permissionCode) { + boolean hasPermission = hasAllFunctionsPermission(); + if (!hasPermission) { + for (final Role role : this.roles) { + if (role.hasPermissionTo(permissionCode)) { + hasPermission = true; + break; + } + } + } + return hasPermission; + } + + private boolean hasAllFunctionsPermission() { + boolean match = false; + for (final Role role : this.roles) { + if (role.hasPermissionTo("ALL_FUNCTIONS")) { + match = true; + break; + } + } + return match; + } + + public boolean hasIdOf(final Long userId) { + return getId().equals(userId); + } + + private boolean hasNotAnyPermission(final List permissions) { + return !hasAnyPermission(permissions); + } + + private boolean hasAnyPermission(final List permissions) { + boolean hasAtLeastOneOf = false; + + for (final String permissionCode : permissions) { + if (hasPermissionTo(permissionCode)) { + hasAtLeastOneOf = true; + break; + } + } + + return hasAtLeastOneOf; + } + + public void validateHasPermissionTo(final String function, + final List allowedPermissions) { + if (hasNotAnyPermission(allowedPermissions)) { + final String authorizationMessage = "User has no authority to: " + + function; + throw new NoAuthorizationException(authorizationMessage); + } + } + + public void validateHasPermissionTo(final String function) { + if (hasNotPermissionTo(function)) { + final String authorizationMessage = "User has no authority to: " + + function; + logger.info("Unauthorized access: userId: " + getId() + " action: " + + function + " allowed: " + getAuthorities()); + throw new NoAuthorizationException(authorizationMessage); + } + } + + public void validateHasCheckerPermissionTo(final String function) { + + final String checkerPermissionName = function.toUpperCase() + + "_CHECKER"; + if (hasNotPermissionTo("CHECKER_SUPER_USER") + && hasNotPermissionTo(checkerPermissionName)) { + final String authorizationMessage = "User has no authority to be a checker for: " + + function; + throw new NoAuthorizationException(authorizationMessage); + } + } + + public void validateHasDatatableReadPermission(final String datatable) { + if (hasNotPermissionForDatatable(datatable, "READ")) { + throw new NoAuthorizationException( + "Not authorised to read datatable: " + datatable); + } + } + + public Long getStaffId() { + Long staffId = null; + if (this.staff != null) { + staffId = this.staff.getId(); + } + return staffId; + } + + public String getStaffDisplayName() { + String staffDisplayName = null; + if (this.staff != null) { + staffDisplayName = this.staff.displayName(); + } + return staffDisplayName; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/Permission.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/Permission.java index 84855dbfb..aedfd1e77 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/Permission.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/Permission.java @@ -21,11 +21,9 @@ public class Permission extends AbstractPersistable { @Column(name = "code", nullable = false, length = 100) private final String code; - @SuppressWarnings("unused") @Column(name = "entity_name", nullable = true, length = 100) private final String entityName; - @SuppressWarnings("unused") @Column(name = "action_name", nullable = true, length = 100) private final String actionName; @@ -66,7 +64,7 @@ public class Permission extends AbstractPersistable { } public boolean enableMakerChecker(final boolean canMakerChecker) { - final boolean isUpdatedValueSame = (this.canMakerChecker == canMakerChecker); + final boolean isUpdatedValueSame = this.canMakerChecker == canMakerChecker; this.canMakerChecker = canMakerChecker; return !isUpdatedValueSame; From 9e6108c11edbb4cafdc5867170758cc4893e285d Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Thu, 8 Aug 2013 11:56:14 +0530 Subject: [PATCH 29/47] added savings transaction adjustment API added Interest posting for undo transactions rebase changes --- ...mission_for_adjust_savings_transaction.sql | 1 + .../commands/domain/CommandWrapper.java | 4 + .../service/CommandWrapperBuilder.java | 77 +++++++++++-------- .../SynchronousCommandProcessingService.java | 2 + .../savings/SavingsApiConstants.java | 12 ++- ...SavingsAccountTransactionsApiResource.java | 28 ++++++- .../savings/domain/SavingsAccount.java | 15 ++-- ...gsAccountTransactionNotFoundException.java | 12 +++ .../TransactionUpdateNotAllowedException.java | 15 ++++ ...gsTransactionAdjustmentCommandHandler.java | 28 +++++++ .../SavingsAccountReadPlatformService.java | 2 + ...SavingsAccountReadPlatformServiceImpl.java | 8 ++ .../SavingsAccountWritePlatformService.java | 2 + ...WritePlatformServiceJpaRepositoryImpl.java | 70 ++++++++++++++++- 14 files changed, 227 insertions(+), 49 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V98__add_permission_for_adjust_savings_transaction.sql create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsAccountTransactionNotFoundException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/TransactionUpdateNotAllowedException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/SavingsTransactionAdjustmentCommandHandler.java diff --git a/mifosng-db/migrations/core_db/V98__add_permission_for_adjust_savings_transaction.sql b/mifosng-db/migrations/core_db/V98__add_permission_for_adjust_savings_transaction.sql new file mode 100755 index 000000000..906bb1497 --- /dev/null +++ b/mifosng-db/migrations/core_db/V98__add_permission_for_adjust_savings_transaction.sql @@ -0,0 +1 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_savings', 'ADJUSTTRANSACTION_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'ADJUSTTRANSACTION', 0); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 3e973eddc..2cc730d21 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -478,6 +478,10 @@ public class CommandWrapper { return this.actionName.equalsIgnoreCase("UNDOTRANSACTION") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT"); } + public boolean isAdjustSavingsAccountTransaction() { + return this.actionName.equalsIgnoreCase("ADJUSTTRANSACTION") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT"); + } + public boolean isCalendarResource() { return this.entityName.equalsIgnoreCase("CALENDAR"); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 445fd23c1..357a9150c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -342,7 +342,7 @@ public class CommandWrapperBuilder { this.href = "/clients/" + clientId + "?command=activate&template=true"; return this; } - + public CommandWrapperBuilder closeClient(final Long clientId) { this.actionName = "CLOSE"; this.entityName = "CLIENT"; @@ -372,30 +372,30 @@ public class CommandWrapperBuilder { } public CommandWrapperBuilder createDBDatatable(final String json) { - this.actionName = "CREATE"; - this.entityName = "DATATABLE"; - this.entityId = null; - this.href = "/datatables/"; - this.json = json; - return this; + this.actionName = "CREATE"; + this.entityName = "DATATABLE"; + this.entityId = null; + this.href = "/datatables/"; + this.json = json; + return this; } public CommandWrapperBuilder updateDBDatatable(final String datatable, final String json) { - this.actionName = "UPDATE"; - this.entityName = "DATATABLE"; - this.entityId = null; - this.href = "/datatables/" + datatable; - this.json = json; - return this; + this.actionName = "UPDATE"; + this.entityName = "DATATABLE"; + this.entityId = null; + this.href = "/datatables/" + datatable; + this.json = json; + return this; } public CommandWrapperBuilder deleteDBDatatable(final String datatable, final String json) { - this.actionName = "DELETE"; - this.entityName = "DATATABLE"; - this.entityId = null; - this.href = "/datatables/" + datatable; - this.json = json; - return this; + this.actionName = "DELETE"; + this.entityName = "DATATABLE"; + this.entityId = null; + this.href = "/datatables/" + datatable; + this.json = json; + return this; } public CommandWrapperBuilder createDatatable(final String datatable, final Long apptableId, final Long datatableId) { @@ -845,6 +845,17 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder adjustSavingsAccountTransaction(final Long accountId, final Long transactionId) { + this.actionName = "ADJUSTTRANSACTION"; + this.entityName = "SAVINGSACCOUNT"; + this.savingsId = accountId; + this.entityId = accountId; + this.subentityId = transactionId; + this.transactionId = transactionId.toString(); + this.href = "/savingsaccounts/" + accountId + "/transactions/" + transactionId + "?command=modify"; + return this; + } + public CommandWrapperBuilder savingsAccountInterestCalculation(final Long accountId) { this.actionName = "CALCULATEINTEREST"; this.entityName = "SAVINGSACCOUNT"; @@ -953,7 +964,7 @@ public class CommandWrapperBuilder { this.href = "/groups/" + groupId + "?command=activate"; return this; } - + public CommandWrapperBuilder saveGroupCollectionSheet(final Long groupId) { this.actionName = "SAVECOLLECTIONSHEET"; this.entityName = "GROUP"; @@ -962,7 +973,7 @@ public class CommandWrapperBuilder { this.href = "/groups/" + groupId + "?command=saveCollectionSheet"; return this; } - + public CommandWrapperBuilder deleteGroup(final Long groupId) { this.actionName = "DELETE"; this.entityName = "GROUP"; @@ -998,16 +1009,16 @@ public class CommandWrapperBuilder { this.href = "/groups/" + groupId; return this; } - + public CommandWrapperBuilder assignGroupStaff(final Long groupId) { this.actionName = "ASSIGNSTAFF"; this.entityName = "GROUP"; this.entityId = groupId; this.groupId = groupId; - this.href = "/groups/" + groupId +"?command=assignStaff"; + this.href = "/groups/" + groupId + "?command=assignStaff"; return this; } - + public CommandWrapperBuilder createCollateral(final Long loanId) { this.actionName = "CREATE"; this.entityName = "COLLATERAL"; @@ -1106,7 +1117,7 @@ public class CommandWrapperBuilder { this.href = "/accountingrules/" + accountingRuleId; return this; } - + public CommandWrapperBuilder createHoliday() { this.actionName = "CREATE"; this.entityName = "HOLIDAY"; @@ -1120,7 +1131,7 @@ public class CommandWrapperBuilder { this.entityName = "GROUP"; this.groupId = groupId; this.entityId = null; - this.href = "/groups/"+groupId+"?command=assignRole"; + this.href = "/groups/" + groupId + "?command=assignRole"; return this; } @@ -1129,7 +1140,7 @@ public class CommandWrapperBuilder { this.entityName = "GROUP"; this.groupId = groupId; this.entityId = roleId; - this.href = "/groups/"+groupId+"?command=unassignRole"; + this.href = "/groups/" + groupId + "?command=unassignRole"; return this; } @@ -1138,10 +1149,10 @@ public class CommandWrapperBuilder { this.entityName = "GROUP"; this.groupId = groupId; this.entityId = roleId; - this.href = "/groups/"+groupId+"?command=updateRole"; + this.href = "/groups/" + groupId + "?command=updateRole"; return this; } - + public CommandWrapperBuilder unassignClientStaff(final Long clientId) { this.actionName = "UNASSIGNSTAFF"; this.entityName = "CLIENT"; @@ -1177,7 +1188,7 @@ public class CommandWrapperBuilder { this.href = "/loanproducts/" + productId + "/productmix"; return this; } - + public CommandWrapperBuilder deleteProductMix(final Long productId) { this.actionName = "DELETE"; this.entityName = "PRODUCTMIX"; @@ -1186,17 +1197,17 @@ public class CommandWrapperBuilder { this.href = "/loanproducts/" + productId + "/productmix"; return this; } - + public CommandWrapperBuilder withProduct(final Long productId) { this.productId = productId; return this; } - + public CommandWrapperBuilder updateJobDetail(final Long jobId) { this.actionName = "UPDATE"; this.entityName = "SCHEDULER"; this.entityId = jobId; - this.href = "/updateJobDetail/" + jobId+"/updateJobDetail"; + this.href = "/updateJobDetail/" + jobId + "/updateJobDetail"; return this; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index eabf31970..e1208c0a4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -412,6 +412,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("applyAnnualFeeSavingsAccountCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isSavingsAccountUndoTransaction()) { handler = this.applicationContext.getBean("undoTransactionSavingsAccountCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isAdjustSavingsAccountTransaction()) { + handler = this.applicationContext.getBean("savingsTransactionAdjustmentCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index b2ba454b1..cade9ee85 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -30,6 +30,11 @@ public class SavingsApiConstants { public static String deleteApplicationAction = ".delete"; public static String undoTransactionAction = ".undotransaction"; public static String applyAnnualFeeTransactionAction = ".applyannualfee"; + public static String adjustTransactionAction = ".adjusttransaction"; + + // command + public static String COMMAND_UNDO_TRANSACTION = "undo"; + public static String COMMAND_ADJUST_TRANSACTION = "modify"; // general public static final String localeParamName = "locale"; @@ -81,6 +86,10 @@ public class SavingsApiConstants { // transaction parameters public static final String transactionDateParamName = "transactionDate"; public static final String transactionAmountParamName = "transactionAmount"; + public static final String paymentDetailDataParamName = "paymentDetailData"; + public static final String runningBalanceParamName = "runningBalance"; + public static final String reversedParamName = "reversed"; + public static final String dateParamName = "date"; public static final Set SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, monthDayFormatParamName, nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, @@ -135,7 +144,8 @@ public class SavingsApiConstants { transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName)); public static final Set SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, - "accountId", accountNoParamName, "currency", "amount")); + "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName, runningBalanceParamName, + reversedParamName)); public static final Set SAVINGS_ACCOUNT_ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, activatedOnDateParamName)); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java index d02f65cad..8a4f9f87e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java @@ -91,6 +91,27 @@ public class SavingsAccountTransactionsApiResource { SavingsApiConstants.SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS); } + @GET + @Path("{transactionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveOne(@PathParam("savingsId") final Long savingsId, @PathParam("transactionId") final Long transactionId, + @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME); + SavingsAccountTransactionData transactionData = this.savingsAccountReadPlatformService.retrieveSavingsTransaction(savingsId, + transactionId); + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + if (settings.isTemplate()) { + final Collection paymentTypeOptions = codeValueReadPlatformService + .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); + transactionData = SavingsAccountTransactionData.templateOnTop(transactionData, paymentTypeOptions); + } + + return this.toApiJsonSerializer.serialize(settings, transactionData, + SavingsApiConstants.SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS); + } + @POST @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @@ -120,7 +141,7 @@ public class SavingsAccountTransactionsApiResource { @Path("{transactionId}") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String undoTransaction(@PathParam("savingsId") final Long savingsId, @PathParam("transactionId") final Long transactionId, + public String adjustTransaction(@PathParam("savingsId") final Long savingsId, @PathParam("transactionId") final Long transactionId, @QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) { String jsonApiRequest = apiRequestBodyAsJson; @@ -131,9 +152,12 @@ public class SavingsAccountTransactionsApiResource { final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest); CommandProcessingResult result = null; - if (is(commandParam, "undo")) { + if (is(commandParam, SavingsApiConstants.COMMAND_UNDO_TRANSACTION)) { final CommandWrapper commandRequest = builder.undoSavingsAccountTransaction(savingsId, transactionId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, SavingsApiConstants.COMMAND_ADJUST_TRANSACTION)) { + final CommandWrapper commandRequest = builder.adjustSavingsAccountTransaction(savingsId, transactionId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } if (result == null) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 7c1c11af3..c4be8705b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -719,8 +719,9 @@ public class SavingsAccount extends AbstractPersistable { } } - private void validateAccountBalanceDoesNotBecomeNegativeDueToUndoAction(final List transactionsSortedByDate) { + public void validateAccountBalanceDoesNotBecomeNegative(String transactionAction) { + final List transactionsSortedByDate = retreiveListOfTransactions(); Money runningBalance = Money.zero(this.currency); for (final SavingsAccountTransaction transaction : transactionsSortedByDate) { @@ -734,7 +735,7 @@ public class SavingsAccount extends AbstractPersistable { // final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.undoTransactionAction); + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + transactionAction); baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("results.in.balance.going.negative"); @@ -1396,8 +1397,7 @@ public class SavingsAccount extends AbstractPersistable { return actualChanges; } - public void undoTransaction(final Long transactionId, final List reversedTransactionIds, final MathContext mc, - final LocalDate upToInterestCalculationDate) { + public void undoTransaction(final Long transactionId, final List reversedTransactionIds) { SavingsAccountTransaction transactionToUndo = null; for (final SavingsAccountTransaction transaction : this.transactions) { @@ -1424,9 +1424,6 @@ public class SavingsAccount extends AbstractPersistable { } } - calculateInterestUsing(mc, upToInterestCalculationDate); - - validateAccountBalanceDoesNotBecomeNegativeDueToUndoAction(retreiveListOfTransactions()); } } @@ -1616,8 +1613,8 @@ public class SavingsAccount extends AbstractPersistable { baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName).value(dateAsString) .failWithCodeNoParameterAddedToErrorCode("cannot.be.before.client.activation.date"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } - } - + } + if (this.group != null && this.group.isActivatedAfter(activationDate)) { final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); final String dateAsString = formatter.print(this.client.getActivationLocalDate()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsAccountTransactionNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsAccountTransactionNotFoundException.java new file mode 100755 index 000000000..10ac75ad7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsAccountTransactionNotFoundException.java @@ -0,0 +1,12 @@ +package org.mifosplatform.portfolio.savings.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class SavingsAccountTransactionNotFoundException extends AbstractPlatformResourceNotFoundException { + + public SavingsAccountTransactionNotFoundException(final Long savingsId, final Long transactionId) { + super("error.msg.saving.account.trasaction.id.invalid", "Savings account with savings identifier " + savingsId + + " and trasaction identifier " + transactionId + " does not exist", savingsId, transactionId); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/TransactionUpdateNotAllowedException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/TransactionUpdateNotAllowedException.java new file mode 100755 index 000000000..7635aa184 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/TransactionUpdateNotAllowedException.java @@ -0,0 +1,15 @@ +package org.mifosplatform.portfolio.savings.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformServiceUnavailableException; + +/** + * A {@link RuntimeException} thrown when update not allowed. + */ +public class TransactionUpdateNotAllowedException extends AbstractPlatformServiceUnavailableException { + + public TransactionUpdateNotAllowedException(final Long savingsId, final Long transactionId) { + super("error.msg.saving.account.trasaction.update.notallowed", + "Savings Account transaction update not allowed with savings identifier " + savingsId + " and trasaction identifier " + + transactionId, savingsId, transactionId); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/SavingsTransactionAdjustmentCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/SavingsTransactionAdjustmentCommandHandler.java new file mode 100755 index 000000000..f6571df9e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/SavingsTransactionAdjustmentCommandHandler.java @@ -0,0 +1,28 @@ +package org.mifosplatform.portfolio.savings.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class SavingsTransactionAdjustmentCommandHandler implements NewCommandSourceHandler { + + private final SavingsAccountWritePlatformService writePlatformService; + + @Autowired + public SavingsTransactionAdjustmentCommandHandler(final SavingsAccountWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + final Long transactionId = Long.valueOf(command.getTransactionId()); + return this.writePlatformService.adjustSavingsTransaction(command.getSavingsId(), transactionId, command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java index 684a5a487..f09125c94 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java @@ -28,4 +28,6 @@ public interface SavingsAccountReadPlatformService { Collection retrieveAllTransactions(Long savingsId); Collection retrieveAccountsWithAnnualFeeDue(); + + SavingsAccountTransactionData retrieveSavingsTransaction(Long savingsId, Long transactionId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 75c7a0561..5f0e4c5df 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -507,6 +507,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead return this.jdbcTemplate.query(sql, this.transactionsMapper, new Object[] { savingsId }); } + @Override + public SavingsAccountTransactionData retrieveSavingsTransaction(final Long savingsId, final Long transactionId) { + + final String sql = "select " + this.transactionsMapper.schema() + " where sa.id = ? and tr.id= ?"; + + return this.jdbcTemplate.queryForObject(sql, this.transactionsMapper, new Object[] { savingsId, transactionId }); + } + @Override public Collection retrieveAccountsWithAnnualFeeDue() { final String sql = "select " + annualFeeMapper.schema() diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java index cf8570b32..283c358ab 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java @@ -26,4 +26,6 @@ public interface SavingsAccountWritePlatformService { CommandProcessingResult undoTransaction(Long savingsId, Long transactionId); void postInterestForAccounts(); + + CommandProcessingResult adjustSavingsTransaction(Long savingsId, Long transactionId, JsonCommand command); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index 119915d13..1f67eeff7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -30,6 +30,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyReposit import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService; +import org.mifosplatform.portfolio.savings.SavingsApiConstants; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDataValidator; import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; @@ -38,6 +39,8 @@ import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository; +import org.mifosplatform.portfolio.savings.exception.SavingsAccountTransactionNotFoundException; +import org.mifosplatform.portfolio.savings.exception.TransactionUpdateNotAllowedException; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -274,15 +277,20 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository.findOneByIdAndSavingsAccountId( transactionId, savingsId); - if (savingsAccountTransaction == null) { - // throw new not found exception - } + if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); } final LocalDate today = DateUtils.getLocalDateOfTenant(); final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); - account.undoTransaction(transactionId, reversedTransactionIds, mc, today); + account.undoTransaction(transactionId, reversedTransactionIds); + if ((savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) + && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { + account.postInterest(mc, today, newTransactionIds, reversedTransactionIds); + } else { + account.calculateInterestUsing(mc, today); + } + account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction); postJournalEntries(account, newTransactionIds, reversedTransactionIds); @@ -295,6 +303,60 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .build(); } + @Override + public CommandProcessingResult adjustSavingsTransaction(final Long savingsId, final Long transactionId, final JsonCommand command) { + + SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository.findOneByIdAndSavingsAccountId( + transactionId, savingsId); + if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); } + + if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException( + savingsId, transactionId); } + this.savingsAccountTransactionDataValidator.validate(command); + + final LocalDate today = DateUtils.getLocalDateOfTenant(); + + final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final LocalDate transactionDate = command.localDateValueOfParameterNamed(SavingsApiConstants.transactionDateParamName); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(SavingsApiConstants.transactionAmountParamName); + final List existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + final Map changes = new LinkedHashMap(); + PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + + final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); + account.undoTransaction(transactionId, existingReversedTransactionIds); + SavingsAccountTransaction transaction = null; + if (savingsAccountTransaction.isDeposit()) { + transaction = account.deposit(fmt, transactionDate, transactionAmount, existingTransactionIds, existingReversedTransactionIds, + paymentDetail); + } else { + transaction = account.withdraw(fmt, transactionDate, transactionAmount, existingTransactionIds, existingReversedTransactionIds, + paymentDetail); + } + final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction); + + if (account.isBeforeLastPostingPeriod(transactionDate) + || account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { + account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + } else { + account.calculateInterestUsing(mc, today); + } + account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction); + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + return new CommandProcessingResultBuilder() // + .withEntityId(newtransactionId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .with(changes)// + .build(); + } + private void postJournalEntries(final SavingsAccount savingsAccount, final List existingTransactionIds, final List existingReversedTransactionIds) { From 5ffafdef08046b746b106d430bcf774ad7d000cd Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Thu, 8 Aug 2013 16:26:15 +0530 Subject: [PATCH 30/47] fixing issues with query for retrieveAccountsWithAnnualFeeDue --- .../ClientIdentifierCommandFromApiJsonDeserializer.java | 2 +- .../savings/service/SavingsAccountReadPlatformServiceImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/serialization/ClientIdentifierCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/serialization/ClientIdentifierCommandFromApiJsonDeserializer.java index 872bdcb63..c506e09fc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/serialization/ClientIdentifierCommandFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/serialization/ClientIdentifierCommandFromApiJsonDeserializer.java @@ -25,7 +25,7 @@ import com.google.gson.JsonElement; import com.google.gson.reflect.TypeToken; /** - * Implementation of {@link FromApiJsonDeserializer} for {@link ClientCommand} + * Implementation of {@link FromApiJsonDeserializer} for {@link ClientIdentifierCommandFromApiJsonDeserializer} * 's. */ @Component diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 5f0e4c5df..22078918a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -518,7 +518,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead @Override public Collection retrieveAccountsWithAnnualFeeDue() { final String sql = "select " + annualFeeMapper.schema() - + " where sa.annual_fee_next_due_date not null and sa.annual_fee_next_due_date <= NOW()"; + + " where sa.annual_fee_next_due_date is not null and sa.annual_fee_next_due_date <= NOW()"; return this.jdbcTemplate.query(sql, this.annualFeeMapper, new Object[] {}); } From 24829781513b431b091abdbe8381c944578bc70f Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Fri, 9 Aug 2013 17:17:49 +0530 Subject: [PATCH 31/47] added rounding to multiples of a decimal modifications as per the comments --- api-docs/apiLive.htm | 144 +++++++++++++++++- ..._added_currency_roundof_for_multipleof.sql | 12 ++ .../monetary/data/CurrencyData.java | 10 +- .../monetary/domain/ApplicationCurrency.java | 26 +++- .../ApplicationCurrencyRepositoryWrapper.java | 2 +- .../monetary/domain/MonetaryCurrency.java | 13 +- .../organisation/monetary/domain/Money.java | 56 ++++--- .../CurrencyReadPlatformServiceImpl.java | 5 +- .../api/OfficeTransactionsApiResource.java | 2 +- .../office/domain/OrganisationCurrency.java | 15 +- .../OfficeReadPlatformServiceImpl.java | 7 +- ...WritePlatformServiceJpaRepositoryImpl.java | 2 +- ...sReadPlatformServiceJpaRepositoryImpl.java | 8 +- ...countTransfersReadPlatformServiceImpl.java | 10 +- .../portfolio/charge/domain/Charge.java | 2 +- .../ChargeReadPlatformServiceImpl.java | 9 +- .../CollateralReadPlatformServiceImpl.java | 7 +- ...ollectionSheetReadPlatformServiceImpl.java | 44 +++--- .../domain/LoanApplicationTerms.java | 34 ++++- .../domain/LoanScheduleModel.java | 3 +- .../LoanChargeReadPlatformServiceImpl.java | 22 ++- .../service/LoanReadPlatformServiceImpl.java | 37 +++-- .../loanproduct/domain/LoanProduct.java | 5 +- .../domain/LoanProductRelatedDetail.java | 15 +- .../LoanProductDataValidator.java | 10 +- .../LoanProductReadPlatformServiceImpl.java | 18 ++- .../savings/SavingsApiConstants.java | 25 +-- .../data/SavingsProductDataValidator.java | 9 ++ .../savings/domain/SavingsProduct.java | 14 +- .../domain/SavingsProductAssembler.java | 4 +- ...SavingsAccountReadPlatformServiceImpl.java | 32 ++-- ...SavingsProductReadPlatformServiceImpl.java | 9 +- .../loanaccount/MonetaryCurrencyBuilder.java | 3 +- 33 files changed, 453 insertions(+), 161 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V99__added_currency_roundof_for_multipleof.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 05feee8c6..f9e121a9e 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -5882,6 +5882,7 @@ GET https://DomainName/api/v1/loans/{loanId}/transactions/template" "total": { "currencyCode": "XOF", "digitsAfterDecimal": 0, + "inMulitplesOf": 0, "amount": 471, "defaultName": "CFA Franc BCEAO", "nameCode": "currency.XOF", @@ -7427,6 +7428,13 @@ Request Body: Override the currency default value for digitsAfterDecimal. + + inMulitplesOf + + + Override the default value for rounding currency to multiples of value provided. + + principal @@ -8107,7 +8115,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
Mandatory Fields
- name, currencyCode, digitsAfterDecimal, principal, + name, currencyCode, digitsAfterDecimal, inMulitplesOf, principal, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyId, @@ -8152,6 +8160,7 @@ Request Body: "currencyCode": "USD", "locale": "en_GB", "digitsAfterDecimal": "2", + "inMulitplesOf": 0, "principal": "100,000.00", "numberOfRepayments": "12", "repaymentEvery": "1", @@ -8181,6 +8190,7 @@ Request Body: "currencyCode": "USD", "locale": "en_GB", "digitsAfterDecimal": "2", + "inMulitplesOf": 0, "principal": "100,000.00", "numberOfRepayments": "12", "repaymentEvery": "1", @@ -8237,6 +8247,7 @@ Request Body: "currencyCode": "USD", "locale": "en_GB", "digitsAfterDecimal": "2", + "inMulitplesOf": 0, "principal": "100,000.00", "numberOfRepayments": "12", "repaymentEvery": "1", @@ -11109,6 +11120,9 @@ Content-Type: application/json digitsAfterDecimal Override the currency default value for digitsAfterDecimal. + inMulitplesOf + Override the default value for rounding currency to multiples of provided value. + nominalAnnualInterestRate The default interest rate set when creating savings accounts of this type of product. e.g. 5% Per year - It number here is always expressed as the Nominal APR. @@ -11655,7 +11669,7 @@ Content-Type: application/json -
Mandatory Fields
name, description, currencyCode, digitsAfterDecimal, nominalAnnualInterestRate, + name, description, currencyCode, digitsAfterDecimal,inMulitplesOf, nominalAnnualInterestRate, interestCompoundingPeriodType, interestCalculationType, interestCalculationDaysInYearType,accountingRule
@@ -11682,6 +11696,7 @@ Request Body: "description": "Daily compounding using Daily Balance, 5% per year, 365 days in year", "currencyCode": "USD", "digitsAfterDecimal": 2, + "inMulitplesOf": 0, "locale": "en", "nominalAnnualInterestRate": "5.0", "interestCompoundingPeriodType": 1, @@ -11705,6 +11720,7 @@ Request Body: "description": "A savings product", "currencyCode": "USD", "digitsAfterDecimal": "2", + "inMulitplesOf": 0, "nominalAnnualInterestRate": "15", "interestCompoundingPeriodType": "1", "interestPostingPeriodType": "4", @@ -12888,6 +12904,71 @@ No Request Body: +  +
+
+

Retrieve Savings Account Transaction:

+

This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of: +

    +
  • Field Defaults
  • +
  • Allowed Value Lists
  • +
+

+

Example Requests:

+
savingsaccounts/1/transactions/1
+
+
+
+ GET https://Domain Name/api/v1/savingsaccounts/{accountId}/transactions/{transactionId} + + +{ + "id": 1, + "transactionType": { + "id": 2, + "code": "savingsAccountTransactionType.withdrawal", + "value": "Withdrawal", + "deposit": false, + "withdrawal": true, + "interestPosting": false, + "feeDeduction": false + }, + "accountId": 1, + "accountNo": "000000001", + "date": [ + 2013, + 8, + 7 + ], + "currency": { + "code": "USD", + "name": "US Dollar", + "decimalPlaces": 2, + "inMulitplesOf": 0, + "displaySymbol": "$", + "nameCode": "currency.USD", + "displayLabel": "US Dollar ($)" + }, + "paymentDetailData": { + "id": 62, + "paymentType": { + "id": 11, + "name": "cash" + }, + "accountNumber": "", + "checkNumber": "", + "routingCode": "", + "receiptNumber": "", + "bankNumber": "" + }, + "amount": 5000, + "runningBalance": 0, + "reversed": true +} + +
+
+  
@@ -12970,6 +13051,49 @@ No Request Body:
+   +
+
+

Adjust Transaction

+

This command modifies the given transaction.

+
+
+ POST https://Domain Name/api/v1/savingsaccounts/{accountsId}/transactions/{transactionId}?command=modify + POST savingsaccounts/1/transactions/1?command=modify +Content-Type: application/json +No Request Body: +{ + "locale": "en", + "dateFormat": "dd MMMM yyyy", + "transactionDate": "27 May 2013", + "transactionAmount": "500", + "paymentTypeId": "14", + "accountNumber": "acc123", + "checkNumber": "che123", + "routingCode": "rou123", + "receiptNumber": "rec123", + "bankNumber": "ban123" +} + + +{ + "officeId": 1, + "clientId": 2, + "savingsId": 1, + "resourceId": 48, + "changes": { + "accountNumber": "acc123", + "checkNumber": "che123", + "routingCode": "rou123", + "receiptNumber": "rec123", + "bankNumber": "ban123" + } +} + +
+
+ +  
@@ -19699,6 +19823,14 @@ GET https://DomainName/api/v1/search?query=000000111 + + + savingsaccounts/{accountId}/transactions/{transactionId}?command=modify + Adjust transaction + + + + savingsaccounts/{accountId}/transactions/template @@ -19707,6 +19839,14 @@ GET https://DomainName/api/v1/search?query=000000111 + + + savingsaccounts/{accountId}/transactions/{transactionId} + + Retrieve savings account transaction + + + Account Transfers accounttransfers/ diff --git a/mifosng-db/migrations/core_db/V99__added_currency_roundof_for_multipleof.sql b/mifosng-db/migrations/core_db/V99__added_currency_roundof_for_multipleof.sql new file mode 100755 index 000000000..571335a04 --- /dev/null +++ b/mifosng-db/migrations/core_db/V99__added_currency_roundof_for_multipleof.sql @@ -0,0 +1,12 @@ +ALTER TABLE `m_savings_account` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `currency_digits`; +ALTER TABLE `m_savings_product` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `currency_digits`; +ALTER TABLE `m_loan` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `currency_digits`; +ALTER TABLE `m_product_loan` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `currency_digits`; +ALTER TABLE `m_currency` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `decimal_places`; +ALTER TABLE `m_organisation_currency` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `decimal_places`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/data/CurrencyData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/data/CurrencyData.java index f5621a9f0..9e321e7c2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/data/CurrencyData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/data/CurrencyData.java @@ -13,6 +13,7 @@ public class CurrencyData { private final String code; private final String name; private final int decimalPlaces; + private final Integer inMulitplesOf; private final String displaySymbol; @SuppressWarnings("unused") private final String nameCode; @@ -20,13 +21,14 @@ public class CurrencyData { private final String displayLabel; public static CurrencyData blank() { - return new CurrencyData("", "", 0, "", ""); + return new CurrencyData("", "", 0,0, "", ""); } - public CurrencyData(final String code, final String name, final int decimalPlaces, final String displaySymbol, final String nameCode) { + public CurrencyData(final String code, final String name, final int decimalPlaces,final Integer inMulitplesOf, final String displaySymbol, final String nameCode) { this.code = code; this.name = name; this.decimalPlaces = decimalPlaces; + this.inMulitplesOf = inMulitplesOf; this.displaySymbol = displaySymbol; this.nameCode = nameCode; this.displayLabel = generateDisplayLabel(); @@ -40,6 +42,10 @@ public class CurrencyData { return this.decimalPlaces; } + public Integer currencyInMulitplesOf() { + return this.inMulitplesOf; + } + @Override public boolean equals(Object obj) { CurrencyData currencyData = (CurrencyData) obj; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java index bdf28007d..270c57acf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java @@ -23,6 +23,9 @@ public class ApplicationCurrency extends AbstractPersistable { @Column(name = "decimal_places", nullable = false) private final Integer decimalPlaces; + @Column(name = "currency_multiplesof") + private final Integer inMulitplesOf; + @Column(name = "name", nullable = false, length = 50) private final String name; @@ -36,19 +39,22 @@ public class ApplicationCurrency extends AbstractPersistable { this.code = null; this.name = null; this.decimalPlaces = null; + this.inMulitplesOf = null; this.nameCode = null; this.displaySymbol = null; } - public static ApplicationCurrency from(final ApplicationCurrency currency, final int decimalPlaces) { - return new ApplicationCurrency(currency.code, currency.name, decimalPlaces, currency.nameCode, currency.displaySymbol); + public static ApplicationCurrency from(final ApplicationCurrency currency, final int decimalPlaces, final Integer inMulitplesOf) { + return new ApplicationCurrency(currency.code, currency.name, decimalPlaces, inMulitplesOf, currency.nameCode, + currency.displaySymbol); } - private ApplicationCurrency(final String code, final String name, final int decimalPlaces, final String nameCode, - final String displaySymbol) { + private ApplicationCurrency(final String code, final String name, final int decimalPlaces, final Integer inMulitplesOf, + final String nameCode, final String displaySymbol) { this.code = code; this.name = name; this.decimalPlaces = decimalPlaces; + this.inMulitplesOf = inMulitplesOf; this.nameCode = nameCode; this.displaySymbol = displaySymbol; } @@ -65,6 +71,10 @@ public class ApplicationCurrency extends AbstractPersistable { return this.decimalPlaces; } + public Integer getCurrencyInMulitplesOf() { + return this.inMulitplesOf; + } + public String getNameCode() { return nameCode; } @@ -74,14 +84,14 @@ public class ApplicationCurrency extends AbstractPersistable { } public CurrencyData toData() { - return new CurrencyData(this.code, this.name, this.decimalPlaces, this.displaySymbol, this.nameCode); + return new CurrencyData(this.code, this.name, this.decimalPlaces, this.inMulitplesOf, this.displaySymbol, this.nameCode); } - public CurrencyData toData(final int digitsAfterDecimalSupported) { - return new CurrencyData(this.code, this.name, digitsAfterDecimalSupported, this.displaySymbol, this.nameCode); + public CurrencyData toData(final int digitsAfterDecimalSupported, final Integer inMulitplesOf) { + return new CurrencyData(this.code, this.name, digitsAfterDecimalSupported, inMulitplesOf, this.displaySymbol, this.nameCode); } public OrganisationCurrency toOrganisationCurrency() { - return new OrganisationCurrency(this.code, this.name, this.decimalPlaces, this.nameCode, this.displaySymbol); + return new OrganisationCurrency(this.code, this.name, this.decimalPlaces,this.inMulitplesOf, this.nameCode, this.displaySymbol); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrencyRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrencyRepositoryWrapper.java index b5d8a94c0..313ed0683 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrencyRepositoryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrencyRepositoryWrapper.java @@ -38,7 +38,7 @@ public class ApplicationCurrencyRepositoryWrapper { if (defaultApplicationCurrency == null) { throw new CurrencyNotFoundException(currency.getCode()); } final ApplicationCurrency applicationCurrency = ApplicationCurrency.from(defaultApplicationCurrency, - currency.getDigitsAfterDecimal()); + currency.getDigitsAfterDecimal(),currency.getCurrencyInMulitplesOf()); return applicationCurrency; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MonetaryCurrency.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MonetaryCurrency.java index 46f5eb196..7f5bd0f99 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MonetaryCurrency.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MonetaryCurrency.java @@ -17,18 +17,23 @@ public class MonetaryCurrency { @Column(name = "currency_digits", nullable = false) private final int digitsAfterDecimal; + @Column(name = "currency_multiplesof") + private final Integer inMulitplesOf; + protected MonetaryCurrency() { this.code = null; this.digitsAfterDecimal = 0; + this.inMulitplesOf = 0; } - public MonetaryCurrency(final String code, final int digitsAfterDecimal) { + public MonetaryCurrency(final String code, final int digitsAfterDecimal,final Integer inMulitplesOf) { this.code = code; this.digitsAfterDecimal = digitsAfterDecimal; + this.inMulitplesOf = inMulitplesOf; } public MonetaryCurrency copy() { - return new MonetaryCurrency(this.code, this.digitsAfterDecimal); + return new MonetaryCurrency(this.code, this.digitsAfterDecimal,this.inMulitplesOf); } public String getCode() { @@ -38,4 +43,8 @@ public class MonetaryCurrency { public int getDigitsAfterDecimal() { return digitsAfterDecimal; } + + public Integer getCurrencyInMulitplesOf() { + return inMulitplesOf; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java index 9b2f2402c..13c71263c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java @@ -21,6 +21,9 @@ public class Money implements Comparable { @Column(name = "currency_digits") private final int currencyDigitsAfterDecimal; + @Column(name = "currency_multiplesof") + private final Integer inMulitplesOf; + @Column(name = "amount", scale = 6, precision = 19) private final BigDecimal amount; @@ -44,46 +47,47 @@ public class Money implements Comparable { } public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount) { - return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), defaultToZeroIfNull(newAmount)); + return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), defaultToZeroIfNull(newAmount), + currency.getCurrencyInMulitplesOf()); } public static Money zero(final MonetaryCurrency currency) { - return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), BigDecimal.ZERO); + return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), BigDecimal.ZERO, currency.getCurrencyInMulitplesOf()); } protected Money() { this.currencyCode = null; this.currencyDigitsAfterDecimal = 0; + this.inMulitplesOf = 0; this.amount = null; } - private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount) { + private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount, final Integer inMulitplesOf) { this.currencyCode = currencyCode; this.currencyDigitsAfterDecimal = digitsAfterDecimal; + this.inMulitplesOf = inMulitplesOf; final BigDecimal amountZeroed = defaultToZeroIfNull(amount); final BigDecimal amountStripped = amountZeroed.stripTrailingZeros(); final BigDecimal amountScaled = amountStripped.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN); - this.amount = amountScaled; - // TODO - KW - below was test of using floor/ceil functions to support - // round monetary amounts into multiples of say 20/50. - // if (this.currencyDigitsAfterDecimal == 0) { - // double existingVal = amountScaled.doubleValue(); - // double ceilingOfValue = ceiling(existingVal, Double.valueOf("20.0")); - // double floorOfValue = floor(existingVal, Double.valueOf("20.0")); - // - // double floorDiff = existingVal - floorOfValue; - // double ceilDiff = ceilingOfValue - existingVal; - // - // if (ceilDiff > floorDiff) { - // this.amount = BigDecimal.valueOf(floorOfValue); - // } else { - // this.amount = BigDecimal.valueOf(ceilingOfValue); - // } - // } else { - // this.amount = amountScaled; - // } + // round monetary amounts into multiplesof say 20/50. + if (inMulitplesOf!= null && this.currencyDigitsAfterDecimal == 0 && inMulitplesOf>0 && amountScaled.doubleValue()>0) { + double existingVal = amountScaled.doubleValue(); + double ceilingOfValue = ceiling(existingVal, inMulitplesOf); + double floorOfValue = floor(existingVal, inMulitplesOf); + + double floorDiff = existingVal - floorOfValue; + double ceilDiff = ceilingOfValue - existingVal; + + if (ceilDiff > floorDiff) { + this.amount = BigDecimal.valueOf(floorOfValue); + } else { + this.amount = BigDecimal.valueOf(ceilingOfValue); + } + } else { + this.amount = amountScaled; + } } public static double ceiling(final double n, final double s) { @@ -119,7 +123,7 @@ public class Money implements Comparable { } public Money copy() { - return new Money(this.currencyCode, this.currencyDigitsAfterDecimal, this.amount.stripTrailingZeros()); + return new Money(this.currencyCode, this.currencyDigitsAfterDecimal, this.amount.stripTrailingZeros(), this.inMulitplesOf); } public Money plus(final Iterable moniesToAdd) { @@ -263,6 +267,10 @@ public class Money implements Comparable { return this.currencyDigitsAfterDecimal; } + public int getCurrencyInMulitplesOf() { + return this.inMulitplesOf; + } + public BigDecimal getAmount() { return this.amount; } @@ -298,7 +306,7 @@ public class Money implements Comparable { } private MonetaryCurrency monetaryCurrency() { - return new MonetaryCurrency(this.currencyCode, this.currencyDigitsAfterDecimal); + return new MonetaryCurrency(this.currencyCode, this.currencyDigitsAfterDecimal, this.inMulitplesOf); } public Money zero() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/service/CurrencyReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/service/CurrencyReadPlatformServiceImpl.java index 661a58bc8..475ba34e4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/service/CurrencyReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/service/CurrencyReadPlatformServiceImpl.java @@ -58,14 +58,15 @@ public class CurrencyReadPlatformServiceImpl implements CurrencyReadPlatformServ final String code = rs.getString("code"); final String name = rs.getString("name"); final int decimalPlaces = JdbcSupport.getInteger(rs, "decimalPlaces"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); final String displaySymbol = rs.getString("displaySymbol"); final String nameCode = rs.getString("nameCode"); - return new CurrencyData(code, name, decimalPlaces, displaySymbol, nameCode); + return new CurrencyData(code, name, decimalPlaces, inMulitplesOf, displaySymbol, nameCode); } public String schema() { - return " c.code as code, c.name as name, c.decimal_places as decimalPlaces, c.display_symbol as displaySymbol, c.internationalized_name_code as nameCode "; + return " c.code as code, c.name as name, c.decimal_places as decimalPlaces,c.currency_multiplesof as inMulitplesOf, c.display_symbol as displaySymbol, c.internationalized_name_code as nameCode "; } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficeTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficeTransactionsApiResource.java index 1fc5490e7..babdcbddf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficeTransactionsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficeTransactionsApiResource.java @@ -41,7 +41,7 @@ import org.springframework.stereotype.Component; public class OfficeTransactionsApiResource { private static final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "transactionDate", "fromOfficeId", - "fromOfficeName", "toOfficeId", "toOfficeIdName", "currencyCode", "digitsAfterDecimal", "transactionAmount", "description", + "fromOfficeName", "toOfficeId", "toOfficeIdName", "currencyCode", "digitsAfterDecimal","inMulitplesOf", "transactionAmount", "description", "allowedOffices", "currencyOptions")); private final String resourceNameForReadPermissions = "OFFICE"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java index d824ee431..17204a9d3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java @@ -18,18 +18,27 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_organisation_currency") public class OrganisationCurrency extends AbstractPersistable { + @SuppressWarnings("unused") @Column(name = "code", nullable = false, length = 3) private final String code; + @SuppressWarnings("unused") @Column(name = "decimal_places", nullable = false) private final Integer decimalPlaces; + @SuppressWarnings("unused") + @Column(name = "currency_multiplesof") + private final Integer inMulitplesOf; + + @SuppressWarnings("unused") @Column(name = "name", nullable = false, length = 50) private final String name; + @SuppressWarnings("unused") @Column(name = "internationalized_name_code", nullable = false, length = 50) private final String nameCode; + @SuppressWarnings("unused") @Column(name = "display_symbol", nullable = true, length = 10) private final String displaySymbol; @@ -37,15 +46,17 @@ public class OrganisationCurrency extends AbstractPersistable { this.code = null; this.name = null; this.decimalPlaces = null; + this.inMulitplesOf = null; this.nameCode = null; this.displaySymbol = null; } - public OrganisationCurrency(final String code, final String name, final int decimalPlaces, final String nameCode, - final String displaySymbol) { + public OrganisationCurrency(final String code, final String name, final int decimalPlaces, final Integer inMulitplesOf, + final String nameCode, final String displaySymbol) { this.code = code; this.name = name; this.decimalPlaces = decimalPlaces; + this.inMulitplesOf = inMulitplesOf; this.nameCode = nameCode; this.displaySymbol = displaySymbol; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java index 564da7eee..af7d04979 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java @@ -90,7 +90,7 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService public String schema() { return " ot.id as id, ot.transaction_date as transactionDate, ot.from_office_id as fromOfficeId, fromoff.name as fromOfficeName, " + " ot.to_office_id as toOfficeId, tooff.name as toOfficeName, ot.transaction_amount as transactionAmount, ot.description as description, " - + " ot.currency_code as currencyCode, rc.decimal_places as currencyDigits, " + + " ot.currency_code as currencyCode, rc.decimal_places as currencyDigits, rc.currency_multiplesof as inMulitplesOf, " + " rc.name as currencyName, rc.internationalized_name_code as currencyNameCode, rc.display_symbol as currencyDisplaySymbol " + " from m_office_transaction ot " + " left join m_office fromoff on fromoff.id = ot.from_office_id " @@ -112,9 +112,10 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService String currencyNameCode = rs.getString("currencyNameCode"); String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); BigDecimal transactionAmount = rs.getBigDecimal("transactionAmount"); String description = rs.getString("description"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java index e926ea009..840b02ffc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java @@ -159,7 +159,7 @@ public class OfficeWritePlatformServiceJpaRepositoryImpl implements OfficeWriteP final String currencyCode = command.stringValueOfParameterNamed("currencyCode"); final ApplicationCurrency appCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currencyCode); - final MonetaryCurrency currency = new MonetaryCurrency(appCurrency.getCode(), appCurrency.getDecimalPlaces()); + final MonetaryCurrency currency = new MonetaryCurrency(appCurrency.getCode(), appCurrency.getDecimalPlaces(),appCurrency.getCurrencyInMulitplesOf()); final Money amount = Money.of(currency, command.bigDecimalValueOfParameterNamed("transactionAmount")); final OfficeTransaction entity = OfficeTransaction.fromJson(fromOffice, toOffice, amount, command); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java index f707853be..ebecb4411 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java @@ -107,7 +107,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou accountsSummary.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, sa.status_enum as statusEnum, "); accountsSummary.append("sa.account_type_enum as accountType, "); accountsSummary.append("sa.account_balance_derived as accountBalance, "); - accountsSummary.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, "); + accountsSummary + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMulitplesOf, "); accountsSummary.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); accountsSummary.append("curr.display_symbol as currencyDisplaySymbol, "); accountsSummary.append("sa.product_id as productId, p.name as productName "); @@ -141,8 +142,9 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, status, currency, accountBalance, accountTypeData); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java index 7a19cac0a..91d662764 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java @@ -206,12 +206,13 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers private final String schemaSql; - public AccountTransfersMapper() { + public AccountTransfersMapper() { final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("sat.id as id, sat.is_reversed as isReversed,"); sqlBuilder.append("sat.transaction_date as transferDate, sat.amount as transferAmount,"); sqlBuilder.append("sat.description as transferDescription,"); - sqlBuilder.append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits,"); + sqlBuilder + .append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits, sat.currency_multiplesof as inMulitplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); @@ -257,8 +258,9 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); final String fromOfficeName = rs.getString("fromOfficeName"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java index cfd3514ab..12ba2a31c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java @@ -229,7 +229,7 @@ public class Charge extends AbstractPersistable { EnumOptionData chargeAppliesTo = ChargeEnumerations.chargeAppliesTo(this.chargeAppliesTo); EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation); - CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, null, null); + CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null); return ChargeData.instance(this.getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, penalty, active); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeReadPlatformServiceImpl.java index c18d08db2..a18b36541 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeReadPlatformServiceImpl.java @@ -111,7 +111,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService return this.jdbcTemplate.query(sql, rm, new Object[] { ChargeAppliesTo.LOAN.getValue() }); } - + @Override public Collection retrieveLoanApplicablePenalties() { this.context.authenticatedUser(); @@ -129,7 +129,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService return "c.id as id, c.name as name, c.amount as amount, c.currency_code as currencyCode, " + "c.charge_applies_to_enum as chargeAppliesTo, c.charge_time_enum as chargeTime, " + "c.charge_calculation_enum as chargeCalculation, c.is_penalty as penalty, c.is_active as active, oc.name as currencyName, " - + "oc.decimal_places as currencyDecimalPlaces, oc.display_symbol as currencyDisplaySymbol, " + + "oc.decimal_places as currencyDecimalPlaces,oc.currency_multiplesof as inMulitplesOf, oc.display_symbol as currencyDisplaySymbol, " + "oc.internationalized_name_code as currencyNameCode from m_charge c " + "join m_organisation_currency oc on c.currency_code = oc.code"; } @@ -149,9 +149,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, currencyDisplaySymbol, - currencyNameCode); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); final int chargeAppliesTo = rs.getInt("chargeAppliesTo"); final EnumOptionData chargeAppliesToType = ChargeEnumerations.chargeAppliesTo(chargeAppliesTo); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/service/CollateralReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/service/CollateralReadPlatformServiceImpl.java index 53703d30d..62fc3345a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/service/CollateralReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/service/CollateralReadPlatformServiceImpl.java @@ -45,7 +45,7 @@ public class CollateralReadPlatformServiceImpl implements CollateralReadPlatform private StringBuilder sqlBuilder = new StringBuilder( "lc.id as id, lc.description as description, lc.value as value, cv.id as typeId, cv.code_value as typeName, oc.code as currencyCode, ") - .append(" oc.name as currencyName,oc.decimal_places as currencyDecimalPlaces, oc.display_symbol as currencyDisplaySymbol, oc.internationalized_name_code as currencyNameCode") + .append(" oc.name as currencyName,oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMulitplesOf, oc.display_symbol as currencyDisplaySymbol, oc.internationalized_name_code as currencyNameCode") .append(" FROM m_loan_collateral lc") // .append(" JOIN m_code_value cv on lc.type_cv_id = cv.id")// .append(" JOIN m_loan loan on lc.loan_id = loan.id")// @@ -71,9 +71,10 @@ public class CollateralReadPlatformServiceImpl implements CollateralReadPlatform final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, currencyDisplaySymbol, - currencyNameCode); + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); return CollateralData.instance(id, type, value, description, currencyData); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java index d17ecb7fb..5de580894 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java @@ -52,7 +52,8 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe @Autowired public CollectionSheetReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, - final CenterReadPlatformService centerReadPlatformService, final GroupReadPlatformService groupReadPlatformService, final CollectionSheetGenerateCommandFromApiJsonDeserializer collectionSheetGenerateCommandFromApiJsonDeserializer) { + final CenterReadPlatformService centerReadPlatformService, final GroupReadPlatformService groupReadPlatformService, + final CollectionSheetGenerateCommandFromApiJsonDeserializer collectionSheetGenerateCommandFromApiJsonDeserializer) { this.context = context; this.centerReadPlatformService = centerReadPlatformService; this.namedParameterjdbcTemplate = new NamedParameterJdbcTemplate(dataSource); @@ -69,9 +70,10 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final CenterData center = this.centerReadPlatformService.retrieveOne(centerId); final String groupHierarchy = center.getHierarchy() + "%"; - - Collection collectionSheetFlatDatas = this.retriveJLGCollectionSheet(groupHierarchy, officeHierarchy, dueDate); - + + Collection collectionSheetFlatDatas = this.retriveJLGCollectionSheet(groupHierarchy, officeHierarchy, + dueDate); + final JLGCollectionSheetData jlgCollectionSheetData = buildJLGCollectionSheet(dueDate, collectionSheetFlatDatas); return jlgCollectionSheetData; @@ -83,7 +85,8 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe * Groups >> Clients >> Loans. */ @SuppressWarnings("null") - private JLGCollectionSheetData buildJLGCollectionSheet(final LocalDate dueDate, final Collection jlgCollectionSheetFlatData) { + private JLGCollectionSheetData buildJLGCollectionSheet(final LocalDate dueDate, + final Collection jlgCollectionSheetFlatData) { boolean firstTime = true; Long prevGroupId = null; @@ -101,9 +104,9 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe for (final JLGCollectionSheetFlatData collectionSheetFlatData : jlgCollectionSheetFlatData) { - if(collectionSheetFlatData.getProductId() !=null){ + if (collectionSheetFlatData.getProductId() != null) { loanProducts.add(LoanProductData.lookupWithCurrency(collectionSheetFlatData.getProductId(), - collectionSheetFlatData.getProductShortName(), collectionSheetFlatData.getCurrency())); + collectionSheetFlatData.getProductShortName(), collectionSheetFlatData.getCurrency())); } corrCollectioSheetFlatData = collectionSheetFlatData; @@ -197,7 +200,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe + "ln.loan_status_id As accountStatusId, " + "pl.name As productShortName, " + "ln.product_id As productId, " - + "ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + + "ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, ln.currency_multiplesof as inMulitplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + "if(ln.loan_status_id = 200 , ln.principal_amount , null) As disbursementAmount, " + "sum(ifnull(if(ln.loan_status_id = 300, ls.principal_amount, 0.0), 0.0) - ifnull(if(ln.loan_status_id = 300, ls.principal_completed_derived, 0.0), 0.0)) As principalDue, " + "ln.principal_repaid_derived As principalPaid, " @@ -206,7 +209,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe + "sum(if(ln.loan_status_id = 300, lc.amount_outstanding_derived, 0.0)) as chargesDue " + "FROM m_group gp " + "LEFT JOIN m_office of ON of.id = gp.office_id AND of.hierarchy like :officeHierarchy " - //+ "LEFT JOIN m_office of ON of.id = gp.office_id " + // + "LEFT JOIN m_office of ON of.id = gp.office_id " + "JOIN m_group_level gl ON gl.id = gp.level_Id " + "LEFT JOIN m_staff sf ON sf.id = gp.staff_id " + "JOIN m_group_client gc ON gc.group_id = gp.id " @@ -216,7 +219,9 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe + "JOIN m_currency rc on rc.`code` = ln.currency_code " + "LEFT JOIN m_loan_repayment_schedule ls ON ls.loan_id = ln.id AND ls.completed_derived = 0 AND ls.duedate <= :dueDate " + "LEFT JOIN m_loan_charge lc ON lc.loan_id = ln.id AND lc.is_paid_derived = 0 AND ( lc.due_for_collection_as_of_date <= :dueDate OR lc.charge_time_enum = 1) "; - //+ "WHERE gp.hierarchy like :hierarchy " + "GROUP BY gp.id ,cl.id , ln.id " + "ORDER BY gp.id , cl.id , ln.id "; + // + "WHERE gp.hierarchy like :hierarchy " + + // "GROUP BY gp.id ,cl.id , ln.id " + + // "ORDER BY gp.id , cl.id , ln.id "; } @Override @@ -241,9 +246,10 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); CurrencyData currencyData = null; - if(currencyCode != null){ - currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, + if (currencyCode != null) { + currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, currencyNameCode); } @@ -263,7 +269,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe @Override public JLGCollectionSheetData generateGroupCollectionSheet(final Long groupId, final JsonQuery query) { - + this.collectionSheetGenerateCommandFromApiJsonDeserializer.validateForGenerateCollectionSheet(query.json()); final AppUser currentUser = this.context.authenticatedUser(); @@ -281,18 +287,18 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe String sql = mapper.collectionSheetSchema(); sql += "WHERE gp.id = :groupId GROUP BY gp.id ,cl.id , ln.id ORDER BY gp.id , cl.id , ln.id"; - final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", dueDateStr).addValue("groupId", group.getId()) - .addValue("officeHierarchy", officeHierarchy); + final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", dueDateStr) + .addValue("groupId", group.getId()).addValue("officeHierarchy", officeHierarchy); final Collection collectionSheetFlatDatas = this.namedParameterjdbcTemplate.query(sql, namedParameters, mapper); return buildJLGCollectionSheet(dueDate, collectionSheetFlatDatas); } - + @Override public JLGCollectionSheetData generateCenterCollectionSheet(final Long centerId, final JsonQuery query) { - + this.collectionSheetGenerateCommandFromApiJsonDeserializer.validateForGenerateCollectionSheet(query.json()); final AppUser currentUser = this.context.authenticatedUser(); @@ -310,8 +316,8 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe String sql = mapper.collectionSheetSchema(); sql += "WHERE gp.parent_id = :centerId GROUP BY gp.id ,cl.id , ln.id ORDER BY gp.id , cl.id , ln.id"; - final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", dueDateStr).addValue("centerId", center.getId()) - .addValue("officeHierarchy", officeHierarchy); + final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", dueDateStr) + .addValue("centerId", center.getId()).addValue("officeHierarchy", officeHierarchy); final Collection collectionSheetFlatDatas = this.namedParameterjdbcTemplate.query(sql, namedParameters, mapper); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java index 552f19e3b..f613b0d06 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java @@ -157,7 +157,7 @@ public final class LoanApplicationTerms { this.expectedDisbursementDate = expectedDisbursementDate; this.repaymentsStartingFromDate = repaymentsStartingFromDate; this.calculatedRepaymentsStartingFromDate = calculatedRepaymentsStartingFromDate; - + this.principalGrace = principalGrace; this.interestPaymentGrace = interestPaymentGrace; this.interestChargingGrace = interestChargingGrace; @@ -172,7 +172,15 @@ public final class LoanApplicationTerms { Money adjusted = principalForPeriod; // adjust cumulative fields for principal & interest if needed - if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) { + if (this.principal.getAmount().subtract(totalCumulativePrincipalToDate.getAmount()).add(principalForPeriod.getAmount()).compareTo(principalForPeriod.getAmount()) == -1) { + adjusted = adjusted.minus(adjusted); + MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, principalForPeriod.getAmount().intValue()); + BigDecimal diff = this.principal.getAmount().subtract(totalCumulativePrincipalToDate.getAmount()).add(principalForPeriod.getAmount()); + if(diff.doubleValue()<0){ + diff = BigDecimal.ZERO; + } + adjusted = adjusted.plus(Money.of(monetaryCurrency,diff)); + } else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) { final Money difference = totalCumulativePrincipalToDate.minus(this.principal); if (difference.isLessThanZero()) { @@ -189,9 +197,16 @@ public final class LoanApplicationTerms { final Money totalInterestDueForLoan, final int periodNumber) { Money adjusted = interestForThisPeriod; - - if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) { - + if (totalInterestDueForLoan.getAmount().subtract(totalCumulativeInterestToDate.getAmount()).add(interestForThisPeriod.getAmount()) + .compareTo(interestForThisPeriod.getAmount()) == -1) { + adjusted = adjusted.minus(adjusted); + MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, interestForThisPeriod.getAmount().intValue()); + BigDecimal diff = totalInterestDueForLoan.getAmount().subtract(totalCumulativeInterestToDate.getAmount()).add(interestForThisPeriod.getAmount()); + if(diff.doubleValue()<0){ + diff = BigDecimal.ZERO; + } + adjusted = adjusted.plus(Money.of(monetaryCurrency,diff)); + } else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) { final Money interestDifference = totalCumulativeInterestToDate.minus(totalInterestDueForLoan); if (interestDifference.isLessThanZero()) { adjusted = interestForThisPeriod.plus(interestDifference.abs()); @@ -199,7 +214,9 @@ public final class LoanApplicationTerms { adjusted = interestForThisPeriod.minus(interestDifference.abs()); } } - + if (adjusted.isLessThanZero()) { + adjusted = adjusted.plus(adjusted); + } return adjusted; } @@ -618,7 +635,8 @@ public final class LoanApplicationTerms { } public LoanProductRelatedDetail toLoanProductRelatedDetail() { - final MonetaryCurrency currency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDecimalPlaces()); + final MonetaryCurrency currency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDecimalPlaces(), + this.currency.getCurrencyInMulitplesOf()); return LoanProductRelatedDetail.createFrom(currency, this.principal.getAmount(), this.interestRatePerPeriod, this.interestRatePeriodFrequencyType, this.annualNominalInterestRate, this.interestMethod, @@ -690,7 +708,7 @@ public final class LoanApplicationTerms { public LocalDate getCalculatedRepaymentsStartingFromLocalDate() { return this.calculatedRepaymentsStartingFromDate; } - + public Money getPrincipal() { return this.principal; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java index 495fcdbea..413b0cd59 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java @@ -62,7 +62,8 @@ public final class LoanScheduleModel { public LoanScheduleData toData() { final int decimalPlaces = this.totalPrincipalDisbursed.getCurrencyDigitsAfterDecimal(); - final CurrencyData currency = applicationCurrency.toData(decimalPlaces); + final Integer inMulitplesOf = this.totalPrincipalDisbursed.getCurrencyInMulitplesOf(); + final CurrencyData currency = applicationCurrency.toData(decimalPlaces,inMulitplesOf); final Collection periodsData = new ArrayList(); for (LoanScheduleModelPeriod modelPeriod : this.periods) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java index e2ddbd66d..628e5453d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java @@ -44,14 +44,19 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform private static final class LoanChargeMapper implements RowMapper { public String schema() { - return "lc.id as id, c.id as chargeId, c.name as name, " + "lc.amount as amountDue, " - + "lc.amount_paid_derived as amountPaid, " + "lc.amount_waived_derived as amountWaived, " - + "lc.amount_writtenoff_derived as amountWrittenOff, " + "lc.amount_outstanding_derived as amountOutstanding, " + return "lc.id as id, c.id as chargeId, c.name as name, " + + "lc.amount as amountDue, " + + "lc.amount_paid_derived as amountPaid, " + + "lc.amount_waived_derived as amountWaived, " + + "lc.amount_writtenoff_derived as amountWrittenOff, " + + "lc.amount_outstanding_derived as amountOutstanding, " + "lc.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, " - + "lc.charge_time_enum as chargeTime, " + "lc.is_penalty as penalty, " - + "lc.due_for_collection_as_of_date as dueAsOfDate, " + "lc.charge_calculation_enum as chargeCalculation, " + + "lc.charge_time_enum as chargeTime, " + + "lc.is_penalty as penalty, " + + "lc.due_for_collection_as_of_date as dueAsOfDate, " + + "lc.charge_calculation_enum as chargeCalculation, " + "c.currency_code as currencyCode, oc.name as currencyName, " - + "oc.decimal_places as currencyDecimalPlaces, oc.display_symbol as currencyDisplaySymbol, " + + "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMulitplesOf, oc.display_symbol as currencyDisplaySymbol, " + "oc.internationalized_name_code as currencyNameCode from m_charge c " + "join m_organisation_currency oc on c.currency_code = oc.code " + "join m_loan_charge lc on lc.charge_id = c.id "; } @@ -76,9 +81,10 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, currencyDisplaySymbol, - currencyNameCode); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); final int chargeTime = rs.getInt("chargeTime"); final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index d348e94bc..df508f5e9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -349,7 +349,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { context.authenticatedUser(); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); - return new LoanTransactionData(null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, null, null, null); + return new LoanTransactionData(null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, null, null, + null); } @Override @@ -409,7 +410,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.interest_calculated_in_period_enum as interestCalculationPeriodType," + " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, " + " lps.name as transactionStrategyName, " - + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMulitplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName, " + " l.principal_disbursed_derived as principalDisbursed," + " l.principal_repaid_derived as principalPaid," @@ -473,8 +474,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); final Long id = rs.getLong("id"); final String accountNo = rs.getString("accountNo"); @@ -593,7 +595,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final LocalDate interestChargedFromDate = JdbcSupport.getLocalDate(rs, "interestChargedFromDate"); final Boolean syncDisbursementWithMeeting = rs.getBoolean("syncDisbursementWithMeeting"); - + final BigDecimal feeChargesDueAtDisbursementCharged = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesDueAtDisbursementCharged"); LoanSummaryData loanSummary = null; @@ -655,18 +657,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { groupData = GroupGeneralData.instance(groupId, groupName, groupExternalId, groupStatus, activationDate, groupOfficeId, null, groupParentId, null, groupStaffId, null, groupHierarchy); } - + final Integer loanCounter = JdbcSupport.getInteger(rs, "loanCounter"); final Integer loanProductCounter = JdbcSupport.getInteger(rs, "loanProductCounter"); return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, principal, totalOverpaid, inArrearsTolerance, termFrequency, - termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId, transactionStrategyName , - amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, - interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, - graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged, - syncDisbursementWithMeeting, loanCounter, loanProductCounter); + termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId, + transactionStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, + interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, + graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary, + feeChargesDueAtDisbursementCharged, syncDisbursementWithMeeting, loanCounter, loanProductCounter); } } @@ -707,7 +709,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Collection periods = new ArrayList(); periods.add(disbursementPeriod); - final MonetaryCurrency monCurrency = new MonetaryCurrency(this.currency.code(), this.currency.decimalPlaces()); + final MonetaryCurrency monCurrency = new MonetaryCurrency(this.currency.code(), this.currency.decimalPlaces(), + this.currency.currencyInMulitplesOf()); final BigDecimal totalPrincipalDisbursed = Money.of(monCurrency, this.disbursement.amount()).getAmount(); Money totalPrincipalExpected = Money.zero(monCurrency); Money totalPrincipalPaid = Money.zero(monCurrency); @@ -843,9 +846,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, " + " pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, " + " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, " - + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, " + + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMulitplesOf, rc.`name` as currencyName, " + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " - + " cv.code_value as paymentTypeName, tr.external_id as externalId " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id" + + " cv.code_value as paymentTypeName, tr.external_id as externalId " + + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id" + " join m_currency rc on rc.`code` = l.currency_code " + " left JOIN m_payment_detail pd ON tr.payment_detail_id = pd.id" + " left join m_code_value cv on pd.payment_type_cv_id = cv.id"; @@ -859,8 +863,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); final Long id = rs.getLong("id"); final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index 95716c966..721159c50 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -99,8 +99,9 @@ public class LoanProduct extends AbstractPersistable { final String description = command.stringValueOfParameterNamed("description"); final String currencyCode = command.stringValueOfParameterNamed("currencyCode"); final Integer digitsAfterDecimal = command.integerValueOfParameterNamed("digitsAfterDecimal"); - - final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal); + final Integer inMulitplesOf = command.integerValueOfParameterNamed("inMulitplesOf"); + + final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); final BigDecimal principal = command.bigDecimalValueOfParameterNamed("principal"); final BigDecimal minPrincipal = command.bigDecimalValueOfParameterNamed("minPrincipal"); final BigDecimal maxPrincipal = command.bigDecimalValueOfParameterNamed("maxPrincipal"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java index 2bfce6916..3724556c8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java @@ -206,6 +206,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche String currencyCode = this.currency.getCode(); Integer digitsAfterDecimal = this.currency.getDigitsAfterDecimal(); + Integer inMulitplesOf = this.currency.getCurrencyInMulitplesOf(); final String digitsAfterDecimalParamName = "digitsAfterDecimal"; if (command.isChangeInIntegerParameterNamed(digitsAfterDecimalParamName, digitsAfterDecimal)) { @@ -213,7 +214,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche actualChanges.put(digitsAfterDecimalParamName, newValue); actualChanges.put("locale", localeAsInput); digitsAfterDecimal = newValue; - this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal); + this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); } final String currencyCodeParamName = "currencyCode"; @@ -221,9 +222,17 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final String newValue = command.stringValueOfParameterNamed(currencyCodeParamName); actualChanges.put(currencyCodeParamName, newValue); currencyCode = newValue; - this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal); + this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); } - + + final String inMulitplesOfParamName = "inMulitplesOf"; + if (command.isChangeInStringParameterNamed(inMulitplesOfParamName, currencyCode)) { + final Integer newValue = command.integerValueOfParameterNamed(inMulitplesOfParamName); + actualChanges.put(inMulitplesOfParamName, newValue); + inMulitplesOf = newValue; + this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); + } + final Map loanApplicationAttributeChanges = updateLoanApplicationAttributes(command, aprCalculator); actualChanges.putAll(loanApplicationAttributeChanges); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java index 3f0177aba..73d76a5d7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -39,7 +39,7 @@ public final class LoanProductDataValidator { * The parameters supported for this command. */ private final Set supportedParameters = new HashSet(Arrays.asList("locale", "dateFormat", "name", "description", "fundId", - "currencyCode", "digitsAfterDecimal", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments", + "currencyCode", "digitsAfterDecimal","inMulitplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments", "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod", "minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment", @@ -91,6 +91,9 @@ public final class LoanProductDataValidator { final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault()); baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); + + final Integer inMulitplesOf = fromApiJsonHelper.extractIntegerNamed("inMulitplesOf", element, Locale.getDefault()); + baseDataValidator.reset().parameter("inMulitplesOf").value(inMulitplesOf).ignoreIfNull().integerZeroOrGreater(); final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element); baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount(); @@ -334,6 +337,11 @@ public final class LoanProductDataValidator { final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault()); baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); } + + if (fromApiJsonHelper.parameterExists("inMulitplesOf", element)) { + final Integer inMulitplesOf = fromApiJsonHelper.extractIntegerNamed("inMulitplesOf", element, Locale.getDefault()); + baseDataValidator.reset().parameter("inMulitplesOf").value(inMulitplesOf).ignoreIfNull().integerZeroOrGreater(); + } String minPrincipalParameterName = "minPrincipal"; BigDecimal minPrincipalAmount = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index 7610b6db1..bbb1edf5a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -99,7 +99,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo public String loanProductSchema() { return "lp.id as id, lp.fund_id as fundId, f.name as fundName, lp.loan_transaction_strategy_id as transactionStrategyId, ltps.name as transactionStrategyName, " + "lp.name as name, lp.description as description, " - + "lp.principal_amount as principal, lp.min_principal_amount as minPrincipal, lp.max_principal_amount as maxPrincipal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, " + + "lp.principal_amount as principal, lp.min_principal_amount as minPrincipal, lp.max_principal_amount as maxPrincipal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, lp.currency_multiplesof as inMulitplesOf, " + "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, " + "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod," + "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, " @@ -129,9 +129,10 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); final BigDecimal principal = rs.getBigDecimal("principal"); final BigDecimal minPrincipal = rs.getBigDecimal("minPrincipal"); @@ -170,7 +171,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final int interestCalculationPeriodTypeId = JdbcSupport.getInteger(rs, "interestCalculationInPeriodMethod"); final EnumOptionData interestCalculationPeriodType = LoanEnumerations .interestCalculationPeriodType(interestCalculationPeriodTypeId); - + final boolean includeInBorrowerCycle = rs.getBoolean("includeInBorrowerCycle"); final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate"); final LocalDate closeDate = JdbcSupport.getLocalDate(rs, "closeDate"); @@ -181,13 +182,14 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo status = "loanProduct.active"; } final String externalId = rs.getString("externalId"); - + return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionStrategyId, transactionStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, - graceOnInterestCharged, this.charges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate, status, externalId); + graceOnInterestCharged, this.charges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate, status, + externalId); } } @@ -202,11 +204,11 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo return "lp.id as id, lp.name as name FROM m_product_loan lp left join m_product_mix pm on pm.product_id=lp.id where lp.id not IN(" + "select lp.id from m_product_loan lp inner join m_product_mix pm on pm.product_id=lp.id)"; } - + public String restrictedProductsSchema() { return "pm.restricted_product_id as id, rp.name as name from m_product_mix pm join m_product_loan rp on rp.id = pm.restricted_product_id "; } - + public String derivedRestrictedProductsSchema() { return "pm.product_id as id, lp.name as name from m_product_mix pm join m_product_loan lp on lp.id=pm.product_id"; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index cade9ee85..9d0dea3ca 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -63,6 +63,7 @@ public class SavingsApiConstants { public static final String descriptionParamName = "description"; public static final String currencyCodeParamName = "currencyCode"; public static final String digitsAfterDecimalParamName = "digitsAfterDecimal"; + public static final String inMulitplesOfParamName = "inMulitplesOf"; public static final String nominalAnnualInterestRateParamName = "nominalAnnualInterestRate"; public static final String interestCompoundingPeriodTypeParamName = "interestCompoundingPeriodType"; public static final String interestPostingPeriodTypeParamName = "interestPostingPeriodType"; @@ -93,11 +94,11 @@ public class SavingsApiConstants { public static final Set SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, monthDayFormatParamName, nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, - nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, - interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, - lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, - annualFeeAmountParamName, annualFeeOnMonthDayParamName, accountingRuleParamName, - SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), + inMulitplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, + interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, + minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, + withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName, annualFeeOnMonthDayParamName, + accountingRuleParamName, SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue())); @@ -108,13 +109,13 @@ public class SavingsApiConstants { * parameters to match those of request parameters. */ public static final Set SAVINGS_PRODUCT_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, - nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, nominalAnnualInterestRateParamName, - interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, - interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, - lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName, - annualFeeOnMonthDayParamName, "currencyOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", - "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", - "withdrawalFeeTypeOptions")); + nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, inMulitplesOfParamName, + nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, + interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, + lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, + annualFeeAmountParamName, annualFeeOnMonthDayParamName, "currencyOptions", "interestCompoundingPeriodTypeOptions", + "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", + "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions")); public static final Set SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, monthDayFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java index f900d1474..ed8023a4d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java @@ -12,6 +12,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeO import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMulitplesOfParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; @@ -87,6 +88,9 @@ public class SavingsProductDataValidator { final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerSansLocaleNamed(digitsAfterDecimalParamName, element); baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); + final Integer inMulitplesOf = fromApiJsonHelper.extractIntegerSansLocaleNamed(inMulitplesOfParamName, element); + baseDataValidator.reset().parameter(inMulitplesOfParamName).value(inMulitplesOf).ignoreIfNull().integerZeroOrGreater(); + final BigDecimal nominalAnnualInterestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, element); baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate).notNull() @@ -252,6 +256,11 @@ public class SavingsProductDataValidator { baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); } + if (this.fromApiJsonHelper.parameterExists(inMulitplesOfParamName, element)) { + final Integer inMulitplesOf = fromApiJsonHelper.extractIntegerSansLocaleNamed(inMulitplesOfParamName, element); + baseDataValidator.reset().parameter(inMulitplesOfParamName).value(inMulitplesOf).ignoreIfNull().integerZeroOrGreater(); + } + if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) { final BigDecimal interestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, element); baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(interestRate).notNull().zeroOrPositiveAmount(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java index 68eaaae63..3eefc93c1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java @@ -12,6 +12,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeO import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMulitplesOfParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; @@ -279,7 +280,7 @@ public class SavingsProduct extends AbstractPersistable { actualChanges.put(digitsAfterDecimalParamName, newValue); actualChanges.put(localeParamName, localeAsInput); digitsAfterDecimal = newValue; - this.currency = new MonetaryCurrency(this.currency.getCode(), digitsAfterDecimal); + this.currency = new MonetaryCurrency(this.currency.getCode(), digitsAfterDecimal,this.currency.getCurrencyInMulitplesOf()); } String currencyCode = this.currency.getCode(); @@ -287,7 +288,16 @@ public class SavingsProduct extends AbstractPersistable { final String newValue = command.stringValueOfParameterNamed(currencyCodeParamName); actualChanges.put(currencyCodeParamName, newValue); currencyCode = newValue; - this.currency = new MonetaryCurrency(currencyCode, this.currency.getDigitsAfterDecimal()); + this.currency = new MonetaryCurrency(currencyCode, this.currency.getDigitsAfterDecimal(),this.currency.getCurrencyInMulitplesOf()); + } + + Integer inMulitplesOf = this.currency.getCurrencyInMulitplesOf(); + if (command.isChangeInIntegerParameterNamed(inMulitplesOfParamName, inMulitplesOf)) { + final Integer newValue = command.integerValueOfParameterNamed(inMulitplesOfParamName); + actualChanges.put(inMulitplesOfParamName, newValue); + actualChanges.put(localeParamName, localeAsInput); + inMulitplesOf = newValue; + this.currency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDigitsAfterDecimal(),inMulitplesOf); } if (command.isChangeInBigDecimalParameterNamed(nominalAnnualInterestRateParamName, this.nominalAnnualInterestRate)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java index 0a01047c2..5cd7cfb84 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java @@ -10,6 +10,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeO import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMulitplesOfParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; @@ -46,7 +47,8 @@ public class SavingsProductAssembler { final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName); final Integer digitsAfterDecimal = command.integerValueOfParameterNamed(digitsAfterDecimalParamName); - MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal); + final Integer inMulitplesOf = command.integerValueOfParameterNamed(inMulitplesOfParamName); + MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); final BigDecimal interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 22078918a..7a0695664 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -203,7 +203,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("cbu.username as closedByUsername,"); sqlBuilder.append("cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,"); - sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, "); + sqlBuilder + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMulitplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); @@ -311,8 +312,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); final BigDecimal nominalAnnualInterestRate = rs.getBigDecimal("nominalAnnualInterestRate"); @@ -542,7 +544,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("sa.id as savingsId, sa.account_no as accountNo,"); sqlBuilder.append("pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "); sqlBuilder.append("pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, "); - sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, "); + sqlBuilder + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMulitplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("cv.code_value as paymentTypeName "); @@ -596,8 +599,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); AccountTransferData transfer = null; final Long fromTransferId = JdbcSupport.getLong(rs, "fromTransferId"); @@ -632,7 +636,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead public SavingsAccountTransactionTemplateMapper() { final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("sa.id as id, sa.account_no as accountNo, "); - sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, "); + sqlBuilder + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMulitplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("sa.min_required_opening_balance as minRequiredOpeningBalance "); @@ -657,8 +662,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); return SavingsAccountTransactionData.template(savingsId, accountNo, DateUtils.getLocalDateOfTenant(), currency); } @@ -677,7 +683,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("sp.id as productId, sp.name as productName, "); - sqlBuilder.append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, "); + sqlBuilder + .append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMulitplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("sp.nominal_annual_interest_rate as nominalAnnualIterestRate, "); @@ -714,8 +721,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); final BigDecimal nominalAnnualIterestRate = rs.getBigDecimal("nominalAnnualIterestRate"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java index a66c18f53..46f01275d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java @@ -75,7 +75,8 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead public SavingProductMapper() { final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("sp.id as id, sp.name as name, sp.description as description, "); - sqlBuilder.append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, "); + sqlBuilder + .append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMulitplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("sp.nominal_annual_interest_rate as nominalAnnualInterestRate, "); @@ -114,9 +115,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead String currencyNameCode = rs.getString("currencyNameCode"); String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); final BigDecimal nominalAnnualInterestRate = rs.getBigDecimal("nominalAnnualInterestRate"); final Integer compoundingInterestPeriodTypeValue = JdbcSupport.getInteger(rs, "compoundingInterestPeriodType"); diff --git a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/MonetaryCurrencyBuilder.java b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/MonetaryCurrencyBuilder.java index 1a4a26e5b..bb7d3199a 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/MonetaryCurrencyBuilder.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/MonetaryCurrencyBuilder.java @@ -6,9 +6,10 @@ public class MonetaryCurrencyBuilder { private String code = "XOF"; private int digitsAfterDecimal = 0; + private Integer inMulitplesOf = null; public MonetaryCurrency build() { - return new MonetaryCurrency(code, digitsAfterDecimal); + return new MonetaryCurrency(code, digitsAfterDecimal,inMulitplesOf); } public MonetaryCurrencyBuilder withCode(final String withCode) { From 662d26771ed4f020276163574932a57cf9087829 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Mon, 12 Aug 2013 17:48:20 +0530 Subject: [PATCH 32/47] Add basic saving summary reports for groups --- .../core_db/V100__Group_saving_summary_report.sql | 1 + .../portfolio/client/data/ClientDataValidator.java | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V100__Group_saving_summary_report.sql diff --git a/mifosng-db/migrations/core_db/V100__Group_saving_summary_report.sql b/mifosng-db/migrations/core_db/V100__Group_saving_summary_report.sql new file mode 100644 index 000000000..d98372bbf --- /dev/null +++ b/mifosng-db/migrations/core_db/V100__Group_saving_summary_report.sql @@ -0,0 +1 @@ +INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_subtype`, `report_category`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('GroupSavingSummary', 'Table', NULL, NULL, 'select ifnull(cur.display_symbol, sa.currency_code) as currency,\ncount(sa.id) as totalSavingAccounts, ifnull(sum(sa.account_balance_derived),0) as totalSavings\nfrom m_group topgroup\njoin m_office o on o.id = topgroup.office_id and o.hierarchy like concat(\'${currentUserHierarchy}\', \'%\')\njoin m_group g on g.hierarchy like concat(topgroup.hierarchy, \'%\')\njoin m_savings_account sa on sa.group_id = g.id\nleft join m_currency cur on cur.code = sa.currency_code\nwhere topgroup.id = ${groupId}\nand sa.activatedon_date is not null\ngroup by sa.currency_code', 'Utility query for getting group or center saving summary details for a group_id', 1, 0); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java index 62f27efd0..9a3ca297c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java @@ -5,8 +5,6 @@ */ package org.mifosplatform.portfolio.client.data; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.activatedOnDateParamName; - import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; @@ -314,8 +312,8 @@ public final class ClientDataValidator { final JsonElement element = command.parsedJson(); - final LocalDate activationDate = this.fromApiJsonHelper.extractLocalDateNamed(activatedOnDateParamName, element); - baseDataValidator.reset().parameter(activatedOnDateParamName).value(activationDate).notNull(); + final LocalDate activationDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.activationDateParamName).value(activationDate).notNull(); throwExceptionIfValidationWarningsExist(dataValidationErrors); } From 5d8cd54dfbc57b9883f0184ed5f861be6b35bb4c Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Mon, 12 Aug 2013 15:23:36 +0100 Subject: [PATCH 33/47] MIFOSX-601: fix issue with negative interest been calculated due to incorrect calculation of end of day balance. --- .../portfolio/savings/domain/SavingsAccountTransaction.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index 4dce66684..edae019c0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -277,7 +277,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable { if (isDeposit()) { endOfDayBalance = openingBalance.plus(getAmount(currency)); } else if (isWithdrawal() || isWithdrawalFeeAndNotReversed()) { - endOfDayBalance = openingBalance.minus(getAmount(currency)); + + if (openingBalance.isGreaterThanZero()) { + endOfDayBalance = openingBalance.minus(getAmount(currency)); + } } return EndOfDayBalance.from(getTransactionLocalDate(), openingBalance, endOfDayBalance, this.balanceNumberOfDays); From 5df9b37c69f490b8f23a94679319aab5ad50bed7 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Mon, 12 Aug 2013 22:26:04 +0530 Subject: [PATCH 34/47] cleaning up sql file numbers --- ...ettings_for_holiday_and_non_workingday.sql | 2 + .../core_db/V95__batch_job_postInterest.sql | 1 + .../V96__savings_accounts_transfers_table.sql | 56 +++++++++++++++++++ ...mission_for_adjust_savings_transaction.sql | 1 + ..._added_currency_roundof_for_multipleof.sql | 12 ++++ 5 files changed, 72 insertions(+) create mode 100644 mifosng-db/migrations/core_db/V91_1__configuration_settings_for_holiday_and_non_workingday.sql create mode 100644 mifosng-db/migrations/core_db/V95__batch_job_postInterest.sql create mode 100644 mifosng-db/migrations/core_db/V96__savings_accounts_transfers_table.sql create mode 100644 mifosng-db/migrations/core_db/V97__add_permission_for_adjust_savings_transaction.sql create mode 100644 mifosng-db/migrations/core_db/V98__added_currency_roundof_for_multipleof.sql diff --git a/mifosng-db/migrations/core_db/V91_1__configuration_settings_for_holiday_and_non_workingday.sql b/mifosng-db/migrations/core_db/V91_1__configuration_settings_for_holiday_and_non_workingday.sql new file mode 100644 index 000000000..0b84ea2b0 --- /dev/null +++ b/mifosng-db/migrations/core_db/V91_1__configuration_settings_for_holiday_and_non_workingday.sql @@ -0,0 +1,2 @@ +INSERT INTO `c_configuration` (`name`, `enabled`) VALUES ('allow-transactions-on-holiday', 0); +INSERT INTO `c_configuration` (`name`, `enabled`) VALUES ('allow-transactions-on-non_workingday', 0); \ No newline at end of file diff --git a/mifosng-db/migrations/core_db/V95__batch_job_postInterest.sql b/mifosng-db/migrations/core_db/V95__batch_job_postInterest.sql new file mode 100644 index 000000000..58fda9d28 --- /dev/null +++ b/mifosng-db/migrations/core_db/V95__batch_job_postInterest.sql @@ -0,0 +1 @@ +INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`) VALUES ('Post Interest For Savings', 'Post Interest For Savings', '0 0 0 1/1 * ? *', now(), 5, NULL, NULL, NULL, NULL, NULL, 1, 0, 1, 0); diff --git a/mifosng-db/migrations/core_db/V96__savings_accounts_transfers_table.sql b/mifosng-db/migrations/core_db/V96__savings_accounts_transfers_table.sql new file mode 100644 index 000000000..425b17b4a --- /dev/null +++ b/mifosng-db/migrations/core_db/V96__savings_accounts_transfers_table.sql @@ -0,0 +1,56 @@ +INSERT INTO `m_permission` +(`grouping`,`code`,`entity_name`,`action_name`,`can_maker_checker`) +VALUES +('transaction_savings', 'READ_ACCOUNTTRANSFER', 'ACCOUNTTRANSFER', 'READ', 0), +('transaction_savings', 'CREATE_ACCOUNTTRANSFER', 'ACCOUNTTRANSFER', 'CREATE', 1), +('transaction_savings', 'CREATE_ACCOUNTTRANSFER_CHECKER', 'ACCOUNTTRANSFER', 'CREATE', 0); + + +DROP TABLE IF EXISTS `m_savings_account_transfer`; + +CREATE TABLE `m_savings_account_transfer` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `from_office_id` bigint(20) NOT NULL, + `to_office_id` bigint(20) NOT NULL, + `from_client_id` bigint(20) NOT NULL, + `to_client_id` bigint(20) NOT NULL, + `from_savings_account_id` bigint(20) DEFAULT NULL, + `to_savings_account_id` bigint(20) DEFAULT NULL, + `from_loan_account_id` bigint(20) DEFAULT NULL, + `to_loan_account_id` bigint(20) DEFAULT NULL, + `from_savings_transaction_id` bigint(20) DEFAULT NULL, + `from_loan_transaction_id` bigint(20) DEFAULT NULL, + `to_savings_transaction_id` bigint(20) DEFAULT NULL, + `to_loan_transaction_id` bigint(20) DEFAULT NULL, + `is_reversed` tinyint(1) NOT NULL, + `transaction_date` date NOT NULL, + `currency_code` varchar(3) NOT NULL, + `currency_digits` smallint(5) NOT NULL, + `amount` decimal(19,6) NOT NULL, + `description` varchar(200) NOT NULL, + PRIMARY KEY (`id`), + KEY `FKTRAN000000001` (`from_office_id`), + KEY `FKTRAN000000002` (`from_client_id`), + KEY `FKTRAN000000003` (`from_savings_account_id`), + KEY `FKTRAN000000004` (`to_office_id`), + KEY `FKTRAN000000005` (`to_client_id`), + KEY `FKTRAN000000006` (`to_savings_account_id`), + KEY `FKTRAN000000007` (`to_loan_account_id`), + KEY `FKTRAN000000008` (`from_savings_transaction_id`), + KEY `FKTRAN000000009` (`to_savings_transaction_id`), + KEY `FKTRAN000000010` (`to_loan_transaction_id`), + KEY `FKTRAN000000011` (`from_loan_account_id`), + KEY `FKTRAN000000012` (`from_loan_transaction_id`), + CONSTRAINT `FKTRAN000000001` FOREIGN KEY (`from_office_id`) REFERENCES `m_office` (`id`), + CONSTRAINT `FKTRAN000000002` FOREIGN KEY (`from_client_id`) REFERENCES `m_client` (`id`), + CONSTRAINT `FKTRAN000000003` FOREIGN KEY (`from_savings_account_id`) REFERENCES `m_savings_account` (`id`), + CONSTRAINT `FKTRAN000000004` FOREIGN KEY (`to_office_id`) REFERENCES `m_office` (`id`), + CONSTRAINT `FKTRAN000000005` FOREIGN KEY (`to_client_id`) REFERENCES `m_client` (`id`), + CONSTRAINT `FKTRAN000000006` FOREIGN KEY (`to_savings_account_id`) REFERENCES `m_savings_account` (`id`), + CONSTRAINT `FKTRAN000000007` FOREIGN KEY (`to_loan_account_id`) REFERENCES `m_loan` (`id`), + CONSTRAINT `FKTRAN000000008` FOREIGN KEY (`from_savings_transaction_id`) REFERENCES `m_savings_account_transaction` (`id`), + CONSTRAINT `FKTRAN000000009` FOREIGN KEY (`to_savings_transaction_id`) REFERENCES `m_savings_account_transaction` (`id`), + CONSTRAINT `FKTRAN000000010` FOREIGN KEY (`to_loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`), + CONSTRAINT `FKTRAN000000011` FOREIGN KEY (`from_loan_account_id`) REFERENCES `m_loan` (`id`), + CONSTRAINT `FKTRAN000000012` FOREIGN KEY (`from_loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/mifosng-db/migrations/core_db/V97__add_permission_for_adjust_savings_transaction.sql b/mifosng-db/migrations/core_db/V97__add_permission_for_adjust_savings_transaction.sql new file mode 100644 index 000000000..906bb1497 --- /dev/null +++ b/mifosng-db/migrations/core_db/V97__add_permission_for_adjust_savings_transaction.sql @@ -0,0 +1 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_savings', 'ADJUSTTRANSACTION_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'ADJUSTTRANSACTION', 0); \ No newline at end of file diff --git a/mifosng-db/migrations/core_db/V98__added_currency_roundof_for_multipleof.sql b/mifosng-db/migrations/core_db/V98__added_currency_roundof_for_multipleof.sql new file mode 100644 index 000000000..571335a04 --- /dev/null +++ b/mifosng-db/migrations/core_db/V98__added_currency_roundof_for_multipleof.sql @@ -0,0 +1,12 @@ +ALTER TABLE `m_savings_account` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `currency_digits`; +ALTER TABLE `m_savings_product` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `currency_digits`; +ALTER TABLE `m_loan` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `currency_digits`; +ALTER TABLE `m_product_loan` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `currency_digits`; +ALTER TABLE `m_currency` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `decimal_places`; +ALTER TABLE `m_organisation_currency` + ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL AFTER `decimal_places`; \ No newline at end of file From a0b2feb8729b85861cb8f865f012b4df0766fb3c Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Mon, 12 Aug 2013 22:42:32 +0530 Subject: [PATCH 35/47] dev branch prepped for 1.8.0 release --- mifosng-provider/gradle.properties | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mifosng-provider/gradle.properties b/mifosng-provider/gradle.properties index 39e7b9f7f..9b500178d 100644 --- a/mifosng-provider/gradle.properties +++ b/mifosng-provider/gradle.properties @@ -1,5 +1,2 @@ -<<<<<<< HEAD releaseVersion=1.8.0.DEVELOP -======= -releaseVersion=1.7.1.RELEASE ->>>>>>> master + From a62b32833c6ba4451dda12b3671eb517530248e4 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Tue, 13 Aug 2013 14:59:39 +0530 Subject: [PATCH 36/47] changed the rounding value while adjusting amounts --- .../loanaccount/loanschedule/domain/LoanApplicationTerms.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java index f613b0d06..9b647a044 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java @@ -174,7 +174,7 @@ public final class LoanApplicationTerms { // adjust cumulative fields for principal & interest if needed if (this.principal.getAmount().subtract(totalCumulativePrincipalToDate.getAmount()).add(principalForPeriod.getAmount()).compareTo(principalForPeriod.getAmount()) == -1) { adjusted = adjusted.minus(adjusted); - MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, principalForPeriod.getAmount().intValue()); + MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, adjusted.getCurrencyInMulitplesOf()); BigDecimal diff = this.principal.getAmount().subtract(totalCumulativePrincipalToDate.getAmount()).add(principalForPeriod.getAmount()); if(diff.doubleValue()<0){ diff = BigDecimal.ZERO; @@ -200,7 +200,7 @@ public final class LoanApplicationTerms { if (totalInterestDueForLoan.getAmount().subtract(totalCumulativeInterestToDate.getAmount()).add(interestForThisPeriod.getAmount()) .compareTo(interestForThisPeriod.getAmount()) == -1) { adjusted = adjusted.minus(adjusted); - MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, interestForThisPeriod.getAmount().intValue()); + MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, adjusted.getCurrencyInMulitplesOf()); BigDecimal diff = totalInterestDueForLoan.getAmount().subtract(totalCumulativeInterestToDate.getAmount()).add(interestForThisPeriod.getAmount()); if(diff.doubleValue()<0){ diff = BigDecimal.ZERO; From 9956e387ce877723b6c81330f040c404bafa95bc Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Tue, 13 Aug 2013 15:52:05 +0530 Subject: [PATCH 37/47] corrected typo errors --- api-docs/apiLive.htm | 22 ++++++++-------- .../monetary/data/CurrencyData.java | 10 +++---- .../monetary/domain/ApplicationCurrency.java | 24 ++++++++--------- .../ApplicationCurrencyRepositoryWrapper.java | 2 +- .../monetary/domain/MonetaryCurrency.java | 14 +++++----- .../organisation/monetary/domain/Money.java | 26 +++++++++---------- .../CurrencyReadPlatformServiceImpl.java | 6 ++--- .../api/OfficeTransactionsApiResource.java | 2 +- .../office/domain/OrganisationCurrency.java | 8 +++--- .../OfficeReadPlatformServiceImpl.java | 6 ++--- ...WritePlatformServiceJpaRepositoryImpl.java | 2 +- ...sReadPlatformServiceJpaRepositoryImpl.java | 6 ++--- ...countTransfersReadPlatformServiceImpl.java | 6 ++--- .../ChargeReadPlatformServiceImpl.java | 6 ++--- .../CollateralReadPlatformServiceImpl.java | 6 ++--- ...ollectionSheetReadPlatformServiceImpl.java | 6 ++--- .../domain/LoanApplicationTerms.java | 6 ++--- .../domain/LoanScheduleModel.java | 4 +-- .../LoanChargeReadPlatformServiceImpl.java | 6 ++--- .../service/LoanReadPlatformServiceImpl.java | 14 +++++----- .../loanproduct/domain/LoanProduct.java | 4 +-- .../domain/LoanProductRelatedDetail.java | 18 ++++++------- .../LoanProductDataValidator.java | 12 ++++----- .../LoanProductReadPlatformServiceImpl.java | 6 ++--- .../savings/SavingsApiConstants.java | 6 ++--- .../data/SavingsProductDataValidator.java | 12 ++++----- .../savings/domain/SavingsProduct.java | 18 ++++++------- .../domain/SavingsProductAssembler.java | 6 ++--- ...SavingsAccountReadPlatformServiceImpl.java | 24 ++++++++--------- ...SavingsProductReadPlatformServiceImpl.java | 6 ++--- .../loanaccount/MonetaryCurrencyBuilder.java | 4 +-- 31 files changed, 149 insertions(+), 149 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index f9e121a9e..f51f579e2 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -5882,7 +5882,7 @@ GET https://DomainName/api/v1/loans/{loanId}/transactions/template" "total": { "currencyCode": "XOF", "digitsAfterDecimal": 0, - "inMulitplesOf": 0, + "inMultiplesOf": 0, "amount": 471, "defaultName": "CFA Franc BCEAO", "nameCode": "currency.XOF", @@ -7429,7 +7429,7 @@ Request Body: Override the currency default value for digitsAfterDecimal. - inMulitplesOf + inMultiplesOf Override the default value for rounding currency to multiples of value provided. @@ -8115,7 +8115,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
Mandatory Fields
- name, currencyCode, digitsAfterDecimal, inMulitplesOf, principal, + name, currencyCode, digitsAfterDecimal, inMultiplesOf, principal, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyId, @@ -8160,7 +8160,7 @@ Request Body: "currencyCode": "USD", "locale": "en_GB", "digitsAfterDecimal": "2", - "inMulitplesOf": 0, + "inMultiplesOf": 0, "principal": "100,000.00", "numberOfRepayments": "12", "repaymentEvery": "1", @@ -8190,7 +8190,7 @@ Request Body: "currencyCode": "USD", "locale": "en_GB", "digitsAfterDecimal": "2", - "inMulitplesOf": 0, + "inMultiplesOf": 0, "principal": "100,000.00", "numberOfRepayments": "12", "repaymentEvery": "1", @@ -8247,7 +8247,7 @@ Request Body: "currencyCode": "USD", "locale": "en_GB", "digitsAfterDecimal": "2", - "inMulitplesOf": 0, + "inMultiplesOf": 0, "principal": "100,000.00", "numberOfRepayments": "12", "repaymentEvery": "1", @@ -11120,7 +11120,7 @@ Content-Type: application/json digitsAfterDecimal Override the currency default value for digitsAfterDecimal. - inMulitplesOf + inMultiplesOf Override the default value for rounding currency to multiples of provided value. nominalAnnualInterestRate @@ -11669,7 +11669,7 @@ Content-Type: application/json -
Mandatory Fields
name, description, currencyCode, digitsAfterDecimal,inMulitplesOf, nominalAnnualInterestRate, + name, description, currencyCode, digitsAfterDecimal,inMultiplesOf, nominalAnnualInterestRate, interestCompoundingPeriodType, interestCalculationType, interestCalculationDaysInYearType,accountingRule
@@ -11696,7 +11696,7 @@ Request Body: "description": "Daily compounding using Daily Balance, 5% per year, 365 days in year", "currencyCode": "USD", "digitsAfterDecimal": 2, - "inMulitplesOf": 0, + "inMultiplesOf": 0, "locale": "en", "nominalAnnualInterestRate": "5.0", "interestCompoundingPeriodType": 1, @@ -11720,7 +11720,7 @@ Request Body: "description": "A savings product", "currencyCode": "USD", "digitsAfterDecimal": "2", - "inMulitplesOf": 0, + "inMultiplesOf": 0, "nominalAnnualInterestRate": "15", "interestCompoundingPeriodType": "1", "interestPostingPeriodType": "4", @@ -12944,7 +12944,7 @@ No Request Body: "code": "USD", "name": "US Dollar", "decimalPlaces": 2, - "inMulitplesOf": 0, + "inMultiplesOf": 0, "displaySymbol": "$", "nameCode": "currency.USD", "displayLabel": "US Dollar ($)" diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/data/CurrencyData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/data/CurrencyData.java index 9e321e7c2..52730b434 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/data/CurrencyData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/data/CurrencyData.java @@ -13,7 +13,7 @@ public class CurrencyData { private final String code; private final String name; private final int decimalPlaces; - private final Integer inMulitplesOf; + private final Integer inMultiplesOf; private final String displaySymbol; @SuppressWarnings("unused") private final String nameCode; @@ -24,11 +24,11 @@ public class CurrencyData { return new CurrencyData("", "", 0,0, "", ""); } - public CurrencyData(final String code, final String name, final int decimalPlaces,final Integer inMulitplesOf, final String displaySymbol, final String nameCode) { + public CurrencyData(final String code, final String name, final int decimalPlaces,final Integer inMultiplesOf, final String displaySymbol, final String nameCode) { this.code = code; this.name = name; this.decimalPlaces = decimalPlaces; - this.inMulitplesOf = inMulitplesOf; + this.inMultiplesOf = inMultiplesOf; this.displaySymbol = displaySymbol; this.nameCode = nameCode; this.displayLabel = generateDisplayLabel(); @@ -42,8 +42,8 @@ public class CurrencyData { return this.decimalPlaces; } - public Integer currencyInMulitplesOf() { - return this.inMulitplesOf; + public Integer currencyInMultiplesOf() { + return this.inMultiplesOf; } @Override diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java index 270c57acf..e7441e151 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java @@ -24,7 +24,7 @@ public class ApplicationCurrency extends AbstractPersistable { private final Integer decimalPlaces; @Column(name = "currency_multiplesof") - private final Integer inMulitplesOf; + private final Integer inMultiplesOf; @Column(name = "name", nullable = false, length = 50) private final String name; @@ -39,22 +39,22 @@ public class ApplicationCurrency extends AbstractPersistable { this.code = null; this.name = null; this.decimalPlaces = null; - this.inMulitplesOf = null; + this.inMultiplesOf = null; this.nameCode = null; this.displaySymbol = null; } - public static ApplicationCurrency from(final ApplicationCurrency currency, final int decimalPlaces, final Integer inMulitplesOf) { - return new ApplicationCurrency(currency.code, currency.name, decimalPlaces, inMulitplesOf, currency.nameCode, + public static ApplicationCurrency from(final ApplicationCurrency currency, final int decimalPlaces, final Integer inMultiplesOf) { + return new ApplicationCurrency(currency.code, currency.name, decimalPlaces, inMultiplesOf, currency.nameCode, currency.displaySymbol); } - private ApplicationCurrency(final String code, final String name, final int decimalPlaces, final Integer inMulitplesOf, + private ApplicationCurrency(final String code, final String name, final int decimalPlaces, final Integer inMultiplesOf, final String nameCode, final String displaySymbol) { this.code = code; this.name = name; this.decimalPlaces = decimalPlaces; - this.inMulitplesOf = inMulitplesOf; + this.inMultiplesOf = inMultiplesOf; this.nameCode = nameCode; this.displaySymbol = displaySymbol; } @@ -71,8 +71,8 @@ public class ApplicationCurrency extends AbstractPersistable { return this.decimalPlaces; } - public Integer getCurrencyInMulitplesOf() { - return this.inMulitplesOf; + public Integer getCurrencyInMultiplesOf() { + return this.inMultiplesOf; } public String getNameCode() { @@ -84,14 +84,14 @@ public class ApplicationCurrency extends AbstractPersistable { } public CurrencyData toData() { - return new CurrencyData(this.code, this.name, this.decimalPlaces, this.inMulitplesOf, this.displaySymbol, this.nameCode); + return new CurrencyData(this.code, this.name, this.decimalPlaces, this.inMultiplesOf, this.displaySymbol, this.nameCode); } - public CurrencyData toData(final int digitsAfterDecimalSupported, final Integer inMulitplesOf) { - return new CurrencyData(this.code, this.name, digitsAfterDecimalSupported, inMulitplesOf, this.displaySymbol, this.nameCode); + public CurrencyData toData(final int digitsAfterDecimalSupported, final Integer inMultiplesOf) { + return new CurrencyData(this.code, this.name, digitsAfterDecimalSupported, inMultiplesOf, this.displaySymbol, this.nameCode); } public OrganisationCurrency toOrganisationCurrency() { - return new OrganisationCurrency(this.code, this.name, this.decimalPlaces,this.inMulitplesOf, this.nameCode, this.displaySymbol); + return new OrganisationCurrency(this.code, this.name, this.decimalPlaces,this.inMultiplesOf, this.nameCode, this.displaySymbol); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrencyRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrencyRepositoryWrapper.java index 313ed0683..009cf6ee9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrencyRepositoryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrencyRepositoryWrapper.java @@ -38,7 +38,7 @@ public class ApplicationCurrencyRepositoryWrapper { if (defaultApplicationCurrency == null) { throw new CurrencyNotFoundException(currency.getCode()); } final ApplicationCurrency applicationCurrency = ApplicationCurrency.from(defaultApplicationCurrency, - currency.getDigitsAfterDecimal(),currency.getCurrencyInMulitplesOf()); + currency.getDigitsAfterDecimal(),currency.getCurrencyInMultiplesOf()); return applicationCurrency; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MonetaryCurrency.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MonetaryCurrency.java index 7f5bd0f99..a34e45b46 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MonetaryCurrency.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MonetaryCurrency.java @@ -18,22 +18,22 @@ public class MonetaryCurrency { private final int digitsAfterDecimal; @Column(name = "currency_multiplesof") - private final Integer inMulitplesOf; + private final Integer inMultiplesOf; protected MonetaryCurrency() { this.code = null; this.digitsAfterDecimal = 0; - this.inMulitplesOf = 0; + this.inMultiplesOf = 0; } - public MonetaryCurrency(final String code, final int digitsAfterDecimal,final Integer inMulitplesOf) { + public MonetaryCurrency(final String code, final int digitsAfterDecimal,final Integer inMultiplesOf) { this.code = code; this.digitsAfterDecimal = digitsAfterDecimal; - this.inMulitplesOf = inMulitplesOf; + this.inMultiplesOf = inMultiplesOf; } public MonetaryCurrency copy() { - return new MonetaryCurrency(this.code, this.digitsAfterDecimal,this.inMulitplesOf); + return new MonetaryCurrency(this.code, this.digitsAfterDecimal,this.inMultiplesOf); } public String getCode() { @@ -44,7 +44,7 @@ public class MonetaryCurrency { return digitsAfterDecimal; } - public Integer getCurrencyInMulitplesOf() { - return inMulitplesOf; + public Integer getCurrencyInMultiplesOf() { + return inMultiplesOf; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java index 13c71263c..ac3be1fae 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java @@ -22,7 +22,7 @@ public class Money implements Comparable { private final int currencyDigitsAfterDecimal; @Column(name = "currency_multiplesof") - private final Integer inMulitplesOf; + private final Integer inMultiplesOf; @Column(name = "amount", scale = 6, precision = 19) private final BigDecimal amount; @@ -48,34 +48,34 @@ public class Money implements Comparable { public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount) { return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), defaultToZeroIfNull(newAmount), - currency.getCurrencyInMulitplesOf()); + currency.getCurrencyInMultiplesOf()); } public static Money zero(final MonetaryCurrency currency) { - return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), BigDecimal.ZERO, currency.getCurrencyInMulitplesOf()); + return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), BigDecimal.ZERO, currency.getCurrencyInMultiplesOf()); } protected Money() { this.currencyCode = null; this.currencyDigitsAfterDecimal = 0; - this.inMulitplesOf = 0; + this.inMultiplesOf = 0; this.amount = null; } - private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount, final Integer inMulitplesOf) { + private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount, final Integer inMultiplesOf) { this.currencyCode = currencyCode; this.currencyDigitsAfterDecimal = digitsAfterDecimal; - this.inMulitplesOf = inMulitplesOf; + this.inMultiplesOf = inMultiplesOf; final BigDecimal amountZeroed = defaultToZeroIfNull(amount); final BigDecimal amountStripped = amountZeroed.stripTrailingZeros(); final BigDecimal amountScaled = amountStripped.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN); // round monetary amounts into multiplesof say 20/50. - if (inMulitplesOf!= null && this.currencyDigitsAfterDecimal == 0 && inMulitplesOf>0 && amountScaled.doubleValue()>0) { + if (inMultiplesOf!= null && this.currencyDigitsAfterDecimal == 0 && inMultiplesOf>0 && amountScaled.doubleValue()>0) { double existingVal = amountScaled.doubleValue(); - double ceilingOfValue = ceiling(existingVal, inMulitplesOf); - double floorOfValue = floor(existingVal, inMulitplesOf); + double ceilingOfValue = ceiling(existingVal, inMultiplesOf); + double floorOfValue = floor(existingVal, inMultiplesOf); double floorDiff = existingVal - floorOfValue; double ceilDiff = ceilingOfValue - existingVal; @@ -123,7 +123,7 @@ public class Money implements Comparable { } public Money copy() { - return new Money(this.currencyCode, this.currencyDigitsAfterDecimal, this.amount.stripTrailingZeros(), this.inMulitplesOf); + return new Money(this.currencyCode, this.currencyDigitsAfterDecimal, this.amount.stripTrailingZeros(), this.inMultiplesOf); } public Money plus(final Iterable moniesToAdd) { @@ -267,8 +267,8 @@ public class Money implements Comparable { return this.currencyDigitsAfterDecimal; } - public int getCurrencyInMulitplesOf() { - return this.inMulitplesOf; + public int getCurrencyInMultiplesOf() { + return this.inMultiplesOf; } public BigDecimal getAmount() { @@ -306,7 +306,7 @@ public class Money implements Comparable { } private MonetaryCurrency monetaryCurrency() { - return new MonetaryCurrency(this.currencyCode, this.currencyDigitsAfterDecimal, this.inMulitplesOf); + return new MonetaryCurrency(this.currencyCode, this.currencyDigitsAfterDecimal, this.inMultiplesOf); } public Money zero() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/service/CurrencyReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/service/CurrencyReadPlatformServiceImpl.java index 475ba34e4..b594d8358 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/service/CurrencyReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/service/CurrencyReadPlatformServiceImpl.java @@ -58,15 +58,15 @@ public class CurrencyReadPlatformServiceImpl implements CurrencyReadPlatformServ final String code = rs.getString("code"); final String name = rs.getString("name"); final int decimalPlaces = JdbcSupport.getInteger(rs, "decimalPlaces"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); final String displaySymbol = rs.getString("displaySymbol"); final String nameCode = rs.getString("nameCode"); - return new CurrencyData(code, name, decimalPlaces, inMulitplesOf, displaySymbol, nameCode); + return new CurrencyData(code, name, decimalPlaces, inMultiplesOf, displaySymbol, nameCode); } public String schema() { - return " c.code as code, c.name as name, c.decimal_places as decimalPlaces,c.currency_multiplesof as inMulitplesOf, c.display_symbol as displaySymbol, c.internationalized_name_code as nameCode "; + return " c.code as code, c.name as name, c.decimal_places as decimalPlaces,c.currency_multiplesof as inMultiplesOf, c.display_symbol as displaySymbol, c.internationalized_name_code as nameCode "; } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficeTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficeTransactionsApiResource.java index babdcbddf..7bfffb2e6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficeTransactionsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/api/OfficeTransactionsApiResource.java @@ -41,7 +41,7 @@ import org.springframework.stereotype.Component; public class OfficeTransactionsApiResource { private static final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "transactionDate", "fromOfficeId", - "fromOfficeName", "toOfficeId", "toOfficeIdName", "currencyCode", "digitsAfterDecimal","inMulitplesOf", "transactionAmount", "description", + "fromOfficeName", "toOfficeId", "toOfficeIdName", "currencyCode", "digitsAfterDecimal","inMultiplesOf", "transactionAmount", "description", "allowedOffices", "currencyOptions")); private final String resourceNameForReadPermissions = "OFFICE"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java index 17204a9d3..e31c19c6f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java @@ -28,7 +28,7 @@ public class OrganisationCurrency extends AbstractPersistable { @SuppressWarnings("unused") @Column(name = "currency_multiplesof") - private final Integer inMulitplesOf; + private final Integer inMultiplesOf; @SuppressWarnings("unused") @Column(name = "name", nullable = false, length = 50) @@ -46,17 +46,17 @@ public class OrganisationCurrency extends AbstractPersistable { this.code = null; this.name = null; this.decimalPlaces = null; - this.inMulitplesOf = null; + this.inMultiplesOf = null; this.nameCode = null; this.displaySymbol = null; } - public OrganisationCurrency(final String code, final String name, final int decimalPlaces, final Integer inMulitplesOf, + public OrganisationCurrency(final String code, final String name, final int decimalPlaces, final Integer inMultiplesOf, final String nameCode, final String displaySymbol) { this.code = code; this.name = name; this.decimalPlaces = decimalPlaces; - this.inMulitplesOf = inMulitplesOf; + this.inMultiplesOf = inMultiplesOf; this.nameCode = nameCode; this.displaySymbol = displaySymbol; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java index af7d04979..c0675c4e4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeReadPlatformServiceImpl.java @@ -90,7 +90,7 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService public String schema() { return " ot.id as id, ot.transaction_date as transactionDate, ot.from_office_id as fromOfficeId, fromoff.name as fromOfficeName, " + " ot.to_office_id as toOfficeId, tooff.name as toOfficeName, ot.transaction_amount as transactionAmount, ot.description as description, " - + " ot.currency_code as currencyCode, rc.decimal_places as currencyDigits, rc.currency_multiplesof as inMulitplesOf, " + + " ot.currency_code as currencyCode, rc.decimal_places as currencyDigits, rc.currency_multiplesof as inMultiplesOf, " + " rc.name as currencyName, rc.internationalized_name_code as currencyNameCode, rc.display_symbol as currencyDisplaySymbol " + " from m_office_transaction ot " + " left join m_office fromoff on fromoff.id = ot.from_office_id " @@ -112,9 +112,9 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService String currencyNameCode = rs.getString("currencyNameCode"); String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); BigDecimal transactionAmount = rs.getBigDecimal("transactionAmount"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java index 840b02ffc..111ebcc86 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java @@ -159,7 +159,7 @@ public class OfficeWritePlatformServiceJpaRepositoryImpl implements OfficeWriteP final String currencyCode = command.stringValueOfParameterNamed("currencyCode"); final ApplicationCurrency appCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currencyCode); - final MonetaryCurrency currency = new MonetaryCurrency(appCurrency.getCode(), appCurrency.getDecimalPlaces(),appCurrency.getCurrencyInMulitplesOf()); + final MonetaryCurrency currency = new MonetaryCurrency(appCurrency.getCode(), appCurrency.getDecimalPlaces(),appCurrency.getCurrencyInMultiplesOf()); final Money amount = Money.of(currency, command.bigDecimalValueOfParameterNamed("transactionAmount")); final OfficeTransaction entity = OfficeTransaction.fromJson(fromOffice, toOffice, amount, command); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java index ebecb4411..4952e81d8 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java @@ -108,7 +108,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou accountsSummary.append("sa.account_type_enum as accountType, "); accountsSummary.append("sa.account_balance_derived as accountBalance, "); accountsSummary - .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMulitplesOf, "); + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); accountsSummary.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); accountsSummary.append("curr.display_symbol as currencyDisplaySymbol, "); accountsSummary.append("sa.product_id as productId, p.name as productName "); @@ -142,8 +142,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, status, currency, accountBalance, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java index 91d662764..053e7ab66 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java @@ -212,7 +212,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers sqlBuilder.append("sat.transaction_date as transferDate, sat.amount as transferAmount,"); sqlBuilder.append("sat.description as transferDescription,"); sqlBuilder - .append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits, sat.currency_multiplesof as inMulitplesOf, "); + .append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits, sat.currency_multiplesof as inMultiplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); @@ -258,8 +258,8 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeReadPlatformServiceImpl.java index a18b36541..8a20c0a55 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeReadPlatformServiceImpl.java @@ -129,7 +129,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService return "c.id as id, c.name as name, c.amount as amount, c.currency_code as currencyCode, " + "c.charge_applies_to_enum as chargeAppliesTo, c.charge_time_enum as chargeTime, " + "c.charge_calculation_enum as chargeCalculation, c.is_penalty as penalty, c.is_active as active, oc.name as currencyName, " - + "oc.decimal_places as currencyDecimalPlaces,oc.currency_multiplesof as inMulitplesOf, oc.display_symbol as currencyDisplaySymbol, " + + "oc.decimal_places as currencyDecimalPlaces,oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, " + "oc.internationalized_name_code as currencyNameCode from m_charge c " + "join m_organisation_currency oc on c.currency_code = oc.code"; } @@ -149,9 +149,9 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMulitplesOf, + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); final int chargeAppliesTo = rs.getInt("chargeAppliesTo"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/service/CollateralReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/service/CollateralReadPlatformServiceImpl.java index 62fc3345a..b6caef1dd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/service/CollateralReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collateral/service/CollateralReadPlatformServiceImpl.java @@ -45,7 +45,7 @@ public class CollateralReadPlatformServiceImpl implements CollateralReadPlatform private StringBuilder sqlBuilder = new StringBuilder( "lc.id as id, lc.description as description, lc.value as value, cv.id as typeId, cv.code_value as typeName, oc.code as currencyCode, ") - .append(" oc.name as currencyName,oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMulitplesOf, oc.display_symbol as currencyDisplaySymbol, oc.internationalized_name_code as currencyNameCode") + .append(" oc.name as currencyName,oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, oc.internationalized_name_code as currencyNameCode") .append(" FROM m_loan_collateral lc") // .append(" JOIN m_code_value cv on lc.type_cv_id = cv.id")// .append(" JOIN m_loan loan on lc.loan_id = loan.id")// @@ -71,9 +71,9 @@ public class CollateralReadPlatformServiceImpl implements CollateralReadPlatform final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMulitplesOf, + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); return CollateralData.instance(id, type, value, description, currencyData); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java index 5de580894..08f8f51c4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java @@ -200,7 +200,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe + "ln.loan_status_id As accountStatusId, " + "pl.name As productShortName, " + "ln.product_id As productId, " - + "ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, ln.currency_multiplesof as inMulitplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + + "ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, ln.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + "if(ln.loan_status_id = 200 , ln.principal_amount , null) As disbursementAmount, " + "sum(ifnull(if(ln.loan_status_id = 300, ls.principal_amount, 0.0), 0.0) - ifnull(if(ln.loan_status_id = 300, ls.principal_completed_derived, 0.0), 0.0)) As principalDue, " + "ln.principal_repaid_derived As principalPaid, " @@ -246,10 +246,10 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); CurrencyData currencyData = null; if (currencyCode != null) { - currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol, + currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java index 9b647a044..0286f054b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java @@ -174,7 +174,7 @@ public final class LoanApplicationTerms { // adjust cumulative fields for principal & interest if needed if (this.principal.getAmount().subtract(totalCumulativePrincipalToDate.getAmount()).add(principalForPeriod.getAmount()).compareTo(principalForPeriod.getAmount()) == -1) { adjusted = adjusted.minus(adjusted); - MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, adjusted.getCurrencyInMulitplesOf()); + MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, adjusted.getCurrencyInMultiplesOf()); BigDecimal diff = this.principal.getAmount().subtract(totalCumulativePrincipalToDate.getAmount()).add(principalForPeriod.getAmount()); if(diff.doubleValue()<0){ diff = BigDecimal.ZERO; @@ -200,7 +200,7 @@ public final class LoanApplicationTerms { if (totalInterestDueForLoan.getAmount().subtract(totalCumulativeInterestToDate.getAmount()).add(interestForThisPeriod.getAmount()) .compareTo(interestForThisPeriod.getAmount()) == -1) { adjusted = adjusted.minus(adjusted); - MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, adjusted.getCurrencyInMulitplesOf()); + MonetaryCurrency monetaryCurrency = new MonetaryCurrency(adjusted.getCurrencyCode(), 0, adjusted.getCurrencyInMultiplesOf()); BigDecimal diff = totalInterestDueForLoan.getAmount().subtract(totalCumulativeInterestToDate.getAmount()).add(interestForThisPeriod.getAmount()); if(diff.doubleValue()<0){ diff = BigDecimal.ZERO; @@ -636,7 +636,7 @@ public final class LoanApplicationTerms { public LoanProductRelatedDetail toLoanProductRelatedDetail() { final MonetaryCurrency currency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDecimalPlaces(), - this.currency.getCurrencyInMulitplesOf()); + this.currency.getCurrencyInMultiplesOf()); return LoanProductRelatedDetail.createFrom(currency, this.principal.getAmount(), this.interestRatePerPeriod, this.interestRatePeriodFrequencyType, this.annualNominalInterestRate, this.interestMethod, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java index 413b0cd59..c337a0f50 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java @@ -62,8 +62,8 @@ public final class LoanScheduleModel { public LoanScheduleData toData() { final int decimalPlaces = this.totalPrincipalDisbursed.getCurrencyDigitsAfterDecimal(); - final Integer inMulitplesOf = this.totalPrincipalDisbursed.getCurrencyInMulitplesOf(); - final CurrencyData currency = applicationCurrency.toData(decimalPlaces,inMulitplesOf); + final Integer inMultiplesOf = this.totalPrincipalDisbursed.getCurrencyInMultiplesOf(); + final CurrencyData currency = applicationCurrency.toData(decimalPlaces,inMultiplesOf); final Collection periodsData = new ArrayList(); for (LoanScheduleModelPeriod modelPeriod : this.periods) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java index 628e5453d..9307315be 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java @@ -56,7 +56,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform + "lc.due_for_collection_as_of_date as dueAsOfDate, " + "lc.charge_calculation_enum as chargeCalculation, " + "c.currency_code as currencyCode, oc.name as currencyName, " - + "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMulitplesOf, oc.display_symbol as currencyDisplaySymbol, " + + "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, " + "oc.internationalized_name_code as currencyNameCode from m_charge c " + "join m_organisation_currency oc on c.currency_code = oc.code " + "join m_loan_charge lc on lc.charge_id = c.id "; } @@ -81,9 +81,9 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMulitplesOf, + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); final int chargeTime = rs.getInt("chargeTime"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index df508f5e9..b4decf64c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -410,7 +410,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.interest_calculated_in_period_enum as interestCalculationPeriodType," + " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, " + " lps.name as transactionStrategyName, " - + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMulitplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName, " + " l.principal_disbursed_derived as principalDisbursed," + " l.principal_repaid_derived as principalPaid," @@ -474,8 +474,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); final Long id = rs.getLong("id"); @@ -710,7 +710,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { periods.add(disbursementPeriod); final MonetaryCurrency monCurrency = new MonetaryCurrency(this.currency.code(), this.currency.decimalPlaces(), - this.currency.currencyInMulitplesOf()); + this.currency.currencyInMultiplesOf()); final BigDecimal totalPrincipalDisbursed = Money.of(monCurrency, this.disbursement.amount()).getAmount(); Money totalPrincipalExpected = Money.zero(monCurrency); Money totalPrincipalPaid = Money.zero(monCurrency); @@ -846,7 +846,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, " + " pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, " + " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, " - + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMulitplesOf, rc.`name` as currencyName, " + + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, " + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + " cv.code_value as paymentTypeName, tr.external_id as externalId " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id" @@ -863,8 +863,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); final Long id = rs.getLong("id"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index 721159c50..df45afe13 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -99,9 +99,9 @@ public class LoanProduct extends AbstractPersistable { final String description = command.stringValueOfParameterNamed("description"); final String currencyCode = command.stringValueOfParameterNamed("currencyCode"); final Integer digitsAfterDecimal = command.integerValueOfParameterNamed("digitsAfterDecimal"); - final Integer inMulitplesOf = command.integerValueOfParameterNamed("inMulitplesOf"); + final Integer inMultiplesOf = command.integerValueOfParameterNamed("inMultiplesOf"); - final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); + final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMultiplesOf); final BigDecimal principal = command.bigDecimalValueOfParameterNamed("principal"); final BigDecimal minPrincipal = command.bigDecimalValueOfParameterNamed("minPrincipal"); final BigDecimal maxPrincipal = command.bigDecimalValueOfParameterNamed("maxPrincipal"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java index 3724556c8..76d58ce9f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java @@ -206,7 +206,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche String currencyCode = this.currency.getCode(); Integer digitsAfterDecimal = this.currency.getDigitsAfterDecimal(); - Integer inMulitplesOf = this.currency.getCurrencyInMulitplesOf(); + Integer inMultiplesOf = this.currency.getCurrencyInMultiplesOf(); final String digitsAfterDecimalParamName = "digitsAfterDecimal"; if (command.isChangeInIntegerParameterNamed(digitsAfterDecimalParamName, digitsAfterDecimal)) { @@ -214,7 +214,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche actualChanges.put(digitsAfterDecimalParamName, newValue); actualChanges.put("locale", localeAsInput); digitsAfterDecimal = newValue; - this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); + this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMultiplesOf); } final String currencyCodeParamName = "currencyCode"; @@ -222,15 +222,15 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final String newValue = command.stringValueOfParameterNamed(currencyCodeParamName); actualChanges.put(currencyCodeParamName, newValue); currencyCode = newValue; - this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); + this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMultiplesOf); } - final String inMulitplesOfParamName = "inMulitplesOf"; - if (command.isChangeInStringParameterNamed(inMulitplesOfParamName, currencyCode)) { - final Integer newValue = command.integerValueOfParameterNamed(inMulitplesOfParamName); - actualChanges.put(inMulitplesOfParamName, newValue); - inMulitplesOf = newValue; - this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); + final String inMultiplesOfParamName = "inMultiplesOf"; + if (command.isChangeInStringParameterNamed(inMultiplesOfParamName, currencyCode)) { + final Integer newValue = command.integerValueOfParameterNamed(inMultiplesOfParamName); + actualChanges.put(inMultiplesOfParamName, newValue); + inMultiplesOf = newValue; + this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMultiplesOf); } final Map loanApplicationAttributeChanges = updateLoanApplicationAttributes(command, aprCalculator); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java index 73d76a5d7..f195673cb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -39,7 +39,7 @@ public final class LoanProductDataValidator { * The parameters supported for this command. */ private final Set supportedParameters = new HashSet(Arrays.asList("locale", "dateFormat", "name", "description", "fundId", - "currencyCode", "digitsAfterDecimal","inMulitplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments", + "currencyCode", "digitsAfterDecimal","inMultiplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments", "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod", "minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment", @@ -92,8 +92,8 @@ public final class LoanProductDataValidator { final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault()); baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); - final Integer inMulitplesOf = fromApiJsonHelper.extractIntegerNamed("inMulitplesOf", element, Locale.getDefault()); - baseDataValidator.reset().parameter("inMulitplesOf").value(inMulitplesOf).ignoreIfNull().integerZeroOrGreater(); + final Integer inMultiplesOf = fromApiJsonHelper.extractIntegerNamed("inMultiplesOf", element, Locale.getDefault()); + baseDataValidator.reset().parameter("inMultiplesOf").value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater(); final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element); baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount(); @@ -338,9 +338,9 @@ public final class LoanProductDataValidator { baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); } - if (fromApiJsonHelper.parameterExists("inMulitplesOf", element)) { - final Integer inMulitplesOf = fromApiJsonHelper.extractIntegerNamed("inMulitplesOf", element, Locale.getDefault()); - baseDataValidator.reset().parameter("inMulitplesOf").value(inMulitplesOf).ignoreIfNull().integerZeroOrGreater(); + if (fromApiJsonHelper.parameterExists("inMultiplesOf", element)) { + final Integer inMultiplesOf = fromApiJsonHelper.extractIntegerNamed("inMultiplesOf", element, Locale.getDefault()); + baseDataValidator.reset().parameter("inMultiplesOf").value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater(); } String minPrincipalParameterName = "minPrincipal"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index bbb1edf5a..44ba1e579 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -99,7 +99,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo public String loanProductSchema() { return "lp.id as id, lp.fund_id as fundId, f.name as fundName, lp.loan_transaction_strategy_id as transactionStrategyId, ltps.name as transactionStrategyName, " + "lp.name as name, lp.description as description, " - + "lp.principal_amount as principal, lp.min_principal_amount as minPrincipal, lp.max_principal_amount as maxPrincipal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, lp.currency_multiplesof as inMulitplesOf, " + + "lp.principal_amount as principal, lp.min_principal_amount as minPrincipal, lp.max_principal_amount as maxPrincipal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, lp.currency_multiplesof as inMultiplesOf, " + "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, " + "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod," + "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, " @@ -129,9 +129,9 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); final BigDecimal principal = rs.getBigDecimal("principal"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index 9d0dea3ca..a3f919e3e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -63,7 +63,7 @@ public class SavingsApiConstants { public static final String descriptionParamName = "description"; public static final String currencyCodeParamName = "currencyCode"; public static final String digitsAfterDecimalParamName = "digitsAfterDecimal"; - public static final String inMulitplesOfParamName = "inMulitplesOf"; + public static final String inMultiplesOfParamName = "inMultiplesOf"; public static final String nominalAnnualInterestRateParamName = "nominalAnnualInterestRate"; public static final String interestCompoundingPeriodTypeParamName = "interestCompoundingPeriodType"; public static final String interestPostingPeriodTypeParamName = "interestPostingPeriodType"; @@ -94,7 +94,7 @@ public class SavingsApiConstants { public static final Set SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, monthDayFormatParamName, nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, - inMulitplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, + inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName, annualFeeOnMonthDayParamName, @@ -109,7 +109,7 @@ public class SavingsApiConstants { * parameters to match those of request parameters. */ public static final Set SAVINGS_PRODUCT_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, - nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, inMulitplesOfParamName, + nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java index ed8023a4d..7f86492fc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java @@ -12,7 +12,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeO import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMulitplesOfParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; @@ -88,8 +88,8 @@ public class SavingsProductDataValidator { final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerSansLocaleNamed(digitsAfterDecimalParamName, element); baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); - final Integer inMulitplesOf = fromApiJsonHelper.extractIntegerSansLocaleNamed(inMulitplesOfParamName, element); - baseDataValidator.reset().parameter(inMulitplesOfParamName).value(inMulitplesOf).ignoreIfNull().integerZeroOrGreater(); + final Integer inMultiplesOf = fromApiJsonHelper.extractIntegerSansLocaleNamed(inMultiplesOfParamName, element); + baseDataValidator.reset().parameter(inMultiplesOfParamName).value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater(); final BigDecimal nominalAnnualInterestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, element); @@ -256,9 +256,9 @@ public class SavingsProductDataValidator { baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); } - if (this.fromApiJsonHelper.parameterExists(inMulitplesOfParamName, element)) { - final Integer inMulitplesOf = fromApiJsonHelper.extractIntegerSansLocaleNamed(inMulitplesOfParamName, element); - baseDataValidator.reset().parameter(inMulitplesOfParamName).value(inMulitplesOf).ignoreIfNull().integerZeroOrGreater(); + if (this.fromApiJsonHelper.parameterExists(inMultiplesOfParamName, element)) { + final Integer inMultiplesOf = fromApiJsonHelper.extractIntegerSansLocaleNamed(inMultiplesOfParamName, element); + baseDataValidator.reset().parameter(inMultiplesOfParamName).value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater(); } if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateParamName, element)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java index 3eefc93c1..80b2f691e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java @@ -12,7 +12,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeO import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMulitplesOfParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; @@ -280,7 +280,7 @@ public class SavingsProduct extends AbstractPersistable { actualChanges.put(digitsAfterDecimalParamName, newValue); actualChanges.put(localeParamName, localeAsInput); digitsAfterDecimal = newValue; - this.currency = new MonetaryCurrency(this.currency.getCode(), digitsAfterDecimal,this.currency.getCurrencyInMulitplesOf()); + this.currency = new MonetaryCurrency(this.currency.getCode(), digitsAfterDecimal,this.currency.getCurrencyInMultiplesOf()); } String currencyCode = this.currency.getCode(); @@ -288,16 +288,16 @@ public class SavingsProduct extends AbstractPersistable { final String newValue = command.stringValueOfParameterNamed(currencyCodeParamName); actualChanges.put(currencyCodeParamName, newValue); currencyCode = newValue; - this.currency = new MonetaryCurrency(currencyCode, this.currency.getDigitsAfterDecimal(),this.currency.getCurrencyInMulitplesOf()); + this.currency = new MonetaryCurrency(currencyCode, this.currency.getDigitsAfterDecimal(),this.currency.getCurrencyInMultiplesOf()); } - Integer inMulitplesOf = this.currency.getCurrencyInMulitplesOf(); - if (command.isChangeInIntegerParameterNamed(inMulitplesOfParamName, inMulitplesOf)) { - final Integer newValue = command.integerValueOfParameterNamed(inMulitplesOfParamName); - actualChanges.put(inMulitplesOfParamName, newValue); + Integer inMultiplesOf = this.currency.getCurrencyInMultiplesOf(); + if (command.isChangeInIntegerParameterNamed(inMultiplesOfParamName, inMultiplesOf)) { + final Integer newValue = command.integerValueOfParameterNamed(inMultiplesOfParamName); + actualChanges.put(inMultiplesOfParamName, newValue); actualChanges.put(localeParamName, localeAsInput); - inMulitplesOf = newValue; - this.currency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDigitsAfterDecimal(),inMulitplesOf); + inMultiplesOf = newValue; + this.currency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDigitsAfterDecimal(),inMultiplesOf); } if (command.isChangeInBigDecimalParameterNamed(nominalAnnualInterestRateParamName, this.nominalAnnualInterestRate)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java index 5cd7cfb84..d881b5a32 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java @@ -10,7 +10,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeO import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMulitplesOfParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; @@ -47,8 +47,8 @@ public class SavingsProductAssembler { final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName); final Integer digitsAfterDecimal = command.integerValueOfParameterNamed(digitsAfterDecimalParamName); - final Integer inMulitplesOf = command.integerValueOfParameterNamed(inMulitplesOfParamName); - MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMulitplesOf); + final Integer inMultiplesOf = command.integerValueOfParameterNamed(inMultiplesOfParamName); + MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal,inMultiplesOf); final BigDecimal interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 7a0695664..22692b48a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -204,7 +204,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,"); sqlBuilder - .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMulitplesOf, "); + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); @@ -312,8 +312,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); final BigDecimal nominalAnnualInterestRate = rs.getBigDecimal("nominalAnnualInterestRate"); @@ -545,7 +545,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "); sqlBuilder.append("pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, "); sqlBuilder - .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMulitplesOf, "); + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("cv.code_value as paymentTypeName "); @@ -599,8 +599,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); AccountTransferData transfer = null; @@ -637,7 +637,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("sa.id as id, sa.account_no as accountNo, "); sqlBuilder - .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMulitplesOf, "); + .append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("sa.min_required_opening_balance as minRequiredOpeningBalance "); @@ -662,8 +662,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); return SavingsAccountTransactionData.template(savingsId, accountNo, DateUtils.getLocalDateOfTenant(), currency); @@ -684,7 +684,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("sp.id as productId, sp.name as productName, "); sqlBuilder - .append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMulitplesOf, "); + .append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMultiplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("sp.nominal_annual_interest_rate as nominalAnnualIterestRate, "); @@ -721,8 +721,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); final BigDecimal nominalAnnualIterestRate = rs.getBigDecimal("nominalAnnualIterestRate"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java index 46f01275d..6cd36970b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java @@ -76,7 +76,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("sp.id as id, sp.name as name, sp.description as description, "); sqlBuilder - .append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMulitplesOf, "); + .append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMultiplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("sp.nominal_annual_interest_rate as nominalAnnualInterestRate, "); @@ -115,8 +115,8 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead String currencyNameCode = rs.getString("currencyNameCode"); String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMulitplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode); final BigDecimal nominalAnnualInterestRate = rs.getBigDecimal("nominalAnnualInterestRate"); diff --git a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/MonetaryCurrencyBuilder.java b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/MonetaryCurrencyBuilder.java index bb7d3199a..9c5e25b23 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/MonetaryCurrencyBuilder.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/MonetaryCurrencyBuilder.java @@ -6,10 +6,10 @@ public class MonetaryCurrencyBuilder { private String code = "XOF"; private int digitsAfterDecimal = 0; - private Integer inMulitplesOf = null; + private Integer inMultiplesOf = null; public MonetaryCurrency build() { - return new MonetaryCurrency(code, digitsAfterDecimal,inMulitplesOf); + return new MonetaryCurrency(code, digitsAfterDecimal,inMultiplesOf); } public MonetaryCurrencyBuilder withCode(final String withCode) { From 28bbd361c209abe0bd73b7384a1d8cb6d65f45ad Mon Sep 17 00:00:00 2001 From: ashok-conflux Date: Wed, 24 Jul 2013 10:27:27 +0530 Subject: [PATCH 38/47] MIFOSX-496: Capture client attendance details --- .../V101__client_attendance_tables.sql | 30 +++ .../commands/domain/CommandWrapper.java | 8 + .../service/CommandWrapperBuilder.java | 35 +++ .../SynchronousCommandProcessingService.java | 12 + .../workingdays/service/WorkingDaysUtil.java | 4 +- .../calendar/api/CalendarsApiResource.java | 36 +-- .../portfolio/calendar/domain/Calendar.java | 6 +- .../calendar/domain/CalendarEntityType.java | 16 ++ .../domain/CalendarInstanceRepository.java | 2 +- .../CalendarInstanceRepositoryWrapper.java | 51 ++++ .../CalendarInstanceNotFoundException.java | 22 ++ .../service/CalendarReadPlatformService.java | 5 + .../CalendarReadPlatformServiceImpl.java | 61 +++-- ...CalendarHelper.java => CalendarUtils.java} | 69 +++++- ...WritePlatformServiceJpaRepositoryImpl.java | 2 +- .../portfolio/client/domain/Client.java | 9 + .../client/domain/ClientRepository.java | 2 +- .../service/ClientReadPlatformService.java | 2 + .../ClientReadPlatformServiceImpl.java | 14 ++ ...ollectionSheetReadPlatformServiceImpl.java | 3 +- .../group/api/CentersApiResource.java | 21 +- .../group/api/GroupingTypesApiConstants.java | 3 +- .../group/api/GroupsApiResource.java | 42 +++- .../portfolio/group/data/CenterData.java | 17 +- .../group/data/GroupGeneralData.java | 33 ++- .../portfolio/group/domain/Group.java | 9 + .../group/domain/GroupRepository.java | 4 +- .../exception/ClientNotInGroupException.java | 8 +- .../portfolio/loanaccount/domain/Loan.java | 36 +-- .../service/LoanScheduleAssembler.java | 14 +- .../LoanEventApiJsonValidator.java | 6 +- .../service/LoanReadPlatformServiceImpl.java | 11 +- ...WritePlatformServiceJpaRepositoryImpl.java | 6 +- .../meeting/MeetingApiConstants.java | 44 ++++ .../meeting/api/MeetingsApiResource.java | 221 +++++++++++++++++ .../meeting/attendance/AttendanceType.java | 63 +++++ .../attendance/data/ClientAttendanceData.java | 35 +++ .../attendance/domain/ClientAttendance.java | 54 +++++ .../domain/ClientAttendanceRepository.java | 13 + ...AttendanceDropdownReadPlatformService.java | 14 ++ ...ndanceDropdownReadPlatformServiceImpl.java | 22 ++ .../service/AttendanceEnumerations.java | 57 +++++ .../ClientAttendanceReadPlatformService.java | 14 ++ ...ientAttendanceReadPlatformServiceImpl.java | 59 +++++ .../portfolio/meeting/data/MeetingData.java | 69 ++++++ .../meeting/data/MeetingDataValidator.java | 144 +++++++++++ .../portfolio/meeting/domain/Meeting.java | 188 ++++++++++++++ .../meeting/domain/MeetingRepository.java | 13 + .../domain/MeetingRepositoryWrapper.java | 51 ++++ .../exception/MeetingDateException.java | 18 ++ .../exception/MeetingNotFoundException.java | 18 ++ .../MeetingNotSupportedResourceException.java | 18 ++ .../handler/CreateMeetingCommandHandler.java | 32 +++ .../handler/DeleteMeetingCommandHandler.java | 31 +++ ...UpdateMeetingAttendanceCommandHandler.java | 33 +++ .../handler/UpdateMeetingCommandHandler.java | 33 +++ .../service/MeetingReadPlatformService.java | 21 ++ .../MeetingReadPlatformServiceImpl.java | 89 +++++++ .../service/MeetingWritePlatformService.java | 20 ++ ...WritePlatformServiceJpaRepositoryImpl.java | 229 ++++++++++++++++++ 60 files changed, 2056 insertions(+), 146 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V101__client_attendance_tables.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepositoryWrapper.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/exception/CalendarInstanceNotFoundException.java rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/{CalendarHelper.java => CalendarUtils.java} (85%) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/MeetingApiConstants.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/api/MeetingsApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/AttendanceType.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/data/ClientAttendanceData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/domain/ClientAttendance.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/domain/ClientAttendanceRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceDropdownReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceDropdownReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceEnumerations.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/ClientAttendanceReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/ClientAttendanceReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/MeetingRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/MeetingRepositoryWrapper.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingDateException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingNotSupportedResourceException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/CreateMeetingCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/DeleteMeetingCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/UpdateMeetingAttendanceCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/UpdateMeetingCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java diff --git a/mifosng-db/migrations/core_db/V101__client_attendance_tables.sql b/mifosng-db/migrations/core_db/V101__client_attendance_tables.sql new file mode 100644 index 000000000..e55b58ac8 --- /dev/null +++ b/mifosng-db/migrations/core_db/V101__client_attendance_tables.sql @@ -0,0 +1,30 @@ +CREATE TABLE `m_meeting` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `calendar_instance_id` BIGINT(20) NOT NULL, + `meeting_date` DATE NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `unique_calendar_instance_id_meeting_date` (`calendar_instance_id`, `meeting_date`), + CONSTRAINT `FK_m_calendar_instance_m_meeting` FOREIGN KEY (`calendar_instance_id`) REFERENCES `m_calendar_instance` (`id`) +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB; + +CREATE TABLE `m_client_attendance` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `client_id` BIGINT(20) NOT NULL DEFAULT '0', + `meeting_id` BIGINT(20) NOT NULL, + `attendance_type_enum` SMALLINT(5) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `unique_client_meeting_attendance` (`client_id`, `meeting_id`), + INDEX `FK_m_meeting_m_client_attendance` (`meeting_id`), + CONSTRAINT `FK_m_client_m_client_attendance` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`), + CONSTRAINT `FK_m_meeting_m_client_attendance` FOREIGN KEY (`meeting_id`) REFERENCES `m_meeting` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'CREATE_MEETING', 'MEETING', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'UPDATE_MEETING', 'MEETING', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'DELETE_MEETING', 'MEETING', 'DELETE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'SAVEORUPDATEATTENDANCE_MEETING', 'MEETING', 'SAVEORUPDATEATTENDANCE', 0); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 2cc730d21..3a809e0c9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -571,4 +571,12 @@ public class CommandWrapper { public boolean isAccountTransferResource() { return this.entityName.equalsIgnoreCase("ACCOUNTTRANSFER"); } + + public boolean isMeetingResource() { + return this.entityName.equalsIgnoreCase("MEETING"); + } + + public boolean isSaveOrUpdateAttendance(){ + return this.actionName.equalsIgnoreCase("SAVEORUPDATEATTENDANCE"); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 357a9150c..caa102376 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -1210,4 +1210,39 @@ public class CommandWrapperBuilder { this.href = "/updateJobDetail/" + jobId + "/updateJobDetail"; return this; } + + public CommandWrapperBuilder createMeeting(final String supportedEntityType, final Long supportedEntityId) { + this.actionName = "CREATE"; + this.entityName = "MEETING"; + this.supportedEntityType = supportedEntityType; + this.supportedEntityId = supportedEntityId; + this.href = "/" + supportedEntityType + "/" + supportedEntityId + "/meetings"; + return this; + } + + public CommandWrapperBuilder updateMeeting(final String supportedEntityType, final Long supportedEntityId, final Long meetingId) { + this.actionName = "UPDATE"; + this.entityName = "MEETING"; + this.entityId = meetingId; + this.href = "/" + supportedEntityType + "/" + supportedEntityId + "/meetings/" + meetingId; + return this; + } + + public CommandWrapperBuilder deleteMeeting(final String supportedEntityType, final Long supportedEntityId, final Long meetingId) { + this.actionName = "DELETE"; + this.entityName = "MEETING"; + this.entityId = meetingId; + this.href = "/" + supportedEntityType + "/" + supportedEntityId + "/meetings/" + meetingId; + return this; + } + + public CommandWrapperBuilder saveOrUpdateAttendance(final Long entityId, final String supportedEntityType, final Long supportedEntityId) { + this.actionName = "SAVEORUPDATEATTENDANCE"; + this.entityName = "MEETING"; + this.entityId = entityId; + this.supportedEntityType = supportedEntityType; + this.supportedEntityId = supportedEntityId; + this.href = "/" + supportedEntityType + "/" + supportedEntityId + "/meetings/" + entityId + "?command=saveOrUpdateAttendance"; + return this; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index e1208c0a4..a70f704d1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -522,6 +522,18 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } else { throw new UnsupportedCommandException(wrapper.commandName()); } + } else if (wrapper.isMeetingResource()) { + if (wrapper.isCreate()) { + handler = this.applicationContext.getBean("createMeetingCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUpdate()) { + handler = this.applicationContext.getBean("updateMeetingCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDelete()) { + handler = this.applicationContext.getBean("deleteMeetingCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isSaveOrUpdateAttendance()) { + handler = this.applicationContext.getBean("updateMeetingAttendanceCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } } else { throw new UnsupportedCommandException(wrapper.commandName()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysUtil.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysUtil.java index 56a4a5b02..4fb1dc9f0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysUtil.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysUtil.java @@ -9,7 +9,7 @@ package org.mifosplatform.organisation.workingdays.service; import org.joda.time.LocalDate; import org.mifosplatform.organisation.workingdays.domain.RepaymentRescheduleType; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; -import org.mifosplatform.portfolio.calendar.service.CalendarHelper; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; public class WorkingDaysUtil { @@ -38,6 +38,6 @@ public class WorkingDaysUtil { } public static boolean isWorkingDay(WorkingDays workingDays, final LocalDate date){ - return CalendarHelper.isValidRedurringDate(workingDays.getRecurrence(), date, date); + return CalendarUtils.isValidRedurringDate(workingDays.getRecurrence(), date, date); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/api/CalendarsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/api/CalendarsApiResource.java index d7a80ef7e..5afcb3311 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/api/CalendarsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/api/CalendarsApiResource.java @@ -20,6 +20,7 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.service.CalendarDropdownReadPlatformService; import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -105,7 +106,7 @@ public class CalendarsApiResource { Collection calendarsData = new ArrayList(); - List calendarTypeOptions = createIntegerListFromQueryParameter(calendarType); + List calendarTypeOptions = CalendarUtils.createIntegerListFromQueryParameter(calendarType); if (!associationParameters.isEmpty()) { @@ -191,37 +192,4 @@ public class CalendarsApiResource { return new CalendarData(calendarData, entityTypeOptions, calendarTypeOptions, remindByOptions); } - - public List createIntegerListFromQueryParameter(String calendarTypeQuery) { - List calendarTypeOptions = new ArrayList(); - // adding all calendar Types if query parameter is "all" - if(calendarTypeQuery.equalsIgnoreCase("all")){ - calendarTypeOptions.add(1); - calendarTypeOptions.add(2); - calendarTypeOptions.add(3); - calendarTypeOptions.add(4); - return calendarTypeOptions; - } - // creating a list of calendar type options from the comma separated query parameter. - List calendarTypeOptionsInQuery = new ArrayList(); - StringTokenizer st = new StringTokenizer(calendarTypeQuery, ","); - while (st.hasMoreElements()) { - calendarTypeOptionsInQuery.add(st.nextElement().toString()); - } - - for(String calType : calendarTypeOptionsInQuery){ - if(calType.equalsIgnoreCase("collection")) { - calendarTypeOptions.add(1); - } else if(calType.equalsIgnoreCase("training")) { - calendarTypeOptions.add(2); - } else if(calType.equalsIgnoreCase("audit")) { - calendarTypeOptions.add(3); - } else if(calType.equalsIgnoreCase("general")) { - calendarTypeOptions.add(4); - } - } - - return calendarTypeOptions; - } - } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/Calendar.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/Calendar.java index 49395caf0..eb25c0a79 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/Calendar.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/Calendar.java @@ -23,7 +23,7 @@ import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom; import org.mifosplatform.portfolio.calendar.CalendarConstants.CALENDAR_SUPPORTED_PARAMETERS; import org.mifosplatform.portfolio.calendar.exception.CalendarDateException; import org.mifosplatform.portfolio.calendar.exception.CalendarParameterUpdateNotSupportedException; -import org.mifosplatform.portfolio.calendar.service.CalendarHelper; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.useradministration.domain.AppUser; @Entity @@ -213,13 +213,13 @@ public class Calendar extends AbstractAuditableCustom { //FIXME: AA - Is this restriction required only for collection type meetings or for all?. //Do not allow to change meeting frequency - if(!CalendarHelper.isFrequencySame(this.recurrence, newValue)){ + if(!CalendarUtils.isFrequencySame(this.recurrence, newValue)){ final String defaultUserMessage = "Update of meeting frequency is not supported"; throw new CalendarParameterUpdateNotSupportedException("meeting.frequency", defaultUserMessage); } //Do not allow to change meeting interval - if(!CalendarHelper.isIntervalSame(this.recurrence, newValue)){ + if(!CalendarUtils.isIntervalSame(this.recurrence, newValue)){ final String defaultUserMessage = "Update of meeting interval is not supported"; throw new CalendarParameterUpdateNotSupportedException("meeting.interval", defaultUserMessage); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarEntityType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarEntityType.java index b893a1b93..95cf5329d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarEntityType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarEntityType.java @@ -66,4 +66,20 @@ public enum CalendarEntityType { public String toString() { return name().toString(); } + + public static boolean isGroup(final Integer value) { + return CalendarEntityType.GROUPS.value.equals(value) ? true : false; + } + + public static boolean isGroup(final String name) { + return CalendarEntityType.GROUPS.name().equalsIgnoreCase(name) ? true : false; + } + + public static boolean isCenter(final Integer value) { + return CalendarEntityType.CENTERS.value.equals(value) ? true : false; + } + + public static boolean isCenter(final String name) { + return CalendarEntityType.CENTERS.name().equalsIgnoreCase(name) ? true : false; + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepository.java index f11547652..fe7fd2553 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepository.java @@ -14,7 +14,7 @@ import org.springframework.data.repository.query.Param; public interface CalendarInstanceRepository extends JpaRepository, JpaSpecificationExecutor { - CalendarInstance findByCalendarAndEntityIdAndEntityTypeId(Long calendarId, Long entityId, Integer entityTypeId); + CalendarInstance findByCalendarIdAndEntityIdAndEntityTypeId(Long calendarId, Long entityId, Integer entityTypeId); Collection findByEntityIdAndEntityTypeId(Long entityId, Integer entityTypeId); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepositoryWrapper.java new file mode 100644 index 000000000..ce94b7088 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepositoryWrapper.java @@ -0,0 +1,51 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.calendar.domain; + +import org.mifosplatform.portfolio.calendar.exception.CalendarInstanceNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * Wrapper for {@link CalendarInstanceRepository} that is responsible for checking + * if {@link CalendarInstance} is returned when using findOne + * repository method and throwing an appropriate not found exception. + *

+ * + *

+ * This is to avoid need for checking and throwing in multiple areas of code + * base where {@link CalendarInstanceRepository} is required. + *

+ */ +@Service +public class CalendarInstanceRepositoryWrapper { + + private final CalendarInstanceRepository repository; + + @Autowired + public CalendarInstanceRepositoryWrapper(final CalendarInstanceRepository repository) { + this.repository = repository; + } + + public CalendarInstance findOneWithNotFoundDetection(final Long CalendarInstanceId) { + final CalendarInstance calendatInstance = this.repository.findOne(CalendarInstanceId); + if (calendatInstance == null) { throw new CalendarInstanceNotFoundException(CalendarInstanceId); } + return calendatInstance; + } + + public void save(final CalendarInstance calendatInstance) { + this.repository.save(calendatInstance); + } + + public void delete(final CalendarInstance calendatInstance) { + this.repository.delete(calendatInstance); + } + + public void saveAndFlush(final CalendarInstance calendatInstance) { + this.repository.saveAndFlush(calendatInstance); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/exception/CalendarInstanceNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/exception/CalendarInstanceNotFoundException.java new file mode 100644 index 000000000..13042f555 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/exception/CalendarInstanceNotFoundException.java @@ -0,0 +1,22 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.calendar.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when CalendarInstance resources are not found. + */ +public class CalendarInstanceNotFoundException extends AbstractPlatformResourceNotFoundException { + + public CalendarInstanceNotFoundException(final Long id) { + super("error.msg.calendar.instance.id.invalid", "Calendar Instance with identifier " + id + " does not exist", id); + } + + public CalendarInstanceNotFoundException(final String postFix, final String defaultUserMessage, final Object... defaultUserMessageArgs) { + super("error.msg.calendar.instance." + postFix, defaultUserMessage, defaultUserMessageArgs); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformService.java index 81b02a433..4a8b719e4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformService.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.calendar.service; +import org.joda.time.LocalDate; import org.mifosplatform.portfolio.calendar.data.CalendarData; import java.util.Collection; @@ -23,9 +24,13 @@ public interface CalendarReadPlatformService { CalendarData retrieveNewCalendarDetails(); CalendarData generateRecurringDate(final CalendarData calendarData); + + CalendarData generateRecurringDate(final CalendarData calendarData, final LocalDate tillDate); Collection generateRecurringDates(final Collection calendarsData); CalendarData retrieveLoanCalendar(final Long loanId); + + CalendarData retrieveCollctionCalendarByEntity(final Long entityId, final Integer entityTypeId); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java index 9927279ea..353c842f5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java @@ -18,6 +18,7 @@ import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.portfolio.calendar.data.CalendarData; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; +import org.mifosplatform.portfolio.calendar.domain.CalendarType; import org.mifosplatform.portfolio.calendar.exception.CalendarNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; @@ -71,7 +72,7 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ if (remindById != null && remindById != 0) remindBy = CalendarEnumerations.calendarRemindBy(remindById); final Integer firstReminder = rs.getInt("firstReminder"); final Integer secondReminder = rs.getInt("secondReminder"); - final String humanReadable = CalendarHelper.getRRuleReadable(startDate, recurrence); + final String humanReadable = CalendarUtils.getRRuleReadable(startDate, recurrence); final LocalDate createdDate = JdbcSupport.getLocalDate(rs, "createdDate"); final LocalDate lastUpdatedDate = JdbcSupport.getLocalDate(rs, "updatedDate"); @@ -113,13 +114,27 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ sql = rm.schema() + " and ci.entity_id = ? and ci.entity_type_enum = ? order by c.start_date "; result = this.jdbcTemplate.query(sql, rm, new Object[] { entityId, entityTypeId }); } else if (!calendarTypeOptions.isEmpty()) { - String sqlCalendarTypeOptions = getSqlCalendarTypeOptionsInString(calendarTypeOptions); + String sqlCalendarTypeOptions = CalendarUtils.getSqlCalendarTypeOptionsInString(calendarTypeOptions); sql = rm.schema() + " and ci.entity_id = ? and ci.entity_type_enum = ? and c.calendar_type_enum in ( " + sqlCalendarTypeOptions + " ) order by c.start_date "; result = this.jdbcTemplate.query(sql, rm, new Object[] { entityId, entityTypeId }); } return result; } + + @Override + public CalendarData retrieveCollctionCalendarByEntity(final Long entityId, final Integer entityTypeId) { + final CalendarDataMapper rm = new CalendarDataMapper(); + + String sql = rm.schema() + " and ci.entity_id = ? and ci.entity_type_enum = ? and calendar_type_enum = ? order by c.start_date "; + List result = this.jdbcTemplate.query(sql, rm, new Object[] { entityId, entityTypeId, CalendarType.COLLECTION.getValue() }); + + if(!result.isEmpty() && result.size() > 0){ + return result.get(0); + } + + return null; + } @Override public Collection retrieveParentCalendarsByEntity(final Long entityId, final Integer entityTypeId, @@ -137,7 +152,7 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ sql = rm.schema() + " " + parentHeirarchyCondition + " and ci.entity_type_enum = ? order by c.start_date "; result = this.jdbcTemplate.query(sql, rm, new Object[] { entityId, CalendarEntityType.CENTERS.getValue() }); } else { - String sqlCalendarTypeOptions = getSqlCalendarTypeOptionsInString(calendarTypeOptions); + String sqlCalendarTypeOptions = CalendarUtils.getSqlCalendarTypeOptionsInString(calendarTypeOptions); sql = rm.schema() + " " + parentHeirarchyCondition + " and ci.entity_type_enum = ? and c.calendar_type_enum in (" + sqlCalendarTypeOptions + ") order by c.start_date "; result = this.jdbcTemplate.query(sql, rm, new Object[] { entityId, CalendarEntityType.CENTERS.getValue() }); @@ -162,6 +177,11 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ @Override public CalendarData generateRecurringDate(final CalendarData calendarData) { + return generateRecurringDate(calendarData, null); + } + + @Override + public CalendarData generateRecurringDate(CalendarData calendarData, LocalDate tillDate) { if (!calendarData.isRepeating()) return calendarData; final String rrule = calendarData.getRecurrence(); final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); @@ -172,13 +192,22 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ startDate = currentDate.minusYears(1); } - if (endDate == null || endDate.isAfter(currentDate.plusYears(1))) { + if(tillDate != null){ + if(endDate != null){ + if(endDate.isAfter(tillDate)){ + endDate = tillDate;//to retrieve meeting dates till specified date (tillDate) + } + }else{ + endDate = tillDate;//end date is null then fetch meeting dates tillDate + } + }else if (endDate == null || endDate.isAfter(currentDate.plusYears(1))) { endDate = currentDate.plusYears(1); } - - final Collection recurringDates = CalendarHelper.getRecurringDates(rrule, seedDate, startDate, endDate, -1); - final Collection nextTenRecurringDates = CalendarHelper.getRecurringDates(rrule, seedDate, endDate); + + final Collection recurringDates = CalendarUtils.getRecurringDates(rrule, seedDate, startDate, endDate, -1); + final Collection nextTenRecurringDates = CalendarUtils.getRecurringDates(rrule, seedDate, endDate); return new CalendarData(calendarData, recurringDates, nextTenRecurringDates); + } @Override @@ -238,21 +267,5 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ return conditionSql; } - - /** - * function returns a comma separated list of calendar_type_enum values ex. - * 1,2,3,4 - * - * @param calendarTypeOptions - * @return - */ - public String getSqlCalendarTypeOptionsInString(final List calendarTypeOptions) { - String sqlCalendarTypeOptions = ""; - int size = calendarTypeOptions.size(); - for (int i = 0; i < size - 1; i++) { - sqlCalendarTypeOptions += calendarTypeOptions.get(i).toString() + ","; - } - sqlCalendarTypeOptions += calendarTypeOptions.get(size - 1).toString(); - return sqlCalendarTypeOptions; - } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarUtils.java similarity index 85% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarHelper.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarUtils.java index df8a4634c..a02cc61fa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarUtils.java @@ -11,6 +11,8 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; import net.fortuna.ical4j.model.Date; import net.fortuna.ical4j.model.DateList; @@ -32,10 +34,10 @@ import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType; -public class CalendarHelper { +public class CalendarUtils { public static LocalDate getNextRecurringDate(final String recurringRule, final LocalDate seedDate, final LocalDate startDate) { - final Recur recur = CalendarHelper.getICalRecur(recurringRule); + final Recur recur = CalendarUtils.getICalRecur(recurringRule); if (recur == null) { return null; } return getNextRecurringDate(recur, seedDate, startDate); } @@ -82,7 +84,7 @@ public class CalendarHelper { public static Collection getRecurringDates(final String recurringRule, final LocalDate seedDate, final LocalDate periodStartDate, final LocalDate periodEndDate, final int maxCount) { - final Recur recur = CalendarHelper.getICalRecur(recurringRule); + final Recur recur = CalendarUtils.getICalRecur(recurringRule); return getRecurringDates(recur, seedDate, periodStartDate, periodEndDate, maxCount); @@ -210,7 +212,7 @@ public class CalendarHelper { public static boolean isValidRedurringDate(final String recurringRule, final LocalDate seedDate, final LocalDate date){ - final Recur recur = CalendarHelper.getICalRecur(recurringRule); + final Recur recur = CalendarUtils.getICalRecur(recurringRule); if (recur == null) { return false; } return isValidRecurringDate(recur, seedDate, date); @@ -248,7 +250,7 @@ public class CalendarHelper { } public static PeriodFrequencyType getMeetingPeriodFrequencyType(final String recurringRule){ - final Recur recur = CalendarHelper.getICalRecur(recurringRule); + final Recur recur = CalendarUtils.getICalRecur(recurringRule); PeriodFrequencyType meetingFrequencyType = PeriodFrequencyType.INVALID; if(recur.getFrequency().equals(Recur.DAILY)){ meetingFrequencyType = PeriodFrequencyType.DAYS; @@ -277,12 +279,12 @@ public class CalendarHelper { } public static int getInterval(final String recurringRule){ - final Recur recur = CalendarHelper.getICalRecur(recurringRule); + final Recur recur = CalendarUtils.getICalRecur(recurringRule); return recur.getInterval(); } public static LocalDate getFirstRepaymentMeetingDate(final Calendar calendar, final LocalDate disbursementDate, final Integer loanRepaymentInterval, final String frequency){ - final Recur recur = CalendarHelper.getICalRecur(calendar.getRecurrence()); + final Recur recur = CalendarUtils.getICalRecur(calendar.getRecurrence()); if (recur == null) { return null; } LocalDate startDate = disbursementDate; final LocalDate seedDate = calendar.getStartDateLocalDate(); @@ -310,7 +312,7 @@ public class CalendarHelper { } public static LocalDate getNewRepaymentMeetingDate(final String recurringRule, final LocalDate seedDate, final LocalDate oldRepaymentDate, final Integer loanRepaymentInterval, final String frequency, final WorkingDays workingDays){ - final Recur recur = CalendarHelper.getICalRecur(recurringRule); + final Recur recur = CalendarUtils.getICalRecur(recurringRule); if (recur == null) { return null; } if(isValidRecurringDate(recur, seedDate, oldRepaymentDate)){ return oldRepaymentDate; @@ -320,7 +322,7 @@ public class CalendarHelper { public static LocalDate getNextRepaymentMeetingDate(final String recurringRule, final LocalDate seedDate, final LocalDate repaymentDate, final Integer loanRepaymentInterval, final String frequency, final WorkingDays workingDays){ - final Recur recur = CalendarHelper.getICalRecur(recurringRule); + final Recur recur = CalendarUtils.getICalRecur(recurringRule); if (recur == null) { return null; } LocalDate tmpDate = repaymentDate; if(isValidRecurringDate(recur, seedDate, repaymentDate)){ @@ -364,4 +366,53 @@ public class CalendarHelper { if (oldRecur == null || oldRecur.getFrequency() == null || newRecur == null || newRecur.getFrequency() == null) { return false; } return (oldRecur.getInterval() == newRecur.getInterval()); } + + public static List createIntegerListFromQueryParameter(String calendarTypeQuery) { + List calendarTypeOptions = new ArrayList(); + // adding all calendar Types if query parameter is "all" + if(calendarTypeQuery.equalsIgnoreCase("all")){ + calendarTypeOptions.add(1); + calendarTypeOptions.add(2); + calendarTypeOptions.add(3); + calendarTypeOptions.add(4); + return calendarTypeOptions; + } + // creating a list of calendar type options from the comma separated query parameter. + List calendarTypeOptionsInQuery = new ArrayList(); + StringTokenizer st = new StringTokenizer(calendarTypeQuery, ","); + while (st.hasMoreElements()) { + calendarTypeOptionsInQuery.add(st.nextElement().toString()); + } + + for(String calType : calendarTypeOptionsInQuery){ + if(calType.equalsIgnoreCase("collection")) { + calendarTypeOptions.add(1); + } else if(calType.equalsIgnoreCase("training")) { + calendarTypeOptions.add(2); + } else if(calType.equalsIgnoreCase("audit")) { + calendarTypeOptions.add(3); + } else if(calType.equalsIgnoreCase("general")) { + calendarTypeOptions.add(4); + } + } + + return calendarTypeOptions; + } + + /** + * function returns a comma separated list of calendar_type_enum values ex. + * 1,2,3,4 + * + * @param calendarTypeOptions + * @return + */ + public static String getSqlCalendarTypeOptionsInString(final List calendarTypeOptions) { + String sqlCalendarTypeOptions = ""; + int size = calendarTypeOptions.size(); + for (int i = 0; i < size - 1; i++) { + sqlCalendarTypeOptions += calendarTypeOptions.get(i).toString() + ","; + } + sqlCalendarTypeOptions += calendarTypeOptions.get(size - 1).toString(); + return sqlCalendarTypeOptions; + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java index ad59d6e4b..fe70c2e74 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java @@ -128,7 +128,7 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr final Calendar calendarForUpdate = this.calendarRepository.findOne(calendarId); if (calendarForUpdate == null) { throw new CalendarNotFoundException(calendarId); } - final CalendarInstance calendarInstanceForUpdate = this.calendarInstanceRepository.findByCalendarAndEntityIdAndEntityTypeId(calendarId, entityId, entityTypeId); + final CalendarInstance calendarInstanceForUpdate = this.calendarInstanceRepository.findByCalendarIdAndEntityIdAndEntityTypeId(calendarId, entityId, entityTypeId); this.calendarInstanceRepository.saveAndFlush(calendarInstanceForUpdate); return new CommandProcessingResultBuilder() // .withCommandId(null) // diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java index 7b644b87f..085ea3d0d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java @@ -468,4 +468,13 @@ public final class Client extends AbstractPersistable { public boolean isActivatedAfter(final LocalDate submittedOn) { return getActivationLocalDate().isAfter(submittedOn); } + + public boolean isChildOfGroup(final Long groupId) { + if (groupId != null && this.groups != null && !this.groups.isEmpty()) { + for (Group group : this.groups) { + if (group.getId().equals(groupId)) return true; + } + } + return false; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientRepository.java index 2b30304dd..1b8af41fa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientRepository.java @@ -9,5 +9,5 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; public interface ClientRepository extends JpaRepository, JpaSpecificationExecutor { - // no added behaviour + // no added behaviour } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java index 4a60e8e86..2d769e9c3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java @@ -28,6 +28,8 @@ public interface ClientReadPlatformService { Collection retrieveClientMembersOfGroup(Long groupId); Collection retrieveActiveClientMembersOfGroup(Long groupId); + + Collection retrieveActiveClientMembersOfCenter(final Long centerId); ClientData retrieveAllClosureReasons(String clientClosureReason); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java index ee93c5285..8eb05e30a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -298,6 +298,20 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService } } + @Override + public Collection retrieveActiveClientMembersOfCenter(final Long centerId) { + + final AppUser currentUser = context.authenticatedUser(); + final String hierarchy = currentUser.getOffice().getHierarchy(); + final String hierarchySearchString = hierarchy + "%"; + + final String sql = "select " + this.membersOfGroupMapper.schema() + + " left join m_group g on pgc.group_id=g.id where o.hierarchy like ? and g.parent_id = ? and c.status_enum = ? group by c.id"; + + return this.jdbcTemplate.query(sql, this.membersOfGroupMapper, + new Object[] { hierarchySearchString, centerId, ClientStatus.ACTIVE.getValue() }); + } + private static final class ClientMapper implements RowMapper { private final String schema; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java index 08f8f51c4..64d7f8386 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java @@ -216,9 +216,10 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe + "JOIN m_client cl ON cl.id = gc.client_id " + "LEFT JOIN m_loan ln ON cl.id = ln.client_id AND ln.group_id is not null AND ( ln.loan_status_id = 300 OR ( ln.loan_status_id =200 AND ln.expected_disbursedon_date <= :dueDate )) " + "LEFT JOIN m_product_loan pl ON pl.id = ln.product_id " - + "JOIN m_currency rc on rc.`code` = ln.currency_code " + + "LEFT JOIN m_currency rc on rc.`code` = ln.currency_code " + "LEFT JOIN m_loan_repayment_schedule ls ON ls.loan_id = ln.id AND ls.completed_derived = 0 AND ls.duedate <= :dueDate " + "LEFT JOIN m_loan_charge lc ON lc.loan_id = ln.id AND lc.is_paid_derived = 0 AND ( lc.due_for_collection_as_of_date <= :dueDate OR lc.charge_time_enum = 1) "; + // + "WHERE gp.hierarchy like :hierarchy " + // "GROUP BY gp.id ,cl.id , ln.id " + // "ORDER BY gp.id , cl.id , ln.id "; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java index f363af10a..70dca5147 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java @@ -6,6 +6,7 @@ package org.mifosplatform.portfolio.group.api; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -38,9 +39,13 @@ import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.portfolio.accountdetails.data.AccountSummaryCollectionData; import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlatformService; +import org.mifosplatform.portfolio.calendar.data.CalendarData; +import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; +import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; import org.mifosplatform.portfolio.collectionsheet.service.CollectionSheetReadPlatformService; import org.mifosplatform.portfolio.group.data.CenterData; +import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.service.CenterReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; import org.springframework.beans.factory.annotation.Autowired; @@ -64,6 +69,7 @@ public class CentersApiResource { private final CollectionSheetReadPlatformService collectionSheetReadPlatformService; private final FromJsonHelper fromJsonHelper; private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; + private final CalendarReadPlatformService calendarReadPlatformService; @Autowired public CentersApiResource(final PlatformSecurityContext context, final CenterReadPlatformService centerReadPlatformService, @@ -72,7 +78,7 @@ public class CentersApiResource { final ApiRequestParameterHelper apiRequestParameterHelper, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper, - final AccountDetailsReadPlatformService accountDetailsReadPlatformService) { + final AccountDetailsReadPlatformService accountDetailsReadPlatformService, final CalendarReadPlatformService calendarReadPlatformService) { this.context = context; this.centerReadPlatformService = centerReadPlatformService; this.centerApiJsonSerializer = centerApiJsonSerializer; @@ -83,6 +89,7 @@ public class CentersApiResource { this.collectionSheetReadPlatformService = collectionSheetReadPlatformService; this.fromJsonHelper = fromJsonHelper; this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; + this.calendarReadPlatformService = calendarReadPlatformService; } @GET @@ -126,7 +133,8 @@ public class CentersApiResource { this.context.authenticatedUser().validateHasReadPermission(GroupingTypesApiConstants.CENTER_RESOURCE_NAME); final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); - + CalendarData collectionMeetingCalendar = null; + Collection groups = null; CenterData center = this.centerReadPlatformService.retrieveOne(centerId); final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); @@ -137,9 +145,16 @@ public class CentersApiResource { if (!associationParameters.isEmpty()) { if (associationParameters.contains("groupMembers")) { - center = CenterData.setGroups(center, this.centerReadPlatformService.retrieveAssociatedGroups(centerId)); + groups = this.centerReadPlatformService.retrieveAssociatedGroups(centerId); } + + if (associationParameters.contains("collectionMeetingCalendar")) { + collectionMeetingCalendar = this.calendarReadPlatformService.retrieveCollctionCalendarByEntity(centerId, CalendarEntityType.CENTERS.getValue()); + } + + center = CenterData.withAssociations(center, groups, collectionMeetingCalendar); } + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.centerApiJsonSerializer.serialize(settings, center, GroupingTypesApiConstants.CENTER_RESPONSE_DATA_PARAMETERS); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java index 9151d270d..935ee0c00 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java @@ -52,6 +52,7 @@ public class GroupingTypesApiConstants { public static final String officeOptionsParamName = "officeOptions"; public static final String staffOptionsParamName = "staffOptions"; public static final String clientOptionsParamName = "clientOptions"; + public static final String collectionMeetingCalendar = "collectionMeetingCalendar"; public static final Set CENTER_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, idParamName, nameParamName, externalIdParamName, officeIdParamName, staffIdParamName, activeParamName, @@ -59,7 +60,7 @@ public class GroupingTypesApiConstants { public static final Set GROUP_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, idParamName, nameParamName, externalIdParamName, centerIdParamName, officeIdParamName, staffIdParamName, activeParamName, - activationDateParamName, clientMembersParamName)); + activationDateParamName, clientMembersParamName, collectionMeetingCalendar)); public static final Set GROUP_ROLES_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(roleParamName, clientIdParamName)); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index 5ccd9f420..33119a77b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.group.api; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.ws.rs.Consumes; @@ -40,6 +41,10 @@ import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.portfolio.accountdetails.data.AccountSummaryCollectionData; import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlatformService; +import org.mifosplatform.portfolio.calendar.data.CalendarData; +import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; +import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; @@ -75,6 +80,7 @@ public class GroupsApiResource { private final FromJsonHelper fromJsonHelper; private final GroupRolesReadPlatformService groupRolesReadPlatformService; private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; + private final CalendarReadPlatformService calendarReadPlatformService; @Autowired public GroupsApiResource(final PlatformSecurityContext context, final GroupReadPlatformService groupReadPlatformService, @@ -86,7 +92,8 @@ public class GroupsApiResource { final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper, final GroupRolesReadPlatformService groupRolesReadPlatformService, - final AccountDetailsReadPlatformService accountDetailsReadPlatformService) { + final AccountDetailsReadPlatformService accountDetailsReadPlatformService, final CalendarReadPlatformService calendarReadPlatformService) { + this.context = context; this.groupReadPlatformService = groupReadPlatformService; this.centerReadPlatformService = centerReadPlatformService; @@ -100,6 +107,7 @@ public class GroupsApiResource { this.fromJsonHelper = fromJsonHelper; this.groupRolesReadPlatformService = groupRolesReadPlatformService; this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; + this.calendarReadPlatformService = calendarReadPlatformService; } @GET @@ -162,9 +170,12 @@ public class GroupsApiResource { Collection membersOfGroup = null; Collection groupRoles = null; GroupRoleData selectedRole = null; + Collection calendars = null; + CalendarData collectionMeetingCalendar = null; + if (!associationParameters.isEmpty()) { if (associationParameters.contains("all")) { - associationParameters.addAll(Arrays.asList("clientMembers", "groupRoles")); + associationParameters.addAll(Arrays.asList("clientMembers", "groupRoles", "calendars", "collectionMeetingCalendar")); } if (associationParameters.contains("clientMembers")) { membersOfGroup = this.clientReadPlatformService.retrieveClientMembersOfGroup(groupId); @@ -174,25 +185,36 @@ public class GroupsApiResource { } if (associationParameters.contains("groupRoles")) { groupRoles = this.groupRolesReadPlatformService.retrieveGroupRoles(groupId); - if (CollectionUtils.isEmpty(membersOfGroup)) { + if (CollectionUtils.isEmpty(groupRoles)) { groupRoles = null; } } - if (roleId != null) { - selectedRole = this.groupRolesReadPlatformService.retrieveGroupRole(groupId, roleId); - if (selectedRole != null) { - group = GroupGeneralData.updateSelectedRole(group, selectedRole); + if (associationParameters.contains("calendars")) { + final List calendarTypeOptions = CalendarUtils.createIntegerListFromQueryParameter("all"); + calendars = this.calendarReadPlatformService.retrieveParentCalendarsByEntity(groupId, CalendarEntityType.GROUPS.getValue(), calendarTypeOptions); + if (CollectionUtils.isEmpty(calendars)) { + calendars = null; } } + if (associationParameters.contains("collectionMeetingCalendar")) { + collectionMeetingCalendar = this.calendarReadPlatformService.retrieveCollctionCalendarByEntity(groupId, CalendarEntityType.GROUPS.getValue()); + } + + group = GroupGeneralData.withAssocations(group, membersOfGroup, groupRoles, calendars, collectionMeetingCalendar); + } + + if (roleId != null) { + selectedRole = this.groupRolesReadPlatformService.retrieveGroupRole(groupId, roleId); + if (selectedRole != null) { + group = GroupGeneralData.updateSelectedRole(group, selectedRole); + } } final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); if (template) { final GroupGeneralData templateGroup = this.groupReadPlatformService.retrieveTemplate(group.officeId(), false, staffInSelectedOfficeOnly); - group = GroupGeneralData.withTemplateAndAssociations(templateGroup, group, membersOfGroup, groupRoles); - } else { - group = GroupGeneralData.withAssocations(group, membersOfGroup, groupRoles); + group = GroupGeneralData.withTemplate(templateGroup, group); } final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/CenterData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/CenterData.java index 277d8c85b..8de5f2068 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/CenterData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/CenterData.java @@ -11,6 +11,7 @@ import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.portfolio.calendar.data.CalendarData; /** * Immutable data object representing groups. @@ -40,17 +41,19 @@ public class CenterData { private final Collection officeOptions; private final Collection staffOptions; private final Collection groupMembersOptions; + private final CalendarData collectionMeetingCalendar; public static CenterData template(final Long officeId, final LocalDate activationDate, final Collection officeOptions, final Collection staffOptions, final Collection groupMembersOptions) { + final CalendarData collectionMeetingCalendar = null; return new CenterData(null, null, null, null, activationDate, officeId, null, null, null, null, null, officeOptions, staffOptions, - groupMembersOptions); + groupMembersOptions, collectionMeetingCalendar); } public static CenterData withTemplate(final CenterData templateCenter, final CenterData center) { return new CenterData(center.id, center.name, center.externalId, center.status, center.activationDate, center.officeId, center.officeName, center.staffId, center.staffName, center.hierarchy, center.groupMembers, templateCenter.officeOptions, - templateCenter.staffOptions, templateCenter.groupMembersOptions); + templateCenter.staffOptions, templateCenter.groupMembersOptions, templateCenter.collectionMeetingCalendar); } public static CenterData instance(final Long id, final String name, final String externalId, final EnumOptionData status, @@ -61,21 +64,22 @@ public class CenterData { final Collection officeOptions = null; final Collection staffOptions = null; final Collection groupMembersOptions = null; + final CalendarData collectionMeetingCalendar = null; return new CenterData(id, name, externalId, status, activationDate, officeId, officeName, staffId, staffName, hierarchy, - groupMembers, officeOptions, staffOptions, groupMembersOptions); + groupMembers, officeOptions, staffOptions, groupMembersOptions, collectionMeetingCalendar); } - public static CenterData setGroups(final CenterData centerData, final Collection groupMembers) { + public static CenterData withAssociations(final CenterData centerData, final Collection groupMembers, final CalendarData collectionMeetingCalendar) { return new CenterData(centerData.id, centerData.name, centerData.externalId, centerData.status, centerData.activationDate, centerData.officeId, centerData.officeName, centerData.staffId, centerData.staffName, centerData.hierarchy, groupMembers, - centerData.officeOptions, centerData.staffOptions, centerData.groupMembersOptions); + centerData.officeOptions, centerData.staffOptions, centerData.groupMembersOptions, collectionMeetingCalendar); } private CenterData(final Long id, final String name, final String externalId, final EnumOptionData status, final LocalDate activationDate, final Long officeId, final String officeName, final Long staffId, final String staffName, final String hierarchy, final Collection groupMembers, final Collection officeOptions, - final Collection staffOptions, final Collection groupMembersOptions) { + final Collection staffOptions, final Collection groupMembersOptions, final CalendarData collectionMeetingCalendar) { this.id = id; this.name = name; this.externalId = externalId; @@ -98,6 +102,7 @@ public class CenterData { this.officeOptions = officeOptions; this.staffOptions = staffOptions; this.groupMembersOptions = groupMembersOptions; + this.collectionMeetingCalendar = collectionMeetingCalendar; } public Long officeId() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java index 63d037eb8..722d84d1e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java @@ -12,6 +12,7 @@ import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.portfolio.calendar.data.CalendarData; import org.mifosplatform.portfolio.client.data.ClientData; /** @@ -40,6 +41,8 @@ public class GroupGeneralData { // associations private final Collection clientMembers; private final Collection groupRoles; + private final Collection calendarsData; + private final CalendarData collectionMeetingCalendar; // template private final Collection centerOptions; @@ -54,7 +57,7 @@ public class GroupGeneralData { final Collection groupRoles = null; return new GroupGeneralData(groupId, groupName, null, null, null, null, null, null, null, null, null, null, clientMembers, null, - null, null, null, groupRoles, null, null); + null, null, null, groupRoles, null, null, null, null); } public static GroupGeneralData template(final Long officeId, final Long centerId, final String centerName, final Long staffId, @@ -66,7 +69,7 @@ public class GroupGeneralData { final Collection groupRoles = null; return new GroupGeneralData(null, null, null, null, null, officeId, null, centerId, centerName, staffId, staffName, null, - clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, null); + clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, null, null, null); } public static GroupGeneralData withTemplate(final GroupGeneralData templatedGrouping, final GroupGeneralData grouping) { @@ -74,24 +77,15 @@ public class GroupGeneralData { grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName, grouping.hierarchy, grouping.clientMembers, templatedGrouping.centerOptions, templatedGrouping.officeOptions, templatedGrouping.staffOptions, templatedGrouping.clientOptions, grouping.groupRoles, templatedGrouping.availableRoles, - grouping.selectedRole); - } - - public static GroupGeneralData withTemplateAndAssociations(final GroupGeneralData templatedGrouping, final GroupGeneralData grouping, - final Collection membersOfGroup, final Collection groupRoles) { - return new GroupGeneralData(grouping.id, grouping.name, grouping.externalId, grouping.status, grouping.activationDate, - grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName, - grouping.hierarchy, membersOfGroup, templatedGrouping.centerOptions, templatedGrouping.officeOptions, - templatedGrouping.staffOptions, templatedGrouping.clientOptions, groupRoles, templatedGrouping.availableRoles, - grouping.selectedRole); + grouping.selectedRole, grouping.calendarsData, grouping.collectionMeetingCalendar); } public static GroupGeneralData withAssocations(final GroupGeneralData grouping, final Collection membersOfGroup, - final Collection groupRoles) { + final Collection groupRoles, final Collection calendarsData, final CalendarData collectionMeetingCalendar) { return new GroupGeneralData(grouping.id, grouping.name, grouping.externalId, grouping.status, grouping.activationDate, grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName, grouping.hierarchy, membersOfGroup, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions, - grouping.clientOptions, groupRoles, grouping.availableRoles, grouping.selectedRole); + grouping.clientOptions, groupRoles, grouping.availableRoles, grouping.selectedRole, calendarsData, collectionMeetingCalendar); } public static GroupGeneralData instance(final Long id, final String name, final String externalId, final EnumOptionData status, @@ -106,10 +100,12 @@ public class GroupGeneralData { final Collection groupRoles = null; final Collection availableRoles = null; final GroupRoleData role = null; + final Collection calendarsData = null; + final CalendarData collectionMeetingCalendar = null; return new GroupGeneralData(id, name, externalId, status, activationDate, officeId, officeName, centerId, centerName, staffId, staffName, hierarchy, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, - role); + role, calendarsData, collectionMeetingCalendar); } private GroupGeneralData(final Long id, final String name, final String externalId, final EnumOptionData status, @@ -117,7 +113,8 @@ public class GroupGeneralData { final Long staffId, final String staffName, final String hierarchy, final Collection clientMembers, final Collection centerOptions, final Collection officeOptions, final Collection staffOptions, final Collection clientOptions, - final Collection groupRoles, final Collection availableRoles, final GroupRoleData role) { + final Collection groupRoles, final Collection availableRoles, final GroupRoleData role, + final Collection calendarsData, final CalendarData collectionMeetingCalendar) { this.id = id; this.name = name; this.externalId = externalId; @@ -152,6 +149,8 @@ public class GroupGeneralData { this.groupRoles = groupRoles; this.availableRoles = availableRoles; this.selectedRole = role; + this.calendarsData = calendarsData; + this.collectionMeetingCalendar = collectionMeetingCalendar; } public Long getId() { @@ -174,7 +173,7 @@ public class GroupGeneralData { return new GroupGeneralData(grouping.id, grouping.name, grouping.externalId, grouping.status, grouping.activationDate, grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName, grouping.hierarchy, grouping.clientMembers, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions, - grouping.clientOptions, grouping.groupRoles, grouping.availableRoles, selectedRole); + grouping.clientOptions, grouping.groupRoles, grouping.availableRoles, selectedRole, grouping.calendarsData, grouping.collectionMeetingCalendar); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java index 4f128779b..65a9915de 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java @@ -384,4 +384,13 @@ public final class Group extends AbstractPersistable { public boolean isCenter() { return this.groupLevel.isCenter(); } + + public boolean isChildClient(final Long clientId){ + if (clientId != null && this.clientMembers != null && !this.clientMembers.isEmpty()) { + for (Client client : this.clientMembers) { + if(client.getId().equals(clientId)) return true; + } + } + return false; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupRepository.java index f99bdabbf..4a0402fef 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupRepository.java @@ -5,9 +5,11 @@ */ package org.mifosplatform.portfolio.group.domain; +import java.util.Collection; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; public interface GroupRepository extends JpaRepository, JpaSpecificationExecutor { - + Collection findByParentId(Long parentId); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/ClientNotInGroupException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/ClientNotInGroupException.java index 951ec72eb..c87e5eb42 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/ClientNotInGroupException.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/ClientNotInGroupException.java @@ -10,8 +10,12 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRul public class ClientNotInGroupException extends AbstractPlatformDomainRuleException { public ClientNotInGroupException(final Long clientId, final Long groupId) { - super("error.msg.group.client.not.in.group", "Client with identifier " + clientId + " is not in Group with identifier " + groupId, - clientId, groupId); + this("group.client.not.in.group", "Client with identifier " + clientId + " is not in Group with identifier " + groupId, clientId, groupId); + + } + + public ClientNotInGroupException(String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) { + super("error.msg." + postFix , defaultUserMessage, defaultUserMessageArgs); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 5c14f0708..dda71b4bc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -56,7 +56,7 @@ import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; import org.mifosplatform.portfolio.accountdetails.domain.AccountType; -import org.mifosplatform.portfolio.calendar.service.CalendarHelper; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.collateral.data.CollateralData; @@ -2387,41 +2387,47 @@ public class Loan extends AbstractPersistable { public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule, final boolean isHolidayEnabled, final List holidays, final WorkingDays workingDays) { - //update repayment dates of repayment schedule installaments - LocalDate tmpFromDate = this.getDisbursementDate();//first repayment's from date is same as disbursement date. + + //first repayment's from date is same as disbursement date. + LocalDate tmpFromDate = this.getDisbursementDate(); final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType(); final Integer loanRepaymentInterval = this.loanRepaymentScheduleDetail.getRepayEvery(); - final String frequency = CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType); + final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType); LocalDate newRepaymentDate = null; for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) { final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate(); // FIXME: AA this won't update repayment dates before current date. if (oldDueDate.isAfter(meetingStartDate) && oldDueDate.isAfter(DateUtils.getLocalDateOfTenant())) { - - newRepaymentDate = CalendarHelper.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate, + newRepaymentDate = CalendarUtils.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate, loanRepaymentInterval, frequency, workingDays); final LocalDate maxDateLimitForNewRepayment = getMaxDateLimitForNewRepayment(repaymentPeriodFrequencyType, loanRepaymentInterval, tmpFromDate); if (newRepaymentDate.isAfter(maxDateLimitForNewRepayment)) { - newRepaymentDate = CalendarHelper.getNextRepaymentMeetingDate(recuringRule, meetingStartDate, tmpFromDate, + newRepaymentDate = CalendarUtils.getNextRepaymentMeetingDate(recuringRule, meetingStartDate, tmpFromDate, loanRepaymentInterval, frequency, workingDays); } if (isHolidayEnabled) { - newRepaymentDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(newRepaymentDate, holidays); + // reset repayment date with new meeting date + newRepaymentDate = CalendarUtils.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate, + loanRepaymentInterval, frequency, workingDays); + if (isHolidayEnabled) { + newRepaymentDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(newRepaymentDate, holidays); + } + + loanRepaymentScheduleInstallment.updateDueDate(newRepaymentDate); + // reset from date to get actual daysInPeriod + loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate); + tmpFromDate = newRepaymentDate;// update with new repayment + // date + } else { + tmpFromDate = oldDueDate; } - loanRepaymentScheduleInstallment.updateDueDate(newRepaymentDate); - // reset from date to get actual daysInPeriod - loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate); - tmpFromDate = newRepaymentDate;// update with new repayment date - } else { - tmpFromDate = oldDueDate; } - } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index 367694e52..9634c8be5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -28,7 +28,7 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarRepository; import org.mifosplatform.portfolio.calendar.exception.CalendarNotFoundException; import org.mifosplatform.portfolio.calendar.exception.MeetingFrequencyMismatchException; -import org.mifosplatform.portfolio.calendar.service.CalendarHelper; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.group.domain.Group; @@ -149,9 +149,9 @@ public class LoanScheduleAssembler { if (calendar == null) { throw new CalendarNotFoundException(calendarId); } // validate repayment frequency and interval with meeting frequency and interval - PeriodFrequencyType meetingPeriodFrequency = CalendarHelper.getMeetingPeriodFrequencyType(calendar.getRecurrence()); + PeriodFrequencyType meetingPeriodFrequency = CalendarUtils.getMeetingPeriodFrequencyType(calendar.getRecurrence()); validateRepaymentFrequencyIsSameAsMeetingFrequency(meetingPeriodFrequency.getValue(), repaymentFrequencyType, - CalendarHelper.getInterval(calendar.getRecurrence()), repaymentEvery); + CalendarUtils.getInterval(calendar.getRecurrence()), repaymentEvery); } if (synchDisbursement != null && synchDisbursement.booleanValue()) { @@ -166,8 +166,8 @@ public class LoanScheduleAssembler { if (calculatedRepaymentsStartingFromDate == null) { // FIXME: AA - Possibility of having next meeting date immediately after disbursement date, // need to have minimum number of days gap between disbursement and first repayment date. - final String frequency = CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType); - calculatedRepaymentsStartingFromDate = CalendarHelper.getFirstRepaymentMeetingDate(calendar, expectedDisbursementDate, + final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType); + calculatedRepaymentsStartingFromDate = CalendarUtils.getFirstRepaymentMeetingDate(calendar, expectedDisbursementDate, repaymentEvery, frequency); } else {// validate user provided repaymentsStartFromDate validateRepaymentsStartDateWithMeetingDates(repaymentsStartingFromDate, calendar); @@ -192,7 +192,7 @@ public class LoanScheduleAssembler { private void validateRepaymentsStartDateWithMeetingDates(final LocalDate repaymentsStartingFromDate, final Calendar calendar) { if (repaymentsStartingFromDate != null - && !CalendarHelper.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), + && !CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), repaymentsStartingFromDate)) { final String errorMessage = "First repayment date '" + repaymentsStartingFromDate + "' do not fall on a meeting date"; throw new LoanApplicationDateException("first.repayment.date.do.not.match.meeting.date", errorMessage, @@ -202,7 +202,7 @@ public class LoanScheduleAssembler { private void validateDisbursementDateWithMeetingDates(final LocalDate expectedDisbursementDate, final Calendar calendar) { // disbursement date should fall on a meeting date - if (!CalendarHelper.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), expectedDisbursementDate)) { + if (!CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), expectedDisbursementDate)) { final String errorMessage = "Expected disbursement date '" + expectedDisbursementDate + "' do not fall on a meeting date"; throw new LoanApplicationDateException("disbursement.date.do.not.match.meeting.date", errorMessage, expectedDisbursementDate); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index a5a9149fd..35631a06e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -24,7 +24,7 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; import org.mifosplatform.portfolio.calendar.exception.NotValidRecurringDateException; -import org.mifosplatform.portfolio.calendar.service.CalendarHelper; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -76,7 +76,7 @@ public final class LoanEventApiJsonValidator { Calendar calendar = calendarInstance.getCalendar(); if (calendar != null && actualDisbursementDate != null) { // Disbursement date should fall on a meeting date - if (!CalendarHelper.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), actualDisbursementDate)) { + if (!CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), actualDisbursementDate)) { final String errorMessage = "Expected disbursement date '" + actualDisbursementDate.toString() + "' does not fall on a meeting date."; throw new NotValidRecurringDateException("loan.actual.disbursement.date", errorMessage, actualDisbursementDate.toString(), @@ -147,7 +147,7 @@ public final class LoanEventApiJsonValidator { Calendar calendar = calendarInstance.getCalendar(); if (calendar != null && repaymentDate != null) { // Disbursement date should fall on a meeting date - if (!CalendarHelper.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), repaymentDate)) { + if (!CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), repaymentDate)) { final String errorMessage = "Transaction date '" + repaymentDate.toString() + "' does not fall on a meeting date."; throw new NotValidRecurringDateException("loan.transaction.date", errorMessage, repaymentDate.toString(), diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index b4decf64c..bc95be622 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -41,6 +41,7 @@ import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.fund.data.FundData; import org.mifosplatform.portfolio.fund.service.FundReadPlatformService; import org.mifosplatform.portfolio.group.data.GroupGeneralData; +import org.mifosplatform.portfolio.group.data.GroupRoleData; import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.loanaccount.data.DisbursementData; @@ -280,7 +281,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { // get group associations Collection membersOfGroup = this.clientReadPlatformService.retrieveClientMembersOfGroup(groupId); if (!CollectionUtils.isEmpty(membersOfGroup)) { - groupAccount = GroupGeneralData.withAssocations(groupAccount, membersOfGroup, null); + final Collection calendarsData = null; + final CalendarData collectionMeetingCalendar = null; + final Collection groupRoles = null; + groupAccount = GroupGeneralData.withAssocations(groupAccount, membersOfGroup, groupRoles, calendarsData, collectionMeetingCalendar); } final LocalDate expectedDisbursementDate = DateUtils.getLocalDateOfTenant(); @@ -958,7 +962,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { // get group associations Collection membersOfGroup = this.clientReadPlatformService.retrieveActiveClientMembersOfGroup(groupId); if (!CollectionUtils.isEmpty(membersOfGroup)) { - groupAccount = GroupGeneralData.withAssocations(groupAccount, membersOfGroup, null); + final Collection calendarsData = null; + final CalendarData collectionMeetingCalendar = null; + final Collection groupRoles = null; + groupAccount = GroupGeneralData.withAssocations(groupAccount, membersOfGroup, groupRoles, calendarsData, collectionMeetingCalendar); } return LoanAccountData.groupDefaults(groupAccount, expectedDisbursementDate); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 4e882616f..6300b2494 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -39,7 +39,7 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository; -import org.mifosplatform.portfolio.calendar.service.CalendarHelper; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.charge.domain.Charge; import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper; import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException; @@ -244,9 +244,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (repaymentScheduleDetails != null) {// Not expecting to be // null final Integer repayEvery = repaymentScheduleDetails.getRepayEvery(); - final String frequency = CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentScheduleDetails + final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentScheduleDetails .getRepaymentPeriodFrequencyType()); - calculatedRepaymentsStartingFromDate = CalendarHelper.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate, repayEvery, + calculatedRepaymentsStartingFromDate = CalendarUtils.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate, repayEvery, frequency); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/MeetingApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/MeetingApiConstants.java new file mode 100644 index 000000000..8e5850057 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/MeetingApiConstants.java @@ -0,0 +1,44 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class MeetingApiConstants { + + public static final String MEETING_RESOURCE_NAME = "meeting"; + + // general + public static final String localeParamName = "locale"; + public static final String dateFormatParamName = "dateFormat"; + + // meetings parameters + public static final String idParamName = "id"; + public static final String meetingDateParamName = "meetingDate"; + public static final String calendarIdParamName = "calendarId"; + public static final String clientsAttendanceParamName = "clientsAttendance"; + + // attendance parameters + public static final String clientIdParamName = "clientId"; + public static final String attendanceTypeParamName = "attendanceType"; + + // attendance response parameters + public static final String clientsAttendance = "clientsAttendance"; + + // template response parameters + public static final String clients = "clients"; + public static final String calendarData = "calendarData"; + public static final String attendanceTypeOptions = "attendanceTypeOptions"; + + public static final Set MEETING_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(meetingDateParamName, + localeParamName, dateFormatParamName, calendarIdParamName, clientsAttendanceParamName)); + + public static final Set MEETING_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, meetingDateParamName, + clientsAttendance, clients, calendarData, attendanceTypeOptions)); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/api/MeetingsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/api/MeetingsApiResource.java new file mode 100644 index 000000000..e69c70753 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/api/MeetingsApiResource.java @@ -0,0 +1,221 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.api; + +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.MEETING_RESOURCE_NAME; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.MEETING_RESPONSE_DATA_PARAMETERS; + +import java.util.Collection; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.calendar.data.CalendarData; +import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; +import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; +import org.mifosplatform.portfolio.meeting.attendance.data.ClientAttendanceData; +import org.mifosplatform.portfolio.meeting.attendance.service.AttendanceDropdownReadPlatformService; +import org.mifosplatform.portfolio.meeting.attendance.service.ClientAttendanceReadPlatformService; +import org.mifosplatform.portfolio.meeting.data.MeetingData; +import org.mifosplatform.portfolio.meeting.exception.MeetingNotSupportedResourceException; +import org.mifosplatform.portfolio.meeting.service.MeetingReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/{entityType}/{entityId}/meetings") +@Component +@Scope("singleton") +public class MeetingsApiResource { + + private final PlatformSecurityContext context; + private final MeetingReadPlatformService readPlatformService; + private final ClientAttendanceReadPlatformService attendanceReadPlatformService; + private final ClientReadPlatformService clientReadPlatformService; + private final CalendarReadPlatformService calendarReadPlatformService; + private final AttendanceDropdownReadPlatformService attendanceDropdownReadPlatformService; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + + @Autowired + public MeetingsApiResource(PlatformSecurityContext context, MeetingReadPlatformService readPlatformService, + ClientAttendanceReadPlatformService attendanceReadPlatformService, final ClientReadPlatformService clientReadPlatformService, + final CalendarReadPlatformService calendarReadPlatformService, + final AttendanceDropdownReadPlatformService attendanceDropdownReadPlatformService, + DefaultToApiJsonSerializer toApiJsonSerializer, ApiRequestParameterHelper apiRequestParameterHelper, + PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { + this.context = context; + this.readPlatformService = readPlatformService; + this.attendanceReadPlatformService = attendanceReadPlatformService; + this.clientReadPlatformService = clientReadPlatformService; + this.calendarReadPlatformService = calendarReadPlatformService; + this.attendanceDropdownReadPlatformService = attendanceDropdownReadPlatformService; + this.toApiJsonSerializer = toApiJsonSerializer; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String template(@PathParam("entityType") final String entityType, @PathParam("entityId") final Long entityId, + @QueryParam("calendarId") Long calendarId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(MEETING_RESOURCE_NAME); + final Integer entityTypeId = CalendarEntityType.valueOf(entityType.toUpperCase()).getValue(); + Collection clients = null; + CalendarData calendarData = null; + + if (CalendarEntityType.isGroup(entityType)) { + clients = this.clientReadPlatformService.retrieveActiveClientMembersOfGroup(entityId); + } else if (CalendarEntityType.isCenter(entityType)) { + clients = this.clientReadPlatformService.retrieveActiveClientMembersOfCenter(entityId); + } else { + final String defaultUserMessage = "Meeting attendance is not supported for the resource " + entityType + + ". The supported resources are [" + CalendarEntityType.GROUPS.name() + ", " + CalendarEntityType.CENTERS.name() + "]"; + throw new MeetingNotSupportedResourceException(defaultUserMessage, CalendarEntityType.GROUPS.name(), CalendarEntityType.CENTERS.name()); + } + + if (calendarId != null) { + calendarData = this.calendarReadPlatformService.retrieveCalendar(calendarId, entityId, entityTypeId); + calendarData = this.calendarReadPlatformService.generateRecurringDate(calendarData, DateUtils.getLocalDateOfTenant()); + } + + final MeetingData meetingData = MeetingData.template(clients, calendarData, + attendanceDropdownReadPlatformService.retrieveAttendanceTypeOptions()); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, meetingData, MEETING_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveMeetings(@PathParam("entityType") final String entityType, @PathParam("entityId") final Long entityId, + @QueryParam("limit") final Integer limit, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(MEETING_RESOURCE_NAME); + + final Collection meetingsData = this.readPlatformService.retrieveMeetingsByEntity(entityId, CalendarEntityType + .valueOf(entityType.toUpperCase()).getValue(), limit); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, meetingsData, MEETING_RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{meetingId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveMeeting(@PathParam("meetingId") final Long meetingId, @PathParam("entityType") final String entityType, + @PathParam("entityId") final Long entityId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(MEETING_RESOURCE_NAME); + final Integer entityTypeId = CalendarEntityType.valueOf(entityType.toUpperCase()).getValue(); + MeetingData meetingData = this.readPlatformService.retrieveMeeting(meetingId, entityId, entityTypeId); + final Collection clientsAttendance = this.attendanceReadPlatformService + .retrieveClientAttendanceByMeetingId(meetingId); + meetingData = MeetingData.withClientsAttendanceAndAttendanceTypeOptions(meetingData, clientsAttendance, + attendanceDropdownReadPlatformService.retrieveAttendanceTypeOptions()); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, meetingData, MEETING_RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String createMeeting(@PathParam("entityType") final String entityType, @PathParam("entityId") final Long entityId, + final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createMeeting(entityType, entityId) + .withJson(apiRequestBodyAsJson).build(); + + CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + + } + + @POST + @Path("{meetingId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String performMeetingCommands(@PathParam("entityType") final String entityType, @PathParam("entityId") final Long entityId, + @PathParam("meetingId") final Long meetingId, @QueryParam("command") final String commandParam, + final String apiRequestBodyAsJson) { + + final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); + + CommandProcessingResult result = null; + if (is(commandParam, "saveOrUpdateAttendance")) { + final CommandWrapper commandRequest = builder.saveOrUpdateAttendance(meetingId, entityType, entityId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else { + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "saveOrUpdateAttendance" }); + } + + return this.toApiJsonSerializer.serialize(result); + + } + + @PUT + @Path("{meetingId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String updateMeeting(@PathParam("entityType") final String entityType, @PathParam("entityId") final Long entityId, + @PathParam("meetingId") final Long meetingId, final String jsonRequestBody) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateMeeting(entityType, entityId, meetingId) + .withJson(jsonRequestBody).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @DELETE + @Path("{meetingId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String deleteMeeting(@PathParam("entityType") final String entityType, @PathParam("entityId") final Long entityId, + @PathParam("meetingId") final Long meetingId) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteMeeting(entityType, entityId, meetingId).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/AttendanceType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/AttendanceType.java new file mode 100644 index 000000000..92fc1383b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/AttendanceType.java @@ -0,0 +1,63 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.attendance; + +import org.mifosplatform.portfolio.meeting.attendance.domain.ClientAttendance; + +/** + * An enumeration of {@link ClientAttendance} type. + */ +public enum AttendanceType { + + INVALID(0, "attendanceType.invalid"), // + PRESENT(1, "attendanceType.present"), // + ABSENT(2, "attendanceType.absent"), // + APPROVED(3, "attendanceType.approved"), // + LEAVE(4, "attendanceType.leave"), // + LATE(5, "attendanceType.late"); + + private final Integer value; + private final String code; + + private AttendanceType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return code; + } + + public static AttendanceType fromInt(final Integer attendanceTypeId) { + + if (attendanceTypeId == null) { return AttendanceType.INVALID; } + + AttendanceType attendanceType = AttendanceType.INVALID; + switch (attendanceTypeId) { + case 1: + attendanceType = AttendanceType.PRESENT; + break; + case 2: + attendanceType = AttendanceType.ABSENT; + break; + case 3: + attendanceType = AttendanceType.APPROVED; + break; + case 4: + attendanceType = AttendanceType.LEAVE; + break; + case 5: + attendanceType = AttendanceType.LATE; + break; + } + return attendanceType; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/data/ClientAttendanceData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/data/ClientAttendanceData.java new file mode 100644 index 000000000..4533d4eb7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/data/ClientAttendanceData.java @@ -0,0 +1,35 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.attendance.data; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; + +/** + * Immutable data object representing a ClientAttendance. + */ +public class ClientAttendanceData { + + @SuppressWarnings("unused") + private final Long id; + @SuppressWarnings("unused") + private final Long clientId; + @SuppressWarnings("unused") + private final String clientName; + @SuppressWarnings("unused") + private final EnumOptionData attendanceType; + + public static ClientAttendanceData instance(Long id, Long clientId, String clientName, EnumOptionData attendanceType){ + return new ClientAttendanceData(id, clientId, clientName, attendanceType); + } + + private ClientAttendanceData(Long id, Long clientId, String clientName, EnumOptionData attendanceType) { + this.id = id; + this.clientId = clientId; + this.clientName = clientName; + this.attendanceType = attendanceType; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/domain/ClientAttendance.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/domain/ClientAttendance.java new file mode 100644 index 000000000..e7c07ae75 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/domain/ClientAttendance.java @@ -0,0 +1,54 @@ +package org.mifosplatform.portfolio.meeting.attendance.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.meeting.domain.Meeting; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_client_attendance", uniqueConstraints = { @UniqueConstraint(columnNames = { "client_id", "meeting_id" }, name = "unique_client_meeting_attendance") }) +public class ClientAttendance extends AbstractPersistable { + + @ManyToOne + @JoinColumn(name = "client_id", nullable = false) + private Client client; + + @ManyToOne + @JoinColumn(name = "meeting_id", nullable = false) + private Meeting meeting; + + @Column(name = "attendance_type_enum", nullable = false) + private Integer attendanceTypeId; + + protected ClientAttendance() { + + } + + public static ClientAttendance createClientAttendance(final Client client, final Meeting meeting, final Integer attendanceTypeId) { + return new ClientAttendance(client, meeting, attendanceTypeId); + } + + private ClientAttendance(final Client client, final Meeting meeting, final Integer attendanceTypeId) { + this.client = client; + this.meeting = meeting; + this.attendanceTypeId = attendanceTypeId; + } + + public Long clientId() { + return client.getId(); + } + + public void updateAttendanceTypeId(final Integer attendanceTypeId) { + this.attendanceTypeId = attendanceTypeId; + } + + public Integer getAttendanceTypeId() { + return this.attendanceTypeId; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/domain/ClientAttendanceRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/domain/ClientAttendanceRepository.java new file mode 100644 index 000000000..e2615baee --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/domain/ClientAttendanceRepository.java @@ -0,0 +1,13 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.attendance.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface ClientAttendanceRepository extends JpaRepository, JpaSpecificationExecutor { + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceDropdownReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceDropdownReadPlatformService.java new file mode 100644 index 000000000..ee99387c8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceDropdownReadPlatformService.java @@ -0,0 +1,14 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.attendance.service; + +import java.util.List; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; + +public interface AttendanceDropdownReadPlatformService { + List retrieveAttendanceTypeOptions(); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceDropdownReadPlatformServiceImpl.java new file mode 100644 index 000000000..86e4dafd9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceDropdownReadPlatformServiceImpl.java @@ -0,0 +1,22 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.attendance.service; + +import java.util.List; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.meeting.attendance.AttendanceType; +import org.springframework.stereotype.Service; + +@Service +public class AttendanceDropdownReadPlatformServiceImpl implements AttendanceDropdownReadPlatformService { + + @Override + public List retrieveAttendanceTypeOptions() { + return AttendanceEnumerations.attendanceType(AttendanceType.values()); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceEnumerations.java new file mode 100644 index 000000000..258739ab9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/AttendanceEnumerations.java @@ -0,0 +1,57 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.attendance.service; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.meeting.attendance.AttendanceType; + +public class AttendanceEnumerations { + + public static EnumOptionData attendanceType(final int attendanceType) { + return attendanceType(AttendanceType.fromInt(attendanceType)); + } + + public static EnumOptionData attendanceType(final AttendanceType attendanceType) { + + EnumOptionData optionData = new EnumOptionData(AttendanceType.INVALID.getValue().longValue(), AttendanceType.INVALID.getCode(), + "Invalid"); + + switch (attendanceType) { + case INVALID: + optionData = new EnumOptionData(AttendanceType.INVALID.getValue().longValue(), AttendanceType.INVALID.getCode(), "Invalid"); + break; + case PRESENT: + optionData = new EnumOptionData(AttendanceType.PRESENT.getValue().longValue(), AttendanceType.PRESENT.getCode(), "Present"); + break; + case ABSENT: + optionData = new EnumOptionData(AttendanceType.ABSENT.getValue().longValue(), AttendanceType.ABSENT.getCode(), "Absent"); + break; + case APPROVED: + optionData = new EnumOptionData(AttendanceType.APPROVED.getValue().longValue(), AttendanceType.APPROVED.getCode(), + "Approved"); + break; + case LEAVE: + optionData = new EnumOptionData(AttendanceType.LEAVE.getValue().longValue(), AttendanceType.LEAVE.getCode(), "Leave"); + break; + case LATE: + optionData = new EnumOptionData(AttendanceType.LATE.getValue().longValue(), AttendanceType.LATE.getCode(), "Late"); + break; + } + return optionData; + } + + public static List attendanceType(final AttendanceType[] attendanceTypes) { + final List optionDatas = new ArrayList(); + for (final AttendanceType attendanceType : attendanceTypes) { + if(attendanceType.getValue().equals(AttendanceType.INVALID.getValue())) continue; + optionDatas.add(attendanceType(attendanceType)); + } + return optionDatas; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/ClientAttendanceReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/ClientAttendanceReadPlatformService.java new file mode 100644 index 000000000..77a22ea54 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/ClientAttendanceReadPlatformService.java @@ -0,0 +1,14 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.attendance.service; + +import java.util.Collection; + +import org.mifosplatform.portfolio.meeting.attendance.data.ClientAttendanceData; + +public interface ClientAttendanceReadPlatformService { + Collection retrieveClientAttendanceByMeetingId(final Long meetingId); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/ClientAttendanceReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/ClientAttendanceReadPlatformServiceImpl.java new file mode 100644 index 000000000..a28c7365e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/attendance/service/ClientAttendanceReadPlatformServiceImpl.java @@ -0,0 +1,59 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.attendance.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.portfolio.meeting.attendance.data.ClientAttendanceData; +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 ClientAttendanceReadPlatformServiceImpl implements ClientAttendanceReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public ClientAttendanceReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class ClientAttendanceDataMapper implements RowMapper { + + public String schema() { + + return " select ca.id as id, ca.client_id as clientId, ca.attendance_type_enum as attendanceTypeId, " + + " c.display_name as clientName from m_meeting m inner join m_client_attendance ca on m.id = ca.meeting_id " + + " inner join m_client c on ca.client_id=c.id "; + } + + @Override + public ClientAttendanceData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final Long clientId = rs.getLong("clientId"); + final Integer attendanceTypeId = rs.getInt("attendanceTypeId"); + final String clientName = rs.getString("clientName"); + final EnumOptionData attendanceType = AttendanceEnumerations.attendanceType(attendanceTypeId); + return ClientAttendanceData.instance(id, clientId, clientName, attendanceType); + } + } + + @Override + public Collection retrieveClientAttendanceByMeetingId(final Long meetingId) { + final ClientAttendanceDataMapper rm = new ClientAttendanceDataMapper(); + final String sql = rm.schema() + " where m.id = ? "; + + return this.jdbcTemplate.query(sql, rm, new Object[] { meetingId }); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingData.java new file mode 100644 index 000000000..8fd10081f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingData.java @@ -0,0 +1,69 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.data; + +import java.util.Collection; +import java.util.List; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.calendar.data.CalendarData; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.meeting.attendance.data.ClientAttendanceData; + +/** + * Immutable data object representing a Meeting. + */ +public class MeetingData { + + private final Long id; + private final LocalDate meetingDate; + private final Collection clientsAttendance; + + // template data + private final Collection clients; + private final CalendarData calendarData; + @SuppressWarnings("unused") + private final List attendanceTypeOptions; + + public static MeetingData instance(final Long id, final LocalDate meetingDate) { + final Collection clientsAttendance = null; + final Collection clients = null; + final CalendarData calendarData = null; + final List attendanceTypeOptions = null; + return new MeetingData(id, meetingDate, clientsAttendance, clients, calendarData, attendanceTypeOptions); + } + + public static MeetingData withClientsAttendanceAndAttendanceTypeOptions(final MeetingData meetingData, + final Collection clientsAttendance, final List attendanceTypesOptions) { + return new MeetingData(meetingData.id, meetingData.meetingDate, clientsAttendance, meetingData.clients, meetingData.calendarData, + attendanceTypesOptions); + } + + public static MeetingData template(final Collection clients, final CalendarData calendarData, + final List attendanceTypeOptions) { + final Long id = null; + final LocalDate meetingDate = null; + final Collection clientsAttendance = null; + return new MeetingData(id, meetingDate, clientsAttendance, clients, calendarData, attendanceTypeOptions); + } + + public static MeetingData withAttendanceTypeOptions(final MeetingData meetingData, final List attendanceTypeOptions) { + + return new MeetingData(meetingData.id, meetingData.meetingDate, meetingData.clientsAttendance, meetingData.clients, + meetingData.calendarData, attendanceTypeOptions); + } + + private MeetingData(Long id, LocalDate meetingDate, Collection clientsAttendance, + final Collection clients, final CalendarData calendarData, final List attendanceTypeOptions) { + this.id = id; + this.meetingDate = meetingDate; + this.clientsAttendance = clientsAttendance; + this.clients = clients; + this.calendarData = calendarData; + this.attendanceTypeOptions = attendanceTypeOptions; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingDataValidator.java new file mode 100644 index 000000000..aab73bdaf --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingDataValidator.java @@ -0,0 +1,144 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.data; + +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.MEETING_REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.MEETING_RESOURCE_NAME; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.clientsAttendanceParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.attendanceTypeParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.calendarIdParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.clientIdParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.meetingDateParamName; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; + +@Component +public class MeetingDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public MeetingDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validateForCreate(final JsonCommand command) { + final String json = command.json(); + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, MEETING_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(MEETING_RESOURCE_NAME); + final JsonElement element = fromApiJsonHelper.parse(json); + + final String meetingDateStr = this.fromApiJsonHelper.extractStringNamed(meetingDateParamName, element); + baseDataValidator.reset().parameter(meetingDateParamName).value(meetingDateStr).notBlank(); + + if(!StringUtils.isBlank(meetingDateStr)){ + final LocalDate meetingDate = this.fromApiJsonHelper.extractLocalDateNamed(meetingDateParamName, element); + baseDataValidator.reset().parameter(meetingDateParamName).value(meetingDate).notNull(); + } + + final Long calendarId = this.fromApiJsonHelper.extractLongNamed(calendarIdParamName, element); + baseDataValidator.reset().parameter(calendarIdParamName).value(calendarId).notNull(); + + validateAttendanceDetails(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForUpdate(final JsonCommand command) { + final String json = command.json(); + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, MEETING_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(MEETING_RESOURCE_NAME); + final JsonElement element = fromApiJsonHelper.parse(json); + + if (this.fromApiJsonHelper.parameterExists(meetingDateParamName, element)) { + final String meetingDateStr = this.fromApiJsonHelper.extractStringNamed(meetingDateParamName, element); + baseDataValidator.reset().parameter(meetingDateParamName).value(meetingDateStr).notBlank(); + + if(!StringUtils.isBlank(meetingDateStr)){ + final LocalDate meetingDate = this.fromApiJsonHelper.extractLocalDateNamed(meetingDateParamName, element); + baseDataValidator.reset().parameter(meetingDateParamName).value(meetingDate).notNull(); + } + } + + if(this.fromApiJsonHelper.parameterExists(calendarIdParamName, element)){ + final Long calendarId = this.fromApiJsonHelper.extractLongNamed(calendarIdParamName, element); + baseDataValidator.reset().parameter(calendarIdParamName).value(calendarId).notNull(); + } + + validateAttendanceDetails(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForUpdateAttendance(final JsonCommand command) { + final String json = command.json(); + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, MEETING_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(MEETING_RESOURCE_NAME); + final JsonElement element = fromApiJsonHelper.parse(json); + + validateAttendanceDetails(element, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + private void validateAttendanceDetails(final JsonElement element, final DataValidatorBuilder baseDataValidator){ + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + if (element.isJsonObject()) { + if (topLevelJsonElement.has(clientsAttendanceParamName) + && topLevelJsonElement.get(clientsAttendanceParamName).isJsonArray()) { + final JsonArray array = topLevelJsonElement.get(clientsAttendanceParamName).getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + final JsonObject attendanceElement = array.get(i).getAsJsonObject(); + final Long clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParamName, attendanceElement); + final Long attendanceType = this.fromApiJsonHelper.extractLongNamed(attendanceTypeParamName, attendanceElement); + baseDataValidator.reset().parameter(clientsAttendanceParamName + "[" + i + "]." + clientIdParamName).value(clientId).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter(clientsAttendanceParamName + "[" + i + "]." + attendanceTypeParamName).value(attendanceType).notNull().integerGreaterThanZero(); + } + } + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java new file mode 100644 index 000000000..1dcabee46 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java @@ -0,0 +1,188 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.mifosplatform.portfolio.meeting.domain; + +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.clientsAttendanceParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.attendanceTypeParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.clientIdParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.meetingDateParamName; + +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.UniqueConstraint; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.portfolio.calendar.domain.Calendar; +import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; +import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; +import org.mifosplatform.portfolio.calendar.exception.NotValidRecurringDateException; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; +import org.mifosplatform.portfolio.meeting.attendance.domain.ClientAttendance; +import org.mifosplatform.portfolio.meeting.exception.MeetingDateException; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_meeting", uniqueConstraints = { @UniqueConstraint(columnNames = { "calendar_instance_id", "meeting_date" }, name = "unique_calendar_instance_id_meeting_date") }) +public class Meeting extends AbstractPersistable { + + @ManyToOne + @JoinColumn(name = "calendar_instance_id", nullable = false) + private CalendarInstance calendarInstance; + + @Column(name = "meeting_date", nullable = false) + @Temporal(TemporalType.DATE) + private Date meetingDate; + + @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "meeting", orphanRemoval = true) + private Set clientsAttendance; + + protected Meeting() { + // + } + + private Meeting(CalendarInstance calendarInstance, Date meetingDate) { + this.calendarInstance = calendarInstance; + this.meetingDate = meetingDate; + } + + public static Meeting createNew(final CalendarInstance calendarInstance, final Date meetingDate) { + + if(!isValidMeetingDate(calendarInstance, meetingDate)){ + throw new NotValidRecurringDateException("meeting", "The date '" + meetingDate + "' is not a valid meeting date.", meetingDate); + } + return new Meeting(calendarInstance, meetingDate); + } + + public void associateClientsAttendance(final Collection clientsAttendance) { + //do not allow to capture attendance in advance. + if(isMeetingDateAfter(DateUtils.getLocalDateOfTenant())){ + final String errorMessage = "Attendance cannot be in the future."; + throw new MeetingDateException("cannot.be.a.future.date", errorMessage, getMeetingDateLocalDate()); + } + this.clientsAttendance = new HashSet(clientsAttendance); + } + + public Map update(final JsonCommand command) { + final Map actualChanges = new LinkedHashMap(9); + final String dateFormatAsInput = command.dateFormat(); + final String localeAsInput = command.locale(); + + if (command.isChangeInLocalDateParameterNamed(meetingDateParamName, getMeetingDateLocalDate())) { + final String valueAsInput = command.stringValueOfParameterNamed(meetingDateParamName); + final LocalDate newValue = command.localDateValueOfParameterNamed(meetingDateParamName); + actualChanges.put(meetingDateParamName, valueAsInput); + actualChanges.put("dateFormat", dateFormatAsInput); + actualChanges.put("locale", localeAsInput); + this.meetingDate = newValue.toDate(); + + if(!isValidMeetingDate(this.calendarInstance, this.meetingDate)){ + throw new NotValidRecurringDateException("meeting", "Not a valid meeting date", meetingDate); + } + + } + + return actualChanges; + } + + public Map updateAttendance(final Collection clientsAttendance) { + final Map actualChanges = new LinkedHashMap(1); + final Map clientAttendanceChanges = new LinkedHashMap(clientsAttendance.size()); + + updateAttendanceLoop: for (ClientAttendance clientAttendance : clientsAttendance) { + for (ClientAttendance clientAttendanceOriginal : this.clientsAttendance) { + if (clientAttendanceOriginal.clientId().equals(clientAttendance.clientId())) { + final Integer newValue = clientAttendance.getAttendanceTypeId(); + if(!newValue.equals(clientAttendanceOriginal.getAttendanceTypeId())){ + clientAttendanceOriginal.updateAttendanceTypeId(newValue); + final Map clientAttendanceChange = new LinkedHashMap(2); + clientAttendanceChange.put(clientIdParamName, clientAttendanceOriginal.clientId()); + clientAttendanceChange.put(attendanceTypeParamName, newValue); + clientAttendanceChanges.put(clientAttendanceOriginal.clientId().toString(), clientAttendanceChange); + } + continue updateAttendanceLoop; + } + } + + final Map clientAttendanceChange = new LinkedHashMap(); + clientAttendanceChange.put(clientIdParamName, clientAttendance.clientId()); + clientAttendanceChange.put(attendanceTypeParamName, clientAttendance.getAttendanceTypeId()); + clientAttendanceChanges.put(clientAttendance.clientId().toString(), clientAttendanceChange); + //New attendance record + this.clientsAttendance.add(clientAttendance); + } + + actualChanges.put(clientsAttendanceParamName, clientAttendanceChanges); + + return actualChanges; + } + + public Long entityId() { + return calendarInstance.getEntityId(); + } + + public boolean isCenterEntity(){ + return CalendarEntityType.isCenter(calendarInstance.getEntityTypeId()); + } + + public boolean isGroupEntity(){ + return CalendarEntityType.isGroup(calendarInstance.getEntityTypeId()); + } + + public LocalDate getMeetingDateLocalDate() { + LocalDate meetingDateLocalDate = null; + if (this.meetingDate != null) { + meetingDateLocalDate = LocalDate.fromDateFields(this.meetingDate); + } + return meetingDateLocalDate; + } + + public Date getMeetingDate(){ + return this.meetingDate; + } + + public boolean isMeetingDateBefore(final LocalDate newStartDate) { + return (this.meetingDate != null && newStartDate != null && getMeetingDateLocalDate().isBefore(newStartDate)) ? true : false; + } + + private static boolean isValidMeetingDate(final CalendarInstance calendarInstance, final Date meetingDate ){ + final Calendar calendar = calendarInstance.getCalendar(); + LocalDate meetingDateLocalDate = null; + if (meetingDate != null) { + meetingDateLocalDate = LocalDate.fromDateFields(meetingDate); + } + if(meetingDateLocalDate == null || !CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), meetingDateLocalDate)){ + return false; + } + return true; + } + + private boolean isMeetingDateAfter(final LocalDate date){ + return getMeetingDateLocalDate().isAfter(date); + } + + public Collection getClientsAttendance() { + return this.clientsAttendance; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/MeetingRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/MeetingRepository.java new file mode 100644 index 000000000..d13b66970 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/MeetingRepository.java @@ -0,0 +1,13 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface MeetingRepository extends JpaRepository, JpaSpecificationExecutor { + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/MeetingRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/MeetingRepositoryWrapper.java new file mode 100644 index 000000000..e80292a62 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/MeetingRepositoryWrapper.java @@ -0,0 +1,51 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.domain; + +import org.mifosplatform.portfolio.meeting.exception.MeetingNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * Wrapper for {@link MeetingRepository} that is responsible for checking + * if {@link Meeting} is returned when using findOne + * repository method and throwing an appropriate not found exception. + *

+ * + *

+ * This is to avoid need for checking and throwing in multiple areas of code + * base where {@link MeetingRepository} is required. + *

+ */ +@Service +public class MeetingRepositoryWrapper { + + private final MeetingRepository repository; + + @Autowired + public MeetingRepositoryWrapper(final MeetingRepository repository) { + this.repository = repository; + } + + public Meeting findOneWithNotFoundDetection(final Long meetingId) { + final Meeting meeting = this.repository.findOne(meetingId); + if (meeting == null) { throw new MeetingNotFoundException(meetingId); } + return meeting; + } + + public void save(final Meeting meeting) { + this.repository.save(meeting); + } + + public void delete(final Meeting meeting) { + this.repository.delete(meeting); + } + + public void saveAndFlush(final Meeting meeting) { + this.repository.saveAndFlush(meeting); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingDateException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingDateException.java new file mode 100644 index 000000000..5b3dd2c0f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingDateException.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * A {@link RuntimeException} thrown when Meeting date violates a domain rule. + */ +public class MeetingDateException extends AbstractPlatformDomainRuleException { + + public MeetingDateException(final String postFix, final String defaultUserMessage, final Object... defaultUserMessageArgs) { + super("error.msg.meeting.date." + postFix, defaultUserMessage, defaultUserMessageArgs); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingNotFoundException.java new file mode 100644 index 000000000..b5e200b93 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingNotFoundException.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when Meeting resources are not found. + */ +public class MeetingNotFoundException extends AbstractPlatformResourceNotFoundException { + + public MeetingNotFoundException(final Long id) { + super("error.msg.meeting.id.invalid", "Meeting with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingNotSupportedResourceException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingNotSupportedResourceException.java new file mode 100644 index 000000000..e240aeea5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/exception/MeetingNotSupportedResourceException.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * {@link AbstractPlatformDomainRuleException} thrown when meeting is captured against not supported resource. + */ +public class MeetingNotSupportedResourceException extends AbstractPlatformDomainRuleException { + + public MeetingNotSupportedResourceException(String defaultUserMessage, Object... defaultUserMessageArgs) { + super("error.msg.meeting.not.supported.resource", defaultUserMessage, defaultUserMessageArgs); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/CreateMeetingCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/CreateMeetingCommandHandler.java new file mode 100644 index 000000000..690817f59 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/CreateMeetingCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.meeting.service.MeetingWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CreateMeetingCommandHandler implements NewCommandSourceHandler { + + private final MeetingWritePlatformService writePlatformService; + + @Autowired + public CreateMeetingCommandHandler(final MeetingWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.createMeeting(command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/DeleteMeetingCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/DeleteMeetingCommandHandler.java new file mode 100644 index 000000000..cf1ef32ed --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/DeleteMeetingCommandHandler.java @@ -0,0 +1,31 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.meeting.service.MeetingWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DeleteMeetingCommandHandler implements NewCommandSourceHandler { + + private final MeetingWritePlatformService writePlatformService; + + @Autowired + public DeleteMeetingCommandHandler(final MeetingWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.deleteMeeting(command.entityId()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/UpdateMeetingAttendanceCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/UpdateMeetingAttendanceCommandHandler.java new file mode 100644 index 000000000..cf52a63e0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/UpdateMeetingAttendanceCommandHandler.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.meeting.service.MeetingWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UpdateMeetingAttendanceCommandHandler implements NewCommandSourceHandler { + + private final MeetingWritePlatformService writePlatformService; + + @Autowired + public UpdateMeetingAttendanceCommandHandler(final MeetingWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.saveOrUpdateAttendance(command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/UpdateMeetingCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/UpdateMeetingCommandHandler.java new file mode 100644 index 000000000..208eeac77 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/handler/UpdateMeetingCommandHandler.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.meeting.service.MeetingWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UpdateMeetingCommandHandler implements NewCommandSourceHandler { + + private final MeetingWritePlatformService writePlatformService; + + @Autowired + public UpdateMeetingCommandHandler(final MeetingWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.updateMeeting(command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformService.java new file mode 100644 index 000000000..05350ccd5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformService.java @@ -0,0 +1,21 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.service; + +import java.util.Collection; +import java.util.List; + +import org.mifosplatform.portfolio.meeting.data.MeetingData; + +public interface MeetingReadPlatformService { + + MeetingData retrieveMeeting(final Long meetingId, Long entityId, Integer entityTypeId); + + Collection retrieveMeetingsByEntity(final Long entityId, final Integer entityTypeId, Integer limit); + + Collection retrieveMeetingsByEntityByCalendarType(final Long entityId, final Integer entityTypeId, + final List calendarTypeOptions); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformServiceImpl.java new file mode 100644 index 000000000..9904e5b7f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformServiceImpl.java @@ -0,0 +1,89 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; +import org.mifosplatform.portfolio.meeting.data.MeetingData; +import org.mifosplatform.portfolio.meeting.exception.MeetingNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class MeetingReadPlatformServiceImpl implements MeetingReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public MeetingReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class MeetingDataMapper implements RowMapper { + + public String schema() { + + return " select m.id as id, m.meeting_date as meetingDate from m_meeting m inner join m_calendar_instance ci on m.calendar_instance_id = ci.id "; + } + + @Override + public MeetingData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final LocalDate meetingDate = JdbcSupport.getLocalDate(rs, "meetingDate"); + + return MeetingData.instance(id, meetingDate); + } + } + + @Override + public MeetingData retrieveMeeting(final Long meetingId, Long entityId, Integer entityTypeId) { + + try { + final MeetingDataMapper rm = new MeetingDataMapper(); + + final String sql = rm.schema() + " where m.id = ? and ci.entity_id = ? and ci.entity_type_enum = ? "; + + return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { meetingId, entityId, entityTypeId }); + } catch (final EmptyResultDataAccessException e) { + throw new MeetingNotFoundException(meetingId); + } + } + + @Override + public Collection retrieveMeetingsByEntity(Long entityId, Integer entityTypeId, Integer limit) { + final MeetingDataMapper rm = new MeetingDataMapper(); + String sql = rm.schema() + " where ci.entity_id = ? and ci.entity_type_enum = ? "; + if(limit != null && limit > 0){ + sql = sql + " order by m.meeting_date desc " + " limit " + limit; + } + + return this.jdbcTemplate.query(sql, rm, new Object[] { entityId, entityTypeId }); + } + + @Override + public Collection retrieveMeetingsByEntityByCalendarType(Long entityId, Integer entityTypeId, + List calendarTypeOptions) { + final MeetingDataMapper rm = new MeetingDataMapper(); + String sqlCalendarTypeOptions = CalendarUtils.getSqlCalendarTypeOptionsInString(calendarTypeOptions); + final String sql = rm.schema() + + " inner join m_calendar c on ci.calendar_id=c.id where ci.entity_id = ? and ci.entity_type_enum = ? and c.calendar_type_enum in (" + + sqlCalendarTypeOptions + ") order by c.start_date "; + + return this.jdbcTemplate.query(sql, rm, new Object[] { entityId, entityTypeId }); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingWritePlatformService.java new file mode 100644 index 000000000..5d7ac4952 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingWritePlatformService.java @@ -0,0 +1,20 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface MeetingWritePlatformService { + + CommandProcessingResult createMeeting(JsonCommand command); + + CommandProcessingResult updateMeeting(JsonCommand command); + + CommandProcessingResult deleteMeeting(Long meetingId); + + CommandProcessingResult saveOrUpdateAttendance(JsonCommand command); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..f4b39854c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,229 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.meeting.service; + +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.attendanceTypeParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.calendarIdParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.clientIdParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.clientsAttendanceParamName; +import static org.mifosplatform.portfolio.meeting.MeetingApiConstants.meetingDateParamName; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Map; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.calendar.domain.Calendar; +import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; +import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; +import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository; +import org.mifosplatform.portfolio.calendar.domain.CalendarRepository; +import org.mifosplatform.portfolio.calendar.exception.CalendarInstanceNotFoundException; +import org.mifosplatform.portfolio.calendar.exception.CalendarNotFoundException; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepository; +import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.group.domain.GroupRepository; +import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException; +import org.mifosplatform.portfolio.meeting.attendance.domain.ClientAttendance; +import org.mifosplatform.portfolio.meeting.data.MeetingDataValidator; +import org.mifosplatform.portfolio.meeting.domain.Meeting; +import org.mifosplatform.portfolio.meeting.domain.MeetingRepositoryWrapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +@Service +public class MeetingWritePlatformServiceJpaRepositoryImpl implements MeetingWritePlatformService { + + private final MeetingRepositoryWrapper meetingRepository; + private final MeetingDataValidator meetingDataValidator; + private final CalendarInstanceRepository calendarInstanceRepository; + private final CalendarRepository calendarRepository; + private final ClientRepository clientRepository; + private final GroupRepository groupRepository; + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public MeetingWritePlatformServiceJpaRepositoryImpl(MeetingRepositoryWrapper meetingRepository, + MeetingDataValidator meetingDataValidator, CalendarInstanceRepository calendarInstanceRepository, + CalendarRepository calendarRepository, final ClientRepository clientRepository, final GroupRepository groupRepository, + final FromJsonHelper fromApiJsonHelper) { + this.meetingRepository = meetingRepository; + this.meetingDataValidator = meetingDataValidator; + this.calendarInstanceRepository = calendarInstanceRepository; + this.calendarRepository = calendarRepository; + this.clientRepository = clientRepository; + this.groupRepository = groupRepository; + this.fromApiJsonHelper = fromApiJsonHelper; + } + + @Override + public CommandProcessingResult createMeeting(JsonCommand command) { + + this.meetingDataValidator.validateForCreate(command); + + final Date meetingDate = command.DateValueOfParameterNamed(meetingDateParamName); + + try { + final CalendarInstance calendarInstance = getCalendarInstance(command); + // create new meeting + final Meeting newMeeting = Meeting.createNew(calendarInstance, meetingDate); + + final Collection clientsAttendance = getClientsAttendance(newMeeting, command); + if (clientsAttendance != null && !clientsAttendance.isEmpty()) { + newMeeting.associateClientsAttendance(clientsAttendance); + } + // save meeting details + this.meetingRepository.save(newMeeting); + final Long groupId = newMeeting.isGroupEntity() ? newMeeting.entityId() : null; + return new CommandProcessingResultBuilder() // + .withEntityId(newMeeting.getId()) // + .withGroupId(groupId).build(); + + } catch (DataIntegrityViolationException dve) { + handleMeetingDataIntegrityIssues(meetingDate, dve); + return new CommandProcessingResultBuilder() // + .build(); + } + } + + private CalendarInstance getCalendarInstance(final JsonCommand command) { + final Long calendarId = command.longValueOfParameterNamed(calendarIdParamName); + final Long entityId = command.getSupportedEntityId(); + final Calendar calendarForUpdate = this.calendarRepository.findOne(calendarId); + if (calendarForUpdate == null) { throw new CalendarNotFoundException(calendarId); } + + final Integer entityTypeId = CalendarEntityType.valueOf(command.getSupportedEntityType().toUpperCase()).getValue(); + final CalendarInstance calendarInstance = calendarInstanceRepository.findByCalendarIdAndEntityIdAndEntityTypeId( + calendarForUpdate.getId(), entityId, entityTypeId); + if(calendarInstance == null) { + final String postFix = "for." + command.getSupportedEntityType() + "not.found"; + final String defaultUserMessage = "No Calendar Instance details found for group with identifier " + entityId + " and calendar with identifier " + calendarId; + throw new CalendarInstanceNotFoundException(postFix, defaultUserMessage, entityId, calendarId); + } + return calendarInstance; + } + + private Collection getClientsAttendance(final Meeting meeting, final JsonCommand command) { + final Collection clientsAttendance = new ArrayList(); + + Collection childGroups = null; + if (meeting.isCenterEntity()) { + childGroups = this.groupRepository.findByParentId(meeting.entityId()); + } + + final String json = command.json(); + final JsonElement element = this.fromApiJsonHelper.parse(json); + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + if (element.isJsonObject()) { + if (topLevelJsonElement.has(clientsAttendanceParamName) && topLevelJsonElement.get(clientsAttendanceParamName).isJsonArray()) { + final JsonArray array = topLevelJsonElement.get(clientsAttendanceParamName).getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + final JsonObject attendanceElement = array.get(i).getAsJsonObject(); + final Long clientId = this.fromApiJsonHelper.extractLongNamed(clientIdParamName, attendanceElement); + final Integer attendanceTypeId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(attendanceTypeParamName, + attendanceElement); + + final Client client = this.clientRepository.findOne(clientId); + + if (meeting.isGroupEntity() && !client.isChildOfGroup(meeting.entityId())) { + throw new ClientNotInGroupException(clientId, meeting.entityId()); + } else if (meeting.isCenterEntity()) { + if (childGroups != null && !childGroups.isEmpty()) { + boolean isChildClient = false; + for (Group group : childGroups) { + if (group.isChildClient(clientId)){ + isChildClient = true; + break; + } + } + if(!isChildClient){ + final String defaultUserMessage = "Client with identifier " + clientId + " is not in center " + + meeting.entityId(); + throw new ClientNotInGroupException("client.not.in.center", defaultUserMessage, clientId, meeting.entityId()); + } + } + } + + final ClientAttendance clientAttendance = ClientAttendance.createClientAttendance(client, meeting, attendanceTypeId); + clientsAttendance.add(clientAttendance); + } + } + } + return clientsAttendance; + } + + @Override + public CommandProcessingResult updateMeeting(JsonCommand command) { + this.meetingDataValidator.validateForUpdate(command); + + final Meeting meetingForUpdate = this.meetingRepository.findOneWithNotFoundDetection(command.entityId()); + final Map changes = meetingForUpdate.update(command); + + try { + if (!changes.isEmpty()) { + this.meetingRepository.saveAndFlush(meetingForUpdate); + } + } catch (DataIntegrityViolationException dve) { + handleMeetingDataIntegrityIssues(meetingForUpdate.getMeetingDate(), dve); + return new CommandProcessingResultBuilder() // + .build(); + } + final Long groupId = meetingForUpdate.isGroupEntity() ? meetingForUpdate.entityId() : null; + return new CommandProcessingResultBuilder() // + .withEntityId(meetingForUpdate.getId()) // + .withGroupId(groupId) // + .with(changes) // + .build(); + } + + @Override + public CommandProcessingResult deleteMeeting(Long meetingId) { + final Meeting meetingForDelete = this.meetingRepository.findOneWithNotFoundDetection(meetingId); + this.meetingRepository.delete(meetingForDelete); + return new CommandProcessingResultBuilder() // + .withEntityId(meetingId) // + .build(); + } + + @Override + public CommandProcessingResult saveOrUpdateAttendance(JsonCommand command) { + this.meetingDataValidator.validateForUpdateAttendance(command); + + final Meeting meetingForUpdate = this.meetingRepository.findOneWithNotFoundDetection(command.entityId()); + final Collection clientsAttendance = getClientsAttendance(meetingForUpdate, command); + final Map changes = meetingForUpdate.updateAttendance(clientsAttendance); + + this.meetingRepository.saveAndFlush(meetingForUpdate); + final Long groupId = meetingForUpdate.isGroupEntity() ? meetingForUpdate.entityId() : null; + return new CommandProcessingResultBuilder() // + .withEntityId(meetingForUpdate.getId()) // + .withGroupId(groupId) // + .with(changes) // + .build(); + } + + private void handleMeetingDataIntegrityIssues(final Date meetingDate, final DataIntegrityViolationException dve) { + final Throwable realCause = dve.getMostSpecificCause(); + if (realCause.getMessage().contains("unique_calendar_instance_id_meeting_date")) { + + throw new PlatformDataIntegrityException("error.msg.meeting.duplicate", "A meeting with date '" + meetingDate + "' already exists", + meetingDateParamName, meetingDate); } + + throw new PlatformDataIntegrityException("error.msg.meeting.unknown.data.integrity.issue", + "Unknown data integrity issue with resource: " + realCause.getMessage()); + } +} From 50823d061b2dcb70e7755364160ee8ffb6189337 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 15 Aug 2013 11:05:27 +0100 Subject: [PATCH 39/47] MIFOSX-595: support savings account to loan account transfers for purposes of making loan repayment. --- api-docs/apiLive.htm | 1112 ++++------------- ...mulitplesof_to_account_transfers_table.sql | 2 + .../office/domain/OrganisationCurrency.java | 6 - .../domain/WorkingDaysRepositoryWrapper.java | 6 +- .../AccountTransfersApiConstants.java | 4 +- .../account/PortfolioAccountType.java | 67 + .../api/AccountTransfersApiResource.java | 8 +- .../data/AccountTransferData.java | 62 +- .../data/AccountTransfersDataValidator.java | 28 +- .../account/data/PortfolioAccountData.java | 78 ++ .../domain/AccountTransfer.java | 37 +- .../domain/AccountTransferAssembler.java | 57 +- .../domain/AccountTransferRepository.java | 2 +- .../AccountTransferNotFoundException.java | 2 +- .../CreateAccountTransferCommandHandler.java | 4 +- .../service/AccountTransferEnumerations.java | 38 + .../AccountTransfersReadPlatformService.java | 4 +- ...countTransfersReadPlatformServiceImpl.java | 142 ++- .../AccountTransfersWritePlatformService.java | 2 +- ...ountTransfersWritePlatformServiceImpl.java | 153 +++ .../PortfolioAccountReadPlatformService.java | 21 + ...rtfolioAccountReadPlatformServiceImpl.java | 248 ++++ ...ountTransfersWritePlatformServiceImpl.java | 91 -- ...ntifierCommandFromApiJsonDeserializer.java | 1 - .../domain/LoanAccountDomainService.java | 18 + .../domain/LoanAccountDomainServiceJpa.java | 166 +++ .../loanaccount/service/LoanAssembler.java | 67 +- ...WritePlatformServiceJpaRepositoryImpl.java | 262 ++-- .../data/SavingsAccountTransactionData.java | 2 +- ...SavingsAccountReadPlatformServiceImpl.java | 2 +- ...WritePlatformServiceJpaRepositoryImpl.java | 134 +- 31 files changed, 1472 insertions(+), 1354 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V101__add_mulitplesof_to_account_transfers_table.sql rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/AccountTransfersApiConstants.java (94%) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/PortfolioAccountType.java rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/api/AccountTransfersApiResource.java (95%) rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/data/AccountTransferData.java (52%) rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/data/AccountTransfersDataValidator.java (76%) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/PortfolioAccountData.java rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/domain/AccountTransfer.java (70%) rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/domain/AccountTransferAssembler.java (51%) rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/domain/AccountTransferRepository.java (88%) rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/exception/AccountTransferNotFoundException.java (90%) rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/handler/CreateAccountTransferCommandHandler.java (87%) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransferEnumerations.java rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/service/AccountTransfersReadPlatformService.java (84%) rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/service/AccountTransfersReadPlatformServiceImpl.java (66%) rename mifosng-provider/src/main/java/org/mifosplatform/portfolio/{accounttransfers => account}/service/AccountTransfersWritePlatformService.java (88%) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/PortfolioAccountReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index f51f579e2..45ad5dfe8 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -13152,7 +13152,7 @@ No Request Body: The type of account to which the transfer is made. 1=Loan Account, 2=Savings Account toAccountId - The id of the account to which the transfer is made. + The id of the account to which the transfer is made. The accouont must be active and must have the same currency as that of the selected fromAccountId. transferDate The date of the transfer. Requires dateFormat and locale parameters. @@ -13193,7 +13193,7 @@ No Request Body: "transferDate": [ 2013, 8, - 7 + 15 ], "fromOffice": { "id": 1, @@ -13207,17 +13207,10 @@ No Request Body: ], "hierarchy": "." }, - "toOffice": { - "id": 1, - "name": "HO", - "nameDecorated": "HO", - "externalId": "1", - "openingDate": [ - 2009, - 1, - 1 - ], - "hierarchy": "." + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "fromOfficeOptions": [ { @@ -13226,18 +13219,30 @@ No Request Body: "nameDecorated": "HO" }, { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1" + "id": 2, + "name": "Branch 1", + "nameDecorated": "....Branch 1" } ], "fromClientOptions": [ { - "id": 2, - "displayName": "headoffice-business", + "id": 1, + "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + { + "id": 3, + "displayName": "Third client", + "officeId": 1, + "officeName": "HO" + } + ], + "fromAccountTypeOptions": [ + { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" } ], "toOfficeOptions": [ @@ -13247,18 +13252,21 @@ No Request Body: "nameDecorated": "HO" }, { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1" + "id": 2, + "name": "Branch 1", + "nameDecorated": "....Branch 1" } ], - "toClientOptions": [ + "toAccountTypeOptions": [ + { + "id": 1, + "code": "accountType.loan", + "value": "Loan Account" + }, { "id": 2, - "displayName": "headoffice-business", - "officeId": 1, - "officeName": "HO", - "imagePresent": false + "code": "accountType.savings", + "value": "Savings Account" } ] } @@ -13274,21 +13282,19 @@ No Request Body: "transferDate": [ 2013, 8, - 7 + 15 ], "fromOffice": { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1", - "externalId": "2", + "id": 1, + "name": "HO", + "nameDecorated": "HO", + "externalId": "1", "openingDate": [ - 2011, - 7, + 2009, + 1, 1 ], - "hierarchy": ".3.", - "parentId": 1, - "parentName": "HO" + "hierarchy": "." }, "fromClient": { "id": 1, @@ -13300,51 +13306,20 @@ No Request Body: }, "active": true, "activationDate": [ - 2012, + 2013, 3, 1 ], "fullname": "Small shop", "displayName": "Small shop", - "officeId": 3, - "officeName": "branch 1", - "imagePresent": false, + "officeId": 1, + "officeName": "HO", "groups": [] }, - "toOffice": { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1", - "externalId": "2", - "openingDate": [ - 2011, - 7, - 1 - ], - "hierarchy": ".3.", - "parentId": 1, - "parentName": "HO" - }, - "toClient": { - "id": 1, - "accountNo": "000000001", - "status": { - "id": 300, - "code": "clientStatusType.active", - "value": "Active" - }, - "active": true, - "activationDate": [ - 2012, - 3, - 1 - ], - "fullname": "Small shop", - "displayName": "Small shop", - "officeId": 3, - "officeName": "branch 1", - "imagePresent": false, - "groups": [] + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "fromOfficeOptions": [ { @@ -13353,18 +13328,30 @@ No Request Body: "nameDecorated": "HO" }, { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1" + "id": 2, + "name": "Branch 1", + "nameDecorated": "....Branch 1" } ], "fromClientOptions": [ { "id": 1, "displayName": "Small shop", - "officeId": 3, - "officeName": "branch 1", - "imagePresent": false + "officeId": 1, + "officeName": "HO" + }, + { + "id": 3, + "displayName": "Third client", + "officeId": 1, + "officeName": "HO" + } + ], + "fromAccountTypeOptions": [ + { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" } ], "fromAccountOptions": [ @@ -13373,41 +13360,9 @@ No Request Body: "accountNo": "000000001", "clientId": 1, "clientName": "Small shop", - "savingsProductId": 1, - "savingsProductName": "Passbook", - "fieldOfficerId": 2, - "fieldOfficerName": "Clare, Claire", - "status": { - "id": 300, - "code": "savingsAccountStatusType.active", - "value": "Active", - "submittedAndPendingApproval": false, - "approved": false, - "rejected": false, - "withdrawnByApplicant": false, - "active": true, - "closed": false - }, - "timeline": { - "submittedOnDate": [ - 2013, - 3, - 1 - ], - "approvedOnDate": [ - 2013, - 3, - 1 - ], - "approvedByUsername": "mifos", - "approvedByFirstname": "App", - "approvedByLastname": "Administrator", - "activatedOnDate": [ - 2013, - 3, - 1 - ] - }, + "productId": 1, + "productName": "Passbook", + "fieldOfficerId": 0, "currency": { "code": "USD", "name": "US Dollar", @@ -13415,125 +13370,24 @@ No Request Body: "displaySymbol": "$", "nameCode": "currency.USD", "displayLabel": "US Dollar ($)" - }, - "nominalAnnualInterestRate": 5, - "interestCompoundingPeriodType": { - "id": 1, - "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", - "value": "Daily" - }, - "interestPostingPeriodType": { - "id": 4, - "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", - "value": "Monthly" - }, - "interestCalculationType": { - "id": 1, - "code": "savingsInterestCalculationType.dailybalance", - "value": "Daily Balance" - }, - "interestCalculationDaysInYearType": { - "id": 365, - "code": "savingsInterestCalculationDaysInYearType.days365", - "value": "365 Days" - }, - "summary": { - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "totalDeposits": 1242, - "totalWithdrawals": 101, - "totalInterestEarned": 26.03, - "accountBalance": 1141 } }, { - "id": 2, - "accountNo": "000000002", + "id": 3, + "accountNo": "000000003", "clientId": 1, "clientName": "Small shop", - "savingsProductId": 1, - "savingsProductName": "Passbook", - "fieldOfficerId": 3, - "fieldOfficerName": "George, John", - "status": { - "id": 300, - "code": "savingsAccountStatusType.active", - "value": "Active", - "submittedAndPendingApproval": false, - "approved": false, - "rejected": false, - "withdrawnByApplicant": false, - "active": true, - "closed": false - }, - "timeline": { - "submittedOnDate": [ - 2013, - 6, - 1 - ], - "approvedOnDate": [ - 2013, - 6, - 1 - ], - "approvedByUsername": "mifos", - "approvedByFirstname": "App", - "approvedByLastname": "Administrator", - "activatedOnDate": [ - 2013, - 6, - 1 - ] - }, + "productId": 2, + "productName": "Shilling product", + "fieldOfficerId": 0, "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "nominalAnnualInterestRate": 5, - "interestCompoundingPeriodType": { - "id": 1, - "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", - "value": "Daily" - }, - "interestPostingPeriodType": { - "id": 4, - "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", - "value": "Monthly" - }, - "interestCalculationType": { - "id": 1, - "code": "savingsInterestCalculationType.dailybalance", - "value": "Daily Balance" - }, - "interestCalculationDaysInYearType": { - "id": 365, - "code": "savingsInterestCalculationDaysInYearType.days365", - "value": "365 Days" - }, - "summary": { - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "totalDeposits": 101, - "totalWithdrawals": 42, - "totalInterestEarned": 0.58, - "accountBalance": 59 + "code": "KES", + "name": "Kenyan Shilling", + "decimalPlaces": 0, + "inMultiplesOf": 0, + "displaySymbol": "KSh", + "nameCode": "currency.KES", + "displayLabel": "Kenyan Shilling (KSh)" } } ], @@ -13544,188 +13398,21 @@ No Request Body: "nameDecorated": "HO" }, { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1" + "id": 2, + "name": "Branch 1", + "nameDecorated": "....Branch 1" } ], - "toClientOptions": [ + "toAccountTypeOptions": [ { "id": 1, - "displayName": "Small shop", - "officeId": 3, - "officeName": "branch 1", - "imagePresent": false - } - ], - "toAccountOptions": [ - { - "id": 1, - "accountNo": "000000001", - "clientId": 1, - "clientName": "Small shop", - "savingsProductId": 1, - "savingsProductName": "Passbook", - "fieldOfficerId": 2, - "fieldOfficerName": "Clare, Claire", - "status": { - "id": 300, - "code": "savingsAccountStatusType.active", - "value": "Active", - "submittedAndPendingApproval": false, - "approved": false, - "rejected": false, - "withdrawnByApplicant": false, - "active": true, - "closed": false - }, - "timeline": { - "submittedOnDate": [ - 2013, - 3, - 1 - ], - "approvedOnDate": [ - 2013, - 3, - 1 - ], - "approvedByUsername": "mifos", - "approvedByFirstname": "App", - "approvedByLastname": "Administrator", - "activatedOnDate": [ - 2013, - 3, - 1 - ] - }, - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "nominalAnnualInterestRate": 5, - "interestCompoundingPeriodType": { - "id": 1, - "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", - "value": "Daily" - }, - "interestPostingPeriodType": { - "id": 4, - "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", - "value": "Monthly" - }, - "interestCalculationType": { - "id": 1, - "code": "savingsInterestCalculationType.dailybalance", - "value": "Daily Balance" - }, - "interestCalculationDaysInYearType": { - "id": 365, - "code": "savingsInterestCalculationDaysInYearType.days365", - "value": "365 Days" - }, - "summary": { - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "totalDeposits": 1242, - "totalWithdrawals": 101, - "totalInterestEarned": 26.03, - "accountBalance": 1141 - } + "code": "accountType.loan", + "value": "Loan Account" }, { "id": 2, - "accountNo": "000000002", - "clientId": 1, - "clientName": "Small shop", - "savingsProductId": 1, - "savingsProductName": "Passbook", - "fieldOfficerId": 3, - "fieldOfficerName": "George, John", - "status": { - "id": 300, - "code": "savingsAccountStatusType.active", - "value": "Active", - "submittedAndPendingApproval": false, - "approved": false, - "rejected": false, - "withdrawnByApplicant": false, - "active": true, - "closed": false - }, - "timeline": { - "submittedOnDate": [ - 2013, - 6, - 1 - ], - "approvedOnDate": [ - 2013, - 6, - 1 - ], - "approvedByUsername": "mifos", - "approvedByFirstname": "App", - "approvedByLastname": "Administrator", - "activatedOnDate": [ - 2013, - 6, - 1 - ] - }, - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "nominalAnnualInterestRate": 5, - "interestCompoundingPeriodType": { - "id": 1, - "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", - "value": "Daily" - }, - "interestPostingPeriodType": { - "id": 4, - "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", - "value": "Monthly" - }, - "interestCalculationType": { - "id": 1, - "code": "savingsInterestCalculationType.dailybalance", - "value": "Daily Balance" - }, - "interestCalculationDaysInYearType": { - "id": 365, - "code": "savingsInterestCalculationDaysInYearType.days365", - "value": "365 Days" - }, - "summary": { - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "totalDeposits": 101, - "totalWithdrawals": 42, - "totalInterestEarned": 0.58, - "accountBalance": 59 - } + "code": "accountType.savings", + "value": "Savings Account" } ] } @@ -13749,21 +13436,19 @@ No Request Body: "transferDate": [ 2013, 8, - 7 + 15 ], "fromOffice": { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1", - "externalId": "2", + "id": 1, + "name": "HO", + "nameDecorated": "HO", + "externalId": "1", "openingDate": [ - 2011, - 7, + 2009, + 1, 1 ], - "hierarchy": ".3.", - "parentId": 1, - "parentName": "HO" + "hierarchy": "." }, "fromClient": { "id": 1, @@ -13775,57 +13460,29 @@ No Request Body: }, "active": true, "activationDate": [ - 2012, + 2013, 3, 1 ], "fullname": "Small shop", "displayName": "Small shop", - "officeId": 3, - "officeName": "branch 1", - "imagePresent": false, + "officeId": 1, + "officeName": "HO", "groups": [] }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" + }, "fromAccount": { "id": 1, "accountNo": "000000001", "clientId": 1, "clientName": "Small shop", - "savingsProductId": 1, - "savingsProductName": "Passbook", - "fieldOfficerId": 2, - "fieldOfficerName": "Clare, Claire", - "status": { - "id": 300, - "code": "savingsAccountStatusType.active", - "value": "Active", - "submittedAndPendingApproval": false, - "approved": false, - "rejected": false, - "withdrawnByApplicant": false, - "active": true, - "closed": false - }, - "timeline": { - "submittedOnDate": [ - 2013, - 3, - 1 - ], - "approvedOnDate": [ - 2013, - 3, - 1 - ], - "approvedByUsername": "mifos", - "approvedByFirstname": "App", - "approvedByLastname": "Administrator", - "activatedOnDate": [ - 2013, - 3, - 1 - ] - }, + "productId": 1, + "productName": "Passbook", + "fieldOfficerId": 0, "currency": { "code": "USD", "name": "US Dollar", @@ -13833,78 +13490,8 @@ No Request Body: "displaySymbol": "$", "nameCode": "currency.USD", "displayLabel": "US Dollar ($)" - }, - "nominalAnnualInterestRate": 5, - "interestCompoundingPeriodType": { - "id": 1, - "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", - "value": "Daily" - }, - "interestPostingPeriodType": { - "id": 4, - "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", - "value": "Monthly" - }, - "interestCalculationType": { - "id": 1, - "code": "savingsInterestCalculationType.dailybalance", - "value": "Daily Balance" - }, - "interestCalculationDaysInYearType": { - "id": 365, - "code": "savingsInterestCalculationDaysInYearType.days365", - "value": "365 Days" - }, - "summary": { - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "totalDeposits": 1242, - "totalWithdrawals": 101, - "totalInterestEarned": 26.03, - "accountBalance": 1141 } }, - "toOffice": { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1", - "externalId": "2", - "openingDate": [ - 2011, - 7, - 1 - ], - "hierarchy": ".3.", - "parentId": 1, - "parentName": "HO" - }, - "toClient": { - "id": 1, - "accountNo": "000000001", - "status": { - "id": 300, - "code": "clientStatusType.active", - "value": "Active" - }, - "active": true, - "activationDate": [ - 2012, - 3, - 1 - ], - "fullname": "Small shop", - "displayName": "Small shop", - "officeId": 3, - "officeName": "branch 1", - "imagePresent": false, - "groups": [] - }, "fromOfficeOptions": [ { "id": 1, @@ -13912,18 +13499,30 @@ No Request Body: "nameDecorated": "HO" }, { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1" + "id": 2, + "name": "Branch 1", + "nameDecorated": "....Branch 1" } ], "fromClientOptions": [ { "id": 1, "displayName": "Small shop", - "officeId": 3, - "officeName": "branch 1", - "imagePresent": false + "officeId": 1, + "officeName": "HO" + }, + { + "id": 3, + "displayName": "Third client", + "officeId": 1, + "officeName": "HO" + } + ], + "fromAccountTypeOptions": [ + { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" } ], "fromAccountOptions": [ @@ -13932,41 +13531,9 @@ No Request Body: "accountNo": "000000001", "clientId": 1, "clientName": "Small shop", - "savingsProductId": 1, - "savingsProductName": "Passbook", - "fieldOfficerId": 2, - "fieldOfficerName": "Clare, Claire", - "status": { - "id": 300, - "code": "savingsAccountStatusType.active", - "value": "Active", - "submittedAndPendingApproval": false, - "approved": false, - "rejected": false, - "withdrawnByApplicant": false, - "active": true, - "closed": false - }, - "timeline": { - "submittedOnDate": [ - 2013, - 3, - 1 - ], - "approvedOnDate": [ - 2013, - 3, - 1 - ], - "approvedByUsername": "mifos", - "approvedByFirstname": "App", - "approvedByLastname": "Administrator", - "activatedOnDate": [ - 2013, - 3, - 1 - ] - }, + "productId": 1, + "productName": "Passbook", + "fieldOfficerId": 0, "currency": { "code": "USD", "name": "US Dollar", @@ -13974,125 +13541,24 @@ No Request Body: "displaySymbol": "$", "nameCode": "currency.USD", "displayLabel": "US Dollar ($)" - }, - "nominalAnnualInterestRate": 5, - "interestCompoundingPeriodType": { - "id": 1, - "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", - "value": "Daily" - }, - "interestPostingPeriodType": { - "id": 4, - "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", - "value": "Monthly" - }, - "interestCalculationType": { - "id": 1, - "code": "savingsInterestCalculationType.dailybalance", - "value": "Daily Balance" - }, - "interestCalculationDaysInYearType": { - "id": 365, - "code": "savingsInterestCalculationDaysInYearType.days365", - "value": "365 Days" - }, - "summary": { - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "totalDeposits": 1242, - "totalWithdrawals": 101, - "totalInterestEarned": 26.03, - "accountBalance": 1141 } }, { - "id": 2, - "accountNo": "000000002", + "id": 3, + "accountNo": "000000003", "clientId": 1, "clientName": "Small shop", - "savingsProductId": 1, - "savingsProductName": "Passbook", - "fieldOfficerId": 3, - "fieldOfficerName": "George, John", - "status": { - "id": 300, - "code": "savingsAccountStatusType.active", - "value": "Active", - "submittedAndPendingApproval": false, - "approved": false, - "rejected": false, - "withdrawnByApplicant": false, - "active": true, - "closed": false - }, - "timeline": { - "submittedOnDate": [ - 2013, - 6, - 1 - ], - "approvedOnDate": [ - 2013, - 6, - 1 - ], - "approvedByUsername": "mifos", - "approvedByFirstname": "App", - "approvedByLastname": "Administrator", - "activatedOnDate": [ - 2013, - 6, - 1 - ] - }, + "productId": 2, + "productName": "Shilling product", + "fieldOfficerId": 0, "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "nominalAnnualInterestRate": 5, - "interestCompoundingPeriodType": { - "id": 1, - "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", - "value": "Daily" - }, - "interestPostingPeriodType": { - "id": 4, - "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", - "value": "Monthly" - }, - "interestCalculationType": { - "id": 1, - "code": "savingsInterestCalculationType.dailybalance", - "value": "Daily Balance" - }, - "interestCalculationDaysInYearType": { - "id": 365, - "code": "savingsInterestCalculationDaysInYearType.days365", - "value": "365 Days" - }, - "summary": { - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "totalDeposits": 101, - "totalWithdrawals": 42, - "totalInterestEarned": 0.58, - "accountBalance": 59 + "code": "KES", + "name": "Kenyan Shilling", + "decimalPlaces": 0, + "inMultiplesOf": 0, + "displaySymbol": "KSh", + "nameCode": "currency.KES", + "displayLabel": "Kenyan Shilling (KSh)" } } ], @@ -14103,104 +13569,21 @@ No Request Body: "nameDecorated": "HO" }, { - "id": 3, - "name": "branch 1", - "nameDecorated": "....branch 1" + "id": 2, + "name": "Branch 1", + "nameDecorated": "....Branch 1" } ], - "toClientOptions": [ + "toAccountTypeOptions": [ { "id": 1, - "displayName": "Small shop", - "officeId": 3, - "officeName": "branch 1", - "imagePresent": false - } - ], - "toAccountOptions": [ + "code": "accountType.loan", + "value": "Loan Account" + }, { "id": 2, - "accountNo": "000000002", - "clientId": 1, - "clientName": "Small shop", - "savingsProductId": 1, - "savingsProductName": "Passbook", - "fieldOfficerId": 3, - "fieldOfficerName": "George, John", - "status": { - "id": 300, - "code": "savingsAccountStatusType.active", - "value": "Active", - "submittedAndPendingApproval": false, - "approved": false, - "rejected": false, - "withdrawnByApplicant": false, - "active": true, - "closed": false - }, - "timeline": { - "submittedOnDate": [ - 2013, - 6, - 1 - ], - "approvedOnDate": [ - 2013, - 6, - 1 - ], - "approvedByUsername": "mifos", - "approvedByFirstname": "App", - "approvedByLastname": "Administrator", - "activatedOnDate": [ - 2013, - 6, - 1 - ] - }, - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "nominalAnnualInterestRate": 5, - "interestCompoundingPeriodType": { - "id": 1, - "code": "savings.interest.period.savingsCompoundingInterestPeriodType.daily", - "value": "Daily" - }, - "interestPostingPeriodType": { - "id": 4, - "code": "savings.interest.posting.period.savingsPostingInterestPeriodType.monthly", - "value": "Monthly" - }, - "interestCalculationType": { - "id": 1, - "code": "savingsInterestCalculationType.dailybalance", - "value": "Daily Balance" - }, - "interestCalculationDaysInYearType": { - "id": 365, - "code": "savingsInterestCalculationDaysInYearType.days365", - "value": "365 Days" - }, - "summary": { - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "totalDeposits": 101, - "totalWithdrawals": 42, - "totalInterestEarned": 0.58, - "accountBalance": 59 - } + "code": "accountType.savings", + "value": "Savings Account" } ] } @@ -14237,6 +13620,10 @@ No Request Body: } +{ + "savingsId": 1, + "resourceId": 1 +}
@@ -14253,7 +13640,7 @@ No Request Body: GET https://Domain Name/api/v1/accounttransfers { - "totalFilteredRecords": 5, + "totalFilteredRecords": 4, "pageItems": [ { "id": 1, @@ -14266,13 +13653,13 @@ No Request Body: "nameCode": "currency.USD", "displayLabel": "US Dollar ($)" }, - "transferAmount": 25, + "transferAmount": 200, "transferDate": [ 2013, - 6, - 8 + 4, + 1 ], - "transferDescription": "another transfer", + "transferDescription": "pay off loan from savings.", "fromOffice": { "id": 1, "name": "HO" @@ -14281,8 +13668,12 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "fromAccount": { "id": 1, @@ -14296,12 +13687,16 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "toAccountType": { + "id": 1, + "code": "accountType.loan", + "value": "Loan Account" }, "toAccount": { - "id": 2, - "accountNo": "000000002" + "id": 1, + "accountNo": "000000001" } }, { @@ -14315,13 +13710,13 @@ No Request Body: "nameCode": "currency.USD", "displayLabel": "US Dollar ($)" }, - "transferAmount": 13, + "transferAmount": 112.45, "transferDate": [ 2013, 6, - 15 + 1 ], - "transferDescription": "T3", + "transferDescription": "A description of the transfer", "fromOffice": { "id": 1, "name": "HO" @@ -14330,8 +13725,12 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "fromAccount": { "id": 1, @@ -14345,12 +13744,16 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "toAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "toAccount": { - "id": 2, - "accountNo": "000000002" + "id": 3, + "accountNo": "000000003" } }, { @@ -14364,13 +13767,13 @@ No Request Body: "nameCode": "currency.USD", "displayLabel": "US Dollar ($)" }, - "transferAmount": 17, + "transferAmount": 112.45, "transferDate": [ 2013, 6, - 22 + 1 ], - "transferDescription": "T4", + "transferDescription": "A description of the transfer", "fromOffice": { "id": 1, "name": "HO" @@ -14379,8 +13782,12 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "fromAccount": { "id": 1, @@ -14394,12 +13801,16 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "toAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "toAccount": { - "id": 2, - "accountNo": "000000002" + "id": 3, + "accountNo": "000000003" } }, { @@ -14413,13 +13824,13 @@ No Request Body: "nameCode": "currency.USD", "displayLabel": "US Dollar ($)" }, - "transferAmount": 21, + "transferAmount": 112.45, "transferDate": [ 2013, 6, - 29 + 1 ], - "transferDescription": "T5", + "transferDescription": "A description of the transfer", "fromOffice": { "id": 1, "name": "HO" @@ -14428,8 +13839,12 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "fromAccount": { "id": 1, @@ -14443,61 +13858,16 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "toAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "toAccount": { - "id": 2, - "accountNo": "000000002" - } - }, - { - "id": 5, - "reversed": false, - "currency": { - "code": "USD", - "name": "US Dollar", - "decimalPlaces": 2, - "displaySymbol": "$", - "nameCode": "currency.USD", - "displayLabel": "US Dollar ($)" - }, - "transferAmount": 42, - "transferDate": [ - 2013, - 7, - 5 - ], - "transferDescription": "pay back loan", - "fromOffice": { - "id": 1, - "name": "HO" - }, - "fromClient": { - "id": 1, - "displayName": "Small shop", - "officeId": 1, - "officeName": "HO", - "imagePresent": false - }, - "fromAccount": { - "id": 2, - "accountNo": "000000002" - }, - "toOffice": { - "id": 1, - "name": "HO" - }, - "toClient": { - "id": 1, - "displayName": "Small shop", - "officeId": 1, - "officeName": "HO", - "imagePresent": false - }, - "toAccount": { - "id": 1, - "accountNo": "000000001" + "id": 3, + "accountNo": "000000003" } } ] @@ -14528,13 +13898,13 @@ No Request Body: "nameCode": "currency.USD", "displayLabel": "US Dollar ($)" }, - "transferAmount": 25, + "transferAmount": 200, "transferDate": [ 2013, - 6, - 8 + 4, + 1 ], - "transferDescription": "another transfer", + "transferDescription": "pay off loan from savings.", "fromOffice": { "id": 1, "name": "HO" @@ -14543,8 +13913,12 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "fromAccountType": { + "id": 2, + "code": "accountType.savings", + "value": "Savings Account" }, "fromAccount": { "id": 1, @@ -14558,12 +13932,16 @@ No Request Body: "id": 1, "displayName": "Small shop", "officeId": 1, - "officeName": "HO", - "imagePresent": false + "officeName": "HO" + }, + "toAccountType": { + "id": 1, + "code": "accountType.loan", + "value": "Loan Account" }, "toAccount": { - "id": 2, - "accountNo": "000000002" + "id": 1, + "accountNo": "000000001" } } diff --git a/mifosng-db/migrations/core_db/V101__add_mulitplesof_to_account_transfers_table.sql b/mifosng-db/migrations/core_db/V101__add_mulitplesof_to_account_transfers_table.sql new file mode 100644 index 000000000..1cc31bce7 --- /dev/null +++ b/mifosng-db/migrations/core_db/V101__add_mulitplesof_to_account_transfers_table.sql @@ -0,0 +1,2 @@ +ALTER TABLE `m_savings_account_transfer` +ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL DEFAULT NULL AFTER `currency_digits`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java index e31c19c6f..f763da754 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OrganisationCurrency.java @@ -18,27 +18,21 @@ import org.springframework.data.jpa.domain.AbstractPersistable; @Table(name = "m_organisation_currency") public class OrganisationCurrency extends AbstractPersistable { - @SuppressWarnings("unused") @Column(name = "code", nullable = false, length = 3) private final String code; - @SuppressWarnings("unused") @Column(name = "decimal_places", nullable = false) private final Integer decimalPlaces; - @SuppressWarnings("unused") @Column(name = "currency_multiplesof") private final Integer inMultiplesOf; - @SuppressWarnings("unused") @Column(name = "name", nullable = false, length = 50) private final String name; - @SuppressWarnings("unused") @Column(name = "internationalized_name_code", nullable = false, length = 50) private final String nameCode; - @SuppressWarnings("unused") @Column(name = "display_symbol", nullable = true, length = 10) private final String displaySymbol; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java index f17d12d21..dc81b8224 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java @@ -29,10 +29,8 @@ public class WorkingDaysRepositoryWrapper { public WorkingDays findOne() { final List workingDaysList = this.repository.findAll(); - - if (workingDaysList == null || workingDaysList.isEmpty()) { - throw new WorkingDaysNotFoundException(); - } + + if (workingDaysList == null || workingDaysList.isEmpty()) { throw new WorkingDaysNotFoundException(); } return workingDaysList.get(0); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountTransfersApiConstants.java similarity index 94% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountTransfersApiConstants.java index 03959d511..a178fd708 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/AccountTransfersApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/AccountTransfersApiConstants.java @@ -3,13 +3,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers; +package org.mifosplatform.portfolio.account; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.account.data.AccountTransferData; public class AccountTransfersApiConstants { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/PortfolioAccountType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/PortfolioAccountType.java new file mode 100644 index 000000000..ad65f8bfb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/PortfolioAccountType.java @@ -0,0 +1,67 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.account; + +import java.util.ArrayList; +import java.util.List; + +public enum PortfolioAccountType { + + INVALID(0, "accountType.invalid"), // + LOAN(1, "accountType.loan"), // + SAVINGS(2, "accountType.savings"); + + private final Integer value; + private final String code; + + private PortfolioAccountType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static Object[] integerValues() { + final List values = new ArrayList(); + for (final PortfolioAccountType enumType : values()) { + if (enumType.getValue() > 0) { + values.add(enumType.getValue()); + } + } + + return values.toArray(); + } + + public static PortfolioAccountType fromInt(final Integer type) { + + PortfolioAccountType enumType = PortfolioAccountType.INVALID; + if (type != null) { + switch (type) { + case 1: + enumType = LOAN; + break; + case 2: + enumType = SAVINGS; + break; + } + } + return enumType; + } + + public boolean isSavingsAccount() { + return this.value == Integer.valueOf(2); + } + + public boolean isLoanAccount() { + return this.value == Integer.valueOf(1); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/api/AccountTransfersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiResource.java similarity index 95% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/api/AccountTransfersApiResource.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiResource.java index 37772bf38..01f551d83 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/api/AccountTransfersApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/api/AccountTransfersApiResource.java @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.api; +package org.mifosplatform.portfolio.account.api; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -25,9 +25,9 @@ import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSeriali import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; -import org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants; -import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; -import org.mifosplatform.portfolio.accounttransfers.service.AccountTransfersReadPlatformService; +import org.mifosplatform.portfolio.account.AccountTransfersApiConstants; +import org.mifosplatform.portfolio.account.data.AccountTransferData; +import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferData.java similarity index 52% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferData.java index e2fd10334..eeeb11d7e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransferData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransferData.java @@ -3,16 +3,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.data; +package org.mifosplatform.portfolio.account.data; import java.math.BigDecimal; import java.util.Collection; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.portfolio.client.data.ClientData; -import org.mifosplatform.portfolio.savings.data.SavingsAccountData; /** * Immutable data object representing a savings account. @@ -28,25 +28,30 @@ public class AccountTransferData { private final String transferDescription; private final OfficeData fromOffice; private final ClientData fromClient; - private final SavingsAccountData fromAccount; + private final EnumOptionData fromAccountType; + private final PortfolioAccountData fromAccount; private final OfficeData toOffice; private final ClientData toClient; - private final SavingsAccountData toAccount; + private final EnumOptionData toAccountType; + private final PortfolioAccountData toAccount; // template private final Collection fromOfficeOptions; private final Collection fromClientOptions; - private final Collection fromAccountOptions; + private final Collection fromAccountTypeOptions; + private final Collection fromAccountOptions; private final Collection toOfficeOptions; private final Collection toClientOptions; - private final Collection toAccountOptions; + private final Collection toAccountTypeOptions; + private final Collection toAccountOptions; public static AccountTransferData template(final OfficeData fromOffice, final ClientData fromClient, - final SavingsAccountData fromAccount, final LocalDate transferDate, final OfficeData toOffice, final ClientData toClient, - final SavingsAccountData toAccount, final Collection fromOfficeOptions, - final Collection fromClientOptions, final Collection fromAccountOptions, + final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final LocalDate transferDate, + final OfficeData toOffice, final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount, + final Collection fromOfficeOptions, final Collection fromClientOptions, + final Collection fromAccountTypeOptions, final Collection fromAccountOptions, final Collection toOfficeOptions, final Collection toClientOptions, - final Collection toAccountOptions) { + final Collection toAccountTypeOptions, final Collection toAccountOptions) { final Long id = null; CurrencyData currency = null; if (fromAccount != null) { @@ -56,41 +61,48 @@ public class AccountTransferData { final BigDecimal transferAmount = BigDecimal.ZERO; final String transferDescription = null; final Boolean reversed = null; - return new AccountTransferData(id, reversed, fromOffice, fromClient, fromAccount, currency, transferAmount, transferDate, - transferDescription, toOffice, toClient, toAccount, fromOfficeOptions, fromClientOptions, fromAccountOptions, - toOfficeOptions, toClientOptions, toAccountOptions); + return new AccountTransferData(id, reversed, fromOffice, fromClient, fromAccountType, fromAccount, currency, transferAmount, + transferDate, transferDescription, toOffice, toClient, toAccountType, toAccount, fromOfficeOptions, fromClientOptions, + fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, toAccountOptions); } public static AccountTransferData instance(final Long id, final Boolean reversed, final LocalDate transferDate, final CurrencyData currency, final BigDecimal transferAmount, final String transferDescription, final OfficeData fromOffice, - final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, final SavingsAccountData fromSavingsAccount, - final SavingsAccountData toSavingsAccount) { + final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, final EnumOptionData fromAccountType, + final PortfolioAccountData fromAccount, final EnumOptionData toAccountType, final PortfolioAccountData toAccount) { - return new AccountTransferData(id, reversed, fromOffice, fromClient, fromSavingsAccount, currency, transferAmount, transferDate, - transferDescription, toOffice, toClient, toSavingsAccount, null, null, null, null, null, null); + return new AccountTransferData(id, reversed, fromOffice, fromClient, fromAccountType, fromAccount, currency, transferAmount, + transferDate, transferDescription, toOffice, toClient, toAccountType, toAccount, null, null, null, null, null, null, + null, null); } public static AccountTransferData transferBasicDetails(final Long id, final CurrencyData currency, final BigDecimal transferAmount, final LocalDate transferDate, final String description, final Boolean reversed) { - return new AccountTransferData(id, reversed, null, null, null, currency, transferAmount, transferDate, description, null, null, - null, null, null, null, null, null, null); + final EnumOptionData fromAccountType = null; + final EnumOptionData toAccountType = null; + + return new AccountTransferData(id, reversed, null, null, fromAccountType, null, currency, transferAmount, transferDate, + description, null, null, toAccountType, null, null, null, null, null, null, null, null, null); } private AccountTransferData(final Long id, final Boolean reversed, final OfficeData fromOffice, final ClientData fromClient, - final SavingsAccountData fromAccount, final CurrencyData currency, final BigDecimal transferAmount, - final LocalDate transferDate, final String transferDescription, final OfficeData toOffice, final ClientData toClient, - final SavingsAccountData toAccount, final Collection fromOfficeOptions, - final Collection fromClientOptions, final Collection fromAccountOptions, + final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final CurrencyData currency, + final BigDecimal transferAmount, final LocalDate transferDate, final String transferDescription, final OfficeData toOffice, + final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount, + final Collection fromOfficeOptions, final Collection fromClientOptions, + final Collection fromAccountTypeOptions, final Collection fromAccountOptions, final Collection toOfficeOptions, final Collection toClientOptions, - final Collection toAccountOptions) { + final Collection toAccountTypeOptions, final Collection toAccountOptions) { this.id = id; this.reversed = reversed; this.fromOffice = fromOffice; this.fromClient = fromClient; + this.fromAccountType = fromAccountType; this.fromAccount = fromAccount; this.toOffice = toOffice; this.toClient = toClient; + this.toAccountType = toAccountType; this.toAccount = toAccount; this.currency = currency; @@ -100,9 +112,11 @@ public class AccountTransferData { this.fromOfficeOptions = fromOfficeOptions; this.fromClientOptions = fromClientOptions; + this.fromAccountTypeOptions = fromAccountTypeOptions; this.fromAccountOptions = fromAccountOptions; this.toOfficeOptions = toOfficeOptions; this.toClientOptions = toClientOptions; + this.toAccountTypeOptions = toAccountTypeOptions; this.toAccountOptions = toAccountOptions; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDataValidator.java similarity index 76% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDataValidator.java index 0de979eb2..1d49518d8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/data/AccountTransfersDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountTransfersDataValidator.java @@ -3,21 +3,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.data; +package org.mifosplatform.portfolio.account.data; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.REQUEST_DATA_PARAMETERS; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDescriptionParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountTypeParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromClientIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromOfficeIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountTypeParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toClientIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toOfficeIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.REQUEST_DATA_PARAMETERS; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromClientIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromOfficeIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toClientIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toOfficeIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDescriptionParamName; import java.lang.reflect.Type; import java.math.BigDecimal; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/PortfolioAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/PortfolioAccountData.java new file mode 100644 index 000000000..f34ea35ea --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/PortfolioAccountData.java @@ -0,0 +1,78 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.account.data; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.mifosplatform.organisation.monetary.data.CurrencyData; + +/** + * Immutable data object representing a savings account. + */ +@SuppressWarnings("unused") +public class PortfolioAccountData { + + private final Long id; + private final String accountNo; + private final String externalId; + private final Long groupId; + private final String groupName; + private final Long clientId; + private final String clientName; + private final Long productId; + private final String productName; + private final Long fieldOfficerId; + private final String fieldOfficerName; + private final CurrencyData currency; + + public static PortfolioAccountData lookup(final Long accountId, final String accountNo) { + return new PortfolioAccountData(accountId, accountNo, null, null, null, null, null, null, null, null, null, null); + } + + public PortfolioAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, final String groupName, + final Long clientId, final String clientName, final Long productId, final String productName, final Long fieldofficerId, + final String fieldofficerName, final CurrencyData currency) { + this.id = id; + this.accountNo = accountNo; + this.externalId = externalId; + this.groupId = groupId; + this.groupName = groupName; + this.clientId = clientId; + this.clientName = clientName; + this.productId = productId; + this.productName = productName; + this.fieldOfficerId = fieldofficerId; + this.fieldOfficerName = fieldofficerName; + this.currency = currency; + } + + @Override + public boolean equals(final Object obj) { + + if (obj == null) { return false; } + if (obj == this) { return true; } + if (obj.getClass() != getClass()) { return false; } + final PortfolioAccountData rhs = (PortfolioAccountData) obj; + return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).append(this.productId, rhs.productId).append(this.productName, rhs.productName).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).append(this.productId).append(this.productName).toHashCode(); + } + + public Long clientId() { + return this.clientId; + } + + public CurrencyData currency() { + return this.currency; + } + + public String currencyCode() { + return this.currency.code(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransfer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransfer.java similarity index 70% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransfer.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransfer.java index b31ba4cbc..6a52ee403 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransfer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransfer.java @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.domain; +package org.mifosplatform.portfolio.account.domain; import java.math.BigDecimal; import java.util.Date; @@ -22,6 +22,8 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; import org.springframework.data.jpa.domain.AbstractPersistable; @@ -52,7 +54,7 @@ public class AccountTransfer extends AbstractPersistable { @ManyToOne @JoinColumn(name = "to_savings_account_id", nullable = true) - private SavingsAccount toAccount; + private SavingsAccount toSavingsAccount; @ManyToOne @JoinColumn(name = "from_savings_transaction_id", nullable = true) @@ -62,6 +64,14 @@ public class AccountTransfer extends AbstractPersistable { @JoinColumn(name = "to_savings_transaction_id", nullable = true) private SavingsAccountTransaction toSavingsTransaction; + @ManyToOne + @JoinColumn(name = "to_loan_account_id", nullable = true) + private Loan toLoanAccount; + + @ManyToOne + @JoinColumn(name = "to_loan_transaction_id", nullable = true) + private LoanTransaction toLoanTransaction; + @Column(name = "is_reversed", nullable = false) private final boolean reversed = false; @@ -83,8 +93,16 @@ public class AccountTransfer extends AbstractPersistable { final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, final LocalDate transactionDate, final Money transactionAmount, final String description) { - return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toSavingsAccount, withdrawal, deposit, - transactionDate, transactionAmount, description); + return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toSavingsAccount, null, withdrawal, + deposit, null, transactionDate, transactionAmount, description); + } + + public static AccountTransfer savingsToLoanTransfer(final Office fromOffice, final Client fromClient, + final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount, + final SavingsAccountTransaction withdrawal, final LoanTransaction loanRepaymentTransaction, final LocalDate transactionDate, + final Money transactionAmount, final String description) { + return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, null, toLoanAccount, withdrawal, null, + loanRepaymentTransaction, transactionDate, transactionAmount, description); } protected AccountTransfer() { @@ -92,17 +110,20 @@ public class AccountTransfer extends AbstractPersistable { } private AccountTransfer(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount, - final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, - final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, final LocalDate transactionDate, - final Money transactionAmount, final String description) { + final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, final Loan toLoanAccount, + final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, + final LoanTransaction loanRepaymentTransaction, final LocalDate transactionDate, final Money transactionAmount, + final String description) { this.fromOffice = fromOffice; this.fromClient = fromClient; this.fromAccount = fromSavingsAccount; this.toOffice = toOffice; this.toClient = toClient; - this.toAccount = toSavingsAccount; + this.toSavingsAccount = toSavingsAccount; this.fromTransaction = withdrawal; this.toSavingsTransaction = deposit; + this.toLoanAccount = toLoanAccount; + this.toLoanTransaction = loanRepaymentTransaction; this.date = transactionDate.toDate(); this.currency = transactionAmount.getCurrency(); this.amount = transactionAmount.getAmountDefaultedToNullIfZero(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferAssembler.java similarity index 51% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferAssembler.java index 6ed7f155d..ffdadd61e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferAssembler.java @@ -3,17 +3,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.domain; +package org.mifosplatform.portfolio.account.domain; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDescriptionParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromClientIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromOfficeIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toClientIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toOfficeIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromClientIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromOfficeIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toClientIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toOfficeIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDescriptionParamName; import java.math.BigDecimal; @@ -25,6 +25,8 @@ import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.office.domain.OfficeRepository; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; @@ -55,19 +57,19 @@ public class AccountTransferAssembler { final JsonElement element = command.parsedJson(); - final Long fromOfficeId = fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); final Office fromOffice = this.officeRepository.findOne(fromOfficeId); - final Long fromClientId = fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId); - final Long toOfficeId = fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); final Office toOffice = this.officeRepository.findOne(toOfficeId); - final Long toClientId = fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); @@ -82,4 +84,31 @@ public class AccountTransferAssembler { return AccountTransfer.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toSavingsAccount, withdrawal, deposit, transactionDate, transactionMonetaryAmount, description); } + + public AccountTransfer assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount, + final Loan toLoanAccount, final SavingsAccountTransaction withdrawal, final LoanTransaction loanRepaymentTransaction) { + + final JsonElement element = command.parsedJson(); + + final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element); + final Office fromOffice = this.officeRepository.findOne(fromOfficeId); + + final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element); + final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId); + + final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element); + final Office toOffice = this.officeRepository.findOne(toOfficeId); + + final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element); + final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId); + + final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); + final Money transactionMonetaryAmount = Money.of(fromSavingsAccount.getCurrency(), transactionAmount); + + final String description = command.stringValueOfParameterNamed(transferDescriptionParamName); + + return AccountTransfer.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount, + withdrawal, loanRepaymentTransaction, transactionDate, transactionMonetaryAmount, description); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRepository.java similarity index 88% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRepository.java index 652565cd9..352490aec 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/domain/AccountTransferRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/domain/AccountTransferRepository.java @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.domain; +package org.mifosplatform.portfolio.account.domain; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/exception/AccountTransferNotFoundException.java similarity index 90% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/exception/AccountTransferNotFoundException.java index e7168bfcc..5ac584ddd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/exception/AccountTransferNotFoundException.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/exception/AccountTransferNotFoundException.java @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.exception; +package org.mifosplatform.portfolio.account.exception; import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/CreateAccountTransferCommandHandler.java similarity index 87% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/CreateAccountTransferCommandHandler.java index 10da9690d..106de2e3e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/handler/CreateAccountTransferCommandHandler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/handler/CreateAccountTransferCommandHandler.java @@ -3,12 +3,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.handler; +package org.mifosplatform.portfolio.account.handler; import org.mifosplatform.commands.handler.NewCommandSourceHandler; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; -import org.mifosplatform.portfolio.accounttransfers.service.AccountTransfersWritePlatformService; +import org.mifosplatform.portfolio.account.service.AccountTransfersWritePlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransferEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransferEnumerations.java new file mode 100644 index 000000000..99fdf7335 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransferEnumerations.java @@ -0,0 +1,38 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.account.service; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.portfolio.account.PortfolioAccountType; + +public class AccountTransferEnumerations { + + public static EnumOptionData accountType(final Integer type) { + return accountType(PortfolioAccountType.fromInt(type)); + } + + public static EnumOptionData accountType(final PortfolioAccountType type) { + + EnumOptionData optionData = null; + + if (type != null) { + switch (type) { + case INVALID: + break; + case LOAN: + optionData = new EnumOptionData(PortfolioAccountType.LOAN.getValue().longValue(), PortfolioAccountType.LOAN.getCode(), + "Loan Account"); + break; + case SAVINGS: + optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(), + PortfolioAccountType.SAVINGS.getCode(), "Savings Account"); + break; + } + } + + return optionData; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java similarity index 84% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java index 945f09d9c..a039841b1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java @@ -3,10 +3,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.service; +package org.mifosplatform.portfolio.account.service; import org.mifosplatform.infrastructure.core.service.Page; -import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.group.service.SearchParameters; public interface AccountTransfersReadPlatformService { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java similarity index 66% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java index 053e7ab66..166bb43df 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.service; +package org.mifosplatform.portfolio.account.service; import java.math.BigDecimal; import java.sql.ResultSet; @@ -14,6 +14,7 @@ import java.util.Collection; import org.joda.time.LocalDate; import org.mifosplatform.commands.exception.UnsupportedCommandException; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.core.service.Page; @@ -22,18 +23,19 @@ import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; -import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; -import org.mifosplatform.portfolio.accounttransfers.exception.AccountTransferNotFoundException; +import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.data.AccountTransferData; +import org.mifosplatform.portfolio.account.data.PortfolioAccountData; +import org.mifosplatform.portfolio.account.exception.AccountTransferNotFoundException; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; -import org.mifosplatform.portfolio.savings.data.SavingsAccountData; -import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; @Service public class AccountTransfersReadPlatformServiceImpl implements AccountTransfersReadPlatformService { @@ -41,7 +43,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers private final JdbcTemplate jdbcTemplate; private final ClientReadPlatformService clientReadPlatformService; private final OfficeReadPlatformService officeReadPlatformService; - private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService; // mapper private final AccountTransfersMapper accountTransfersMapper; @@ -52,11 +54,11 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers @Autowired public AccountTransfersReadPlatformServiceImpl(final RoutingDataSource dataSource, final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, - final SavingsAccountReadPlatformService savingsAccountReadPlatformService) { + final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.clientReadPlatformService = clientReadPlatformService; this.officeReadPlatformService = officeReadPlatformService; - this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; + this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformService; this.accountTransfersMapper = new AccountTransfersMapper(); } @@ -65,30 +67,38 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers public AccountTransferData retrieveTemplate(final Long fromOfficeId, final Long fromClientId, final Long fromAccountId, final Integer fromAccountType, final Long toOfficeId, final Long toClientId, final Long toAccountId, final Integer toAccountType) { + final EnumOptionData loanAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); + final EnumOptionData savingsAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); + + final Collection fromAccountTypeOptions = Arrays.asList(savingsAccountType); + final Collection toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType); + + // TODO - remove this after doing overpaid loan to savings transfers Integer mostRelevantFromAccountType = fromAccountType; if (mostRelevantFromAccountType == null) { mostRelevantFromAccountType = 2; // savings } - Integer mostRelevantToAccountType = toAccountType; - if (mostRelevantToAccountType == null) { - mostRelevantToAccountType = 2; // savings - } + final Integer mostRelevantToAccountType = toAccountType; + + final EnumOptionData fromAccountTypeData = AccountTransferEnumerations.accountType(mostRelevantFromAccountType); + final EnumOptionData toAccountTypeData = AccountTransferEnumerations.accountType(mostRelevantToAccountType); + // check for now that only savings account type is passed and used. - if (mostRelevantFromAccountType != 2) { throw new UnsupportedCommandException("transferFromLoanAccount"); } + if (mostRelevantFromAccountType != 2) { throw new UnsupportedCommandException("transferFromLoanAccountNotSupported"); } // from settings OfficeData fromOffice = null; ClientData fromClient = null; - SavingsAccountData fromAccount = null; + PortfolioAccountData fromAccount = null; OfficeData toOffice = null; ClientData toClient = null; - SavingsAccountData toAccount = null; + PortfolioAccountData toAccount = null; // template - Collection fromAccountOptions = null; - Collection toAccountOptions = null; + Collection fromAccountOptions = null; + Collection toAccountOptions = null; Long mostRelevantFromOfficeId = fromOfficeId; Long mostRelevantFromClientId = fromClientId; @@ -96,20 +106,21 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers Long mostRelevantToOfficeId = toOfficeId; Long mostRelevantToClientId = toClientId; - if (fromAccountId != null && fromAccountType == 2) { - fromAccount = this.savingsAccountReadPlatformService.retrieveOne(fromAccountId); + if (fromAccountId != null && mostRelevantFromAccountType == 2) { + fromAccount = this.portfolioAccountReadPlatformService.retrieveOne(fromAccountId, PortfolioAccountType.SAVINGS.getValue()); // override provided fromClient with client of account mostRelevantFromClientId = fromAccount.clientId(); + } else if (fromAccountId != null && mostRelevantFromAccountType == 1) { // loans + // fetch loan account } if (mostRelevantFromClientId != null) { fromClient = this.clientReadPlatformService.retrieveOne(mostRelevantFromClientId); mostRelevantFromOfficeId = fromClient.officeId(); - toClient = fromClient; - fromAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantFromClientId); - toAccountOptions = new ArrayList(fromAccountOptions); - toAccountOptions.remove(fromAccount); + + fromAccountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(mostRelevantFromAccountType, + mostRelevantFromClientId); } Collection fromOfficeOptions = null; @@ -121,29 +132,25 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers } // defaults - LocalDate transferDate = DateUtils.getLocalDateOfTenant(); - toOffice = fromOffice; + final LocalDate transferDate = DateUtils.getLocalDateOfTenant(); Collection toOfficeOptions = fromOfficeOptions; - Collection toClientOptions = fromClientOptions; + Collection toClientOptions = null; - if (toAccountId != null) { - toAccount = this.savingsAccountReadPlatformService.retrieveOne(toAccountId); - - // override provided toClient with client of account + if (toAccountId != null && fromAccount != null) { + toAccount = this.portfolioAccountReadPlatformService.retrieveOne(toAccountId, mostRelevantToAccountType, fromAccount.currencyCode()); mostRelevantToClientId = toAccount.clientId(); } - if (mostRelevantToClientId != null && mostRelevantToClientId != mostRelevantFromClientId) { + if (mostRelevantToClientId != null) { toClient = this.clientReadPlatformService.retrieveOne(mostRelevantToClientId); mostRelevantToOfficeId = toClient.officeId(); toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); - toAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantToClientId); - toAccountOptions.remove(fromAccount); + toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); } - if (mostRelevantToOfficeId != null && mostRelevantToOfficeId != mostRelevantFromOfficeId) { + if (mostRelevantToOfficeId != null) { toOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantToOfficeId); toOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); @@ -151,21 +158,37 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers if (toClientOptions != null && toClientOptions.size() == 1) { toClient = new ArrayList(toClientOptions).get(0); - toAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantToClientId); - toAccountOptions.remove(fromAccount); + toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); } } - return AccountTransferData.template(fromOffice, fromClient, fromAccount, transferDate, toOffice, toClient, toAccount, - fromOfficeOptions, fromClientOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountOptions); + return AccountTransferData.template(fromOffice, fromClient, fromAccountTypeData, fromAccount, transferDate, toOffice, toClient, + toAccountTypeData, toAccount, fromOfficeOptions, fromClientOptions, fromAccountTypeOptions, fromAccountOptions, + toOfficeOptions, toClientOptions, toAccountTypeOptions, toAccountOptions); + } + + private Collection retrieveToAccounts(final PortfolioAccountData excludeThisAccountFromOptions, + final Integer toAccountType, final Long toClientId) { + + final String currencyCode = excludeThisAccountFromOptions != null ? excludeThisAccountFromOptions.currencyCode() : null; + + Collection accountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(toAccountType, + toClientId, currencyCode); + if (!CollectionUtils.isEmpty(accountOptions)) { + accountOptions.remove(excludeThisAccountFromOptions); + } else { + accountOptions = null; + } + + return accountOptions; } @Override public Page retrieveAll(final SearchParameters searchParameters) { - StringBuilder sqlBuilder = new StringBuilder(200); + final StringBuilder sqlBuilder = new StringBuilder(200); sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); - sqlBuilder.append(accountTransfersMapper.schema()); + sqlBuilder.append(this.accountTransfersMapper.schema()); if (searchParameters.isOrderByRequested()) { sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); @@ -183,7 +206,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers } final Object[] objectArray = new Object[2]; - int arrayPos = 0; + final int arrayPos = 0; final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos); final String sqlCountRows = "SELECT FOUND_ROWS()"; return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, @@ -197,7 +220,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers final String sql = "select " + this.accountTransfersMapper.schema() + " where sat.id = ?"; return this.jdbcTemplate.queryForObject(sql, this.accountTransfersMapper, new Object[] { transferId }); - } catch (EmptyResultDataAccessException e) { + } catch (final EmptyResultDataAccessException e) { throw new AccountTransferNotFoundException(transferId); } } @@ -206,13 +229,13 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers private final String schemaSql; - public AccountTransfersMapper() { + public AccountTransfersMapper() { final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("sat.id as id, sat.is_reversed as isReversed,"); sqlBuilder.append("sat.transaction_date as transferDate, sat.amount as transferAmount,"); sqlBuilder.append("sat.description as transferDescription,"); - sqlBuilder - .append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits, sat.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits,"); + sqlBuilder.append("sat.currency_multiplesof as inMultiplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); @@ -221,6 +244,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); + sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo,"); sqlBuilder.append("fromsavtran.id as fromSavingsAccountTransactionId,"); sqlBuilder.append("fromsavtran.transaction_type_enum as fromSavingsAccountTransactionType,"); sqlBuilder.append("tosavtran.id as toSavingsAccountTransactionId,"); @@ -233,6 +257,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers sqlBuilder.append("join m_client toclient on toclient.id = sat.to_client_id "); sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = sat.from_savings_account_id "); sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = sat.to_savings_account_id "); + sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = sat.to_loan_account_id "); sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = sat.from_savings_transaction_id "); sqlBuilder.append("left join m_savings_account_transaction tosavtran on tosavtran.id = sat.to_savings_transaction_id "); @@ -264,30 +289,43 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); final String fromOfficeName = rs.getString("fromOfficeName"); - OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); + final OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); final String toOfficeName = rs.getString("toOfficeName"); - OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); + final OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId"); final String fromClientName = rs.getString("fromClientName"); - ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); + final ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); final Long toClientId = JdbcSupport.getLong(rs, "toClientId"); final String toClientName = rs.getString("toClientName"); - ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); + final ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); + + final EnumOptionData fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); - SavingsAccountData fromSavingsAccount = SavingsAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); + final PortfolioAccountData fromSavingsAccount = PortfolioAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); + PortfolioAccountData toAccount = null; + EnumOptionData toAccountType = null; final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId"); final String toSavingsAccountNo = rs.getString("toSavingsAccountNo"); - SavingsAccountData toSavingsAccount = SavingsAccountData.lookup(toSavingsAccountId, toSavingsAccountNo); + final Long toLoanAccountId = JdbcSupport.getLong(rs, "toLoanAccountId"); + final String toLoanAccountNo = rs.getString("toLoanAccountNo"); + + if (toSavingsAccountId != null) { + toAccount = PortfolioAccountData.lookup(toSavingsAccountId, toSavingsAccountNo); + toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); + } else if (toLoanAccountId != null) { + toAccount = PortfolioAccountData.lookup(toLoanAccountId, toLoanAccountNo); + toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); + } return AccountTransferData.instance(id, reversed, transferDate, currency, transferAmount, transferDescription, fromOffice, - toOffice, fromClient, toClient, fromSavingsAccount, toSavingsAccount); + toOffice, fromClient, toClient, fromAccountType, fromSavingsAccount, toAccountType, toAccount); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformService.java similarity index 88% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformService.java index d950863b1..821478df9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformService.java @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.accounttransfers.service; +package org.mifosplatform.portfolio.account.service; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java new file mode 100644 index 000000000..fed01d77b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java @@ -0,0 +1,153 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.account.service; + +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountIdParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountTypeParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferAmountParamName; +import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName; + +import java.math.BigDecimal; +import java.util.Locale; + +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.data.AccountTransfersDataValidator; +import org.mifosplatform.portfolio.account.domain.AccountTransfer; +import org.mifosplatform.portfolio.account.domain.AccountTransferAssembler; +import org.mifosplatform.portfolio.account.domain.AccountTransferRepository; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountDomainService; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class AccountTransfersWritePlatformServiceImpl implements AccountTransfersWritePlatformService { + + private final AccountTransfersDataValidator accountTransfersDataValidator; + private final AccountTransferAssembler accountTransferAssembler; + private final AccountTransferRepository accountTransferRepository; + private final SavingsAccountAssembler savingsAccountAssembler; + private final SavingsAccountDomainService savingsAccountDomainService; + private final LoanAssembler loanAccountAssembler; + private final LoanAccountDomainService loanAccountDomainService; + + @Autowired + public AccountTransfersWritePlatformServiceImpl(final AccountTransfersDataValidator accountTransfersDataValidator, + final AccountTransferAssembler accountTransferAssembler, final AccountTransferRepository accountTransferRepository, + final SavingsAccountAssembler savingsAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService, + final LoanAssembler loanAssembler, final LoanAccountDomainService loanAccountDomainService) { + this.accountTransfersDataValidator = accountTransfersDataValidator; + this.accountTransferAssembler = accountTransferAssembler; + this.accountTransferRepository = accountTransferRepository; + this.savingsAccountAssembler = savingsAccountAssembler; + this.savingsAccountDomainService = savingsAccountDomainService; + this.loanAccountAssembler = loanAssembler; + this.loanAccountDomainService = loanAccountDomainService; + } + + @Transactional + @Override + public CommandProcessingResult create(final JsonCommand command) { + + this.accountTransfersDataValidator.validate(command); + + final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + + final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName); + final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId); + + final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName); + final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId); + + final PaymentDetail paymentDetail = null; + Long fromSavingsAccountId = null; + Long transferTransactionId = null; + if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) { + + fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId); + + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, + transactionDate, transactionAmount, paymentDetail); + + final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); + final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId); + + final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, + transactionDate, transactionAmount, paymentDetail); + + final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, withdrawal, + deposit); + this.accountTransferRepository.saveAndFlush(transferTransaction); + transferTransactionId = transferTransaction.getId(); + + } else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) { + // + fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName); + final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId); + + final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, + transactionDate, transactionAmount, paymentDetail); + + final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName); + final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId); + + final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccountId, + new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null); + + final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command, + fromSavingsAccount, toLoanAccount, withdrawal, loanRepaymentTransaction); + this.accountTransferRepository.saveAndFlush(transferTransaction); + transferTransactionId = transferTransaction.getId(); + + } else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) { + // FIXME - kw - ADD overpaid loan to savings account transfer support. + } else { + + } + + final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(transferTransactionId); + + if (fromAccountType.isSavingsAccount()) { + + builder.withSavingsId(fromSavingsAccountId); + } + + return builder.build(); + } + + private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount(); + } + + private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount(); + } + + private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) { + return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/PortfolioAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/PortfolioAccountReadPlatformService.java new file mode 100644 index 000000000..7fc417b2e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/PortfolioAccountReadPlatformService.java @@ -0,0 +1,21 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.account.service; + +import java.util.Collection; + +import org.mifosplatform.portfolio.account.data.PortfolioAccountData; + +public interface PortfolioAccountReadPlatformService { + + PortfolioAccountData retrieveOne(Long accountId, Integer accountTypeId); + + PortfolioAccountData retrieveOne(Long accountId, Integer accountTypeId, String currencyCode); + + Collection retrieveAllForLookup(Integer toAccountType, Long toClientId); + + Collection retrieveAllForLookup(Integer toAccountType, Long toClientId, String currencyCode); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java new file mode 100644 index 000000000..5eb5906bc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java @@ -0,0 +1,248 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.account.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.data.PortfolioAccountData; +import org.mifosplatform.portfolio.account.exception.AccountTransferNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccountReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + // mapper + private final PortfolioSavingsAccountMapper savingsAccountMapper; + private final PortfolioLoanAccountMapper loanAccountMapper; + + @Autowired + public PortfolioAccountReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.savingsAccountMapper = new PortfolioSavingsAccountMapper(); + this.loanAccountMapper = new PortfolioLoanAccountMapper(); + } + + @Override + public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId) { + return retrieveOne(accountId, accountTypeId, null); + } + + @Override + public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId, final String currencyCode) { + + Object[] sqlParams = new Object[] {accountId}; + PortfolioAccountData accountData = null; + try { + String sql = null; + final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId); + switch (accountType) { + case INVALID: + break; + case LOAN: + + sql = "select " + this.loanAccountMapper.schema() + " where la.id = ?"; + if (currencyCode != null) { + sql += " and la.currency_code = ?"; + sqlParams = new Object[] {accountId, currencyCode}; + } + + accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams); + break; + case SAVINGS: + sql = "select " + this.savingsAccountMapper.schema() + " where sa.id = ?"; + if (currencyCode != null) { + sql += " and la.currency_code = ?"; + sqlParams = new Object[] {accountId, currencyCode}; + } + + accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams); + break; + default: + break; + } + } catch (final EmptyResultDataAccessException e) { + throw new AccountTransferNotFoundException(accountId); + } + + return accountData; + } + + @Override + public Collection retrieveAllForLookup(final Integer accountTypeId, final Long clientId) { + return retrieveAllForLookup(accountTypeId, clientId, null); + } + + @Override + public Collection retrieveAllForLookup(final Integer accountTypeId, final Long clientId, final String currencyCode) { + + Object[] sqlParams = new Object[] {clientId}; + Collection accounts = null; + String sql = null; + final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId); + switch (accountType) { + case INVALID: + break; + case LOAN: + sql = "select " + this.loanAccountMapper.schema() + " where la.client_id = ? and la.loan_status_id = 300"; + if (currencyCode != null) { + sql += " and la.currency_code = ?"; + sqlParams = new Object[] {clientId, currencyCode}; + } + + accounts = this.jdbcTemplate.query(sql, this.loanAccountMapper, sqlParams); + break; + case SAVINGS: + sql = "select " + this.savingsAccountMapper.schema() + " where sa.client_id = ? and sa.status_enum = 300"; + if (currencyCode != null) { + sql += " and sa.currency_code = ?"; + sqlParams = new Object[] {clientId, currencyCode}; + } + + accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams); + break; + default: + break; + } + + return accounts; + } + + private static final class PortfolioSavingsAccountMapper implements RowMapper { + + private final String schemaSql; + + public PortfolioSavingsAccountMapper() { + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, "); + sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); + sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); + sqlBuilder.append("sp.id as productId, sp.name as productName, "); + sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); + sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits,"); + sqlBuilder.append("sa.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); + sqlBuilder.append("from m_savings_account sa "); + sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id "); + sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); + sqlBuilder.append("left join m_client c ON c.id = sa.client_id "); + sqlBuilder.append("left join m_group g ON g.id = sa.group_id "); + sqlBuilder.append("left join m_staff s ON s.id = sa.field_officer_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + + final Long groupId = JdbcSupport.getLong(rs, "groupId"); + final String groupName = rs.getString("groupName"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final String clientName = rs.getString("clientName"); + + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final Long fieldOfficerId = rs.getLong("fieldOfficerId"); + final String fieldOfficerName = rs.getString("fieldOfficerName"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); + + return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, currency); + } + } + + private static final class PortfolioLoanAccountMapper implements RowMapper { + + private final String schemaSql; + + public PortfolioLoanAccountMapper() { + + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, "); + sqlBuilder.append("c.id as clientId, c.display_name as clientName, "); + sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); + sqlBuilder.append("lp.id as productId, lp.name as productName, "); + sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, "); + sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,"); + sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol "); + sqlBuilder.append("from m_loan la "); + sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id "); + sqlBuilder.append("join m_currency curr on curr.code = la.currency_code "); + sqlBuilder.append("left join m_client c ON c.id = la.client_id "); + sqlBuilder.append("left join m_group g ON g.id = la.group_id "); + sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + + final Long groupId = JdbcSupport.getLong(rs, "groupId"); + final String groupName = rs.getString("groupName"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final String clientName = rs.getString("clientName"); + + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final Long fieldOfficerId = rs.getLong("fieldOfficerId"); + final String fieldOfficerName = rs.getString("fieldOfficerName"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, + currencyDisplaySymbol, currencyNameCode); + + return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, currency); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java deleted file mode 100644 index 6bc88c0ff..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/accounttransfers/service/AccountTransfersWritePlatformServiceImpl.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.mifosplatform.portfolio.accounttransfers.service; - -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName; -import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName; - -import java.math.BigDecimal; -import java.util.Locale; - -import org.joda.time.LocalDate; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; -import org.mifosplatform.infrastructure.core.api.JsonCommand; -import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; -import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; -import org.mifosplatform.portfolio.accounttransfers.data.AccountTransfersDataValidator; -import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransfer; -import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransferAssembler; -import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransferRepository; -import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; -import org.mifosplatform.portfolio.savings.domain.SavingsAccount; -import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; -import org.mifosplatform.portfolio.savings.domain.SavingsAccountDomainService; -import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -public class AccountTransfersWritePlatformServiceImpl implements AccountTransfersWritePlatformService { - - private final AccountTransfersDataValidator accountTransfersDataValidator; - private final AccountTransferAssembler accountTransferAssembler; - private final AccountTransferRepository accountTransferRepository; - private final SavingsAccountAssembler savingsAccountAssembler; - private final SavingsAccountDomainService savingsAccountDomainService; - - @Autowired - public AccountTransfersWritePlatformServiceImpl(final AccountTransfersDataValidator accountTransfersDataValidator, - final AccountTransferAssembler accountTransferAssembler, final AccountTransferRepository accountTransferRepository, - final SavingsAccountAssembler savingsAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService) { - this.accountTransfersDataValidator = accountTransfersDataValidator; - this.accountTransferAssembler = accountTransferAssembler; - this.accountTransferRepository = accountTransferRepository; - this.savingsAccountAssembler = savingsAccountAssembler; - this.savingsAccountDomainService = savingsAccountDomainService; - } - - @Transactional - @Override - public CommandProcessingResult create(final JsonCommand command) { - - this.accountTransfersDataValidator.validate(command); - - final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName); - final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName); - - final Locale locale = command.extractLocale(); - final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); - - // check fromAccountType settings to see if its a savings account - // withdrawal or a loan account transfer - final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName); - final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId); - - final PaymentDetail paymentDetail = null; - - final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, - transactionDate, transactionAmount, paymentDetail); - - final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName); - final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId); - - final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, - transactionAmount, paymentDetail); - - AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, withdrawal, deposit); - this.accountTransferRepository.saveAndFlush(transferTransaction); - - return new CommandProcessingResultBuilder() // - .withSavingsId(fromSavingsId) // - .withEntityId(transferTransaction.getId()) // - .build(); - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/serialization/ClientIdentifierCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/serialization/ClientIdentifierCommandFromApiJsonDeserializer.java index c506e09fc..2bfc6d928 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/serialization/ClientIdentifierCommandFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/serialization/ClientIdentifierCommandFromApiJsonDeserializer.java @@ -16,7 +16,6 @@ import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer; import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; -import org.mifosplatform.portfolio.client.command.ClientCommand; import org.mifosplatform.portfolio.client.command.ClientIdentifierCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainService.java new file mode 100644 index 000000000..ef82156a8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainService.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanaccount.domain; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; + +public interface LoanAccountDomainService { + + LoanTransaction makeRepayment(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate, + BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java new file mode 100644 index 000000000..63d34dee1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -0,0 +1,166 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanaccount.domain; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService; +import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.organisation.holiday.domain.Holiday; +import org.mifosplatform.organisation.holiday.domain.HolidayRepository; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; +import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.workingdays.domain.WorkingDays; +import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; +import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; +import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler; +import org.mifosplatform.portfolio.note.domain.Note; +import org.mifosplatform.portfolio.note.domain.NoteRepository; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { + + private final LoanAssembler loanAccountAssembler; + private final LoanRepository loanRepository; + private final LoanTransactionRepository loanTransactionRepository; + private final ConfigurationDomainService configurationDomainService; + private final HolidayRepository holidayRepository; + private final WorkingDaysRepositoryWrapper workingDaysRepository; + + private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; + private final JournalEntryWritePlatformService journalEntryWritePlatformService; + private final NoteRepository noteRepository; + + @Autowired + public LoanAccountDomainServiceJpa(final LoanAssembler loanAccountAssembler, final LoanRepository loanRepository, + final LoanTransactionRepository loanTransactionRepository, final NoteRepository noteRepository, + final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository, + final WorkingDaysRepositoryWrapper workingDaysRepository, + final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, + final JournalEntryWritePlatformService journalEntryWritePlatformService) { + this.loanAccountAssembler = loanAccountAssembler; + this.loanRepository = loanRepository; + this.loanTransactionRepository = loanTransactionRepository; + this.noteRepository = noteRepository; + this.configurationDomainService = configurationDomainService; + this.holidayRepository = holidayRepository; + this.workingDaysRepository = workingDaysRepository; + this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; + this.journalEntryWritePlatformService = journalEntryWritePlatformService; + } + + @Transactional + @Override + public LoanTransaction makeRepayment(final Long accountId, final CommandProcessingResultBuilder builderResult, + final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, + final String txnExternalId) { + + final Loan loan = this.loanAccountAssembler.assembleFrom(accountId); + checkClientOrGroupActive(loan); + + // TODO: Is it required to validate transaction date with meeting dates + // if repayments is synced with meeting? + /* + * if(loan.isSyncDisbursementWithMeeting()){ // validate actual + * disbursement date against meeting date CalendarInstance + * calendarInstance = + * this.calendarInstanceRepository.findCalendarInstaneByLoanId + * (loan.getId(), CalendarEntityType.LOANS.getValue()); + * this.loanEventApiJsonValidator + * .validateRepaymentDateWithMeetingDate(transactionDate, + * calendarInstance); } + */ + + final List existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + + final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount); + final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, paymentDetail, transactionDate, + txnExternalId); + final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); + final List holidays = this.holidayRepository + .findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate()); + final WorkingDays workingDays = this.workingDaysRepository.findOne(); + final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); + + final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction, + defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, allowTransactionsOnHoliday, + holidays, workingDays, allowTransactionsOnNonWorkingDay); + + this.loanTransactionRepository.save(newRepaymentTransaction); + + /*** + * TODO Vishwas Batch save is giving me a + * HibernateOptimisticLockingFailureException, looping and saving for + * the time being, not a major issue for now as this loop is entered + * only in edge cases (when a payment is made before the latest payment + * recorded against the loan) + ***/ + if (changedTransactionDetail != null) { + for (final LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { + this.loanTransactionRepository.save(loanTransaction); + } + } + this.loanRepository.save(loan); + + if (StringUtils.isNotBlank(noteText)) { + final Note note = Note.loanTransactionNote(loan, newRepaymentTransaction, noteText); + this.noteRepository.save(note); + } + + postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + + builderResult.withEntityId(newRepaymentTransaction.getId()) // + .withOfficeId(loan.getOfficeId()) // + .withClientId(loan.getClientId()) // + .withGroupId(loan.getGroupId()); // + + return newRepaymentTransaction; + } + + private void postJournalEntries(final Loan loanAccount, final List existingTransactionIds, + final List existingReversedTransactionIds) { + + final MonetaryCurrency currency = loanAccount.getCurrency(); + final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); + + final Map accountingBridgeData = loanAccount.deriveAccountingBridgeData(applicationCurrency.toData(), + existingTransactionIds, existingReversedTransactionIds); + this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); + } + + private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { + final List allowedLoanStatuses = Arrays.asList(LoanStatus.values()); + return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses); + } + + private void checkClientOrGroupActive(final Loan loan) { + final Client client = loan.client(); + if (client != null) { + if (client.isNotActive()) { throw new ClientNotActiveException(client.getId()); } + } + final Group group = loan.group(); + if (group != null) { + if (group.isNotActive()) { throw new GroupNotActiveException(group.getId()); } + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java index 60f9482d7..07cd579d8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java @@ -43,6 +43,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; +import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository; @@ -65,6 +66,8 @@ import com.google.gson.JsonElement; @Service public class LoanAssembler { + private final FromJsonHelper fromApiJsonHelper; + private final LoanRepositoryWrapper loanRepository; private final LoanProductRepository loanProductRepository; private final ClientRepositoryWrapper clientRepository; private final GroupRepository groupRepository; @@ -75,7 +78,6 @@ public class LoanAssembler { private final LoanScheduleAssembler loanScheduleAssembler; private final LoanChargeAssembler loanChargeAssembler; private final CollateralAssembler loanCollateralAssembler; - private final FromJsonHelper fromApiJsonHelper; private final LoanSummaryWrapper loanSummaryWrapper; private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory; private final HolidayRepository holidayRepository; @@ -83,8 +85,9 @@ public class LoanAssembler { private final WorkingDaysRepositoryWrapper workingDaysRepository; @Autowired - public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository, - final ClientRepositoryWrapper clientRepository, final GroupRepository groupRepository, final FundRepository fundRepository, + public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanRepositoryWrapper loanRepository, + final LoanProductRepository loanProductRepository, final ClientRepositoryWrapper clientRepository, + final GroupRepository groupRepository, final FundRepository fundRepository, final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository, final StaffRepository staffRepository, final CodeValueRepositoryWrapper codeValueRepository, final LoanScheduleAssembler loanScheduleAssembler, final LoanChargeAssembler loanChargeAssembler, @@ -93,6 +96,7 @@ public class LoanAssembler { final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService, final WorkingDaysRepositoryWrapper workingDaysRepository) { this.fromApiJsonHelper = fromApiJsonHelper; + this.loanRepository = loanRepository; this.loanProductRepository = loanProductRepository; this.clientRepository = clientRepository; this.groupRepository = groupRepository; @@ -110,24 +114,32 @@ public class LoanAssembler { this.workingDaysRepository = workingDaysRepository; } + public Loan assembleFrom(final Long accountId) { + final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId); + loanAccount.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, + this.loanRepaymentScheduleTransactionProcessorFactory); + + return loanAccount; + } + public Loan assembleFrom(final JsonCommand command, final AppUser currentUser) { final JsonElement element = command.parsedJson(); - final Long clientId = fromApiJsonHelper.extractLongNamed("clientId", element); - final Long groupId = fromApiJsonHelper.extractLongNamed("groupId", element); + final Long clientId = this.fromApiJsonHelper.extractLongNamed("clientId", element); + final Long groupId = this.fromApiJsonHelper.extractLongNamed("groupId", element); return assembleApplication(element, clientId, groupId, currentUser); } private Loan assembleApplication(final JsonElement element, final Long clientId, final Long groupId, final AppUser currentUser) { - final String accountNo = fromApiJsonHelper.extractStringNamed("accountNo", element); - final Long productId = fromApiJsonHelper.extractLongNamed("productId", element); - final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element); - final Long loanOfficerId = fromApiJsonHelper.extractLongNamed("loanOfficerId", element); - final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element); - final Long loanPurposeId = fromApiJsonHelper.extractLongNamed("loanPurposeId", element); - final Boolean syncDisbursementWithMeeting = fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element); + final String accountNo = this.fromApiJsonHelper.extractStringNamed("accountNo", element); + final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element); + final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element); + final Long loanOfficerId = this.fromApiJsonHelper.extractLongNamed("loanOfficerId", element); + final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element); + final Long loanPurposeId = this.fromApiJsonHelper.extractLongNamed("loanPurposeId", element); + final Boolean syncDisbursementWithMeeting = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element); final LoanProduct loanProduct = this.loanProductRepository.findOne(productId); if (loanProduct == null) { throw new LoanProductNotFoundException(productId); } @@ -137,11 +149,11 @@ public class LoanAssembler { final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId); CodeValue loanPurpose = null; if (loanPurposeId != null) { - loanPurpose = codeValueRepository.findOneWithNotFoundDetection(loanPurposeId); + loanPurpose = this.codeValueRepository.findOneWithNotFoundDetection(loanPurposeId); } final Set collateral = this.loanCollateralAssembler.fromParsedJson(element); final Set loanCharges = this.loanChargeAssembler.fromParsedJson(element); - for (LoanCharge loanCharge : loanCharges) { + for (final LoanCharge loanCharge : loanCharges) { if (!loanProduct.hasCurrencyCodeOf(loanCharge.currencyCode())) { final String errorMessage = "Charge and Loan must have the same currency."; throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage); @@ -155,14 +167,12 @@ public class LoanAssembler { final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element); final String loanTypeParameterName = "loanType"; - final String loanTypeStr = fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element); + final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element); final EnumOptionData loanType = AccountEnumerations.loanType(loanTypeStr); if (clientId != null) { client = this.clientRepository.findOneWithNotFoundDetection(clientId); - if (client.isNotActive()) { - throw new ClientNotActiveException(clientId); - } + if (client.isNotActive()) { throw new ClientNotActiveException(clientId); } loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral); @@ -171,9 +181,7 @@ public class LoanAssembler { if (groupId != null) { group = this.groupRepository.findOne(groupId); if (group == null) { throw new GroupNotFoundException(groupId); } - if (group.isNotActive()) { - throw new GroupNotActiveException(groupId); - } + if (group.isNotActive()) { throw new GroupNotActiveException(groupId); } loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, syncDisbursementWithMeeting); @@ -184,11 +192,12 @@ public class LoanAssembler { if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); } loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(), - loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, syncDisbursementWithMeeting); + loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, + syncDisbursementWithMeeting); } - final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element); - final LocalDate submittedOnDate = fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element); + final String externalId = this.fromApiJsonHelper.extractStringNamed("externalId", element); + final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element); if (loanApplication == null) { throw new IllegalStateException("No loan application exists for either a client or group (or both)."); } loanApplication.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, @@ -196,11 +205,13 @@ public class LoanAssembler { final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(), loanApplicationTerms.getExpectedDisbursementDate().toDate()); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(), + loanApplicationTerms.getExpectedDisbursementDate().toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); - final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms, isHolidayEnabled, holidays, workingDays, element); + final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms, + isHolidayEnabled, holidays, workingDays, element); loanApplication.loanApplicationSubmittal(currentUser, loanScheduleModel, loanApplicationTerms, defaultLoanLifecycleStateMachine(), submittedOnDate, externalId, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay); @@ -208,7 +219,7 @@ public class LoanAssembler { } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { - List allowedLoanStatuses = Arrays.asList(LoanStatus.values()); + final List allowedLoanStatuses = Arrays.asList(LoanStatus.values()); return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses); } @@ -256,7 +267,7 @@ public class LoanAssembler { loanApplication.getExpectedDisbursedOnLocalDate().toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); - + loanApplication.validateExpectedDisbursementForHolidayAndNonWorkingDay(workingDays, allowTransactionsOnHoliday, holidays, allowTransactionsOnNonWorkingDay); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 4e882616f..db1594e49 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -61,17 +61,15 @@ import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand; import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail; import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService; import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository; import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine; -import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; -import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository; import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException; -import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException; @@ -99,6 +97,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanEventApiJsonValidator loanEventApiJsonValidator; private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer; private final LoanRepository loanRepository; + private final LoanAccountDomainService loanAccountDomainService; private final NoteRepository noteRepository; private final LoanTransactionRepository loanTransactionRepository; private final LoanAssembler loanAssembler; @@ -106,8 +105,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanChargeRepository loanChargeRepository; private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository; private final JournalEntryWritePlatformService journalEntryWritePlatformService; - private final LoanSummaryWrapper loanSummaryWrapper; - private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory; private final LoanScheduleGeneratorFactory loanScheduleFactory; private final CalendarInstanceRepository calendarInstanceRepository; private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; @@ -120,20 +117,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanEventApiJsonValidator loanEventApiJsonValidator, final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanAssembler loanAssembler, - final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository, - final NoteRepository noteRepository, final ChargeRepositoryWrapper chargeRepository, - final LoanChargeRepository loanChargeRepository, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, - final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanSummaryWrapper loanSummaryWrapper, - final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory, + final LoanRepository loanRepository, final LoanAccountDomainService loanAccountDomainService, + final LoanTransactionRepository loanTransactionRepository, final NoteRepository noteRepository, + final ChargeRepositoryWrapper chargeRepository, final LoanChargeRepository loanChargeRepository, + final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, + final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanScheduleGeneratorFactory loanScheduleFactory, final CalendarInstanceRepository calendarInstanceRepository, final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService, final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanProductReadPlatformService loanProductReadPlatformService) { - this.context = context; this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanAssembler = loanAssembler; this.loanRepository = loanRepository; + this.loanAccountDomainService = loanAccountDomainService; this.loanTransactionRepository = loanTransactionRepository; this.noteRepository = noteRepository; this.chargeRepository = chargeRepository; @@ -141,8 +138,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.applicationCurrencyRepository = applicationCurrencyRepository; this.journalEntryWritePlatformService = journalEntryWritePlatformService; this.loanUpdateCommandFromApiJsonDeserializer = loanUpdateCommandFromApiJsonDeserializer; - this.loanSummaryWrapper = loanSummaryWrapper; - this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory; this.loanScheduleFactory = loanScheduleFactory; this.calendarInstanceRepository = calendarInstanceRepository; this.paymentDetailWritePlatformService = paymentDetailWritePlatformService; @@ -153,7 +148,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { - List allowedLoanStatuses = Arrays.asList(LoanStatus.values()); + final List allowedLoanStatuses = Arrays.asList(LoanStatus.values()); return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses); } @@ -161,14 +156,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Override public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand command) { - final AppUser currentUser = context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); this.loanEventApiJsonValidator.validateDisbursement(command.json()); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); - //check for product mix validations + // check for product mix validations checkForProductMixRestrictions(loan); // validate actual disbursement date against meeting date @@ -188,13 +183,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Map changes = new LinkedHashMap(); - PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); // Recalculate first repayment date based in actual disbursement date. final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); - final LocalDate calculatedRepaymentsStartingFromDate = getCalculatedRepaymentsStartingFromDate(actualDisbursementDate, loan, calendarInstance); + final LocalDate calculatedRepaymentsStartingFromDate = getCalculatedRepaymentsStartingFromDate(actualDisbursementDate, loan, + calendarInstance); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), actualDisbursementDate.toDate()); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), + actualDisbursementDate.toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); @@ -209,7 +206,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { - Note note = Note.loanNote(loan, noteText); + final Note note = Note.loanNote(loan, noteText); this.noteRepository.save(note); } @@ -229,7 +226,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private LocalDate getCalculatedRepaymentsStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan, final CalendarInstance calendarInstance) { - final Calendar calendar = (calendarInstance == null) ? null : calendarInstance.getCalendar(); + final Calendar calendar = calendarInstance == null ? null : calendarInstance.getCalendar(); LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate(); if (calendar != null) {// sync repayments @@ -246,8 +243,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Integer repayEvery = repaymentScheduleDetails.getRepayEvery(); final String frequency = CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentScheduleDetails .getRepaymentPeriodFrequencyType()); - calculatedRepaymentsStartingFromDate = CalendarHelper.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate, repayEvery, - frequency); + calculatedRepaymentsStartingFromDate = CalendarHelper.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate, + repayEvery, frequency); } } } @@ -260,16 +257,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Transactional @Override public Map bulkLoanDisbursal(final JsonCommand command, final CollectionSheetBulkDisbursalCommand bulkDisbursalCommand) { - final AppUser currentUser = context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); - SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions(); - Map changes = new LinkedHashMap(); + final SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions(); + final Map changes = new LinkedHashMap(); if (disbursalCommand == null) { return changes; } for (int i = 0; i < disbursalCommand.length; i++) { - SingleDisbursalCommand singleLoanDisbursalCommand = disbursalCommand[i]; + final SingleDisbursalCommand singleLoanDisbursalCommand = disbursalCommand[i]; - final Loan loan = retrieveLoanBy(singleLoanDisbursalCommand.getLoanId()); + final Loan loan = this.loanAssembler.assembleFrom(singleLoanDisbursalCommand.getLoanId()); checkClientOrGroupActive(loan); final MonetaryCurrency currency = loan.getCurrency(); final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); @@ -277,7 +274,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); - PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); // Bulk disbursement should happen on meeting date (mostly from // collection sheet). @@ -288,7 +285,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate firstRepaymentOnDate = null; final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), actualDisbursementDate.toDate()); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), + actualDisbursementDate.toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); @@ -303,7 +301,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { - Note note = Note.loanNote(loan, noteText); + final Note note = Note.loanNote(loan, noteText); this.noteRepository.save(note); } @@ -317,9 +315,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Override public CommandProcessingResult undoLoanDisbursal(final Long loanId, final JsonCommand command) { - context.authenticatedUser(); - - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); removeLoanCycle(loan); final List existingTransactionIds = new ArrayList(); @@ -330,7 +326,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { - Note note = Note.loanNote(loan, noteText); + final Note note = Note.loanNote(loan, noteText); this.noteRepository.save(note); } @@ -339,7 +335,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Map accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(), existingTransactionIds, existingReversedTransactionIds); - journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); + this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); } return new CommandProcessingResultBuilder() // @@ -357,7 +353,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Override public CommandProcessingResult makeLoanRepayment(final Long loanId, final JsonCommand command) { - context.authenticatedUser(); + this.context.authenticatedUser(); this.loanEventApiJsonValidator.validateNewRepaymentTransaction(command.json()); @@ -376,10 +372,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf changes.put("note", noteText); } - PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); + final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); - CommandProcessingResultBuilder commandProcessingResultBuilder = saveLoanRepayment(loanId, paymentDetail, transactionAmount, - transactionDate, noteText, txnExternalId); + final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder(); + this.loanAccountDomainService.makeRepayment(loanId, commandProcessingResultBuilder, transactionDate, transactionAmount, + paymentDetail, noteText, txnExternalId); return commandProcessingResultBuilder.withCommandId(command.commandId()) // .withLoanId(loanId) // @@ -387,86 +384,27 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .build(); } - private CommandProcessingResultBuilder saveLoanRepayment(final Long loanId, final PaymentDetail paymentDetail, - final BigDecimal transactionAmount, final LocalDate transactionDate, final String noteText, final String txnExternalId) { - final Loan loan = retrieveLoanBy(loanId); - checkClientOrGroupActive(loan); - - // TODO: Is it required to validate transaction date with meeting dates - // if repayments is synced with meeting? - /* - * if(loan.isSyncDisbursementWithMeeting()){ // validate actual - * disbursement date against meeting date CalendarInstance - * calendarInstance = - * this.calendarInstanceRepository.findCalendarInstaneByLoanId - * (loan.getId(), CalendarEntityType.LOANS.getValue()); - * this.loanEventApiJsonValidator - * .validateRepaymentDateWithMeetingDate(transactionDate, - * calendarInstance); } - */ - - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); - - final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount); - final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, paymentDetail, transactionDate, txnExternalId); - final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate()); - final WorkingDays workingDays = this.workingDaysRepository.findOne(); - final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); - - final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction, - defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, allowTransactionsOnHoliday, - holidays, workingDays, allowTransactionsOnNonWorkingDay); - - this.loanTransactionRepository.save(newRepaymentTransaction); - - /*** - * TODO Vishwas Batch save is giving me a - * HibernateOptimisticLockingFailureException, looping and saving for - * the time being, not a major issue for now as this loop is entered - * only in edge cases (when a payment is made before the latest payment - * recorded against the loan) - ***/ - if (changedTransactionDetail != null) { - for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { - this.loanTransactionRepository.save(loanTransaction); - } - } - this.loanRepository.save(loan); - - if (StringUtils.isNotBlank(noteText)) { - Note note = Note.loanTransactionNote(loan, newRepaymentTransaction, noteText); - this.noteRepository.save(note); - } - - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - - return new CommandProcessingResultBuilder() // - .withEntityId(newRepaymentTransaction.getId()) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()); // - } - @Transactional @Override public Map makeLoanBulkRepayment(final CollectionSheetBulkRepaymentCommand bulkRepaymentCommand) { - context.authenticatedUser(); + this.context.authenticatedUser(); final SingleRepaymentCommand[] repaymentCommand = bulkRepaymentCommand.getLoanTransactions(); final Map changes = new LinkedHashMap(); - if (repaymentCommand == null) return changes; + if (repaymentCommand == null) { return changes; } - for (SingleRepaymentCommand singleLoanRepaymentCommand : repaymentCommand) { + for (final SingleRepaymentCommand singleLoanRepaymentCommand : repaymentCommand) { /**** * TODO Vishwas, have a re-look at this implementation, defaulting * it to null for now ***/ - PaymentDetail paymentDetail = null; - saveLoanRepayment(singleLoanRepaymentCommand.getLoanId(), paymentDetail, singleLoanRepaymentCommand.getTransactionAmount(), - bulkRepaymentCommand.getTransactionDate(), bulkRepaymentCommand.getNote(), null); + final PaymentDetail paymentDetail = null; + final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder(); + this.loanAccountDomainService.makeRepayment(singleLoanRepaymentCommand.getLoanId(), commandProcessingResultBuilder, + bulkRepaymentCommand.getTransactionDate(), singleLoanRepaymentCommand.getTransactionAmount(), paymentDetail, + bulkRepaymentCommand.getNote(), null); + changes.put("bulkTransations", singleLoanRepaymentCommand); } return changes; @@ -476,11 +414,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Override public CommandProcessingResult adjustLoanTransaction(final Long loanId, final Long transactionId, final JsonCommand command) { - context.authenticatedUser(); + this.context.authenticatedUser(); this.loanEventApiJsonValidator.validateTransaction(command.json()); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); final LoanTransaction transactionToAdjust = this.loanTransactionRepository.findOne(transactionId); @@ -500,14 +438,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingReversedTransactionIds = new ArrayList(); final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount); - PaymentDetail paymentDetail = paymentDetailWritePlatformService.createPaymentDetail(command, changes); - LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate, txnExternalId); + final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createPaymentDetail(command, changes); + LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate, + txnExternalId); if (transactionToAdjust.isInterestWaiver()) { newTransactionDetail = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate); } final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate()); + final List holidays = this.holidayRepository + .findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); @@ -517,7 +457,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) { if (paymentDetail != null) { - paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail); + this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail); } this.loanTransactionRepository.save(newTransactionDetail); } @@ -530,7 +470,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf * payment recorded against the loan) ***/ if (changedTransactionDetail != null) { - for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { + for (final LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { this.loanTransactionRepository.save(loanTransaction); } } @@ -570,7 +510,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Override public CommandProcessingResult waiveInterestOnLoan(final Long loanId, final JsonCommand command) { - context.authenticatedUser(); + this.context.authenticatedUser(); this.loanEventApiJsonValidator.validateTransaction(command.json()); @@ -582,14 +522,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); final List existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount); - LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate); + final LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate); final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds); @@ -604,7 +544,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf * recorded against the loan) ***/ if (changedTransactionDetail != null) { - for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { + for (final LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { this.loanTransactionRepository.save(loanTransaction); } } @@ -643,7 +583,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf changes.put("locale", command.locale()); changes.put("dateFormat", command.dateFormat()); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); removeLoanCycle(loan); @@ -679,11 +619,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Override public CommandProcessingResult closeLoan(final Long loanId, final JsonCommand command) { - context.authenticatedUser(); + this.context.authenticatedUser(); this.loanEventApiJsonValidator.validateTransactionWithNoAmount(command.json()); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); final Map changes = new LinkedHashMap(); @@ -742,11 +682,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Transactional @Override public CommandProcessingResult closeAsRescheduled(final Long loanId, final JsonCommand command) { - context.authenticatedUser(); + this.context.authenticatedUser(); this.loanEventApiJsonValidator.validateTransactionWithNoAmount(command.json()); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); removeLoanCycle(loan); @@ -785,7 +725,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanEventApiJsonValidator.validateAddLoanCharge(command.json()); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); final Long chargeDefinitionId = command.longValueOfParameterNamed("chargeId"); @@ -818,7 +758,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf * latest payment recorded against the loan) ***/ if (changedTransactionDetail != null) { - for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { + for (final LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { this.loanTransactionRepository.save(loanTransaction); } } @@ -846,7 +786,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanEventApiJsonValidator.validateUpdateOfLoanCharge(command.json()); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId); @@ -876,7 +816,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.context.authenticatedUser(); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId); @@ -921,7 +861,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.context.authenticatedUser(); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId); @@ -943,13 +883,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .build(); } - private Loan retrieveLoanBy(final Long loanId) { - final Loan loan = this.loanRepository.findOne(loanId); - if (loan == null) { throw new LoanNotFoundException(loanId); } - loan.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory); - return loan; - } - private LoanCharge retrieveLoanChargeBy(final Long loanId, final Long loanChargeId) { final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId); if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); } @@ -969,11 +902,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Long fromLoanOfficerId = command.longValueOfParameterNamed("fromLoanOfficerId"); final Long toLoanOfficerId = command.longValueOfParameterNamed("toLoanOfficerId"); - final Staff fromLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(fromLoanOfficerId); - final Staff toLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(toLoanOfficerId); + final Staff fromLoanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(fromLoanOfficerId); + final Staff toLoanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(toLoanOfficerId); final LocalDate dateOfLoanOfficerAssignment = command.localDateValueOfParameterNamed("assignmentDate"); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); if (!loan.hasLoanOfficer(fromLoanOfficer)) { throw new LoanOfficerAssignmentException(loanId, fromLoanOfficerId); } @@ -1004,12 +937,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate dateOfLoanOfficerAssignment = command.localDateValueOfParameterNamed("assignmentDate"); - final Staff fromLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(fromLoanOfficerId); - final Staff toLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(toLoanOfficerId); + final Staff fromLoanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(fromLoanOfficerId); + final Staff toLoanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(toLoanOfficerId); for (final String loanIdString : loanIds) { final Long loanId = Long.valueOf(loanIdString); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); if (!loan.hasLoanOfficer(fromLoanOfficer)) { throw new LoanOfficerAssignmentException(loanId, fromLoanOfficerId); } @@ -1037,7 +970,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate dateOfLoanOfficerunAssigned = command.localDateValueOfParameterNamed("unassignedDate"); - final Loan loan = retrieveLoanBy(loanId); + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); if (loan.getLoanOfficer() == null) { throw new LoanOfficerUnassignmentException(loanId); } @@ -1064,31 +997,33 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Map accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(), existingTransactionIds, existingReversedTransactionIds); - journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); + this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); } @Transactional @Override - public void applyMeetingDateChanges(final Calendar calendar, Collection loanCalendarInstances) { - + public void applyMeetingDateChanges(final Calendar calendar, final Collection loanCalendarInstances) { + final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final Collection loanStatuses = new ArrayList(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue())); - final Collection loanTypes = new ArrayList(Arrays.asList(AccountType.GROUP.getValue(), AccountType.JLG.getValue())); + final Collection loanTypes = new ArrayList( + Arrays.asList(AccountType.GROUP.getValue(), AccountType.JLG.getValue())); final Collection loanIds = new ArrayList(loanCalendarInstances.size()); // loop through loanCalendarInstances to get loan ids - for (CalendarInstance calendarInstance : loanCalendarInstances) { + for (final CalendarInstance calendarInstance : loanCalendarInstances) { loanIds.add(calendarInstance.getEntityId()); } final List loans = this.loanRepository.findByIdsAndLoanStatusAndLoanType(loanIds, loanStatuses, loanTypes); List holidays = null; // loop through each loan to reschedule the repayment dates - for (Loan loan : loans) { + for (final Loan loan : loans) { if (loan != null) { holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate()); - loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled, holidays, workingDays); + loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled, + holidays, workingDays); this.loanRepository.save(loan); } } @@ -1247,32 +1182,33 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public void applyHolidaysToLoans() { final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); - - if(!isHolidayEnabled) return; - + + if (!isHolidayEnabled) { return; } + final Collection loanStatuses = new ArrayList(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue())); // Get all Holidays which are yet to applied to loans - List holidays = this.holidayRepository.findUnprocessed(); + final List holidays = this.holidayRepository.findUnprocessed(); // Loop through all holidays - for (Holiday holiday : holidays) { + for (final Holiday holiday : holidays) { // All offices to which holiday is applied final Set offices = holiday.getOffices(); - Collection officeIds = new ArrayList(offices.size()); - for (Office office : offices) { + final Collection officeIds = new ArrayList(offices.size()); + for (final Office office : offices) { officeIds.add(office.getId()); } // get all loans - List loans = new ArrayList(); + final List loans = new ArrayList(); // get all individual and jlg loans loans.addAll(this.loanRepository.findByClientOfficeIdsAndLoanStatus(officeIds, loanStatuses)); - //FIXME: AA optimize to get all client and group loans belongs to a office id - //get all group loans + // FIXME: AA optimize to get all client and group loans belongs to a + // office id + // get all group loans loans.addAll(this.loanRepository.findByGroupOfficeIdsAndLoanStatus(officeIds, loanStatuses)); - for (Loan loan : loans) { + for (final Loan loan : loans) { // apply holiday loan.applyHolidayToRepaymentScheduleDates(holiday); } @@ -1302,7 +1238,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (!CollectionUtils.isEmpty(activeLoansLoanProductIds)) { final Collection restrictedPrdouctsList = this.loanProductReadPlatformService .retrieveRestrictedProductsForMix(productId); - for (LoanProductData restrictedProduct : restrictedPrdouctsList) { + for (final LoanProductData restrictedProduct : restrictedPrdouctsList) { if (activeLoansLoanProductIds.contains(restrictedProduct.getId())) { throw new LoanDisbursalException(productName, restrictedProduct.getName()); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java index 88d5324b3..9dfce4890 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionData.java @@ -11,7 +11,7 @@ import java.util.Collection; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.organisation.monetary.data.CurrencyData; -import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData; /** diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 22692b48a..fcbe61f4b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -26,7 +26,7 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; -import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData; +import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.group.data.GroupGeneralData; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java index 9f439c3c5..acde05eb2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java @@ -151,14 +151,14 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final Long savingsId = account.getId(); return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(savingsId) // - .withOfficeId(account.officeId()) // - .withClientId(account.clientId()) // - .withGroupId(account.groupId()) // - .withSavingsId(savingsId) // - .build(); - } catch (DataAccessException dve) { + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .build(); + } catch (final DataAccessException dve) { handleDataIntegrityIssues(command, dve); return CommandProcessingResult.empty(); } @@ -230,15 +230,15 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl } return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(savingsId) // - .withOfficeId(account.officeId()) // - .withClientId(account.clientId()) // - .withGroupId(account.groupId()) // - .withSavingsId(savingsId) // - .with(changes) // - .build(); - } catch (DataAccessException dve) { + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); + } catch (final DataAccessException dve) { handleDataIntegrityIssues(command, dve); return new CommandProcessingResult(Long.valueOf(-1)); } @@ -254,33 +254,33 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl if (account.isNotSubmittedAndPendingApproval()) { final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.deleteApplicationAction); + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.deleteApplicationAction); baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName) - .failWithCodeNoParameterAddedToErrorCode("not.in.submittedandpendingapproval.state"); + .failWithCodeNoParameterAddedToErrorCode("not.in.submittedandpendingapproval.state"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - List relatedNotes = this.noteRepository.findBySavingsAccountId(savingsId); + final List relatedNotes = this.noteRepository.findBySavingsAccountId(savingsId); this.noteRepository.deleteInBatch(relatedNotes); this.savingAccountRepository.delete(account); return new CommandProcessingResultBuilder() // - .withEntityId(savingsId) // - .withOfficeId(account.officeId()) // - .withClientId(account.clientId()) // - .withGroupId(account.groupId()) // - .withSavingsId(savingsId) // - .build(); + .withEntityId(savingsId) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // + .withSavingsId(savingsId) // + .build(); } @Transactional @Override public CommandProcessingResult approveApplication(final Long savingsId, final JsonCommand command) { - final AppUser currentUser = context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); this.savingsAccountApplicationTransitionApiJsonValidator.validateApproval(command.json()); @@ -293,28 +293,28 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { - Note note = Note.savingNote(savingsAccount, noteText); + final Note note = Note.savingNote(savingsAccount, noteText); changes.put("note", noteText); this.noteRepository.save(note); } } return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(savingsId) // - .withOfficeId(savingsAccount.officeId()) // - .withClientId(savingsAccount.clientId()) // - .withGroupId(savingsAccount.groupId()) // - .withSavingsId(savingsId) // - .with(changes) // - .build(); + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); } @Transactional @Override public CommandProcessingResult undoApplicationApproval(final Long savingsId, final JsonCommand command) { - context.authenticatedUser(); + this.context.authenticatedUser(); this.savingsAccountApplicationTransitionApiJsonValidator.validateForUndo(command.json()); @@ -327,28 +327,28 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { - Note note = Note.savingNote(savingsAccount, noteText); + final Note note = Note.savingNote(savingsAccount, noteText); changes.put("note", noteText); this.noteRepository.save(note); } } return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(savingsId) // - .withOfficeId(savingsAccount.officeId()) // - .withClientId(savingsAccount.clientId()) // - .withGroupId(savingsAccount.groupId()) // - .withSavingsId(savingsId) // - .with(changes) // - .build(); + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); } @Transactional @Override public CommandProcessingResult rejectApplication(final Long savingsId, final JsonCommand command) { - final AppUser currentUser = context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); this.savingsAccountApplicationTransitionApiJsonValidator.validateRejection(command.json()); @@ -361,27 +361,27 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { - Note note = Note.savingNote(savingsAccount, noteText); + final Note note = Note.savingNote(savingsAccount, noteText); changes.put("note", noteText); this.noteRepository.save(note); } } return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(savingsId) // - .withOfficeId(savingsAccount.officeId()) // - .withClientId(savingsAccount.clientId()) // - .withGroupId(savingsAccount.groupId()) // - .withSavingsId(savingsId) // - .with(changes) // - .build(); + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); } @Transactional @Override public CommandProcessingResult applicantWithdrawsFromApplication(final Long savingsId, final JsonCommand command) { - final AppUser currentUser = context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); this.savingsAccountApplicationTransitionApiJsonValidator.validateApplicantWithdrawal(command.json()); @@ -395,21 +395,21 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { - Note note = Note.savingNote(savingsAccount, noteText); + final Note note = Note.savingNote(savingsAccount, noteText); changes.put("note", noteText); this.noteRepository.save(note); } } return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(savingsId) // - .withOfficeId(savingsAccount.officeId()) // - .withClientId(savingsAccount.clientId()) // - .withGroupId(savingsAccount.groupId()) // - .withSavingsId(savingsId) // - .with(changes) // - .build(); + .withCommandId(command.commandId()) // + .withEntityId(savingsId) // + .withOfficeId(savingsAccount.officeId()) // + .withClientId(savingsAccount.clientId()) // + .withGroupId(savingsAccount.groupId()) // + .withSavingsId(savingsId) // + .with(changes) // + .build(); } private void postJournalEntries(final SavingsAccount savingsAccount, final List existingTransactionIds, @@ -420,7 +420,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final Map accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(), existingTransactionIds, existingReversedTransactionIds); - journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); + this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); } private void checkClientOrGroupActive(final SavingsAccount account) { From 8d0fbe4197ab19c77c7ccfb39b8142fd30d3277e Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Mon, 19 Aug 2013 06:50:02 +0530 Subject: [PATCH 40/47] added cluster support for Scheduler --- .../V103__cluster_support_for_batch_jobs.sql | 14 +++ .../jobs/domain/ScheduledJobDetail.java | 19 ++++ .../domain/ScheduledJobDetailRepository.java | 8 ++ .../jobs/domain/ScheduledJobRunHistory.java | 8 ++ .../jobs/domain/SchedulerDetail.java | 49 ++++++++++ .../domain/SchedulerDetailRepository.java | 8 ++ .../jobs/service/JobRegisterServiceImpl.java | 95 +++++++++++-------- .../SchedularWritePlatformService.java | 9 +- ...WritePlatformServiceJpaRepositoryImpl.java | 45 ++++++++- .../jobs/service/SchedulerJobListener.java | 22 +---- .../service/SchedulerTriggerListener.java | 66 +++++++++++++ 11 files changed, 281 insertions(+), 62 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V103__cluster_support_for_batch_jobs.sql create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/SchedulerDetail.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/SchedulerDetailRepository.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerTriggerListener.java diff --git a/mifosng-db/migrations/core_db/V103__cluster_support_for_batch_jobs.sql b/mifosng-db/migrations/core_db/V103__cluster_support_for_batch_jobs.sql new file mode 100755 index 000000000..7343f7f74 --- /dev/null +++ b/mifosng-db/migrations/core_db/V103__cluster_support_for_batch_jobs.sql @@ -0,0 +1,14 @@ +ALTER TABLE `job` + ADD COLUMN `is_misfired` TINYINT(1) NOT NULL DEFAULT '0' AFTER `scheduler_group`; + +CREATE TABLE `scheduler_detail` ( + `id` SMALLINT(2) NOT NULL AUTO_INCREMENT, + `is_suspended` TINYINT(1) NOT NULL DEFAULT '0', + `execute_misfired_jobs` TINYINT(1) NOT NULL DEFAULT '1', + `reset_scheduler_on_bootup` TINYINT(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + +INSERT INTO `scheduler_detail` (`is_suspended`, `execute_misfired_jobs`, `reset_scheduler_on_bootup`) VALUES (FALSE,TRUE,TRUE); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java index 7664cc5bc..48b6ba191 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java @@ -28,6 +28,7 @@ public class ScheduledJobDetail extends AbstractPersistable { @Column(name = "cron_expression") private String cronExpression; + @SuppressWarnings("unused") @Column(name = "create_time") @Temporal(TemporalType.TIMESTAMP) private Date createTime; @@ -38,6 +39,7 @@ public class ScheduledJobDetail extends AbstractPersistable { @Column(name = "group_name") private String groupName; + @SuppressWarnings("unused") @Column(name = "previous_run_start_time") @Temporal(TemporalType.TIMESTAMP) private Date previousRunStartTime; @@ -49,6 +51,7 @@ public class ScheduledJobDetail extends AbstractPersistable { @Column(name = "job_key") private String jobKey; + @SuppressWarnings("unused") @Column(name = "initializing_errorlog") private String errorLog; @@ -58,12 +61,16 @@ public class ScheduledJobDetail extends AbstractPersistable { @Column(name = "currently_running") private boolean currentlyRunning; + @SuppressWarnings("unused") @Column(name = "updates_allowed") private boolean updatesAllowed; @Column(name = "scheduler_group") private Short schedulerGroup; + @Column(name = "is_misfired") + private boolean triggerMisfired; + protected ScheduledJobDetail() { } @@ -104,6 +111,10 @@ public class ScheduledJobDetail extends AbstractPersistable { this.previousRunStartTime = previousRunStartTime; } + public Date getNextRunTime() { + return this.nextRunTime; + } + public void updateNextRunTime(final Date nextRunTime) { this.nextRunTime = nextRunTime; } @@ -147,4 +158,12 @@ public class ScheduledJobDetail extends AbstractPersistable { return actualChanges; } + public boolean isTriggerMisfired() { + return this.triggerMisfired; + } + + public void updateTriggerMisfired(boolean triggerMisfired) { + this.triggerMisfired = triggerMisfired; + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetailRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetailRepository.java index c673f7ea0..50cb0dc5f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetailRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetailRepository.java @@ -1,7 +1,10 @@ package org.mifosplatform.infrastructure.jobs.domain; +import javax.persistence.LockModeType; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -14,4 +17,9 @@ public interface ScheduledJobDetailRepository extends JpaRepository { @ManyToOne @JoinColumn(name = "job_id") + @SuppressWarnings("unused") private ScheduledJobDetail scheduledJobDetail; @Column(name = "version") + @SuppressWarnings("unused") private Long version; @Column(name = "start_time") @Temporal(TemporalType.TIMESTAMP) + @SuppressWarnings("unused") private Date startTime; @Column(name = "end_time") @Temporal(TemporalType.TIMESTAMP) + @SuppressWarnings("unused") private Date endTime; @Column(name = "status") + @SuppressWarnings("unused") private String status; @Column(name = "error_message") + @SuppressWarnings("unused") private String errorMessage; @Column(name = "trigger_type") + @SuppressWarnings("unused") private String triggerType; @Column(name = "error_log") + @SuppressWarnings("unused") private String errorLog; public ScheduledJobRunHistory() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/SchedulerDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/SchedulerDetail.java new file mode 100755 index 000000000..3cb68eee3 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/SchedulerDetail.java @@ -0,0 +1,49 @@ +package org.mifosplatform.infrastructure.jobs.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "scheduler_detail") +public class SchedulerDetail extends AbstractPersistable { + + @Column(name = "execute_misfired_jobs") + private boolean executeInstructionForMisfiredJobs; + + @Column(name = "is_suspended") + private boolean suspended; + + @Column(name = "reset_scheduler_on_bootup") + private boolean resetSchedulerOnBootup; + + protected SchedulerDetail() { + + } + + public boolean isExecuteInstructionForMisfiredJobs() { + return this.executeInstructionForMisfiredJobs; + } + + public void updateExecuteInstructionForMisfiredJobs(boolean executeInstructionForMisfiredJobs) { + this.executeInstructionForMisfiredJobs = executeInstructionForMisfiredJobs; + } + + public boolean isSuspended() { + return this.suspended; + } + + public void updateSuspendedState(boolean suspended) { + this.suspended = suspended; + } + + public boolean isResetSchedulerOnBootup() { + return this.resetSchedulerOnBootup; + } + + public void updateResetSchedulerOnBootup(boolean resetSchedulerOnBootup) { + this.resetSchedulerOnBootup = resetSchedulerOnBootup; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/SchedulerDetailRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/SchedulerDetailRepository.java new file mode 100755 index 000000000..ee3d8cd8a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/SchedulerDetailRepository.java @@ -0,0 +1,8 @@ +package org.mifosplatform.infrastructure.jobs.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface SchedulerDetailRepository extends JpaRepository, JpaSpecificationExecutor { + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java index c395cfd71..405e8d156 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Properties; import java.util.TimeZone; @@ -15,6 +14,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformInternalServerExc import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.jobs.annotation.CronMethodParser; import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetail; +import org.mifosplatform.infrastructure.jobs.domain.SchedulerDetail; import org.mifosplatform.infrastructure.jobs.exception.JobNotFoundException; import org.mifosplatform.infrastructure.security.service.TenantDetailsService; import org.quartz.JobDataMap; @@ -24,6 +24,7 @@ import org.quartz.JobListener; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; +import org.quartz.TriggerListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -50,8 +51,6 @@ public class JobRegisterServiceImpl implements JobRegisterService { private final TenantDetailsService tenantDetailsService; - private final HashMap schedulerStatus = new HashMap(2); - private final HashMap schedulers = new HashMap(4); @Autowired @@ -68,24 +67,32 @@ public class JobRegisterServiceImpl implements JobRegisterService { for (MifosPlatformTenant tenant : allTenants) { ThreadLocalContextUtil.setTenant(tenant); List scheduledJobDetails = this.schedularWritePlatformService.retrieveAllJobs(); - schedulerStatus.put(tenant.getId(), true); for (ScheduledJobDetail jobDetails : scheduledJobDetails) { scheduleJob(jobDetails); + jobDetails.updateTriggerMisfired(false); this.schedularWritePlatformService.saveOrUpdate(jobDetails); } + SchedulerDetail schedulerDetail = this.schedularWritePlatformService.retriveSchedulerDetail(); + if (schedulerDetail.isResetSchedulerOnBootup()) { + schedulerDetail.updateSuspendedState(false); + this.schedularWritePlatformService.updateSchedulerDetail(schedulerDetail); + } } } - public void executeJob(final ScheduledJobDetail scheduledJobDetail) { + public void executeJob(final ScheduledJobDetail scheduledJobDetail, String triggerType) { try { final JobDataMap jobDataMap = new JobDataMap(); - jobDataMap.put(SchedulerServiceConstants.TRIGGER_TYPE_REFERENCE, SchedulerServiceConstants.TRIGGER_TYPE_APPLICATION); + if (triggerType == null) { + triggerType = SchedulerServiceConstants.TRIGGER_TYPE_APPLICATION; + } + jobDataMap.put(SchedulerServiceConstants.TRIGGER_TYPE_REFERENCE, triggerType); jobDataMap.put(SchedulerServiceConstants.TENANT_IDENTIFIER, ThreadLocalContextUtil.getTenant().getTenantIdentifier()); String key = scheduledJobDetail.getJobKey(); JobKey jobKey = constructJobKey(key); String schedulerName = getSchedulerName(scheduledJobDetail); Scheduler scheduler = schedulers.get(schedulerName); - if (scheduler == null || scheduler.isInStandbyMode() || !scheduler.checkExists(jobKey)) { + if (scheduler == null || !scheduler.checkExists(jobKey)) { JobDetail jobDetail = createJobDetail(scheduledJobDetail); String tempSchedulerName = "temp" + scheduledJobDetail.getId(); List> listenerClasses = new ArrayList>(2); @@ -101,7 +108,6 @@ public class JobRegisterServiceImpl implements JobRegisterService { } } catch (Exception e) { - e.printStackTrace(); throw new PlatformInternalServerException("error.msg.sheduler.job.execution.failed", "Job execution failed for job with id:" + scheduledJobDetail.getId(), scheduledJobDetail.getId()); } @@ -128,32 +134,48 @@ public class JobRegisterServiceImpl implements JobRegisterService { @Override public void pauseScheduler() { - schedulerStatus.put(ThreadLocalContextUtil.getTenant().getId(), false); - for (Map.Entry schedulerEntry : this.schedulers.entrySet()) { - try { - String schedulerName = schedulerEntry.getValue().getSchedulerName(); - if (isCurrentTenantsScheduler(schedulerName)) { - schedulerEntry.getValue().standby(); - } - } catch (SchedulerException e) { - logger.error(e.getMessage(), e); - } + SchedulerDetail schedulerDetail = this.schedularWritePlatformService.retriveSchedulerDetail(); + if (!schedulerDetail.isSuspended()) { + schedulerDetail.updateSuspendedState(true); + this.schedularWritePlatformService.updateSchedulerDetail(schedulerDetail); } } @Override public void startScheduler() { - for (Map.Entry schedulerEntry : this.schedulers.entrySet()) { - try { - String schedulerName = schedulerEntry.getValue().getSchedulerName(); - if (isCurrentTenantsScheduler(schedulerName)) { - schedulerEntry.getValue().start(); + SchedulerDetail schedulerDetail = this.schedularWritePlatformService.retriveSchedulerDetail(); + if (schedulerDetail.isSuspended()) { + schedulerDetail.updateSuspendedState(false); + this.schedularWritePlatformService.updateSchedulerDetail(schedulerDetail); + if (schedulerDetail.isExecuteInstructionForMisfiredJobs()) { + List scheduledJobDetails = this.schedularWritePlatformService.retrieveAllJobs(); + for (ScheduledJobDetail jobDetail : scheduledJobDetails) { + if (jobDetail.isTriggerMisfired()) { + if (jobDetail.isActiveSchedular()) { + executeJob(jobDetail, SchedulerServiceConstants.TRIGGER_TYPE_CRON); + } + String schedulerName = getSchedulerName(jobDetail); + Scheduler scheduler = schedulers.get(schedulerName); + if (scheduler != null) { + String key = jobDetail.getJobKey(); + JobKey jobKey = constructJobKey(key); + try { + List triggers = scheduler.getTriggersOfJob(jobKey); + for (Trigger trigger : triggers) { + if (trigger.getNextFireTime() != null && trigger.getNextFireTime().after(jobDetail.getNextRunTime())) { + jobDetail.updateNextRunTime(trigger.getNextFireTime()); + } + } + } catch (SchedulerException e) { + logger.error(e.getMessage(), e); + } + } + jobDetail.updateTriggerMisfired(false); + this.schedularWritePlatformService.saveOrUpdate(jobDetail); + } } - } catch (SchedulerException e) { - logger.error(e.getMessage(), e); } } - schedulerStatus.put(ThreadLocalContextUtil.getTenant().getId(), true); } @Override @@ -166,23 +188,12 @@ public class JobRegisterServiceImpl implements JobRegisterService { public void executeJob(final Long jobId) { ScheduledJobDetail scheduledJobDetail = this.schedularWritePlatformService.findByJobId(jobId); if (scheduledJobDetail == null) { throw new JobNotFoundException(String.valueOf(jobId)); } - executeJob(scheduledJobDetail); + executeJob(scheduledJobDetail, null); } @Override public boolean isSchedulerRunning() { - return this.schedulerStatus.get(ThreadLocalContextUtil.getTenant().getId()); - } - - private boolean isCurrentTenantsScheduler(final String schedulerName) { - int beginIndex = SchedulerServiceConstants.SCHEDULER.length(); - int endIndex = schedulerName.length(); - if (schedulerName.contains(SchedulerServiceConstants.SCHEDULER_GROUP)) { - endIndex = schedulerName.indexOf(SchedulerServiceConstants.SCHEDULER_GROUP); - } - String tenantId = schedulerName.substring(beginIndex, endIndex); - MifosPlatformTenant tenant = ThreadLocalContextUtil.getTenant(); - return tenant.getId() == Long.valueOf(tenantId); + return !this.schedularWritePlatformService.retriveSchedulerDetail().isSuspended(); } private void scheduleJob(final ScheduledJobDetail scheduledJobDetails) { @@ -195,9 +206,6 @@ public class JobRegisterServiceImpl implements JobRegisterService { JobDetail jobDetail = createJobDetail(scheduledJobDetails); Trigger trigger = createTrigger(scheduledJobDetails, jobDetail); Scheduler scheduler = getScheduler(scheduledJobDetails); - if (!this.schedulerStatus.get(ThreadLocalContextUtil.getTenant().getId()) && !scheduler.isInStandbyMode()) { - scheduler.standby(); - } scheduler.scheduleJob(jobDetail, trigger); scheduledJobDetails.updateJobKey(getJobKeyAsString(jobDetail.getKey())); scheduledJobDetails.updateNextRunTime(trigger.getNextFireTime()); @@ -253,6 +261,9 @@ public class JobRegisterServiceImpl implements JobRegisterService { listenerClasses.add(SchedulerJobListener.class); } schedulerFactoryBean.setGlobalJobListeners(getGlobalListener(listenerClasses)); + TriggerListener globalTriggerListener = applicationContext.getBean("schedulerTriggerListener", TriggerListener.class); + TriggerListener[] globalTriggerListeners = { globalTriggerListener }; + schedulerFactoryBean.setGlobalTriggerListeners(globalTriggerListeners); Properties quartzProperties = new Properties(); quartzProperties.put(SchedulerFactoryBean.PROP_THREAD_COUNT, Integer.toString(noOfThreads)); schedulerFactoryBean.setQuartzProperties(quartzProperties); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformService.java index b5c6a680c..193294ee0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformService.java @@ -6,6 +6,7 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetail; import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobRunHistory; +import org.mifosplatform.infrastructure.jobs.domain.SchedulerDetail; public interface SchedularWritePlatformService { @@ -21,6 +22,12 @@ public interface SchedularWritePlatformService { public ScheduledJobDetail findByJobId(Long jobId); - CommandProcessingResult updateJobDetail(Long jobId, JsonCommand command); + public CommandProcessingResult updateJobDetail(Long jobId, JsonCommand command); + + public SchedulerDetail retriveSchedulerDetail(); + + public void updateSchedulerDetail(final SchedulerDetail schedulerDetail); + + public boolean processJobDetailForExecution(String jobKey, String triggerType); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformServiceJpaRepositoryImpl.java index 74a3b96a9..b686ef849 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformServiceJpaRepositoryImpl.java @@ -1,5 +1,6 @@ package org.mifosplatform.infrastructure.jobs.service; +import java.util.Date; import java.util.List; import java.util.Map; @@ -11,6 +12,8 @@ import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetail; import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetailRepository; import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobRunHistory; import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobRunHistoryRepository; +import org.mifosplatform.infrastructure.jobs.domain.SchedulerDetail; +import org.mifosplatform.infrastructure.jobs.domain.SchedulerDetailRepository; import org.mifosplatform.infrastructure.jobs.exception.JobNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -23,13 +26,17 @@ public class SchedularWritePlatformServiceJpaRepositoryImpl implements Schedular private final ScheduledJobRunHistoryRepository scheduledJobRunHistoryRepository; + private final SchedulerDetailRepository schedulerDetailRepository; + private final JobDetailDataValidator dataValidator; @Autowired public SchedularWritePlatformServiceJpaRepositoryImpl(final ScheduledJobDetailRepository scheduledJobDetailsRepository, - final ScheduledJobRunHistoryRepository scheduledJobRunHistoryRepository, final JobDetailDataValidator dataValidator) { + final ScheduledJobRunHistoryRepository scheduledJobRunHistoryRepository, final JobDetailDataValidator dataValidator, + final SchedulerDetailRepository schedulerDetailRepository) { this.scheduledJobDetailsRepository = scheduledJobDetailsRepository; this.scheduledJobRunHistoryRepository = scheduledJobRunHistoryRepository; + this.schedulerDetailRepository = schedulerDetailRepository; this.dataValidator = dataValidator; } @@ -71,6 +78,22 @@ public class SchedularWritePlatformServiceJpaRepositoryImpl implements Schedular return this.scheduledJobDetailsRepository.findByJobId(jobId); } + @Override + @Transactional + public void updateSchedulerDetail(final SchedulerDetail schedulerDetail) { + this.schedulerDetailRepository.save(schedulerDetail); + } + + @Override + public SchedulerDetail retriveSchedulerDetail() { + SchedulerDetail schedulerDetail = null; + List schedulerDetailList = schedulerDetailRepository.findAll(); + if (schedulerDetailList != null) { + schedulerDetail = schedulerDetailList.get(0); + } + return schedulerDetail; + } + @Transactional @Override public CommandProcessingResult updateJobDetail(final Long jobId, final JsonCommand command) { @@ -89,4 +112,24 @@ public class SchedularWritePlatformServiceJpaRepositoryImpl implements Schedular } + @Transactional + @Override + public boolean processJobDetailForExecution(String jobKey, String triggerType) { + boolean isStopExecution = false; + final ScheduledJobDetail scheduledJobDetail = scheduledJobDetailsRepository.findByJobKeyWithLock(jobKey); + if (scheduledJobDetail.isCurrentlyRunning() + || (triggerType == SchedulerServiceConstants.TRIGGER_TYPE_CRON && (scheduledJobDetail.getNextRunTime().after(new Date())))) { + isStopExecution = true; + } + final SchedulerDetail schedulerDetail = retriveSchedulerDetail(); + if (triggerType == SchedulerServiceConstants.TRIGGER_TYPE_CRON && schedulerDetail.isSuspended()) { + scheduledJobDetail.updateTriggerMisfired(true); + isStopExecution = true; + } else if (!isStopExecution) { + scheduledJobDetail.updateCurrentlyRunningStatus(true); + } + scheduledJobDetailsRepository.save(scheduledJobDetail); + return isStopExecution; + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java index a83811f07..542397498 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java @@ -2,12 +2,9 @@ package org.mifosplatform.infrastructure.jobs.service; import java.util.Date; -import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant; import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetail; import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobRunHistory; -import org.mifosplatform.infrastructure.jobs.exception.JobInProcessExecution; -import org.mifosplatform.infrastructure.security.service.TenantDetailsService; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; @@ -31,12 +28,9 @@ public class SchedulerJobListener implements JobListener { private final SchedularWritePlatformService schedularService; - private final TenantDetailsService tenantDetailsService; - @Autowired - public SchedulerJobListener(final SchedularWritePlatformService schedularService, final TenantDetailsService tenantDetailsService) { + public SchedulerJobListener(final SchedularWritePlatformService schedularService) { this.schedularService = schedularService; - this.tenantDetailsService = tenantDetailsService; } @Override @@ -45,16 +39,7 @@ public class SchedulerJobListener implements JobListener { } @Override - public void jobToBeExecuted(final JobExecutionContext context) { - String tenantIdentifier = context.getTrigger().getJobDataMap().getString(SchedulerServiceConstants.TENANT_IDENTIFIER); - MifosPlatformTenant tenant = this.tenantDetailsService.loadTenantById(tenantIdentifier); - ThreadLocalContextUtil.setTenant(tenant); - JobKey key = context.getTrigger().getJobKey(); - String jobKey = key.getName() + SchedulerServiceConstants.JOB_KEY_SEPERATOR + key.getGroup(); - final ScheduledJobDetail scheduledJobDetail = schedularService.findByJobKey(jobKey); - if (scheduledJobDetail.isCurrentlyRunning()) { throw new JobInProcessExecution(scheduledJobDetail.getJobName()); } - scheduledJobDetail.updateCurrentlyRunningStatus(true); - this.schedularService.saveOrUpdate(scheduledJobDetail); + public void jobToBeExecuted(@SuppressWarnings("unused") final JobExecutionContext context) { } @@ -93,7 +78,8 @@ public class SchedulerJobListener implements JobListener { if (context.getMergedJobDataMap().containsKey(SchedulerServiceConstants.TRIGGER_TYPE_REFERENCE)) { triggerType = context.getMergedJobDataMap().getString(SchedulerServiceConstants.TRIGGER_TYPE_REFERENCE); } - if (triggerType == SchedulerServiceConstants.TRIGGER_TYPE_CRON) { + if (triggerType == SchedulerServiceConstants.TRIGGER_TYPE_CRON && trigger.getNextFireTime() != null + && trigger.getNextFireTime().after(scheduledJobDetails.getNextRunTime())) { scheduledJobDetails.updateNextRunTime(trigger.getNextFireTime()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerTriggerListener.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerTriggerListener.java new file mode 100755 index 000000000..b33daeeae --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerTriggerListener.java @@ -0,0 +1,66 @@ +package org.mifosplatform.infrastructure.jobs.service; + +import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant; +import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; +import org.mifosplatform.infrastructure.security.service.TenantDetailsService; +import org.quartz.JobExecutionContext; +import org.quartz.JobKey; +import org.quartz.Trigger; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.TriggerListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SchedulerTriggerListener implements TriggerListener { + + private final String name = "Global trigger Listner"; + + private final SchedularWritePlatformService schedularService; + + private final TenantDetailsService tenantDetailsService; + + @Autowired + public SchedulerTriggerListener(final SchedularWritePlatformService schedularService, final TenantDetailsService tenantDetailsService) { + this.schedularService = schedularService; + this.tenantDetailsService = tenantDetailsService; + + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void triggerFired(@SuppressWarnings("unused") Trigger trigger, @SuppressWarnings("unused") JobExecutionContext context) { + + } + + @Override + public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { + + String tenantIdentifier = trigger.getJobDataMap().getString(SchedulerServiceConstants.TENANT_IDENTIFIER); + MifosPlatformTenant tenant = this.tenantDetailsService.loadTenantById(tenantIdentifier); + ThreadLocalContextUtil.setTenant(tenant); + JobKey key = trigger.getJobKey(); + String jobKey = key.getName() + SchedulerServiceConstants.JOB_KEY_SEPERATOR + key.getGroup(); + String triggerType = SchedulerServiceConstants.TRIGGER_TYPE_CRON; + if (context.getMergedJobDataMap().containsKey(SchedulerServiceConstants.TRIGGER_TYPE_REFERENCE)) { + triggerType = context.getMergedJobDataMap().getString(SchedulerServiceConstants.TRIGGER_TYPE_REFERENCE); + } + return schedularService.processJobDetailForExecution(jobKey, triggerType); + } + + @Override + public void triggerMisfired(@SuppressWarnings("unused") Trigger trigger) { + + } + + @Override + public void triggerComplete(@SuppressWarnings("unused") Trigger trigger, @SuppressWarnings("unused") JobExecutionContext context, + @SuppressWarnings("unused") CompletedExecutionInstruction triggerInstructionCode) { + + } + +} From 7c184966203c6999569674f08cab3793ebda865f Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Mon, 19 Aug 2013 18:11:07 +0530 Subject: [PATCH 41/47] Add data validations for bulkloanreassignment --- .../LoanEventApiJsonValidator.java | 26 +++++++++++++++++++ .../BulkLoansReadPlatformServiceImpl.java | 10 ++++--- ...WritePlatformServiceJpaRepositoryImpl.java | 2 +- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index 35631a06e..506a1e9f3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -263,4 +263,30 @@ public final class LoanEventApiJsonValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } + + public void validateForBulkLoanReassignment(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Set supportedParameters = new HashSet(Arrays.asList("assignmentDate", "fromLoanOfficerId", + "toLoanOfficerId", "loans", "locale", "dateFormat")); + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanOfficer"); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final LocalDate assignmentDate = this.fromApiJsonHelper.extractLocalDateNamed("assignmentDate", element); + baseDataValidator.reset().parameter("assignmentDate").value(assignmentDate).notNull(); + final Long fromLoanOfficerId = this.fromApiJsonHelper.extractLongNamed("fromLoanOfficerId", element); + baseDataValidator.reset().parameter("fromLoanOfficerId").value(fromLoanOfficerId).notNull().longGreaterThanZero(); + final Long toLoanOfficerId = this.fromApiJsonHelper.extractLongNamed("toLoanOfficerId", element); + baseDataValidator.reset().parameter("toLoanOfficerId").value(toLoanOfficerId).notNull().longGreaterThanZero(); + final String[] loans = this.fromApiJsonHelper.extractArrayNamed("loans", element); + baseDataValidator.reset().parameter("loans").value(loans).arrayNotEmpty(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/BulkLoansReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/BulkLoansReadPlatformServiceImpl.java index 8a6506259..6e5d1c4de 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/BulkLoansReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/BulkLoansReadPlatformServiceImpl.java @@ -16,6 +16,8 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext import org.mifosplatform.organisation.staff.data.StaffAccountSummaryCollectionData; import org.mifosplatform.portfolio.accountdetails.data.LoanAccountSummaryData; import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlatformService; +import org.mifosplatform.portfolio.client.domain.ClientStatus; +import org.mifosplatform.portfolio.group.domain.GroupingTypeStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -42,13 +44,13 @@ public class BulkLoansReadPlatformServiceImpl implements BulkLoansReadPlatformSe context.authenticatedUser(); final StaffClientMapper staffClientMapper = new StaffClientMapper(); - final String clientSql = "select distinct " + staffClientMapper.schema(); + final String clientSql = "select distinct " + staffClientMapper.schema() +" and c.status_enum=?"; final StaffGroupMapper staffGroupMapper = new StaffGroupMapper(); - final String groupSql = "select distinct " + staffGroupMapper.schema(); + final String groupSql = "select distinct " + staffGroupMapper.schema() +" and g.status_enum=?"; final List clientSummaryList = this.jdbcTemplate.query(clientSql, - staffClientMapper, new Object[] { loanOfficerId }); + staffClientMapper, new Object[] { loanOfficerId, ClientStatus.ACTIVE.getValue()}); for (StaffAccountSummaryCollectionData.LoanAccountSummary clientSummary : clientSummaryList) { @@ -59,7 +61,7 @@ public class BulkLoansReadPlatformServiceImpl implements BulkLoansReadPlatformSe } final List groupSummaryList = this.jdbcTemplate.query(groupSql, staffGroupMapper, - new Object[] { loanOfficerId }); + new Object[] { loanOfficerId, GroupingTypeStatus.ACTIVE.getValue()}); for (StaffAccountSummaryCollectionData.LoanAccountSummary groupSummary : groupSummaryList) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 918a96693..f84d63c7f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -930,6 +930,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public CommandProcessingResult bulkLoanReassignment(final JsonCommand command) { this.context.authenticatedUser(); + this.loanEventApiJsonValidator.validateForBulkLoanReassignment(command.json()); final Long fromLoanOfficerId = command.longValueOfParameterNamed("fromLoanOfficerId"); final Long toLoanOfficerId = command.longValueOfParameterNamed("toLoanOfficerId"); @@ -950,7 +951,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loan.reassignLoanOfficer(toLoanOfficer, dateOfLoanOfficerAssignment); this.loanRepository.save(loan); } - this.loanRepository.flush(); return new CommandProcessingResultBuilder() // From e1a4ebdf9226033eac28e1d1e26621f9309a3f1c Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Mon, 19 Aug 2013 18:13:17 +0530 Subject: [PATCH 42/47] Corrected postJournalEntries to call createJournalEntriesForLoan API --- .../loanaccount/domain/LoanAccountDomainServiceJpa.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index 63d34dee1..6bbb5ee16 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -145,7 +145,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { final Map accountingBridgeData = loanAccount.deriveAccountingBridgeData(applicationCurrency.toData(), existingTransactionIds, existingReversedTransactionIds); - this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); + this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { From 40bbf46e0507368fd9c2f08d2bc5175fa9dab6b1 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Tue, 20 Aug 2013 17:00:56 +0530 Subject: [PATCH 43/47] initial work on transfers --- .../V104__permissions_for_transfers.sql | 11 + .../commands/domain/CommandWrapper.java | 8 + .../service/CommandWrapperBuilder.java | 18 ++ .../SynchronousCommandProcessingService.java | 6 +- .../core/data/DataValidatorBuilder.java | 1 + .../domain/OfficeRepositoryWrapper.java | 45 +++ .../domain/CalendarInstanceRepository.java | 10 +- .../client/api/ClientsApiResource.java | 3 + .../portfolio/client/domain/Client.java | 8 + .../ClientHasBeenClosedException.java | 11 + ...WritePlatformServiceJpaRepositoryImpl.java | 5 + .../group/api/GroupsApiResource.java | 4 + .../portfolio/loanaccount/domain/Loan.java | 46 ++-- .../loanaccount/domain/LoanRepository.java | 16 +- .../loanaccount/domain/LoanStatus.java | 4 + .../transfer/api/TransferApiConstants.java | 34 +++ .../transfer/data/TransfersDataValidator.java | 111 ++++++++ .../TransferNotSupportedException.java | 51 ++++ ...erClientBetweenBranchesCommandHandler.java | 32 +++ ...ferClientsBetweenGroupsCommandHandler.java | 32 +++ .../service/TransferWritePlatformService.java | 17 ++ ...WritePlatformServiceJpaRepositoryImpl.java | 260 ++++++++++++++++++ 22 files changed, 705 insertions(+), 28 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V104__permissions_for_transfers.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OfficeRepositoryWrapper.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientHasBeenClosedException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/data/TransfersDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/exception/TransferNotSupportedException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/handler/TransferClientBetweenBranchesCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/handler/TransferClientsBetweenGroupsCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java diff --git a/mifosng-db/migrations/core_db/V104__permissions_for_transfers.sql b/mifosng-db/migrations/core_db/V104__permissions_for_transfers.sql new file mode 100644 index 000000000..485010005 --- /dev/null +++ b/mifosng-db/migrations/core_db/V104__permissions_for_transfers.sql @@ -0,0 +1,11 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) +VALUES ('portfolio_group', 'TRANSFERCLIENTS_GROUP', 'GROUP', 'TRANSFERCLIENTS', 0); + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) +VALUES ('portfolio_group', 'TRANSFERCLIENTS_GROUP_CHECKER', 'GROUP', 'TRANSFERCLIENTS', 0); + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) +VALUES ('portfolio', 'TRANSFER_CLIENT', 'CLIENT', 'TRANSFER', 0); + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) +VALUES ('portfolio', 'TRANSFER_CLIENT_CHECKER', 'CLIENT', 'TRANSFER', 0); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 3a809e0c9..90ff1c732 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -551,6 +551,10 @@ public class CommandWrapper { public boolean isUpdateGroupRole() { return this.entityName.equalsIgnoreCase("GROUP") && this.actionName.equalsIgnoreCase("UPDATEROLE"); } + + public boolean isTransferClientsBetweenGroups() { + return this.entityName.equalsIgnoreCase("GROUP") && this.actionName.equalsIgnoreCase("TRANSFERCLIENTS"); + } public boolean isClientAssignStaff() { return this.actionName.equalsIgnoreCase("ASSIGNSTAFF") && this.entityName.equalsIgnoreCase("CLIENT"); @@ -559,6 +563,10 @@ public class CommandWrapper { public boolean isClientClose() { return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("CLIENT"); } + + public boolean isClientTransfer() { + return this.actionName.equalsIgnoreCase("TRANSFER") && this.entityName.equalsIgnoreCase("CLIENT"); + } public boolean isProductMixResource() { return this.entityName.equalsIgnoreCase("PRODUCTMIX"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index caa102376..49d7838ad 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -351,6 +351,15 @@ public class CommandWrapperBuilder { this.href = "/clients/" + clientId + "?command=close&template=true"; return this; } + + public CommandWrapperBuilder transferClient(final Long clientId) { + this.actionName = "TRANSFER"; + this.entityName = "CLIENT"; + this.entityId = clientId; + this.clientId = clientId; + this.href = "/clientId/" + clientId + "?command=transfer"; + return this; + } public CommandWrapperBuilder updateClient(final Long clientId) { this.actionName = "UPDATE"; @@ -1000,6 +1009,15 @@ public class CommandWrapperBuilder { this.href = "/groups/" + groupId + "?command=disassociateClients"; return this; } + + public CommandWrapperBuilder transferClientsBetweenGroups(final Long sourceGroupId) { + this.actionName = "TRANSFERCLIENTS"; + this.entityName = "GROUP"; + this.entityId = sourceGroupId; + this.groupId = sourceGroupId; + this.href = "/groups/" + sourceGroupId + "?command=transferClients"; + return this; + } public CommandWrapperBuilder unassignGroupStaff(final Long groupId) { this.actionName = "UNASSIGNSTAFF"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index a70f704d1..b8822b31c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -170,6 +170,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("assignClientStaffCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isClientClose()) { handler = this.applicationContext.getBean("closeClientCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isClientTransfer()) { + handler = this.applicationContext.getBean("transferClientBetweenBranchesCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } @@ -452,7 +454,9 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("updateGroupRoleCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isAssignStaff()) { handler = this.applicationContext.getBean("assignGroupStaffCommandHandler", NewCommandSourceHandler.class); - }else { + } else if (wrapper.isTransferClientsBetweenGroups()) { + handler = this.applicationContext.getBean("transferClientsBetweenGroupsCommandHandler", NewCommandSourceHandler.class); + } else { throw new UnsupportedCommandException(wrapper.commandName()); } } else if (wrapper.isCenterResource()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java index a6f66c1f9..0e9c76e28 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java @@ -128,6 +128,7 @@ public class DataValidatorBuilder { return this; } + /***FIXME: Vishwas, why does this method have a parameter? Seems wrong***/ public DataValidatorBuilder trueOrFalseRequired(final boolean trueOfFalseFieldProvided) { if (!trueOfFalseFieldProvided && !ignoreNullValue) { StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OfficeRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OfficeRepositoryWrapper.java new file mode 100644 index 000000000..7c660cb0c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/domain/OfficeRepositoryWrapper.java @@ -0,0 +1,45 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.organisation.office.domain; + +import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * Wrapper for {@link OfficeRepository} that adds NULL checking and Error + * handling capabilities + *

+ */ +@Service +public class OfficeRepositoryWrapper { + + private final OfficeRepository repository; + + @Autowired + public OfficeRepositoryWrapper(final OfficeRepository repository) { + this.repository = repository; + } + + public Office findOneWithNotFoundDetection(final Long id) { + final Office office = this.repository.findOne(id); + if (office == null) { throw new OfficeNotFoundException(id); } + return office; + } + + public void save(final Office entity) { + this.repository.save(entity); + } + + public void saveAndFlush(final Office entity) { + this.repository.saveAndFlush(entity); + } + + public void delete(final Office entity) { + this.repository.delete(entity); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepository.java index fe7fd2553..1a3a3b4c9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarInstanceRepository.java @@ -6,6 +6,7 @@ package org.mifosplatform.portfolio.calendar.domain; import java.util.Collection; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; @@ -18,8 +19,15 @@ public interface CalendarInstanceRepository extends JpaRepository findByEntityIdAndEntityTypeId(Long entityId, Integer entityTypeId); + CalendarInstance findByEntityIdAndEntityTypeIdAndCalendarTypeId(Long entityId, Integer entityTypeId, Integer calendarTypeId); + @Query("from CalendarInstance ci where ci.entityId = :loanId and ci.entityTypeId = :entityTypeId") CalendarInstance findCalendarInstaneByLoanId(@Param("loanId") Long loanId, @Param("entityTypeId") Integer entityTypeId); - + Collection findByCalendarIdAndEntityTypeId(Long calendarId, Integer entityTypeId); + + /**Should use in clause, can I do it without creating a new class?**/ + @Query("from CalendarInstance ci where ci.entityId in (select id from Loan loan where loan.client.id = :clientId and loan.group.id=:groupId and (loan.loanStatus = 100 or loan.loanStatus = 200 or loan.loanStatus = 300)) and ci.entityTypeId = 3") + List findCalendarInstancesForActiveLoansByGroupIdAndClientId(@Param("clientId") Long clientId, + @Param("groupId") Long groupId); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java index a760e9e6d..4fc537a27 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java @@ -207,6 +207,9 @@ public class ClientsApiResource { } else if (is(commandParam, "close")) { commandRequest = builder.closeClient(clientId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + }else if (is(commandParam, "transfer")) { + commandRequest = builder.transferClient(clientId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java index 085ea3d0d..aaf9fca4b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java @@ -429,6 +429,10 @@ public final class Client extends AbstractPersistable { public Office getOffice() { return this.office; } + + public void updateOffice(final Office office) { + this.office = office; + } private Long staffId() { Long staffId = null; @@ -453,6 +457,10 @@ public final class Client extends AbstractPersistable { public void assignStaff(final Staff staff) { this.staff = staff; } + + public Set getGroups(){ + return this.groups; + } public void close(final Long closureReasonId, final Date closureDate) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientHasBeenClosedException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientHasBeenClosedException.java new file mode 100644 index 000000000..465421c46 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientHasBeenClosedException.java @@ -0,0 +1,11 @@ +package org.mifosplatform.portfolio.client.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +public class ClientHasBeenClosedException extends AbstractPlatformDomainRuleException { + + public ClientHasBeenClosedException(final Long clientId) { + super("error.msg.client.closed", "Client with identifier " + clientId + " has already been closed"); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index bd7270965..a97f2bbec 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -306,6 +306,11 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP final Long staffId = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName); if (staffId != null) { staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, clientForUpdate.getOffice().getHierarchy()); + /** + * TODO Vishwas: We maintain history of chage of loan officer w.r.t + * loan in a history table, should we do the same for a client? + * Especially useful when the change happens due to a transfer etc + **/ clientForUpdate.assignStaff(staff); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index 33119a77b..c46c238a1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -329,6 +329,10 @@ public class GroupsApiResource { final CommandWrapper commandRequest = builder.updateRole(groupId, roleId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return this.toApiJsonSerializer.serialize(result); + } else if (is(commandParam, "transferClients")) { + final CommandWrapper commandRequest = builder.transferClientsBetweenGroups(groupId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); } else { throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet", "saveCollectionSheet", "unassignStaff", "assignRole", "unassignRole", "updateassignRole" }); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index dda71b4bc..b88a003ce 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1113,12 +1113,12 @@ public class Loan extends AbstractPersistable { for (final LoanCharge loanCharge : setOfLoanCharges()) { validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); } - - //validate if disbursement date is a holiday or a non-working day + + // validate if disbursement date is a holiday or a non-working day validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay); validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays); } - + private LocalDate determineExpectedMaturityDate() { final int numberOfInstallments = this.repaymentScheduleInstallments.size(); return this.repaymentScheduleInstallments.get(numberOfInstallments - 1).getDueDate(); @@ -1323,7 +1323,7 @@ public class Loan extends AbstractPersistable { final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); - + this.actualDisbursementDate = actualDisbursementDate.toDate(); this.disbursedBy = currentUser; updateLoanScheduleDependentDerivedFields(); @@ -1332,14 +1332,15 @@ public class Loan extends AbstractPersistable { actualChanges.put("dateFormat", command.dateFormat()); actualChanges.put("actualDisbursementDate", command.stringValueOfParameterNamed("actualDisbursementDate")); - //validate if disbursement date is a holiday or a non-working day + // validate if disbursement date is a holiday or a non-working day validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay); validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays); - + handleDisbursementTransaction(paymentDetail, actualDisbursementDate, txnExternalId); if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) { - regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays); + regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, + holidays, workingDays); updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate); } else { updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate); @@ -1366,8 +1367,8 @@ public class Loan extends AbstractPersistable { * details/state. */ private void regenerateRepaymentSchedule(final LoanScheduleGeneratorFactory loanScheduleFactory, - final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, - final List holidays, final WorkingDays workingDays) { + final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate, + final boolean isHolidayEnabled, final List holidays, final WorkingDays workingDays) { final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod(); final LoanScheduleGenerator loanScheduleGenerator = loanScheduleFactory.create(interestMethod); @@ -1386,7 +1387,8 @@ public class Loan extends AbstractPersistable { updateLoanSchedule(loanSchedule); } - private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn, final String txnExternalId) { + private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn, + final String txnExternalId) { // track disbursement transaction final LoanTransaction disbursementTransaction = LoanTransaction.disbursement(this.loanRepaymentScheduleDetail.getPrincipal(), paymentDetail, disbursedOn, txnExternalId); @@ -1511,7 +1513,7 @@ public class Loan extends AbstractPersistable { validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays); validateRepaymentDateIsOnNonWorkingDay(repaymentTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay); - + existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); @@ -1875,7 +1877,7 @@ public class Loan extends AbstractPersistable { final LocalDate closureDate = command.localDateValueOfParameterNamed("transactionDate"); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); - + this.closedOnDate = closureDate.toDate(); changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); @@ -2387,7 +2389,6 @@ public class Loan extends AbstractPersistable { public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule, final boolean isHolidayEnabled, final List holidays, final WorkingDays workingDays) { - //first repayment's from date is same as disbursement date. LocalDate tmpFromDate = this.getDisbursementDate(); final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType(); @@ -2483,9 +2484,10 @@ public class Loan extends AbstractPersistable { private void validateDisbursementDateIsOnNonWorkingDay(final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) { if (!allowTransactionsOnNonWorkingDay) { - if(!WorkingDaysUtil.isWorkingDay(workingDays, getDisbursementDate())){ + if (!WorkingDaysUtil.isWorkingDay(workingDays, getDisbursementDate())) { final String errorMessage = "Expected disbursement date cannot be on a non working day"; - throw new LoanApplicationDateException("disbursement.date.on.non.working.day", errorMessage, getExpectedDisbursedOnLocalDate()); + throw new LoanApplicationDateException("disbursement.date.on.non.working.day", errorMessage, + getExpectedDisbursedOnLocalDate()); } } } @@ -2494,12 +2496,11 @@ public class Loan extends AbstractPersistable { if (!allowTransactionsOnHoliday) { if (HolidayUtil.isHoliday(getDisbursementDate(), holidays)) { final String errorMessage = "Expected disbursement date cannot be on a holiday"; - throw new LoanApplicationDateException("disbursement.date.on.holiday", errorMessage, - getExpectedDisbursedOnLocalDate()); + throw new LoanApplicationDateException("disbursement.date.on.holiday", errorMessage, getExpectedDisbursedOnLocalDate()); } } } - + private void validateRepaymentDateIsOnNonWorkingDay(final LocalDate repaymentDate, final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) { if (!allowTransactionsOnNonWorkingDay) { @@ -2518,12 +2519,16 @@ public class Loan extends AbstractPersistable { throw new LoanApplicationDateException("repayment.date.on.holiday", errorMessage, repaymentDate); } } - } + } public Group group() { return this.group; } + public void updateGroup(Group newGroup) { + this.group = newGroup; + } + public Integer getCurrentLoanCounter() { return this.loanCounter; } @@ -2564,8 +2569,5 @@ public class Loan extends AbstractPersistable { } - public void updateGroup(Group group) { - this.group = group; - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java index a084795b4..07854f45b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java @@ -52,6 +52,8 @@ public interface LoanRepository extends JpaRepository, JpaSpecificat public static final String FIND_ACTIVE_LOANS_PRODUCT_IDS_BY_GROUP = "Select loan.loanProduct.id from Loan loan where " + "loan.group.id = :groupId and loan.loanStatus = :loanStatus and loan.client.id is NULL group by loan.loanProduct.id"; + public static final String DOES_CLIENT_HAVE_ACTIVE_LOANS = "select case when (count (loan) > 0) then true else false end from Loan loan where loan.client.id = :clientId and loan.loanStatus in (100,200,300)"; + @Query(FIND_GROUP_LOANS_DISBURSED_AFTER) List getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId, @Param("loanType") Integer loanType); @@ -101,15 +103,21 @@ public interface LoanRepository extends JpaRepository, JpaSpecificat List getLoansDisbursedAfter(@Param("disbursalDate") Date disbursalDate); @Query("from Loan loan where loan.client.office.id IN :officeIds and loan.loanStatus IN :loanStatuses") - List findByClientOfficeIdsAndLoanStatus(@Param("officeIds") Collection officeIds, @Param("loanStatuses") Collection loanStatuses); - - @Query("from Loan loan where loan.group.office.id IN :officeIds and loan.loanStatus IN :loanStatuses") - List findByGroupOfficeIdsAndLoanStatus(@Param("officeIds") Collection officeIds, @Param("loanStatuses") Collection loanStatuses); + List findByClientOfficeIdsAndLoanStatus(@Param("officeIds") Collection officeIds, + @Param("loanStatuses") Collection loanStatuses); + @Query("from Loan loan where loan.group.office.id IN :officeIds and loan.loanStatus IN :loanStatuses") + List findByGroupOfficeIdsAndLoanStatus(@Param("officeIds") Collection officeIds, + @Param("loanStatuses") Collection loanStatuses); + + /*** FIXME: Add more appropriate names for the query ***/ @Query(FIND_ACTIVE_LOANS_PRODUCT_IDS_BY_CLIENT) List findActiveLoansLoanProductIdsByClient(@Param("clientId") Long clientId, @Param("loanStatus") Integer loanStatus); @Query(FIND_ACTIVE_LOANS_PRODUCT_IDS_BY_GROUP) List findActiveLoansLoanProductIdsByGroup(@Param("groupId") Long groupId, @Param("loanStatus") Integer loanStatus); + @Query(DOES_CLIENT_HAVE_ACTIVE_LOANS) + boolean doesClientHaveActiveLoans(@Param("clientId") Long clientId); + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanStatus.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanStatus.java index abf9b99a9..958a551f2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanStatus.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanStatus.java @@ -111,4 +111,8 @@ public enum LoanStatus { public boolean isRejected() { return this.value.equals(LoanStatus.REJECTED.getValue()); } + + public boolean isActiveOrAwaitingApprovalOrDisbursal() { + return this.isApproved() || this.isSubmittedAndPendingApproval() || this.isActive(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java new file mode 100644 index 000000000..9b6a6221d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java @@ -0,0 +1,34 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.transfer.api; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class TransferApiConstants { + + // general + public static final String localeParamName = "locale"; + public static final String dateFormatParamName = "dateFormat"; + + // request parameters + public static final String idParamName = "id"; + public static final String destinationGroupIdParamName = "destinationGroupId"; + public static final String clients = "clients"; + public static final String inheritDestinationGroupLoanOfficer = "inheritDestinationGroupLoanOfficer"; + public static final String newStaffIdParamName = "newStaffId"; + public static final String transferActiveLoans = "transferActiveLoans"; + public static final String destinationOfficeIdParamName = "destinationOfficeIdParamName"; + + public static final Set TRANSFER_CLIENTS_BETWEEN_GROUPS_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, destinationGroupIdParamName, clients, inheritDestinationGroupLoanOfficer, newStaffIdParamName, + transferActiveLoans)); + + public static final Set TRANSFER_CLIENT_BETWEEN_BRANCHES_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, destinationOfficeIdParamName, newStaffIdParamName, transferActiveLoans)); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/data/TransfersDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/data/TransfersDataValidator.java new file mode 100644 index 000000000..a71646c74 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/data/TransfersDataValidator.java @@ -0,0 +1,111 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.transfer.data; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; +import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants; +import org.mifosplatform.portfolio.transfer.api.TransferApiConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +@Component +public final class TransfersDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public TransfersDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + public void validateForClientsTransferBetweenGroups(final String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + TransferApiConstants.TRANSFER_CLIENTS_BETWEEN_GROUPS_DATA_PARAMETERS); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(GroupingTypesApiConstants.GROUP_RESOURCE_NAME); + + final Long destinationGroupId = this.fromApiJsonHelper.extractLongNamed(TransferApiConstants.destinationGroupIdParamName, element); + baseDataValidator.reset().parameter(TransferApiConstants.destinationGroupIdParamName).value(destinationGroupId).notNull() + .integerGreaterThanZero(); + + if (this.fromApiJsonHelper.parameterExists(TransferApiConstants.newStaffIdParamName, element)) { + final Long newStaffId = this.fromApiJsonHelper.extractLongNamed(TransferApiConstants.newStaffIdParamName, element); + baseDataValidator.reset().parameter(TransferApiConstants.newStaffIdParamName).value(newStaffId).notNull() + .integerGreaterThanZero(); + } + + if (this.fromApiJsonHelper.parameterExists(TransferApiConstants.transferActiveLoans, element)) { + final Boolean inheritDestinationGroupLoanOfficer = this.fromApiJsonHelper.extractBooleanNamed( + TransferApiConstants.transferActiveLoans, element); + baseDataValidator.reset().parameter(TransferApiConstants.transferActiveLoans).value(inheritDestinationGroupLoanOfficer) + .notNull(); + } + + if (this.fromApiJsonHelper.parameterExists(TransferApiConstants.inheritDestinationGroupLoanOfficer, element)) { + final Boolean inheritDestinationGroupLoanOfficer = this.fromApiJsonHelper.extractBooleanNamed( + TransferApiConstants.inheritDestinationGroupLoanOfficer, element); + baseDataValidator.reset().parameter(TransferApiConstants.inheritDestinationGroupLoanOfficer) + .value(inheritDestinationGroupLoanOfficer).notNull(); + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateForClientTransferBetweenBranches(final String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + TransferApiConstants.TRANSFER_CLIENT_BETWEEN_BRANCHES_DATA_PARAMETERS); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ClientApiConstants.CLIENT_RESOURCE_NAME); + + final Long destinationOfficeId = this.fromApiJsonHelper + .extractLongNamed(TransferApiConstants.destinationOfficeIdParamName, element); + baseDataValidator.reset().parameter(TransferApiConstants.destinationOfficeIdParamName).value(destinationOfficeId).notNull() + .integerGreaterThanZero(); + + if (this.fromApiJsonHelper.parameterExists(TransferApiConstants.newStaffIdParamName, element)) { + final Long newStaffId = this.fromApiJsonHelper.extractLongNamed(TransferApiConstants.newStaffIdParamName, element); + baseDataValidator.reset().parameter(TransferApiConstants.newStaffIdParamName).value(newStaffId).notNull() + .integerGreaterThanZero(); + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/exception/TransferNotSupportedException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/exception/TransferNotSupportedException.java new file mode 100644 index 000000000..a4959234d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/exception/TransferNotSupportedException.java @@ -0,0 +1,51 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.transfer.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * {@link AbstractPlatformDomainRuleException} thrown when not supported loan + * template type is sent. + */ +public class TransferNotSupportedException extends AbstractPlatformDomainRuleException { + + /** + * Default Constructor (cannot transfer entities with active loan/savings + * accounts) + */ + public TransferNotSupportedException() { + super("error.msg.entity.transfers.with.active.accounts", "Cannot transfer Clients/Groups having active loan or savings accounts"); + } + + /** + * Overloaded Constructor (Cannot transfer Clients with active accounts + * between groups with different meeting frequency) + * + * @param clientId + * @param sourceGroupId + * @param destinationGroupId + */ + public TransferNotSupportedException(Long clientId, Long sourceGroupId, Long destinationGroupId) { + super("error.msg.client.transfers.with.active.accounts.between.groups.with.different.meeting.frequency", + "Cannot transfer Clients with active accounts between groups with different meeting frequency", clientId, sourceGroupId, + destinationGroupId); + } + + /** + * Overloaded Constructor (Cannot transfer Clients with active (JLG) + * accounts to a group with no meetings defined) + * + * @param clientId + * @param sourceGroupId + * @param destinationGroupId + */ + public TransferNotSupportedException(Long clientId, Long sourceGroupId, Long destinationGroupId, boolean destinationGroupMeetingAbsent) { + super("error.msg.client.transfers.with.active.accounts.to.group.with.no.meeting.frequency", + "Cannot transfer Client with active accounts to a groups with no meeting frequency", clientId, sourceGroupId, + destinationGroupId,destinationGroupMeetingAbsent); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/handler/TransferClientBetweenBranchesCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/handler/TransferClientBetweenBranchesCommandHandler.java new file mode 100644 index 000000000..5d2edea84 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/handler/TransferClientBetweenBranchesCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.transfer.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.transfer.service.TransferWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class TransferClientBetweenBranchesCommandHandler implements NewCommandSourceHandler { + + private final TransferWritePlatformService writePlatformService; + + @Autowired + public TransferClientBetweenBranchesCommandHandler(final TransferWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.transferClientBetweenBranches(command.entityId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/handler/TransferClientsBetweenGroupsCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/handler/TransferClientsBetweenGroupsCommandHandler.java new file mode 100644 index 000000000..f8e79ace7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/handler/TransferClientsBetweenGroupsCommandHandler.java @@ -0,0 +1,32 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.transfer.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.transfer.service.TransferWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class TransferClientsBetweenGroupsCommandHandler implements NewCommandSourceHandler { + + private final TransferWritePlatformService writePlatformService; + + @Autowired + public TransferClientsBetweenGroupsCommandHandler(final TransferWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.transferClientsBetweenGroups(command.entityId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformService.java new file mode 100644 index 000000000..6e11e6d46 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformService.java @@ -0,0 +1,17 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.transfer.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface TransferWritePlatformService { + + CommandProcessingResult transferClientsBetweenGroups(final Long sourceGroupId, final JsonCommand jsonCommand); + + CommandProcessingResult transferClientBetweenBranches(final Long clientId, final JsonCommand jsonCommand); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..584bae5fb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,260 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.transfer.service; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.organisation.office.domain.OfficeRepositoryWrapper; +import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.portfolio.calendar.domain.Calendar; +import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; +import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; +import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository; +import org.mifosplatform.portfolio.calendar.domain.CalendarType; +import org.mifosplatform.portfolio.calendar.service.CalendarUtils; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.client.exception.ClientHasBeenClosedException; +import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper; +import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; +import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; +import org.mifosplatform.portfolio.transfer.api.TransferApiConstants; +import org.mifosplatform.portfolio.transfer.data.TransfersDataValidator; +import org.mifosplatform.portfolio.transfer.exception.TransferNotSupportedException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +@Service +public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWritePlatformService { + + private final ClientRepositoryWrapper clientRepository; + private final OfficeRepositoryWrapper officeRepository; + private final CalendarInstanceRepository calendarInstanceRepository; + private final GroupRepositoryWrapper groupRepository; + private final LoanWritePlatformService loanWritePlatformService; + private final LoanRepository loanRepository; + private final TransfersDataValidator transfersDataValidator; + + @Autowired + public TransferWritePlatformServiceJpaRepositoryImpl(final ClientRepositoryWrapper clientRepository, + final OfficeRepositoryWrapper officeRepository, final CalendarInstanceRepository calendarInstanceRepository, + final LoanWritePlatformService loanWritePlatformService, final GroupRepositoryWrapper groupRepository, + final LoanRepository loanRepository, final TransfersDataValidator transfersDataValidator) { + this.clientRepository = clientRepository; + this.officeRepository = officeRepository; + this.calendarInstanceRepository = calendarInstanceRepository; + this.loanWritePlatformService = loanWritePlatformService; + this.groupRepository = groupRepository; + this.loanRepository = loanRepository; + this.transfersDataValidator = transfersDataValidator; + } + + @Override + @Transactional + public CommandProcessingResult transferClientsBetweenGroups(Long sourceGroupId, JsonCommand jsonCommand) { + this.transfersDataValidator.validateForClientsTransferBetweenGroups(jsonCommand.json()); + + final Group sourceGroup = this.groupRepository.findOneWithNotFoundDetection(sourceGroupId); + final Long destinationGroupId = jsonCommand.longValueOfParameterNamed(TransferApiConstants.destinationGroupIdParamName); + final Group destinationGroup = this.groupRepository.findOneWithNotFoundDetection(destinationGroupId); + + final List clients = this.assembleListOfClients(jsonCommand); + + for (Client client : clients) { + transferClientBetweenGroups(sourceGroup, client, destinationGroup); + } + + return new CommandProcessingResultBuilder() // + .withEntityId(sourceGroupId) // + .build(); + } + + /**** + * Variables that would make sense
+ *
    + *
  • inheritDestinationGroupLoanOfficer: Default False
  • + *
  • newStaffId: Optional field with Id of new Loan Officer to be linked + * to this client and all his JLG loans for this group
  • + *
  • transferActiveLoans:Default False, unless overridden, this client + * cannot be transferred between groups within the same branch if he has + * active JLG loans and across branches if he has any active loan
  • + *
  • + *
  • + *
  • + *
  • + *
  • + *
  • + *
  • + *
+ * ***/ + @Transactional + public void transferClientBetweenGroups(final Group sourceGroup, final Client client, final Group destinationGroup) { + + // next I shall validate that the client is present in this group + if (!sourceGroup.hasClientAsMember(client)) { throw new ClientNotInGroupException(client.getId(), sourceGroup.getId()); } + // Is client active? + if (client.isNotActive()) { throw new ClientHasBeenClosedException(client.getId()); } + + /*** Code for intra-branch transfers ***/ + if (client.getOffice().getId() == destinationGroup.getOffice().getId()) { + /** + * TODO: for now we need to ensure that only one collection sheet + * calendar can be linked with a center or group entity
+ **/ + final CalendarInstance sourceGroupCalendarInstance = this.calendarInstanceRepository + .findByEntityIdAndEntityTypeIdAndCalendarTypeId(sourceGroup.getId(), CalendarEntityType.GROUPS.getValue(), + CalendarType.COLLECTION.getValue()); + // get all customer loans synced with this group calendar Instance + final List activeLoanCalendarInstances = this.calendarInstanceRepository + .findCalendarInstancesForActiveLoansByGroupIdAndClientId(sourceGroup.getId(), client.getId()); + + /** + * if a calendar is present in the source group along with loans + * synced with it, we should ensure that the destination group also + * has a collection calendar + **/ + if (sourceGroupCalendarInstance != null && !activeLoanCalendarInstances.isEmpty()) { + // get the destination calendar + final CalendarInstance destinationGroupCalendarInstance = this.calendarInstanceRepository + .findByEntityIdAndEntityTypeIdAndCalendarTypeId(destinationGroup.getId(), CalendarEntityType.GROUPS.getValue(), + CalendarType.COLLECTION.getValue()); + + if (destinationGroupCalendarInstance == null) { throw new TransferNotSupportedException(client.getId(), + sourceGroup.getId(), destinationGroup.getId(), false); + + } + Calendar sourceGroupCalendar = destinationGroupCalendarInstance.getCalendar(); + Calendar destinationGroupCalendar = destinationGroupCalendarInstance.getCalendar(); + + /*** + * Ensure that the recurrence pattern are same for collection + * meeting in both the source and the destination calendar + ***/ + if (!(CalendarUtils.isFrequencySame(sourceGroupCalendar.getRecurrence(), destinationGroupCalendar.getRecurrence()) && CalendarUtils + .isIntervalSame(sourceGroupCalendar.getRecurrence(), destinationGroupCalendar.getRecurrence()))) { throw new TransferNotSupportedException( + client.getId(), sourceGroup.getId(), destinationGroup.getId()); } + + /** map all JLG loans for this client to the destinationGroup **/ + for (CalendarInstance calendarInstance : activeLoanCalendarInstances) { + calendarInstance.updateCalendar(destinationGroupCalendar); + } + // reschedule all JLG Loans to follow new Calendar + this.loanWritePlatformService.applyMeetingDateChanges(destinationGroupCalendar, activeLoanCalendarInstances); + } + + } else { + Office destinationOffice = destinationGroup.getOffice(); + transferClientToNewBranch(client, destinationOffice); + } + + /** + * Now Change the loan officer for this client and all his active JLG + * loans + **/ + Staff destinationGroupLoanOfficer = destinationGroup.getStaff(); + if (destinationGroupLoanOfficer != null) { + client.updateStaff(destinationGroupLoanOfficer); + } + + client.getGroups().add(destinationGroup); + this.clientRepository.saveAndFlush(client); + + /** + * Active JLG loans are now linked to the new Group and Loan officer + **/ + List allClientJLGLoans = loanRepository.findByClientIdAndGroupId(client.getId(), sourceGroup.getId()); + for (Loan loan : allClientJLGLoans) { + if (loan.status().isActiveOrAwaitingApprovalOrDisbursal()) { + loan.updateGroup(destinationGroup); + if (destinationGroupLoanOfficer != null) { + loan.reassignLoanOfficer(destinationGroupLoanOfficer, DateUtils.getLocalDateOfTenant()); + } + this.loanRepository.saveAndFlush(loan); + } + } + + // change client group membership + client.getGroups().remove(sourceGroup); + + } + + /** + * This API is meant for transferring clients between branches mainly by + * Organizations following an Individual lending Model
+ * If the Client is linked to any Groups, we can optionally choose to have + * all the linkages broken and all JLG Loans are converted into Individual + * Loans + * + * @param clientId + * @param destinationOfficeId + * @param jsonCommand + * @return + **/ + @Transactional + @Override + public CommandProcessingResult transferClientBetweenBranches(final Long clientId, final JsonCommand jsonCommand) { + // validation + this.transfersDataValidator.validateForClientsTransferBetweenGroups(jsonCommand.json()); + + final Long destinationOfficeId = jsonCommand.longValueOfParameterNamed(TransferApiConstants.destinationOfficeIdParamName); + final Office office = this.officeRepository.findOneWithNotFoundDetection(destinationOfficeId); + final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); + /*** Remove all group associations for this client ***/ + client.getGroups().clear(); + transferClientToNewBranch(client, office); + this.clientRepository.save(client); + + return new CommandProcessingResultBuilder() // + .withClientId(clientId) // + .withEntityId(clientId) // + .build(); + } + + private void transferClientToNewBranch(final Client client, final Office destinationOffice) { + /** + * For now, do not allow Clients with active loans to be transferred + * between branches + **/ + if (loanRepository.doesClientHaveActiveLoans(client.getId())) { throw new TransferNotSupportedException(); } + // set the new office for the client + client.updateOffice(destinationOffice); + } + + private List assembleListOfClients(final JsonCommand command) { + + final List clients = new ArrayList(); + + if (command.parameterExists(TransferApiConstants.clients)) { + JsonArray clientsArray = command.arrayOfParameterNamed(TransferApiConstants.clients); + if (clientsArray != null) { + for (int i = 0; i < clientsArray.size(); i++) { + + final JsonObject jsonObject = clientsArray.get(i).getAsJsonObject(); + if (jsonObject.has(TransferApiConstants.idParamName)) { + final Long id = jsonObject.get(TransferApiConstants.idParamName).getAsLong(); + final Client client = this.clientRepository.findOneWithNotFoundDetection(id); + clients.add(client); + } + } + } + } + return clients; + } + +} \ No newline at end of file From f9e001f16b32d6950662c18bc80e120446df9156 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Tue, 20 Aug 2013 18:57:28 +0530 Subject: [PATCH 44/47] initial documentation stub for client transfers between groups --- api-docs/apiLive.htm | 120 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 45ad5dfe8..62b1bf2bb 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -575,6 +575,14 @@ + + + groups/{groupId}?command=transferClients + Transfer Clients + + + + @@ -3782,6 +3790,118 @@ Request Body: + +   +
+
+

Associate Clients

+

This API allows to associate existing clients to a group.

+

The clients are listed from the office to which the group is associated.

+

If client(s) is already associated with group then API will result in an error.

+
+ +
+ POST https://Domain Name/api/v1/groups/{groupId}?command=associateClients + POST groups/1?command=associateClients +Content-Type: application/json +Request Body: +{ + "clientMembers":[1,2] +} + + +{ + "officeId": 1, + "groupId": 1, + "resourceId": 1, + "clientMembers": [1,2] +} + +
+
+   +
+
+

Transfer Clients across groups

+

This API allows to transfer clients from one group to another

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field Descriptions
destinationGroupId
The identifier of the group to which the clients are to be transferred.
clients
Identifiers of all clients who need to be transferred +
inheritDestinationGroupLoanOfficer
Flag specifies if the transferred clients (and all their active loans) + should be linked to the assigned loan officer for the group +
transferActiveLoans
Flag specifies if transfers should proceed when active loans are encountered. + If set to true, the schedules of the actives loans are updated to match that meeting schedule + of the destination Group +
+
+ + + + + + + +
Mandatory Fields
destinationGroupId, clients
+
+ + + + + + + +
Optional Fields
inheritDestinationGroupLoanOfficer (defaults to true), transferActiveLoans (defaults to true)
+ + +
+ + +
+ + POST https://Domain Name/api/v1/groups/{groupId}?command=transferClients + POST groups/1?command=transferClients +Content-Type: application/json +Request Body: +{ + destinationGroupId:2, + clients:[{id:1}] +} + + +{ + "resourceId": 1 +} + + + +
+
 
From d563b87468f177a39f97211f7e06e85d238117bb Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Tue, 20 Aug 2013 19:40:36 +0530 Subject: [PATCH 45/47] Transfer clients between groups --- .../group/api/GroupsApiResource.java | 49 ++++++++++++++++++- .../group/data/GroupTransferData.java | 40 +++++++++++++++ .../service/GroupReadPlatformService.java | 4 ++ .../service/GroupReadPlatformServiceImpl.java | 26 ++++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupTransferData.java diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index c46c238a1..18b8cd54a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -39,6 +39,8 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; import org.mifosplatform.portfolio.accountdetails.data.AccountSummaryCollectionData; import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlatformService; import org.mifosplatform.portfolio.calendar.data.CalendarData; @@ -51,6 +53,7 @@ import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; import org.mifosplatform.portfolio.collectionsheet.service.CollectionSheetReadPlatformService; import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.data.GroupRoleData; +import org.mifosplatform.portfolio.group.data.GroupTransferData; import org.mifosplatform.portfolio.group.service.CenterReadPlatformService; import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.group.service.GroupRolesReadPlatformService; @@ -81,6 +84,7 @@ public class GroupsApiResource { private final GroupRolesReadPlatformService groupRolesReadPlatformService; private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; private final CalendarReadPlatformService calendarReadPlatformService; + private final StaffReadPlatformService staffReadPlatformService; @Autowired public GroupsApiResource(final PlatformSecurityContext context, final GroupReadPlatformService groupReadPlatformService, @@ -92,7 +96,8 @@ public class GroupsApiResource { final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper, final GroupRolesReadPlatformService groupRolesReadPlatformService, - final AccountDetailsReadPlatformService accountDetailsReadPlatformService, final CalendarReadPlatformService calendarReadPlatformService) { + final AccountDetailsReadPlatformService accountDetailsReadPlatformService, + final CalendarReadPlatformService calendarReadPlatformService, final StaffReadPlatformService staffReadPlatformService) { this.context = context; this.groupReadPlatformService = groupReadPlatformService; @@ -108,6 +113,7 @@ public class GroupsApiResource { this.groupRolesReadPlatformService = groupRolesReadPlatformService; this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; this.calendarReadPlatformService = calendarReadPlatformService; + this.staffReadPlatformService = staffReadPlatformService; } @GET @@ -361,4 +367,45 @@ public class GroupsApiResource { return this.groupSummaryToApiJsonSerializer.serialize(settings, groupAccount, GROUP_ACCOUNTS_DATA_PARAMETERS); } + @GET + @Path("{groupId}/clientstransfertemplate") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveClientTranferTemplate(@Context final UriInfo uriInfo, @PathParam("groupId") final Long groupId, + @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) { + + this.context.authenticatedUser().validateHasReadPermission("GROUP"); + GroupGeneralData group = this.groupReadPlatformService.retrieveOne(groupId); + + final boolean transferActiveLoans = true; + final boolean inheritDestinationGroupLoanOfficer = true; + + Collection membersOfGroup = this.clientReadPlatformService.retrieveClientMembersOfGroup(groupId); + if (CollectionUtils.isEmpty(membersOfGroup)) { + membersOfGroup = null; + } + + final boolean loanOfficersOnly = false; + Collection staffOptions = null; + if (staffInSelectedOfficeOnly) { + staffOptions = this.staffReadPlatformService.retrieveAllStaffForDropdown(group.officeId()); + } else { + staffOptions = this.staffReadPlatformService.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(group.officeId(), + loanOfficersOnly); + } + if (CollectionUtils.isEmpty(staffOptions)) { + staffOptions = null; + } + + Collection groupOptions = this.groupReadPlatformService.retrieveGroupsForLookup(group.officeId(), groupId); + GroupTransferData data = GroupTransferData.template(groupId, membersOfGroup, groupOptions, staffOptions, transferActiveLoans, + inheritDestinationGroupLoanOfficer); + + final Set GROUP_TRANSFERS_DATA_PARAMETERS = new HashSet(Arrays.asList("groupId", "clientOptions", "groupOptions", + "staffOptions", "transferActiveLoans", "inheritDestinationGroupLoanOfficer")); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, data, GROUP_TRANSFERS_DATA_PARAMETERS); + + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupTransferData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupTransferData.java new file mode 100644 index 000000000..ee1f4e10b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupTransferData.java @@ -0,0 +1,40 @@ +package org.mifosplatform.portfolio.group.data; + +import java.util.Collection; + +import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.portfolio.client.data.ClientData; + +public class GroupTransferData { + + @SuppressWarnings("unused") + private final Long groupId; + @SuppressWarnings("unused") + private final Collection clientOptions; + @SuppressWarnings("unused") + private final Collection groupOptions; + @SuppressWarnings("unused") + private final Collection staffOptions; + @SuppressWarnings("unused") + private final boolean transferActiveLoans; + @SuppressWarnings("unused") + private final boolean inheritDestinationGroupLoanOfficer; + + private GroupTransferData(final Long groupId, final Collection clientOptions, + final Collection groupOptions, final Collection staffOptions, final boolean transferActiveLoans, + final boolean inheritDestinationGroupLoanOfficer) { + this.groupId = groupId; + this.clientOptions = clientOptions; + this.groupOptions = groupOptions; + this.staffOptions = staffOptions; + this.transferActiveLoans = transferActiveLoans; + this.inheritDestinationGroupLoanOfficer = inheritDestinationGroupLoanOfficer; + } + + public static GroupTransferData template(final Long groupId, final Collection clientOptions, + final Collection groupOptions, final Collection staffOptions, final boolean transferActiveLoans, + final boolean inheritDestinationGroupLoanOfficer) { + return new GroupTransferData(groupId, clientOptions, groupOptions, staffOptions, transferActiveLoans, + inheritDestinationGroupLoanOfficer); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java index f850d8845..f83e9d84e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java @@ -5,6 +5,8 @@ */ package org.mifosplatform.portfolio.group.service; +import java.util.Collection; + import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.portfolio.group.data.GroupGeneralData; @@ -15,4 +17,6 @@ public interface GroupReadPlatformService { Page retrieveAll(SearchParameters searchParameters); GroupGeneralData retrieveOne(Long groupId); + + Collection retrieveGroupsForLookup(Long officeId, Long groupId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java index d10d84f35..931e052e3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java @@ -5,12 +5,15 @@ */ package org.mifosplatform.portfolio.group.service; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.Collection; import org.apache.commons.lang.StringUtils; 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.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.core.service.PaginationHelper; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; @@ -30,6 +33,7 @@ import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -200,4 +204,26 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService { throw new GroupNotFoundException(groupId); } } + + @Override + public Collection retrieveGroupsForLookup(final Long officeId, final Long groupId) { + this.context.authenticatedUser(); + GroupLookupDataMapper rm = new GroupLookupDataMapper(); + String sql = "Select " + rm.schema() + " where g.office_id=? and g.id !=?"; + return this.jdbcTemplate.query(sql, rm, new Object[] { officeId, groupId }); + } + + private static final class GroupLookupDataMapper implements RowMapper { + + public final String schema() { + return "g.id as id, g.display_name as displayName from m_group g"; + } + + @Override + public GroupGeneralData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final Long id = JdbcSupport.getLong(rs, "id"); + final String displayName = rs.getString("displayName"); + return GroupGeneralData.lookup(id, displayName); + } + } } \ No newline at end of file From 2727dee436c5b8e5e4b0f7004fcc374344e992ca Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Tue, 20 Aug 2013 19:47:56 +0530 Subject: [PATCH 46/47] initial stub for client transfer (between branches) --- api-docs/apiLive.htm | 44 ++++++++++++++++++- .../transfer/api/TransferApiConstants.java | 2 +- ...WritePlatformServiceJpaRepositoryImpl.java | 2 +- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 62b1bf2bb..dd4ed50ab 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -208,6 +208,14 @@ + + clients/{clientId}?command=transfer + Transfer Client + + + + + clients/{clientId}/accounts @@ -2128,7 +2136,33 @@ Request Body: }
-
+ + +   +
+
+

Transfer a Client

+

Allows you to transfer a Client between Offices.

+
+
+ POST https://Domain Name/api/v1/clients/{clientId}?command=transfer + POST clients/1?command=transfer +Content-Type: application/json +Request Body: +{ + destinationOfficeId:1 +} + + +{ + "clientId": 2, + "resourceId": 2 +} + + + +
+
 
@@ -18278,6 +18312,14 @@ GET https://DomainName/api/v1/search?query=000000111 + + + clients/{clientId}?command=transfer + Transfer Client + + + + Client Identifiers clients/{clientId}/identifiers diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java index 9b6a6221d..4920de273 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java @@ -22,7 +22,7 @@ public class TransferApiConstants { public static final String inheritDestinationGroupLoanOfficer = "inheritDestinationGroupLoanOfficer"; public static final String newStaffIdParamName = "newStaffId"; public static final String transferActiveLoans = "transferActiveLoans"; - public static final String destinationOfficeIdParamName = "destinationOfficeIdParamName"; + public static final String destinationOfficeIdParamName = "destinationOfficeId"; public static final Set TRANSFER_CLIENTS_BETWEEN_GROUPS_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, destinationGroupIdParamName, clients, inheritDestinationGroupLoanOfficer, newStaffIdParamName, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java index 584bae5fb..4da7825a5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java @@ -210,7 +210,7 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr @Override public CommandProcessingResult transferClientBetweenBranches(final Long clientId, final JsonCommand jsonCommand) { // validation - this.transfersDataValidator.validateForClientsTransferBetweenGroups(jsonCommand.json()); + this.transfersDataValidator.validateForClientTransferBetweenBranches(jsonCommand.json()); final Long destinationOfficeId = jsonCommand.longValueOfParameterNamed(TransferApiConstants.destinationOfficeIdParamName); final Office office = this.officeRepository.findOneWithNotFoundDetection(destinationOfficeId); From 89865a08007c5d3020a8dc99ee7817d736e647e4 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Tue, 20 Aug 2013 20:01:16 +0530 Subject: [PATCH 47/47] prep for 1.8.0 RELEASE --- api-docs/apiLive.htm | 2 +- mifosng-provider/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index dd4ed50ab..a5e87e3ac 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -90,7 +90,7 @@
- +

Overview

diff --git a/mifosng-provider/gradle.properties b/mifosng-provider/gradle.properties index 9b500178d..839efdab6 100644 --- a/mifosng-provider/gradle.properties +++ b/mifosng-provider/gradle.properties @@ -1,2 +1,2 @@ -releaseVersion=1.8.0.DEVELOP +releaseVersion=1.8.0.RELEASE