From 051e0372f55659b18fcb3e245416feb20a34b98c Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Tue, 16 Jul 2013 19:17:56 +0530 Subject: [PATCH 1/2] Product Mix Functionality Updating Api Docs --- api-docs/apiLive.htm | 393 ++++++++++++++++++ .../V84__product_mix_related_changes.sql | 19 + .../commands/domain/CommandSource.java | 11 +- .../commands/domain/CommandWrapper.java | 25 +- .../service/CommandWrapperBuilder.java | 35 +- ...CommandSourceWritePlatformServiceImpl.java | 6 +- .../SynchronousCommandProcessingService.java | 12 +- .../infrastructure/core/api/JsonCommand.java | 16 +- .../core/data/CommandProcessingResult.java | 14 +- .../data/CommandProcessingResultBuilder.java | 8 +- .../api/LoanProductsApiResource.java | 39 +- .../productmix/api/ProductMixApiResource.java | 129 ++++++ .../productmix/data/ProductMixData.java | 44 ++ .../productmix/domain/ProductMix.java | 44 ++ .../domain/ProductMixRepository.java | 25 ++ .../ProductMixNotFoundException.java | 15 + .../CreateProductMixCommandHandler.java | 29 ++ .../DeleteProductMixCommandHandler.java | 29 ++ .../UpdateProductMixCommandHandler.java | 29 ++ .../ProductMixDataValidator.java | 87 ++++ .../ProductMixReadPlatformService.java | 13 + .../ProductMixReadPlatformServiceImpl.java | 109 +++++ .../ProductMixWritePlatformService.java | 14 + ...WritePlatformServiceJpaRepositoryImpl.java | 225 ++++++++++ .../LoanProductReadPlatformService.java | 6 + .../LoanProductReadPlatformServiceImpl.java | 53 +++ 26 files changed, 1403 insertions(+), 26 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V84__product_mix_related_changes.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/api/ProductMixApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/data/ProductMixData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/domain/ProductMix.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/domain/ProductMixRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/exception/ProductMixNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/CreateProductMixCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/DeleteProductMixCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/UpdateProductMixCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/serialization/ProductMixDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixWritePlatformServiceJpaRepositoryImpl.java diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 75227bd99..c1599492c 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -891,6 +891,22 @@ Update a Charge Delete a Charge + + Loan Product Mix + loanproducts?associations=productMixes + + List Loan Products Mix + + + + + + loanproducts/{productId}/productmix + Create a Loan Product Mix + Retrieve Loan Products Mix + Update Loan Product Mix + Delete Loan Products Mix + @@ -1404,6 +1420,10 @@ loanproducts/template Retrieve Loan Product Template + + loanproducts/template?isProductMixTemplate=true + Retrieve Loan Product Mix Template + loans/template?clientId={clientId} Retrieve Loan Template @@ -8050,6 +8070,355 @@ Request Body: + +   +
+
+

Loan Products Mix

+

If you have the appropriate permissions, you can decide which types of products a given client or group can mix. In this way, you can restrict clients from having active loans accounts of different products at the same time.

+ + + + + + + + + + + +
Field Descriptions
restrictedProducts
List of productIds to create a product mix
+
+
+ +   +
+
+

List Loan Product Mix

+

Example Requests:

+
loanproducts?associations=productMixes
+
+
+ +GET https://DomainName/api/v1/loanproducts?associations=productMixes + + +[ + { + "productId": 1, + "productName": "Personal Loan", + "restrictedProducts": [ + { + "id": 2, + "name": "Joint Loan", + "includeInBorrowerCycle": false + }, + { + "id": 5, + "name": "Primary Loan", + "includeInBorrowerCycle": false + }, + { + "id": 3, + "name": "Daily Loan", + "includeInBorrowerCycle": false + } + ], + "allowedProducts": [ + { + "id": 1, + "name": "Personal Loan", + "includeInBorrowerCycle": false + }, + { + "id": 6, + "name": "Personal Loan -2", + "includeInBorrowerCycle": false + }, + { + "id": 4, + "name": "Weekly Loan", + "includeInBorrowerCycle": false + } + ] + }, + { + "productId": 2, + "productName": "Joint Loan", + "restrictedProducts": [ + { + "id": 1, + "name": "Personal Loan", + "includeInBorrowerCycle": false + }, + { + "id": 6, + "name": "Personal Loan -2", + "includeInBorrowerCycle": false + } + ], + "allowedProducts": [ + { + "id": 3, + "name": "Daily Loan", + "includeInBorrowerCycle": false + }, + { + "id": 2, + "name": "Joint Loan", + "includeInBorrowerCycle": false + }, + { + "id": 5, + "name": "Primary Loan", + "includeInBorrowerCycle": false + }, + { + "id": 4, + "name": "Weekly Loan", + "includeInBorrowerCycle": false + } + ] + } +] + +
+
+ +   +
+
+

Retrieve Product Mix Details 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 Request:

+

This request gets all the products details for which productmix is not defined.

+
+
loanproducts/template?isProductMixTemplate=true
+

+

This request gets the productmix details for the specific loanproduct.

+
+
loanproducts/5/productmix
+
+
loanproducts/5/productmix?template=true
+ +
+
+ +GET https://DomainName/api/v1/loanproducts/template?isProductMixTemplate=true + + +{ + "productOptions": [ + { + "id": 5, + "name": "Primary Loan" + }, + { + "id": 4, + "name": "Weekly Loan" + } + ] +} + + +GET https://DomainName/api/v1/loanproducts/5/productmix + + +{ + "restrictedProducts": [ + { + "id": 1, + "name": "Personal Loan", + "includeInBorrowerCycle": false + } + ], + "allowedProducts": [ + { + "id": 3, + "name": "Daily Loan", + "includeInBorrowerCycle": false + }, + { + "id": 2, + "name": "Joint Loan", + "includeInBorrowerCycle": false + }, + { + "id": 6, + "name": "Personal Loan -2", + "includeInBorrowerCycle": false + }, + { + "id": 5, + "name": "Primary Loan", + "includeInBorrowerCycle": false + }, + { + "id": 4, + "name": "Weekly Loan", + "includeInBorrowerCycle": false + } + ] +} + +
+
+ +   +
+
+

Retrieve Loan Product Mix

+

Example Requests:

