diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 71b3ccea0..e02adcfcc 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -240,6 +240,14 @@ + + + clients/{clientId}?command=updateSavingsAccount + Update Default Savings Account + + + + clients/{clientId}?command=proposeAndAcceptTransfer @@ -2268,6 +2276,35 @@ Request Body: +   +
+
+

Update Default Savings Account

+

Allows you to modify or assign default overdraft savings account for existed Client.

+

The selected savings account should be belongs to the client's.

+
+
+ POST https://Domain Name/api/v1/clients/{clientId}?command=updateSavingsAccount + POST clients/1?command=updateSavingsAccount +Content-Type: application/json +Request Body: +{ + "savingsAccountId": "22" +} + + +{ + "officeId":1, + "clientId":1, + "resourceId":1 + ,"changes":{ + "savingsAccountId":22 + } +} + +
+
+  
@@ -2431,38 +2468,45 @@ GET https://DomainName/api/v1/clients/{clientId} { - "id": 2, - "accountNo": "000000002", + "id": 27, + "accountNo": "000000027", "status": { "id": 300, "code": "clientStatusType.active", "value": "Active" }, + "active": true, "activationDate": [ - 2009, - 3, - 4 + 2013, + 1, + 1 ], - "fullname": "Client of group", - "displayName": "Client of group", + "firstname": "savings", + "lastname": "test", + "displayName": "savings test", "officeId": 1, "officeName": "Head Office", "timeline": { + "submittedOnDate": [ + 2013, + 1, + 1 + ], + "submittedByUsername": "mifos", + "submittedByFirstname": "App", + "submittedByLastname": "Administrator", "activatedOnDate": [ - 2009, - 3, - 4 + 2013, + 1, + 1 ], "activatedByUsername": "mifos", "activatedByFirstname": "App", "activatedByLastname": "Administrator" }, - "groups": [ - { - "id": 1, - "name": "Example Group" - } - ] + "savingsProductId": 4, + "savingsProductName": "account overdraft", + "groups": [] }
@@ -21832,6 +21876,14 @@ No Request Body: + + + clients/{clientId}?command=updateSavingsAccount + Update Default Savings Account + + + + Client Identifiers clients/{clientId}/identifiers diff --git a/mifosng-db/migrations/core_db/V151__add_default_savings_account_to_client.sql b/mifosng-db/migrations/core_db/V151__add_default_savings_account_to_client.sql new file mode 100755 index 000000000..c3e040e10 --- /dev/null +++ b/mifosng-db/migrations/core_db/V151__add_default_savings_account_to_client.sql @@ -0,0 +1,5 @@ +ALTER TABLE `m_client` + ADD COLUMN `default_savings_account` BIGINT(20) NULL DEFAULT NULL AFTER `default_savings_product`, + ADD CONSTRAINT `FK_m_client_m_savings_account` FOREIGN KEY (`default_savings_account`) REFERENCES `m_savings_account` (`id`); + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'UPDATESAVINGSACCOUNT_CLIENT', 'CLIENT', 'UPDATESAVINGSACCOUNT', 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 02ed76875..af4f157e2 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 @@ -660,6 +660,10 @@ public class CommandWrapper { public boolean isRejectClientTransfer() { return this.actionName.equalsIgnoreCase("REJECTTRANSFER") && this.entityName.equalsIgnoreCase("CLIENT"); } + + public boolean isUpdateClientSavingsAccount() { + return this.actionName.equalsIgnoreCase("UPDATESAVINGSACCOUNT") && 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 8419a112d..deead4c67 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 @@ -1425,6 +1425,16 @@ public class CommandWrapperBuilder { this.href = "/clients/" + clientId + "?command=assignStaff"; return this; } + + public CommandWrapperBuilder updateClientSavingsAccount(final Long clientId) { + this.actionName = "UPDATESAVINGSACCOUNT"; + this.entityName = "CLIENT"; + this.entityId = clientId; + this.clientId = clientId; + this.href = "/clients/" + clientId + "?command=updateSavingsAccount"; + return this; + } + public CommandWrapperBuilder createProductMix(final Long productId) { this.actionName = "CREATE"; 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 f8348a4ec..fdcef6809 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 @@ -177,6 +177,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("acceptClientTransferCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isRejectClientTransfer()) { handler = this.applicationContext.getBean("rejectClientTransferCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUpdateClientSavingsAccount()) { + handler = this.applicationContext.getBean("updateClientSavingsAccountCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java index f81c5b94e..e797a8ecc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java @@ -39,6 +39,7 @@ public class ClientApiConstants { public static final String closureReasonIdParamName = "closureReasonId"; public static final String submittedOnDateParamName = "submittedOnDate"; public static final String savingsProductIdParamName = "savingsProductId"; + public static final String savingsAccountIdParamName = "savingsAccountId"; // response parameters public static final String statusParamName = "status"; public static final String hierarchyParamName = "hierarchy"; 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 c4e23f962..7e36343a8 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 @@ -6,6 +6,7 @@ package org.mifosplatform.portfolio.client.api; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -39,6 +40,8 @@ import org.mifosplatform.portfolio.accountdetails.service.AccountDetailsReadPlat 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.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -55,6 +58,7 @@ public class ClientsApiResource { private final ApiRequestParameterHelper apiRequestParameterHelper; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; + private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; @Autowired public ClientsApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService, @@ -62,7 +66,8 @@ public class ClientsApiResource { final ToApiJsonSerializer clientAccountSummaryToApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, - final AccountDetailsReadPlatformService accountDetailsReadPlatformService) { + final AccountDetailsReadPlatformService accountDetailsReadPlatformService, + final SavingsAccountReadPlatformService savingsAccountReadPlatformService) { this.context = context; this.clientReadPlatformService = readPlatformService; this.toApiJsonSerializer = toApiJsonSerializer; @@ -70,6 +75,7 @@ public class ClientsApiResource { this.apiRequestParameterHelper = apiRequestParameterHelper; this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; + this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; } @GET @@ -133,6 +139,11 @@ public class ClientsApiResource { final ClientData templateData = this.clientReadPlatformService.retrieveTemplate(clientData.officeId(), staffInSelectedOfficeOnly); clientData = ClientData.templateOnTop(clientData, templateData); + Collection savingAccountOptions = this.savingsAccountReadPlatformService.retrieveForLookup( + clientId, true); + if (savingAccountOptions != null && savingAccountOptions.size() > 0) { + clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions); + } } return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS); @@ -224,10 +235,14 @@ public class ClientsApiResource { } else if (is(commandParam, "rejectTransfer")) { commandRequest = builder.rejectClientTransfer(clientId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "updateSavingsAccount")) { + commandRequest = builder.updateClientSavingsAccount(clientId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", - "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer", "acceptTransfer", "rejectTransfer" }); } + "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer", "acceptTransfer", "rejectTransfer", + "updateSavingsAccount" }); } return this.toApiJsonSerializer.serialize(result); } 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 b7cfe4e78..0fa0f6728 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 @@ -17,6 +17,7 @@ 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.group.data.GroupGeneralData; +import org.mifosplatform.portfolio.savings.data.SavingsAccountData; import org.mifosplatform.portfolio.savings.data.SavingsProductData; /** @@ -54,6 +55,8 @@ final public class ClientData implements Comparable { private final Long savingsProductId; private final String savingsProductName; + private final Long savingsAccountId; + // associations private final Collection groups; @@ -63,12 +66,13 @@ final public class ClientData implements Comparable { @SuppressWarnings("unused") private final Collection closureReasons; private final Collection savingProductOptions; + private final Collection savingAccountOptions; public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection officeOptions, final Collection staffOptions, final Collection closureReasons, final Collection savingProductOptions) { return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, null, null, null, joinedDate, null, - null, null, officeOptions, null, staffOptions, closureReasons, null, savingProductOptions, null, null); + null, null, officeOptions, null, staffOptions, closureReasons, null, savingProductOptions, null, null, null, null); } @@ -79,7 +83,19 @@ final public class ClientData implements Comparable { clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, templateData.officeOptions, clientData.groups, templateData.staffOptions, null, null, templateData.savingProductOptions, clientData.savingsProductId, - clientData.savingsProductName); + clientData.savingsProductName, clientData.savingsAccountId, clientData.savingAccountOptions); + + } + + public static ClientData templateWithSavingAccountOptions(final ClientData clientData, + final Collection savingAccountOptions) { + + return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, + clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename, + clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, + clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, clientData.officeOptions, + clientData.groups, clientData.staffOptions, null, null, clientData.savingProductOptions, clientData.savingsProductId, + clientData.savingsProductName, clientData.savingsAccountId, savingAccountOptions); } @@ -89,7 +105,7 @@ final public class ClientData implements Comparable { clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, clientData.officeOptions, parentGroups, clientData.staffOptions, null, clientData.timeline, clientData.savingProductOptions, - clientData.savingsProductId, clientData.savingsProductName); + clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId, clientData.savingAccountOptions); } @@ -98,13 +114,13 @@ final public class ClientData implements Comparable { final String officeName) { return new ClientData(accountNo, status, officeId, officeName, null, null, id, firstname, middlename, lastname, fullname, - displayName, null, null, null, null, null, null, null, null, null, null, null, null, null, null); + displayName, 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, null, null, id, null, null, null, null, displayName, 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, null); } @@ -112,10 +128,10 @@ final public class ClientData implements Comparable { final Long transferToOfficeId, final String transferToOfficeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, final String displayName, final String externalId, final String mobileNo, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName, - final ClientTimelineData timeline, final Long savingsProductId, final String savingsProductName) { + final ClientTimelineData timeline, final Long savingsProductId, final String savingsProductName, final Long savingsAccountId) { return new ClientData(accountNo, status, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName, null, null, null, null, - timeline, null, savingsProductId, savingsProductName); + timeline, null, savingsProductId, savingsProductName, savingsAccountId, null); } @@ -125,7 +141,8 @@ final public class ClientData implements Comparable { final String mobileNo, 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 ClientTimelineData timeline, - final Collection savingProductOptions, final Long savingsProductId, final String savingsProductName) { + final Collection savingProductOptions, final Long savingsProductId, final String savingsProductName, + final Long savingsAccountId, final Collection savingAccountOptions) { this.accountNo = accountNo; this.status = status; if (status != null) { @@ -167,6 +184,8 @@ final public class ClientData implements Comparable { this.savingProductOptions = savingProductOptions; this.savingsProductId = savingsProductId; this.savingsProductName = savingsProductName; + this.savingsAccountId = savingsAccountId; + this.savingAccountOptions = savingAccountOptions; } public Long id() { 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 1fdccdcb5..230967b7a 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 @@ -425,5 +425,30 @@ public final class ClientDataValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } + + public void validateForSavingsAccount(final String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + + final Set supportedParameters = new HashSet(Arrays.asList(ClientApiConstants.savingsAccountIdParamName)); + + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ClientApiConstants.CLIENT_RESOURCE_NAME); + + final String savingsIdParameterName = ClientApiConstants.savingsAccountIdParamName; + final Long savingsId = this.fromApiJsonHelper.extractLongNamed(savingsIdParameterName, element); + baseDataValidator.reset().parameter(savingsIdParameterName).value(savingsId).notNull().longGreaterThanZero(); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + + } + } \ No newline at end of file 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 0782cf676..2aa37ea13 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 @@ -45,6 +45,7 @@ import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.group.domain.Group; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsProduct; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.data.jpa.domain.AbstractPersistable; @@ -143,6 +144,10 @@ public final class Client extends AbstractPersistable { @ManyToOne @JoinColumn(name = "default_savings_product", nullable = true) private SavingsProduct savingsProduct; + + @ManyToOne + @JoinColumn(name = "default_savings_account", nullable = true) + private SavingsAccount savingsAccount; public static Client createNew(final AppUser currentUser, final Office clientOffice, final Group clientParentGroup, final Staff staff, final SavingsProduct savingsProduct, final JsonCommand command) { @@ -179,7 +184,7 @@ public final class Client extends AbstractPersistable { } return new Client(currentUser, status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, - activationDate, officeJoiningDate, externalId, mobileNo, staff, submittedOnDate, savingsProduct); + activationDate, officeJoiningDate, externalId, mobileNo, staff, submittedOnDate, savingsProduct, null); } protected Client() { @@ -189,7 +194,7 @@ public final class Client extends AbstractPersistable { private Client(final AppUser currentUser, final ClientStatus status, final Office office, final Group clientParentGroup, final String accountNo, final String firstname, final String middlename, final String lastname, final String fullname, final LocalDate activationDate, final LocalDate officeJoiningDate, final String externalId, final String mobileNo, - final Staff staff, final LocalDate submittedOnDate, final SavingsProduct savingsProduct) { + final Staff staff, final LocalDate submittedOnDate, final SavingsProduct savingsProduct,final SavingsAccount savingsAccount) { if (StringUtils.isBlank(accountNo)) { this.accountNumber = new RandomPasswordGenerator(19).generate(); @@ -253,6 +258,7 @@ public final class Client extends AbstractPersistable { this.staff = staff; this.savingsProduct = savingsProduct; + this.savingsAccount = savingsAccount; deriveDisplayName(); validate(); @@ -659,4 +665,12 @@ public final class Client extends AbstractPersistable { public AppUser activatedBy() { return this.activatedBy; } + + public SavingsAccount savingsAccount() { + return this.savingsAccount; + } + + public void updateSavingsAccount(SavingsAccount savingsAccount) { + this.savingsAccount = savingsAccount; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/UpdateClientSavingsAccountCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/UpdateClientSavingsAccountCommandHandler.java new file mode 100755 index 000000000..2d4bf44c5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/UpdateClientSavingsAccountCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.client.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.client.service.ClientWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UpdateClientSavingsAccountCommandHandler implements NewCommandSourceHandler { + + private final ClientWritePlatformService clientWritePlatformService; + + @Autowired + public UpdateClientSavingsAccountCommandHandler(final ClientWritePlatformService clientWritePlatformService){ + this.clientWritePlatformService = clientWritePlatformService; + } + + @Override + @Transactional + public CommandProcessingResult processCommand(JsonCommand command) { + return this.clientWritePlatformService.updateDefaultSavingsAccount(command.entityId(), command); + } + +} 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 fe483257f..8cc29daab 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 @@ -273,6 +273,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, "); sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,"); sqlBuilder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, "); + sqlBuilder.append("c.default_savings_account as savingsAccountId"); sqlBuilder.append("c.submittedon_date as submittedOnDate, "); sqlBuilder.append("sbu.username as submittedByUsername, "); @@ -336,6 +337,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final Long savingsProductId = JdbcSupport.getLong(rs, "savingsProductId"); final String savingsProductName = rs.getString("savingsProductName"); + + final Long savingsAccountId = JdbcSupport.getLong(rs, "savingsAccountId"); + final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate"); final String closedByUsername = rs.getString("closedByUsername"); @@ -357,7 +361,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService return ClientData.instance(accountNo, status, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName, - timeline, savingsProductId, savingsProductName); + timeline, savingsProductId, savingsProductName,savingsAccountId); } } @@ -408,7 +412,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService builder.append("c.activation_date as activationDate, c.image_id as imageId, "); builder.append("c.staff_id as staffId, s.display_name as staffName, "); - builder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName "); + builder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, "); + builder.append("c.default_savings_account as savingsAccountId "); builder.append("from m_client c "); builder.append("join m_office o on o.id = c.office_id "); builder.append("left join m_staff s on s.id = c.staff_id "); @@ -454,6 +459,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final Long savingsProductId = JdbcSupport.getLong(rs, "savingsProductId"); final String savingsProductName = rs.getString("savingsProductName"); + final Long savingsAccountId = JdbcSupport.getLong(rs, "savingsAccountId"); + final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate"); final String closedByUsername = rs.getString("closedByUsername"); @@ -475,7 +482,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService return ClientData.instance(accountNo, status, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, activationDate, imageId, staffId, staffName, - timeline, savingsProductId, savingsProductName); + timeline, savingsProductId, savingsProductName,savingsAccountId); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java index 8e58cfc46..f69973812 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java @@ -24,4 +24,6 @@ public interface ClientWritePlatformService { CommandProcessingResult assignClientStaff(Long clientId, JsonCommand command); + CommandProcessingResult updateDefaultSavingsAccount(Long clientId, JsonCommand command); + } \ No newline at end of file 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 7e171237f..3c958879e 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 @@ -52,6 +52,7 @@ import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; import org.mifosplatform.portfolio.savings.domain.SavingsProduct; import org.mifosplatform.portfolio.savings.domain.SavingsProductRepository; +import org.mifosplatform.portfolio.savings.exception.SavingsAccountNotFoundException; import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException; import org.mifosplatform.portfolio.savings.service.SavingsApplicationProcessWritePlatformService; import org.mifosplatform.useradministration.domain.AppUser; @@ -88,7 +89,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP final ClientDataValidator fromApiJsonDeserializer, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final GroupRepository groupRepository, final StaffRepositoryWrapper staffRepository, final CodeValueRepositoryWrapper codeValueRepository, final LoanRepository loanRepository, - final SavingsAccountRepository savingsRepository,final SavingsProductRepository savingsProductRepository, + final SavingsAccountRepository savingsRepository, final SavingsProductRepository savingsProductRepository, final SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService, final CommandProcessingService commandProcessingService) { this.context = context; @@ -181,18 +182,18 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP if (staffId != null) { staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, clientOffice.getHierarchy()); } - + SavingsProduct savingsProduct = null; final Long savingsProductId = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName); - if(savingsProductId != null){ + if (savingsProductId != null) { savingsProduct = this.savingsProductRepository.findOne(savingsProductId); if (savingsProduct == null) { throw new SavingsProductNotFoundException(savingsProductId); } } - final Client newClient = Client.createNew(currentUser,clientOffice, clientParentGroup, staff, savingsProduct, command); + final Client newClient = Client.createNew(currentUser, clientOffice, clientParentGroup, staff, savingsProduct, command); boolean rollbackTransaction = false; - if(newClient.isActive()){ + if (newClient.isActive()) { final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateClient(null).build(); rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, currentUser); } @@ -205,10 +206,13 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP newClient.updateAccountNo(accountNoGenerator.generate()); this.clientRepository.save(newClient); } - + final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); CommandProcessingResult result = openOverdraftSavingsAccount(newClient, fmt); + if (result.getSavingsId() != null) { + this.clientRepository.save(newClient); + } return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // @@ -237,7 +241,6 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP final String clientHierarchy = clientForUpdate.getOffice().getHierarchy(); this.context.validateAccessRights(clientHierarchy); - final Map changes = clientForUpdate.update(command); @@ -251,21 +254,19 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP } clientForUpdate.updateStaff(newStaff); } - + if (changes.containsKey(ClientApiConstants.savingsProductIdParamName)) { - if(clientForUpdate.isActive()){ - throw new ClientActiveForUpdateException(clientId, ClientApiConstants.savingsProductIdParamName); - } + if (clientForUpdate.isActive()) { throw new ClientActiveForUpdateException(clientId, + ClientApiConstants.savingsProductIdParamName); } SavingsProduct savingsProduct = null; final Long savingsProductId = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName); - if(savingsProductId != null){ + if (savingsProductId != null) { savingsProduct = this.savingsProductRepository.findOne(savingsProductId); if (savingsProduct == null) { throw new SavingsProductNotFoundException(savingsProductId); } } clientForUpdate.updateSavingsProduct(savingsProduct); } - if (!changes.isEmpty()) { this.clientRepository.saveAndFlush(clientForUpdate); } @@ -316,13 +317,19 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP private CommandProcessingResult openOverdraftSavingsAccount(final Client client, final DateTimeFormatter fmt) { CommandProcessingResult commandProcessingResult = CommandProcessingResult.empty(); - if(client.isActive() && client.SavingsProduct() != null){ - if(!client.SavingsProduct().isAllowOverdraft()){ + if (client.isActive() && client.SavingsProduct() != null) { + if (!client.SavingsProduct().isAllowOverdraft()) { String defaultUserMessage = "Client's saving account must be a overdraft account"; - throw new InvalidClientSavingProductException("saving.product", "must.be.ovrdraft.account", defaultUserMessage, client.SavingsProduct().getId(),client.getId()); + throw new InvalidClientSavingProductException("saving.product", "must.be.ovrdraft.account", defaultUserMessage, client + .SavingsProduct().getId(), client.getId()); } - SavingsAccountDataDTO savingsAccountDataDTO = new SavingsAccountDataDTO(client, null, client.SavingsProduct(), client.getActivationLocalDate(), client.activatedBy(), fmt); + SavingsAccountDataDTO savingsAccountDataDTO = new SavingsAccountDataDTO(client, null, client.SavingsProduct(), + client.getActivationLocalDate(), client.activatedBy(), fmt); commandProcessingResult = this.savingsApplicationProcessWritePlatformService.createActiveApplication(savingsAccountDataDTO); + if (commandProcessingResult.getSavingsId() != null) { + SavingsAccount savingsAccount = this.savingsRepository.findOne(commandProcessingResult.getSavingsId()); + client.updateSavingsAccount(savingsAccount); + } } return commandProcessingResult; } @@ -463,7 +470,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP } } - client.close(currentUser,closureReason, closureDate.toDate()); + client.close(currentUser, closureReason, closureDate.toDate()); this.clientRepository.saveAndFlush(client); return new CommandProcessingResultBuilder() // @@ -477,4 +484,46 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP } } + @Override + public CommandProcessingResult updateDefaultSavingsAccount(final Long clientId, final JsonCommand command) { + + this.context.authenticatedUser(); + + final Map actualChanges = new LinkedHashMap(5); + + this.fromApiJsonDeserializer.validateForSavingsAccount(command.json()); + + final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId); + + SavingsAccount savingsAccount = null; + final Long savingsId = command.longValueOfParameterNamed(ClientApiConstants.savingsAccountIdParamName); + if (savingsId != null) { + savingsAccount = this.savingsRepository.findOne(savingsId); + if (savingsAccount == null) { throw new SavingsAccountNotFoundException(savingsId); } + if(!savingsAccount.getClient().identifiedBy(clientId)){ + String defaultUserMessage = "saving account must belongs to client"; + throw new InvalidClientSavingProductException("saving.account", "must.belongs.to.client", defaultUserMessage, savingsId, + clientForUpdate.getId()); + } + if (!savingsAccount.allowOverdraft()) { + String defaultUserMessage = "Client's saving account must be a overdraft account"; + throw new InvalidClientSavingProductException("saving.account", "must.be.ovrdraft.account", defaultUserMessage, savingsId, + clientForUpdate.getId()); + } + clientForUpdate.updateSavingsAccount(savingsAccount); + } + + this.clientRepository.saveAndFlush(clientForUpdate); + + actualChanges.put(ClientApiConstants.savingsAccountIdParamName, savingsId); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withOfficeId(clientForUpdate.officeId()) // + .withEntityId(clientForUpdate.getId()) // + .withClientId(clientId) // + .with(actualChanges) // + .build(); + } + } \ 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 13bad5430..a9614d51f 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 @@ -157,6 +157,59 @@ public class SavingsAccountData { withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit); } + public static SavingsAccountData lookupWithProductDetails(final Long accountId, final String accountNo, final Long productId, + final String productName, final SavingsAccountStatusEnumData status) { + + final String externalId = null; + final Long groupId = null; + final Long clientId = null; + final String clientName = null; + final String groupName = null; + final Long fieldOfficerId = null; + final String fieldOfficerName = null; + final SavingsAccountApplicationTimelineData timeline = null; + final CurrencyData currency = null; + final BigDecimal nominalAnnualInterestRate = null; + final EnumOptionData interestPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + final BigDecimal minRequiredOpeningBalance = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; + // final BigDecimal withdrawalFeeAmount = null; + // final EnumOptionData withdrawalFeeType = null; + final boolean withdrawalFeeForTransfers = false; + // final BigDecimal annualFeeAmount = null; + // final MonthDay annualFeeOnMonthDay = null; + // final LocalDate annualFeeNextDueDate = null; + final boolean allowOverdraft = false; + final BigDecimal overdraftLimit = null; + + final SavingsAccountSummaryData summary = null; + final Collection transactions = null; + + 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 charges = null; + final Collection chargeOptions = null; + + return new SavingsAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit); + } + public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template, final Collection transactions, final Collection charges) { 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 de5f2654f..7e5988603 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 @@ -2001,4 +2001,8 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + public boolean allowOverdraft() { + return this.allowOverdraft; + } + } \ 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 f93b9fa03..b9bce25af 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 @@ -30,4 +30,6 @@ public interface SavingsAccountReadPlatformService { // retrieveAccountsWithAnnualFeeDue(); SavingsAccountTransactionData retrieveSavingsTransaction(Long savingsId, Long transactionId); + + Collection retrieveForLookup(Long clientId,Boolean overdraft); } \ 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 8918a50e2..7bc672f3a 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 @@ -101,7 +101,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead public Collection retrieveAllForLookup(final Long clientId) { final StringBuilder sqlBuilder = new StringBuilder("select " + this.savingAccountMapper.schema()); - sqlBuilder.append(" where sa.client_id = ? and sa.status_enum = 300"); + sqlBuilder.append(" where sa.client_id = ? and sa.status_enum = 300 "); final Object[] queryParameters = new Object[] { clientId }; return this.jdbcTemplate.query(sqlBuilder.toString(), this.savingAccountMapper, queryParameters); @@ -404,6 +404,42 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead } } + private static final class SavingAccountMapperForLookup implements RowMapper { + + private final String schemaSql; + + public SavingAccountMapperForLookup() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("sa.id as id, sa.account_no as accountNo, "); + sqlBuilder.append("sp.id as productId, sp.name as productName, "); + sqlBuilder.append("sa.status_enum as statusEnum "); + + sqlBuilder.append("from m_savings_account sa "); + sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public SavingsAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum"); + final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusEnum); + + return SavingsAccountData.lookupWithProductDetails(id, accountNo, productId, productName, status); + } + } + @Override public SavingsAccountData retrieveTemplate(final Long clientId, final Long groupId, final Long productId, final boolean staffInSelectedOfficeOnly) { @@ -861,6 +897,23 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead } } + @Override + public Collection retrieveForLookup(Long clientId, Boolean overdraft) { + + SavingAccountMapperForLookup accountMapperForLookup = new SavingAccountMapperForLookup(); + final StringBuilder sqlBuilder = new StringBuilder("select " + accountMapperForLookup.schema()); + sqlBuilder.append(" where sa.client_id = ? and sa.status_enum = 300"); + Object[] queryParameters = null; + if (overdraft == null) { + queryParameters = new Object[] { clientId }; + } else { + sqlBuilder.append(" and sa.allow_overdraft = ?"); + queryParameters = new Object[] { clientId, overdraft }; + } + return this.jdbcTemplate.query(sqlBuilder.toString(), accountMapperForLookup, queryParameters); + + } + /* * private static final class SavingsAccountAnnualFeeMapper implements * RowMapper { @@ -879,6 +932,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead * public String schema() { return this.schemaSql; } * * @Override public SavingsAccountAnnualFeeData mapRow(final ResultSet rs, + * * @SuppressWarnings("unused") final int rowNum) throws SQLException { * * final Long id = rs.getLong("id"); final String accountNo =