/** * 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.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<String> restrictedIds = new HashSet<>(Arrays.asList(command.arrayValueOfParameterNamed("restrictedProducts"))); // remove the existed restriction if it is not exists in // restrictedIds. final List<Long> removedRestrictions = updateRestrictionsForProduct(productId, restrictedIds); final Map<Long, LoanProduct> restrictedProductsAsMap = getRestrictedProducts(restrictedIds); final List<ProductMix> productMixes = new ArrayList<>(); createNewProductMix(restrictedProductsAsMap, productId, productMixes); this.productMixRepository.save(productMixes); final Map<String, Object> 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<Long> updateRestrictionsForProduct(final Long productId, final Set<String> restrictedIds) { final List<Long> removedRestrictions = new ArrayList<>(); final List<ProductMix> mixesToRemove = new ArrayList<>(); final List<ProductMix> 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<Long, LoanProduct> restrictedProductsAsMap, final Long productId, final List<ProductMix> 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<String, Object> changes = new LinkedHashMap<>(); final List<ProductMix> existedProductMixes = this.productMixRepository.findByProductId(productId); if (CollectionUtils.isEmpty(existedProductMixes)) { throw new ProductMixNotFoundException(productId); } final Set<String> restrictedIds = new HashSet<>(Arrays.asList(command.arrayValueOfParameterNamed("restrictedProducts"))); // updating with empty array means deleting the existed records. if (restrictedIds.isEmpty()) { final List<Long> 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<ProductMix> productMixesToRemove = updateRestrictedIds(restrictedIds, existedProductMixes); final Map<Long, LoanProduct> 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<Long, LoanProduct> getRestrictedProducts(final Set<String> restrictedIds) { final Map<Long, LoanProduct> 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<ProductMix> updateRestrictedIds(final Set<String> restrictedIds, final List<ProductMix> existedProductMixes) { final List<ProductMix> 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<String, Object> changes = new LinkedHashMap<>(); final List<ProductMix> 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<Long> getProductIdsFromCollection(final List<ProductMix> collection) { final List<Long> productIds = new ArrayList<>(); for (final ProductMix productMix : collection) { productIds.add(productMix.getRestrictedProductId()); } return productIds; } }