+
loanproducts/5/productmix
+
+
+ +GET https://DomainName/api/v1/loanproducts/5/productmix + + +{ + "restrictedProducts": [ + { + "id": 1, + "name": "Personal Loan", + "includeInBorrowerCycle": false + } + ], + "allowedProducts": [ + { + "id": 3, + "name": "Daily Loan", + "includeInBorrowerCycle": false + }, + { + "id": 2, + "name": "Joint Loan", + "includeInBorrowerCycle": false + }, + { + "id": 6, + "name": "Personal Loan -2", + "includeInBorrowerCycle": false + }, + { + "id": 5, + "name": "Primary Loan", + "includeInBorrowerCycle": false + }, + { + "id": 4, + "name": "Weekly Loan", + "includeInBorrowerCycle": false + } + ] +} + +
+
+ +   +
+
+

Create a Loan Product Mix

+ + + + + + + + +
Mandatory Fields
restrictedProducts
+
+
+ +POST https://DomainName/api/v1/loanproducts/5/productmix + + +POST loanproducts/{productId}/productmix +Content-Type: application/json +Request Body: +{"restrictedProducts":["1"]} + + +{ + "changes":{ + "restrictedProductsForMix":[1], + "removedProductsForMix":[] + }, + "productId":5 +} + +
+
+ +   +
+
+

Update a Loan Product Mix

+
+
+ +PUT https://DomainName/api/v1/loanproducts/{productId}/productmix + + +PUT loanproducts/5/productmix +Content-Type: application/json +Request Body: +{ + "restrictedProducts":["3"] +} + + +{ + "changes": { + "restrictedProductsForMix": [3], + "removedProductsForMix": [1] + }, + "productId": 5 +} + +
+
+ +   +
+
+

Delete a Loan Product Mix

+
+
+ +DELETE https://DomainName/api/v1/loanproducts/{productId}/productmix + + +DELETE loanproducts/5/productmix + + +{ + "changes": { + "removedProductsForMix": [3] + }, + "productId": 5 +} + +
+
+ +  
@@ -16373,6 +16742,30 @@ GET https://DomainName/api/v1/search?query=000000111 Update a Loan Product + + Loan Product Mix + loanproducts/template?isProductMixTemplate=true + + Retrieve Loan Product Mix Template + + + + + + loanproducts?associations=productMixes + + List Loan Products Mix + + + + + + loanproducts/{productId}/productmix + Create a Loan Product Mix + Retrieve Loan Products Mix + Update Loan Product Mix + Delete Loan Products Mix + Loans loans?calculateLoanSchedule diff --git a/mifosng-db/migrations/core_db/V84__product_mix_related_changes.sql b/mifosng-db/migrations/core_db/V84__product_mix_related_changes.sql new file mode 100644 index 000000000..704fe22a1 --- /dev/null +++ b/mifosng-db/migrations/core_db/V84__product_mix_related_changes.sql @@ -0,0 +1,19 @@ +ALTER TABLE `m_portfolio_command_source` + ADD COLUMN `product_id` BIGINT NULL DEFAULT NULL AFTER `processing_result_enum`; + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'CREATE_PRODUCTMIX', 'PRODUCTMIX', 'CREATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'UPDATE_PRODUCTMIX', 'PRODUCTMIX', 'UPDATE', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'DELETE_PRODUCTMIX', 'PRODUCTMIX', 'DELETE', 0); + +CREATE TABLE `m_product_mix` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `product_id` BIGINT(20) NOT NULL, + `restricted_product_id` BIGINT(20) NOT NULL, + PRIMARY KEY (`id`), + INDEX `FK_m_product_mix_product_id_to_m_product_loan` (`product_id`), + INDEX `FK_m_product_mix_restricted_product_id_to_m_product_loan` (`restricted_product_id`), + CONSTRAINT `FK_m_product_mix_restricted_product_id_to_m_product_loan` FOREIGN KEY (`restricted_product_id`) REFERENCES `m_product_loan` (`id`), + CONSTRAINT `FK_m_product_mix_product_id_to_m_product_loan` FOREIGN KEY (`product_id`) REFERENCES `m_product_loan` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; \ No newline at end of file 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 15256020e..b8c948061 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 @@ -85,6 +85,9 @@ public class CommandSource extends AbstractPersistable { @Column(name = "processing_result_enum", nullable = false) private Integer processingResult; + + @Column(name = "product_id") + private Long productId; 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(), @@ -172,15 +175,21 @@ public class CommandSource extends AbstractPersistable { return false; } - public void updateForAudit(final Long officeId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId) { + 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 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/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 17b2ca473..d27b79f27 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 @@ -25,18 +25,19 @@ public class CommandWrapper { private final String transactionId; private final String supportedEntityType; private final Long supportedEntityId; + private final Long productId; public static CommandWrapper wrap(final String actionName, final String entityName, final Long resourceId, final Long subresourceId) { - return new CommandWrapper(null, actionName, entityName, resourceId, subresourceId, null); + return new CommandWrapper(null, actionName, entityName, resourceId, subresourceId, null, null); } public static CommandWrapper fromExistingCommand(final Long commandId, final String actionName, final String entityName, - final Long resourceId, final Long subresourceId, final String resourceGetUrl) { - return new CommandWrapper(commandId, actionName, entityName, resourceId, subresourceId, resourceGetUrl); + final Long resourceId, final Long subresourceId, final String resourceGetUrl, final Long productId) { + return new CommandWrapper(commandId, actionName, entityName, resourceId, subresourceId, resourceGetUrl, productId); } private CommandWrapper(final Long commandId, final String actionName, final String entityName, final Long resourceId, - final Long subresourceId, final String resourceGetUrl) { + final Long subresourceId, final String resourceGetUrl, final Long productId) { this.commandId = commandId; this.officeId = null; this.groupId = null; @@ -54,12 +55,14 @@ public class CommandWrapper { this.href = resourceGetUrl; this.json = null; this.transactionId = null; + this.productId = productId; } public CommandWrapper(final Long officeId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final String actionName, final String entityName, final Long entityId, final Long subentityId, final Long codeId, - final String supportedEntityType, final Long supportedEntityId, final String href, final String json, final String transactionId) { + final String supportedEntityType, final Long supportedEntityId, final String href, final String json, + final String transactionId, final Long productId) { this.commandId = null; this.officeId = officeId; this.groupId = groupId; @@ -77,7 +80,7 @@ public class CommandWrapper { this.href = href; this.json = json; this.transactionId = transactionId; - + this.productId = productId; } public Long commandId() { @@ -176,6 +179,10 @@ public class CommandWrapper { return this.supportedEntityType; } + public Long getProductId() { + return this.productId; + } + public boolean isUpdate() { // permissions resource has special update which involves no resource. return (isPermissionResource() && isUpdateOperation()) || (isCurrencyResource() && isUpdateOperation()) @@ -190,7 +197,7 @@ public class CommandWrapper { return isDeleteOperation() && this.entityId != null; } - private boolean isDeleteOperation() { + public boolean isDeleteOperation() { return this.actionName.equalsIgnoreCase("DELETE"); } @@ -536,4 +543,8 @@ public class CommandWrapper { public boolean isClientClose() { return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("CLIENT"); } + + public boolean isProductMixResource() { + return this.entityName.equalsIgnoreCase("PRODUCTMIX"); + } } \ 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 45db68066..78b2338c6 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 @@ -24,11 +24,12 @@ public class CommandWrapperBuilder { private String transactionId; private String supportedEntityType; private Long supportedEntityId; + private Long productId; public CommandWrapper build() { return new CommandWrapper(this.officeId, this.groupId, this.clientId, this.loanId, this.savingsId, this.actionName, this.entityName, this.entityId, this.subentityId, this.codeId, this.supportedEntityType, this.supportedEntityId, this.href, - this.json, this.transactionId); + this.json, this.transactionId, this.productId); } public CommandWrapperBuilder withLoanId(final Long withLoanId) { @@ -1116,4 +1117,36 @@ public class CommandWrapperBuilder { this.href = "/clients/" + clientId + "?command=assignStaff"; return this; } + + public CommandWrapperBuilder createProductMix(final Long productId) { + this.actionName = "CREATE"; + this.entityName = "PRODUCTMIX"; + this.entityId = null; + this.productId = productId; + this.href = "/loanproducts/" + productId + "/productmix"; + return this; + } + + public CommandWrapperBuilder updateProductMix(final Long productId) { + this.actionName = "UPDATE"; + this.entityName = "PRODUCTMIX"; + this.entityId = null; + this.productId = productId; + this.href = "/loanproducts/" + productId + "/productmix"; + return this; + } + + public CommandWrapperBuilder deleteProductMix(final Long productId) { + this.actionName = "DELETE"; + this.entityName = "PRODUCTMIX"; + this.entityId = null; + this.productId = productId; + this.href = "/loanproducts/" + productId + "/productmix"; + return this; + } + + public CommandWrapperBuilder withProduct(final Long productId) { + this.productId = productId; + return this; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java index c68ca88c8..57a6073e1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java @@ -63,7 +63,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio final JsonCommand command = JsonCommand.from(json, parsedCommand, this.fromApiJsonHelper, wrapper.getEntityName(), wrapper.getEntityId(), wrapper.getSubentityId(), wrapper.getGroupId(), wrapper.getClientId(), wrapper.getLoanId(), wrapper.getSavingsId(), wrapper.getCodeId(), wrapper.getSupportedEntityType(), wrapper.getSupportedEntityId(), - wrapper.getTransactionId(), wrapper.getHref()); + wrapper.getTransactionId(), wrapper.getHref(), wrapper.getProductId()); result = this.processAndLogCommandService.processAndLogCommand(wrapper, command, isApprovedByChecker); } catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) { @@ -81,12 +81,12 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio final CommandWrapper wrapper = CommandWrapper.fromExistingCommand(makerCheckerId, commandSourceInput.getActionName(), commandSourceInput.getEntityName(), commandSourceInput.resourceId(), commandSourceInput.subresourceId(), - commandSourceInput.getResourceGetUrl()); + commandSourceInput.getResourceGetUrl(), commandSourceInput.getProductId()); final JsonElement parsedCommand = this.fromApiJsonHelper.parse(commandSourceInput.json()); final JsonCommand command = JsonCommand.fromExistingCommand(makerCheckerId, commandSourceInput.json(), parsedCommand, this.fromApiJsonHelper, commandSourceInput.getEntityName(), commandSourceInput.resourceId(), - commandSourceInput.subresourceId(), commandSourceInput.getResourceGetUrl()); + commandSourceInput.subresourceId(), commandSourceInput.getResourceGetUrl(), commandSourceInput.getProductId()); final boolean makerCheckerApproval = true; return this.processAndLogCommandService.processAndLogCommand(wrapper, command, makerCheckerApproval); 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 68c98a282..4835a4676 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 @@ -70,7 +70,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } commandSourceResult.updateResourceId(result.resourceId()); commandSourceResult.updateForAudit(result.getOfficeId(), result.getGroupId(), result.getClientId(), result.getLoanId(), - result.getSavingsId()); + result.getSavingsId(), result.getProductId()); String changesOnlyJson = null; if (result.hasChanges()) { @@ -491,6 +491,16 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer if (wrapper.isCreate()) { handler = this.applicationContext.getBean("createHolidayCommandHandler", NewCommandSourceHandler.class); } + } else if (wrapper.isProductMixResource()) { + if (wrapper.isCreate()) { + handler = this.applicationContext.getBean("createProductMixCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isUpdateOperation()) { + handler = this.applicationContext.getBean("updateProductMixCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isDeleteOperation()) { + handler = this.applicationContext.getBean("deleteProductMixCommandHandler", NewCommandSourceHandler.class); + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } } else { throw new UnsupportedCommandException(wrapper.commandName()); } 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 0f0847c1c..eda5217d5 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 @@ -46,26 +46,27 @@ public final class JsonCommand { private final Long supportedEntityId; private final String transactionId; private final String url; + private final Long productId; public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final Long codeId, final String supportedEntityType, final Long supportedEntityId, - final String transactionId, final String url) { + final String transactionId, final String url, final Long productId) { return new JsonCommand(null, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, groupId, - clientId, loanId, savingsId, codeId, supportedEntityType, supportedEntityId, transactionId, url); + clientId, loanId, savingsId, codeId, supportedEntityType, supportedEntityId, transactionId, url, productId); } public static JsonCommand fromExistingCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId, - final String url) { + final String url, final Long productId) { return new JsonCommand(commandId, jsonCommand, parsedCommand, fromApiJsonHelper, entityName, resourceId, subresourceId, null, null, - null, null, null, null, null, null, url); + null, null, null, null, null, null, url, productId); } public JsonCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final Long codeId, - final String supportedEntityType, final Long supportedEntityId, final String transactionId, final String url) { + final String supportedEntityType, final Long supportedEntityId, final String transactionId, final String url, final Long productId) { this.commandId = commandId; this.jsonCommand = jsonCommand; this.parsedCommand = parsedCommand; @@ -82,6 +83,7 @@ public final class JsonCommand { this.supportedEntityId = supportedEntityId; this.transactionId = transactionId; this.url = url; + this.productId = productId; } public String json() { @@ -153,6 +155,10 @@ public final class JsonCommand { return this.url; } + public Long getProductId() { + return this.productId; + } + private boolean differenceExists(final LocalDate baseValue, final LocalDate workingCopyValue) { boolean differenceExists = false; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/CommandProcessingResult.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/CommandProcessingResult.java index c0a1bcc60..0370c58df 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/CommandProcessingResult.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/CommandProcessingResult.java @@ -25,12 +25,13 @@ public class CommandProcessingResult { private final Map changes; @SuppressWarnings("unused") private final String resourceIdentifier; + private final Long productId; public static CommandProcessingResult fromDetails(final Long commandId, final Long officeId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final String resourceIdentifier, final Long entityId, final String transactionId, - final Map changes) { + final Map changes, final Long productId) { return new CommandProcessingResult(commandId, officeId, groupId, clientId, loanId, savingsId, resourceIdentifier, entityId, - transactionId, changes); + transactionId, changes, productId); } public static CommandProcessingResult commandOnlyResult(final Long commandId) { @@ -79,11 +80,12 @@ public class CommandProcessingResult { this.savingsId = null; this.transactionId = null; this.changes = new HashMap(); + this.productId = null; } private CommandProcessingResult(final Long commandId, final Long officeId, final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final String resourceIdentifier, final Long resourceId, final String transactionId, - final Map changesOnly) { + final Map changesOnly, final Long productId) { this.commandId = commandId; this.officeId = officeId; this.groupId = groupId; @@ -94,6 +96,7 @@ public class CommandProcessingResult { this.resourceId = resourceId; this.changes = changesOnly; this.transactionId = transactionId; + this.productId = productId; } private CommandProcessingResult(final Long resourceId, final Long officeId, final Long commandId, final Map changesOnly) { @@ -111,6 +114,7 @@ public class CommandProcessingResult { this.transactionId = null; this.commandId = commandId; this.changes = changesOnly; + this.productId = null; } public Long commandId() { @@ -161,4 +165,8 @@ public class CommandProcessingResult { final boolean noChanges = this.changes == null || this.changes.isEmpty(); return !noChanges; } + + public Long getProductId() { + return this.productId; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/CommandProcessingResultBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/CommandProcessingResultBuilder.java index 6045306f6..dfa4b7937 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/CommandProcessingResultBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/CommandProcessingResultBuilder.java @@ -23,10 +23,11 @@ public class CommandProcessingResultBuilder { private Long entityId; private String transactionId; private Map changes; + private Long productId; public CommandProcessingResult build() { return CommandProcessingResult.fromDetails(commandId, officeId, groupId, clientId, loanId, savingsId, resourceIdentifier, entityId, - transactionId, changes); + transactionId, changes, productId); } public CommandProcessingResultBuilder withCommandId(final Long withCommandId) { @@ -78,5 +79,10 @@ public class CommandProcessingResultBuilder { this.transactionId = withTransactionId; return this; } + + public CommandProcessingResultBuilder withProductId(final Long productId) { + this.productId = productId; + return this; + } } \ No newline at end of file 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 c105e3300..6231b8454 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 @@ -19,6 +19,7 @@ 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; @@ -33,6 +34,7 @@ import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; +import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.EnumOptionData; @@ -47,6 +49,8 @@ import org.mifosplatform.portfolio.fund.data.FundData; import org.mifosplatform.portfolio.fund.service.FundReadPlatformService; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData; +import org.mifosplatform.portfolio.loanproduct.productmix.data.ProductMixData; +import org.mifosplatform.portfolio.loanproduct.productmix.service.ProductMixReadPlatformService; import org.mifosplatform.portfolio.loanproduct.service.LoanDropdownReadPlatformService; import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService; import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; @@ -69,6 +73,9 @@ public class LoanProductsApiResource { "currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "accountingRuleOptions", "accountingMappingOptions")); + + private final Set PRODUCT_MIX_DATA_PARAMETERS = new HashSet(Arrays.asList("productId", "productName", + "restrictedProducts", "allowedProducts", "productOptions")); private final String resourceNameForPermissions = "LOANPRODUCT"; @@ -84,6 +91,8 @@ public class LoanProductsApiResource { private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService; private final CodeValueReadPlatformService codeValueReadPlatformService; private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService; + private final DefaultToApiJsonSerializer productMixDataApiJsonSerializer; + private final ProductMixReadPlatformService productMixReadPlatformService; @Autowired public LoanProductsApiResource(final PlatformSecurityContext context, final LoanProductReadPlatformService readPlatformService, @@ -94,7 +103,9 @@ public class LoanProductsApiResource { final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService, final CodeValueReadPlatformService codeValueReadPlatformService, - final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService) { + final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService, + final DefaultToApiJsonSerializer productMixDataApiJsonSerializer, + final ProductMixReadPlatformService productMixReadPlatformService) { this.context = context; this.loanProductReadPlatformService = readPlatformService; this.chargeReadPlatformService = chargeReadPlatformService; @@ -107,6 +118,8 @@ public class LoanProductsApiResource { this.accountMappingReadPlatformService = accountMappingReadPlatformService; this.codeValueReadPlatformService = codeValueReadPlatformService; this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService; + this.productMixDataApiJsonSerializer = productMixDataApiJsonSerializer; + this.productMixReadPlatformService = productMixReadPlatformService; } @POST @@ -127,10 +140,19 @@ public class LoanProductsApiResource { public String retrieveAllLoanProducts(@Context final UriInfo uriInfo) { context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + if (!associationParameters.isEmpty()) { + if (associationParameters.contains("productMixes")) { + context.authenticatedUser().validateHasReadPermission("PRODUCTMIX"); + Collection productMixes = this.productMixReadPlatformService.retrieveAllProductMixes(); + return this.productMixDataApiJsonSerializer.serialize(settings, productMixes, PRODUCT_MIX_DATA_PARAMETERS); + } + } final Collection products = this.loanProductReadPlatformService.retrieveAllLoanProducts(); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, products, LOAN_PRODUCT_DATA_PARAMETERS); } @@ -138,14 +160,22 @@ public class LoanProductsApiResource { @Path("template") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveTemplate(@Context final UriInfo uriInfo) { + public String retrieveTemplate(@Context final UriInfo uriInfo, @QueryParam("isProductMixTemplate") final boolean isProductMixTemplate) { context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + if (isProductMixTemplate) { + context.authenticatedUser().validateHasReadPermission("PRODUCTMIX"); + + Collection productOptions = this.loanProductReadPlatformService.retrieveAvailableLoanProductsForMix(); + ProductMixData productMixData = ProductMixData.template(productOptions); + return this.productMixDataApiJsonSerializer.serialize(settings, productMixData, PRODUCT_MIX_DATA_PARAMETERS); + } LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveNewLoanProductDetails(); loanProduct = handleTemplate(loanProduct); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, loanProduct, LOAN_PRODUCT_DATA_PARAMETERS); } @@ -237,4 +267,5 @@ public class LoanProductsApiResource { interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions); } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/api/ProductMixApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/api/ProductMixApiResource.java new file mode 100644 index 000000000..6edbff9dc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/api/ProductMixApiResource.java @@ -0,0 +1,129 @@ +/** + * 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.loanproduct.productmix.api; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +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.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.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; +import org.mifosplatform.portfolio.loanproduct.productmix.data.ProductMixData; +import org.mifosplatform.portfolio.loanproduct.productmix.service.ProductMixReadPlatformService; +import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/loanproducts/{productId}/productmix") +@Component +@Scope("singleton") +public class ProductMixApiResource { + + private final String resourceNameForPermissions = "PRODUCTMIX"; + + private final Set PRODUCT_MIX_DATA_PARAMETERS = new HashSet(Arrays.asList("productId", "productName", + "restrictedProducts", "allowedProducts", "productOptions")); + + private final PlatformSecurityContext context; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + + private final ProductMixReadPlatformService productMixReadPlatformService; + private final LoanProductReadPlatformService loanProductReadPlatformService; + + @Autowired + public ProductMixApiResource(final PlatformSecurityContext context, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final ProductMixReadPlatformService productMixReadPlatformService, + final LoanProductReadPlatformService loanProductReadPlatformService) { + this.context = context; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.toApiJsonSerializer = toApiJsonSerializer; + this.productMixReadPlatformService = productMixReadPlatformService; + this.loanProductReadPlatformService = loanProductReadPlatformService; + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveTemplate(@PathParam("productId") final Long productId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); + + ProductMixData productMixData = this.productMixReadPlatformService.retrieveLoanProductMixDetails(productId); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + if (settings.isTemplate()) { + final Collection productOptions = this.loanProductReadPlatformService.retrieveAvailableLoanProductsForMix(); + productMixData = ProductMixData.withTemplateOptions(productMixData, productOptions); + } + return this.toApiJsonSerializer.serialize(settings, productMixData, this.PRODUCT_MIX_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String createProductMix(@PathParam("productId") final Long productId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createProductMix(productId).withJson(apiRequestBodyAsJson) + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @PUT + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String updateProductMix(@PathParam("productId") final Long productId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateProductMix(productId).withJson(apiRequestBodyAsJson) + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @DELETE + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String deleteProductMix(@PathParam("productId") final Long productId) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteProductMix(productId).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/data/ProductMixData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/data/ProductMixData.java new file mode 100644 index 000000000..4fe07286d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/data/ProductMixData.java @@ -0,0 +1,44 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.data; + +import java.util.Collection; + +import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; + +public class ProductMixData { + + private final Long productId; + private final String productName; + private final Collection restrictedProducts; + private final Collection allowedProducts; + @SuppressWarnings("unused") + private final Collection productOptions; + + public ProductMixData(final Long productId, final String productName, final Collection restrictedProducts, + final Collection allowedProducts, final Collection productOptions) { + this.productId = productId; + this.productName = productName; + this.restrictedProducts = restrictedProducts; + this.allowedProducts = allowedProducts; + this.productOptions = productOptions; + } + + public static ProductMixData template(final Collection productOptions) { + return new ProductMixData(null, null, null, null, productOptions); + } + + public static ProductMixData withTemplateOptions(final ProductMixData productMixData, final Collection productOptions) { + return new ProductMixData(productMixData.productId, productMixData.productName, productMixData.restrictedProducts, + productMixData.allowedProducts, productOptions); + } + + public static ProductMixData withDetails(Long productId, String productName, Collection restrictedProducts, + Collection allowedProducts) { + return new ProductMixData(productId, productName, restrictedProducts, allowedProducts, null); + } + + public static ProductMixData withRestrictedOptions(Collection restrictedProducts, + Collection allowedProducts) { + return new ProductMixData(null, null, restrictedProducts, allowedProducts, null); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/domain/ProductMix.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/domain/ProductMix.java new file mode 100644 index 000000000..bffc37936 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/domain/ProductMix.java @@ -0,0 +1,44 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.domain; + +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_product_mix") +public class ProductMix extends AbstractPersistable { + + @ManyToOne + @JoinColumn(name = "product_id", nullable = false) + private LoanProduct product; + + @ManyToOne + @JoinColumn(name = "restricted_product_id", nullable = false) + private LoanProduct restrictedProduct; + + public ProductMix() { + // + } + + private ProductMix(final LoanProduct product, final LoanProduct restrictedProduct) { + this.product = product; + this.restrictedProduct = restrictedProduct; + } + + public static ProductMix createNew(final LoanProduct product, final LoanProduct restrictedProduct) { + return new ProductMix(product, restrictedProduct); + } + + public Long getRestrictedProductId() { + return this.restrictedProduct.getId(); + } + + public Long getProductId() { + return this.product.getId(); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/domain/ProductMixRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/domain/ProductMixRepository.java new file mode 100644 index 000000000..df94d515a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/domain/ProductMixRepository.java @@ -0,0 +1,25 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.domain; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface ProductMixRepository extends JpaRepository, JpaSpecificationExecutor { + + public static final String GET_PRODUCTMIXES_BY_PRODUCTID_SCHEMA = "from ProductMix pm where pm.product.id = :productId"; + public static final String GET_RESTRICTED_PRODUCTIDS_SCHEMA = "Select pm.restrictedProduct.id from ProductMix pm where pm.product.id = :productId"; + public static final String GET_RESTRICTED_PRODUCTS_SCHEMA = "from ProductMix pm where pm.restrictedProduct.id = :restrictedProductId"; + + @Query(GET_PRODUCTMIXES_BY_PRODUCTID_SCHEMA) + List findByProductId(@Param("productId") Long productId); + + @Query(GET_RESTRICTED_PRODUCTIDS_SCHEMA) + List findRestrictedProductIdsByProductId(@Param("productId") Long productId); + + @Query(GET_RESTRICTED_PRODUCTS_SCHEMA) + List findRestrictedProducts(@Param("restrictedProductId") Long restrictedProductId); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/exception/ProductMixNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/exception/ProductMixNotFoundException.java new file mode 100644 index 000000000..9b6a5c3fd --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/exception/ProductMixNotFoundException.java @@ -0,0 +1,15 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when no product mixes found with the + * productId. + */ +public class ProductMixNotFoundException extends AbstractPlatformResourceNotFoundException { + + public ProductMixNotFoundException(final Long productId) { + super("error.msg.no.product.mixes.exists", "No product mixes are defined with the productId `" + productId + "`.", productId); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/CreateProductMixCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/CreateProductMixCommandHandler.java new file mode 100644 index 000000000..063665027 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/CreateProductMixCommandHandler.java @@ -0,0 +1,29 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.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.loanproduct.productmix.service.ProductMixWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CreateProductMixCommandHandler implements NewCommandSourceHandler { + + private final ProductMixWritePlatformService productMixWritePlatformService; + + @Autowired + public CreateProductMixCommandHandler(final ProductMixWritePlatformService productMixWritePlatformService) { + + this.productMixWritePlatformService = productMixWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.productMixWritePlatformService.createProductMix(command.getProductId(), command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/DeleteProductMixCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/DeleteProductMixCommandHandler.java new file mode 100644 index 000000000..44f8dcc06 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/DeleteProductMixCommandHandler.java @@ -0,0 +1,29 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.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.loanproduct.productmix.service.ProductMixWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DeleteProductMixCommandHandler implements NewCommandSourceHandler { + + private final ProductMixWritePlatformService productMixWritePlatformService; + + @Autowired + public DeleteProductMixCommandHandler(final ProductMixWritePlatformService productMixWritePlatformService) { + + this.productMixWritePlatformService = productMixWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.productMixWritePlatformService.deleteProductMix(command.getProductId()); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/UpdateProductMixCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/UpdateProductMixCommandHandler.java new file mode 100644 index 000000000..749268053 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/handler/UpdateProductMixCommandHandler.java @@ -0,0 +1,29 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.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.loanproduct.productmix.service.ProductMixWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UpdateProductMixCommandHandler implements NewCommandSourceHandler { + + private final ProductMixWritePlatformService productMixWritePlatformService; + + @Autowired + public UpdateProductMixCommandHandler(final ProductMixWritePlatformService productMixWritePlatformService) { + + this.productMixWritePlatformService = productMixWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.productMixWritePlatformService.updateProductMix(command.getProductId(), command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/serialization/ProductMixDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/serialization/ProductMixDataValidator.java new file mode 100644 index 000000000..1230b8126 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/serialization/ProductMixDataValidator.java @@ -0,0 +1,87 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.serialization; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.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 ProductMixDataValidator { + + /** + * The parameters supported for this command. + */ + private final Set supportedParameters = new HashSet(Arrays.asList("restrictedProducts")); + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public ProductMixDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validateForCreate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("productmix"); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final String[] restrictedProducts = this.fromApiJsonHelper.extractArrayNamed("restrictedProducts", element); + baseDataValidator.reset().parameter("restrictedProducts").value(restrictedProducts).arrayNotEmpty(); + if (restrictedProducts != null) { + validateRestrictedProducts(restrictedProducts, baseDataValidator); + } + + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void validateRestrictedProducts(final String[] restrictedProducts, final DataValidatorBuilder baseDataValidator) { + for (final String restrictedId : restrictedProducts) { + baseDataValidator.reset().parameter("restrictedProduct").value(restrictedId).notBlank().longGreaterThanZero(); + } + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + + public void validateForUpdate(String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("productmix"); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + final String[] restrictedProducts = this.fromApiJsonHelper.extractArrayNamed("restrictedProducts", element); + validateRestrictedProducts(restrictedProducts, baseDataValidator); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixReadPlatformService.java new file mode 100644 index 000000000..a80fa9851 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixReadPlatformService.java @@ -0,0 +1,13 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.service; + +import java.util.Collection; + +import org.mifosplatform.portfolio.loanproduct.productmix.data.ProductMixData; + + +public interface ProductMixReadPlatformService { + + ProductMixData retrieveLoanProductMixDetails(Long productId); + + Collection retrieveAllProductMixes(); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixReadPlatformServiceImpl.java new file mode 100644 index 000000000..19e2416bb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixReadPlatformServiceImpl.java @@ -0,0 +1,109 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; +import org.mifosplatform.portfolio.loanproduct.productmix.data.ProductMixData; +import org.mifosplatform.portfolio.loanproduct.productmix.exception.ProductMixNotFoundException; +import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.stereotype.Service; + +@Service +public class ProductMixReadPlatformServiceImpl implements ProductMixReadPlatformService { + + private final PlatformSecurityContext context; + private final JdbcTemplate jdbcTemplate; + private final LoanProductReadPlatformService loanProductReadPlatformService; + + @Autowired + public ProductMixReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, + final LoanProductReadPlatformService loanProductReadPlatformService) { + this.context = context; + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.loanProductReadPlatformService = loanProductReadPlatformService; + } + + @Override + public ProductMixData retrieveLoanProductMixDetails(final Long productId) { + try { + + this.context.authenticatedUser(); + + final ProductMixDataExtractor extractor = new ProductMixDataExtractor(this.loanProductReadPlatformService, productId); + + final String sql = "Select " + extractor.schema() + " where pm.product_id=? group by pm.product_id"; + + final Map productMixData = this.jdbcTemplate.query(sql, extractor, new Object[] { productId }); + + return productMixData.get(productId); + + } catch (final EmptyResultDataAccessException e) { + throw new ProductMixNotFoundException(productId); + } + } + + @Override + public Collection retrieveAllProductMixes() { + + this.context.authenticatedUser(); + + final ProductMixDataExtractor extractor = new ProductMixDataExtractor(this.loanProductReadPlatformService, null); + + final String sql = "Select " + extractor.schema() + " group by pm.product_id"; + + final Map productMixData = this.jdbcTemplate.query(sql, extractor, new Object[] {}); + + return productMixData.values(); + } + + private static final class ProductMixDataExtractor implements ResultSetExtractor> { + + private final LoanProductReadPlatformService loanProductReadPlatformService; + private final Long productId; + + public String schema() { + return "pm.product_id as productId, lp.name as name from m_product_mix pm join m_product_loan lp on lp.id=pm.product_id"; + } + + public ProductMixDataExtractor(final LoanProductReadPlatformService loanProductReadPlatformService, final Long productId) { + this.loanProductReadPlatformService = loanProductReadPlatformService; + this.productId = productId; + } + + @Override + public Map extractData(final ResultSet rs) throws SQLException, DataAccessException { + final Map extractedData = new HashMap(); + + if (!rs.next()) { + final Collection restrictedProducts = this.loanProductReadPlatformService.retrieveRestrictedProductsForMix(this.productId); + final Collection allowedProducts = this.loanProductReadPlatformService.retrieveAllowedProductsForMix(this.productId); + ProductMixData productMixData = ProductMixData.withRestrictedOptions(restrictedProducts, allowedProducts); + extractedData.put(this.productId, productMixData); + return extractedData; + } + /* move the cursor to starting of resultset */ + rs.beforeFirst(); + while (rs.next()) { + final Long productId = rs.getLong("productId"); + final String name = rs.getString("name"); + final Collection restrictedProducts = this.loanProductReadPlatformService.retrieveRestrictedProductsForMix(productId); + final Collection allowedProducts = this.loanProductReadPlatformService.retrieveAllowedProductsForMix(productId); + final ProductMixData productMixData = ProductMixData.withDetails(productId, name, restrictedProducts, allowedProducts); + extractedData.put(productId, productMixData); + } + return extractedData; + } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixWritePlatformService.java new file mode 100644 index 000000000..93c7554b8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixWritePlatformService.java @@ -0,0 +1,14 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface ProductMixWritePlatformService { + + CommandProcessingResult createProductMix(Long productId, JsonCommand command); + + CommandProcessingResult updateProductMix(Long productId, JsonCommand command); + + CommandProcessingResult deleteProductMix(Long productId); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..8ddef46f1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/productmix/service/ProductMixWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,225 @@ +package org.mifosplatform.portfolio.loanproduct.productmix.service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; +import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository; +import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException; +import org.mifosplatform.portfolio.loanproduct.productmix.domain.ProductMix; +import org.mifosplatform.portfolio.loanproduct.productmix.domain.ProductMixRepository; +import org.mifosplatform.portfolio.loanproduct.productmix.exception.ProductMixNotFoundException; +import org.mifosplatform.portfolio.loanproduct.productmix.serialization.ProductMixDataValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +@Service +public class ProductMixWritePlatformServiceJpaRepositoryImpl implements ProductMixWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(ProductMixWritePlatformServiceJpaRepositoryImpl.class); + private final PlatformSecurityContext context; + private final ProductMixDataValidator fromApiJsonDeserializer; + private final ProductMixRepository productMixRepository; + private final LoanProductRepository productRepository; + + @Autowired + public ProductMixWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, + final ProductMixDataValidator fromApiJsonDeserializer, final ProductMixRepository productMixRepository, + final LoanProductRepository productRepository) { + this.context = context; + this.fromApiJsonDeserializer = fromApiJsonDeserializer; + this.productMixRepository = productMixRepository; + this.productRepository = productRepository; + } + + @Transactional + @Override + public CommandProcessingResult createProductMix(final Long productId, final JsonCommand command) { + + try { + + this.context.authenticatedUser(); + + this.fromApiJsonDeserializer.validateForCreate(command.json()); + + final Set restrictedIds = new HashSet(Arrays.asList(command.arrayValueOfParameterNamed("restrictedProducts"))); + + // remove the existed restriction if it is not exists in restrictedIds. + final List removedRestrictions = updateRestrictionsForProduct(productId, restrictedIds); + final Map restrictedProductsAsMap = getRestrictedProducts(restrictedIds); + final List productMixes = new ArrayList(); + + createNewProductMix(restrictedProductsAsMap, productId, productMixes); + + this.productMixRepository.save(productMixes); + + final Map changes = new LinkedHashMap(); + changes.put("restrictedProductsForMix", restrictedProductsAsMap.keySet()); + changes.put("removedProductsForMix", removedRestrictions); + return new CommandProcessingResultBuilder().withProductId(productId).with(changes).withCommandId(command.commandId()).build(); + } catch (final DataIntegrityViolationException dve) { + + handleDataIntegrityIssues(dve); + return CommandProcessingResult.empty(); + } + } + + private List updateRestrictionsForProduct(final Long productId, final Set restrictedIds) { + + final List removedRestrictions = new ArrayList(); + final List mixesToRemove = new ArrayList(); + + final List existedProductMixes = this.productMixRepository.findRestrictedProducts(productId); + for (final ProductMix productMix : existedProductMixes) { + if (!restrictedIds.contains(productMix.getProductId().toString())) { + mixesToRemove.add(productMix); + removedRestrictions.add(productMix.getId()); + } + } + if (!CollectionUtils.isEmpty(mixesToRemove)) { + this.productMixRepository.delete(mixesToRemove); + } + return removedRestrictions; + } + + private void createNewProductMix(final Map restrictedProductsAsMap, final Long productId, + final List productMixes) { + + final LoanProduct productMixInstance = findByProductIdIfProvided(productId); + for (final LoanProduct restrictedProduct : restrictedProductsAsMap.values()) { + final ProductMix productMix = ProductMix.createNew(productMixInstance, restrictedProduct); + productMixes.add(productMix); + } + } + + @Override + public CommandProcessingResult updateProductMix(final Long productId, final JsonCommand command) { + + try { + this.context.authenticatedUser(); + this.fromApiJsonDeserializer.validateForUpdate(command.json()); + final Map changes = new LinkedHashMap(); + + final List existedProductMixes = this.productMixRepository.findByProductId(productId); + if (CollectionUtils.isEmpty(existedProductMixes)) { throw new ProductMixNotFoundException(productId); } + final Set restrictedIds = new HashSet(Arrays.asList(command.arrayValueOfParameterNamed("restrictedProducts"))); + + // updating with empty array means deleting the existed records. + if (restrictedIds.isEmpty()) { + final List removedRestrictedProductIds = this.productMixRepository.findRestrictedProductIdsByProductId(productId); + this.productMixRepository.delete(existedProductMixes); + changes.put("removedProductsForMix", removedRestrictedProductIds); + return new CommandProcessingResultBuilder().with(changes).withProductId(productId).withCommandId(command.commandId()) + .build(); + } + + /* + * if restrictedProducts array is not empty delete the duplicate ids + * which are already exists and update existedProductMixes + */ + final List productMixesToRemove = updateRestrictedIds(restrictedIds, existedProductMixes); + final Map restrictedProductsAsMap = getRestrictedProducts(restrictedIds); + createNewProductMix(restrictedProductsAsMap, productId, existedProductMixes); + + this.productMixRepository.save(existedProductMixes); + changes.put("restrictedProductsForMix", getProductIdsFromCollection(existedProductMixes)); + + if (!CollectionUtils.isEmpty(productMixesToRemove)) { + this.productMixRepository.delete(productMixesToRemove); + changes.put("removedProductsForMix", getProductIdsFromCollection(productMixesToRemove)); + } + return new CommandProcessingResultBuilder().with(changes).withProductId(productId).withCommandId(command.commandId()).build(); + } catch (final DataIntegrityViolationException dve) { + + handleDataIntegrityIssues(dve); + return CommandProcessingResult.empty(); + } + } + + private LoanProduct findByProductIdIfProvided(final Long productId) { + + final LoanProduct product = this.productRepository.findOne(productId); + if (product == null) { throw new LoanProductNotFoundException(productId); } + return product; + } + + private Map getRestrictedProducts(final Set restrictedIds) { + + final Map restricrtedProducts = new HashMap(); + + for (final String restrictedId : restrictedIds) { + final Long restrictedIdAsLong = Long.valueOf(restrictedId); + final LoanProduct restrictedProduct = findByProductIdIfProvided(Long.valueOf(restrictedId)); + restricrtedProducts.put(restrictedIdAsLong, restrictedProduct); + } + return restricrtedProducts; + } + + private void handleDataIntegrityIssues(final DataIntegrityViolationException dve) { + logAsErrorUnexpectedDataIntegrityException(dve); + throw new PlatformDataIntegrityException("error.msg.product.loan.unknown.data.integrity.issue", + "Unknown data integrity issue with resource."); + } + + private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) { + logger.error(dve.getMessage(), dve); + } + + private List updateRestrictedIds(final Set restrictedIds, final List existedProductMixes) { + + final List productMixesToRemove = new ArrayList(); + for (final ProductMix productMix : existedProductMixes) { + final String currentMixId = productMix.getRestrictedProductId().toString(); + if (restrictedIds.contains(currentMixId)) { + restrictedIds.remove(currentMixId); + } else { + productMixesToRemove.add(productMix); + } + } + existedProductMixes.removeAll(productMixesToRemove); + return productMixesToRemove; + } + + @Override + public CommandProcessingResult deleteProductMix(final Long productId) { + try { + this.context.authenticatedUser(); + final Map changes = new LinkedHashMap(); + + final List existedProductMixes = this.productMixRepository.findByProductId(productId); + if (CollectionUtils.isEmpty(existedProductMixes)) { throw new ProductMixNotFoundException(productId); } + this.productMixRepository.delete(existedProductMixes); + changes.put("removedProductsForMix", getProductIdsFromCollection(existedProductMixes)); + return new CommandProcessingResultBuilder().with(changes).withProductId(productId).build(); + } catch (final DataIntegrityViolationException dve) { + + handleDataIntegrityIssues(dve); + return CommandProcessingResult.empty(); + } + } + + private List getProductIdsFromCollection(final List collection) { + final List productIds = new ArrayList(); + for (ProductMix productMix : collection) { + productIds.add(productMix.getRestrictedProductId()); + } + return productIds; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java index 91279ffbf..5f33ab8d2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java @@ -18,4 +18,10 @@ public interface LoanProductReadPlatformService { LoanProductData retrieveLoanProduct(Long productId); LoanProductData retrieveNewLoanProductDetails(); + + Collection retrieveAvailableLoanProductsForMix(); + + Collection retrieveRestrictedProductsForMix(Long productId); + + Collection retrieveAllowedProductsForMix(Long productId); } \ No newline at end of file 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 49e1a28eb..5ba844d04 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 @@ -197,6 +197,19 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo return "lp.id as id, lp.name as name from m_product_loan lp"; } + public String productMixSchema() { + 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"; + } + @Override public LoanProductData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { @@ -206,4 +219,44 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo return LoanProductData.lookup(id, name); } } + + @Override + public Collection retrieveAvailableLoanProductsForMix() { + + this.context.authenticatedUser(); + + final LoanProductLookupMapper rm = new LoanProductLookupMapper(); + + final String sql = "Select " + rm.productMixSchema(); + + return this.jdbcTemplate.query(sql, rm, new Object[] {}); + } + + @Override + public Collection retrieveRestrictedProductsForMix(final Long productId) { + + this.context.authenticatedUser(); + + final LoanProductLookupMapper rm = new LoanProductLookupMapper(); + + final String sql = "Select " + rm.restrictedProductsSchema() + " where pm.product_id=? UNION Select " + + rm.derivedRestrictedProductsSchema() + " where pm.restricted_product_id=?"; + + return this.jdbcTemplate.query(sql, rm, new Object[] { productId, productId }); + } + + @Override + public Collection retrieveAllowedProductsForMix(final Long productId) { + + this.context.authenticatedUser(); + + final LoanProductLookupMapper rm = new LoanProductLookupMapper(); + + final String sql = "Select " + rm.schema() + " where lp.id not in (" + + "Select pm.restricted_product_id from m_product_mix pm where pm.product_id=? " + "UNION " + + "Select pm.product_id from m_product_mix pm where pm.restricted_product_id=?)"; + + return this.jdbcTemplate.query(sql, rm, new Object[] { productId, productId }); + } + } \ No newline at end of file From 6f63a33ef406a0a034c9874030e35cb5c15cc92b Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Fri, 19 Jul 2013 13:54:15 +0530 Subject: [PATCH 2/2] incrementing version number of product mix db patch --- ...x_related_changes.sql => V85__product_mix_related_changes.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mifosng-db/migrations/core_db/{V84__product_mix_related_changes.sql => V85__product_mix_related_changes.sql} (100%) diff --git a/mifosng-db/migrations/core_db/V84__product_mix_related_changes.sql b/mifosng-db/migrations/core_db/V85__product_mix_related_changes.sql similarity index 100% rename from mifosng-db/migrations/core_db/V84__product_mix_related_changes.sql rename to mifosng-db/migrations/core_db/V85__product_mix_related_changes.sql