Adding unique constraint on Loan Product name
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
UPDATE m_product_loan SET name = CONCAT(id,'-',name); -- concating unique value to make sure column does not contain any duplicate value
|
||||||
|
ALTER TABLE m_product_loan
|
||||||
|
ADD CONSTRAINT unq_name UNIQUE (name);
|
||||||
+3
-2
@@ -14,6 +14,7 @@ import javax.persistence.JoinTable;
|
|||||||
import javax.persistence.ManyToMany;
|
import javax.persistence.ManyToMany;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.UniqueConstraint;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
@@ -33,7 +34,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
|
|||||||
* They allow for constraints to be added at product level.
|
* They allow for constraints to be added at product level.
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "m_product_loan")
|
@Table(name = "m_product_loan" , uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name")})
|
||||||
public class LoanProduct extends AbstractPersistable<Long> {
|
public class LoanProduct extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@@ -44,7 +45,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
|
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
|
||||||
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
|
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
|
||||||
|
|
||||||
@Column(name = "name", nullable = false)
|
@Column(name = "name", nullable = false , unique = true)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Column(name = "description")
|
@Column(name = "description")
|
||||||
|
|||||||
+114
-53
@@ -8,6 +8,7 @@ import org.mifosplatform.accounting.service.ProductToGLAccountMappingWritePlatfo
|
|||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepository;
|
import org.mifosplatform.portfolio.charge.domain.ChargeRepository;
|
||||||
@@ -26,14 +27,18 @@ import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyExceptio
|
|||||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||||
import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductCommandFromApiJsonDeserializer;
|
import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductCommandFromApiJsonDeserializer;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService {
|
public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService {
|
||||||
|
|
||||||
private final PlatformSecurityContext context;
|
private final static Logger logger = LoggerFactory.getLogger(LoanProductWritePlatformServiceJpaRepositoryImpl.class);
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
private final LoanProductCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
private final LoanProductCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
||||||
private final LoanProductRepository loanProductRepository;
|
private final LoanProductRepository loanProductRepository;
|
||||||
private final AprCalculator aprCalculator;
|
private final AprCalculator aprCalculator;
|
||||||
@@ -62,32 +67,48 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
|||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult createLoanProduct(final JsonCommand command) {
|
public CommandProcessingResult createLoanProduct(final JsonCommand command) {
|
||||||
|
|
||||||
this.context.authenticatedUser();
|
try {
|
||||||
|
|
||||||
this.fromApiJsonDeserializer.validateForCreate(command.json());
|
this.context.authenticatedUser();
|
||||||
|
|
||||||
// associating fund with loan product at creation is optional for now.
|
this.fromApiJsonDeserializer.validateForCreate(command.json());
|
||||||
final Fund fund = findFundByIdIfProvided(command.longValueOfParameterNamed("fundId"));
|
|
||||||
|
|
||||||
final Long transactionProcessingStrategyId = command.longValueOfParameterNamed("transactionProcessingStrategyId");
|
// associating fund with loan product at creation is optional for
|
||||||
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
|
// now.
|
||||||
|
final Fund fund = findFundByIdIfProvided(command
|
||||||
|
.longValueOfParameterNamed("fundId"));
|
||||||
|
|
||||||
final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
|
final Long transactionProcessingStrategyId = command
|
||||||
final Set<Charge> charges = this.assembleSetOfCharges(command, currencyCode);
|
.longValueOfParameterNamed("transactionProcessingStrategyId");
|
||||||
|
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
|
||||||
|
|
||||||
final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command,
|
final String currencyCode = command
|
||||||
this.aprCalculator);
|
.stringValueOfParameterNamed("currencyCode");
|
||||||
|
final Set<Charge> charges = this.assembleSetOfCharges(command,
|
||||||
|
currencyCode);
|
||||||
|
|
||||||
this.loanProductRepository.save(loanproduct);
|
final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund,
|
||||||
|
loanTransactionProcessingStrategy, charges, command,
|
||||||
|
this.aprCalculator);
|
||||||
|
|
||||||
// save accounting mappings
|
this.loanProductRepository.save(loanproduct);
|
||||||
accountMappingWritePlatformService.createLoanProductToGLAccountMapping(loanproduct.getId(), command);
|
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder() //
|
// save accounting mappings
|
||||||
.withCommandId(command.commandId()) //
|
accountMappingWritePlatformService
|
||||||
.withEntityId(loanproduct.getId()) //
|
.createLoanProductToGLAccountMapping(loanproduct.getId(),
|
||||||
.build();
|
command);
|
||||||
}
|
|
||||||
|
return new CommandProcessingResultBuilder() //
|
||||||
|
.withCommandId(command.commandId()) //
|
||||||
|
.withEntityId(loanproduct.getId()) //
|
||||||
|
.build();
|
||||||
|
|
||||||
|
} catch (DataIntegrityViolationException dve) {
|
||||||
|
handleDataIntegrityIssues(command, dve);
|
||||||
|
return new CommandProcessingResult(Long.valueOf(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
|
private LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
|
||||||
LoanTransactionProcessingStrategy strategy = null;
|
LoanTransactionProcessingStrategy strategy = null;
|
||||||
@@ -111,49 +132,67 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
|||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult updateLoanProduct(final Long loanProductId, final JsonCommand command) {
|
public CommandProcessingResult updateLoanProduct(final Long loanProductId, final JsonCommand command) {
|
||||||
|
|
||||||
this.context.authenticatedUser();
|
try {
|
||||||
|
|
||||||
this.fromApiJsonDeserializer.validateForUpdate(command.json());
|
this.context.authenticatedUser();
|
||||||
|
|
||||||
final LoanProduct product = this.loanProductRepository.findOne(loanProductId);
|
this.fromApiJsonDeserializer.validateForUpdate(command.json());
|
||||||
if (product == null) { throw new LoanProductNotFoundException(loanProductId); }
|
|
||||||
|
|
||||||
final Map<String, Object> changes = product.update(command, this.aprCalculator);
|
final LoanProduct product = this.loanProductRepository
|
||||||
|
.findOne(loanProductId);
|
||||||
|
if (product == null) {
|
||||||
|
throw new LoanProductNotFoundException(loanProductId);
|
||||||
|
}
|
||||||
|
|
||||||
// associating fund with loan product at creation is optional for now.
|
final Map<String, Object> changes = product.update(command,
|
||||||
if (changes.containsKey("fundId")) {
|
this.aprCalculator);
|
||||||
final Long fundId = (Long) changes.get("fundId");
|
|
||||||
final Fund fund = findFundByIdIfProvided(fundId);
|
|
||||||
product.update(fund);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.containsKey("transactionProcessingStrategyId")) {
|
// associating fund with loan product at creation is optional for
|
||||||
final Long transactionProcessingStrategyId = (Long) changes.get("transactionProcessingStrategyId");
|
// now.
|
||||||
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
|
if (changes.containsKey("fundId")) {
|
||||||
product.update(loanTransactionProcessingStrategy);
|
final Long fundId = (Long) changes.get("fundId");
|
||||||
}
|
final Fund fund = findFundByIdIfProvided(fundId);
|
||||||
|
product.update(fund);
|
||||||
|
}
|
||||||
|
|
||||||
if (changes.containsKey("charges")) {
|
if (changes.containsKey("transactionProcessingStrategyId")) {
|
||||||
final Set<Charge> charges = this.assembleSetOfCharges(command, product.getCurrency().getCode());
|
final Long transactionProcessingStrategyId = (Long) changes
|
||||||
product.update(charges);
|
.get("transactionProcessingStrategyId");
|
||||||
}
|
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
|
||||||
|
product.update(loanTransactionProcessingStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
// accounting related changes
|
if (changes.containsKey("charges")) {
|
||||||
boolean accountingTypeChanged = changes.containsKey("accountingType");
|
final Set<Charge> charges = this.assembleSetOfCharges(command,
|
||||||
if (accountingTypeChanged) {
|
product.getCurrency().getCode());
|
||||||
accountMappingWritePlatformService.updateLoanProductToGLAccountMapping(product.getId(), command, accountingTypeChanged);
|
product.update(charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!changes.isEmpty()) {
|
// accounting related changes
|
||||||
this.loanProductRepository.save(product);
|
boolean accountingTypeChanged = changes
|
||||||
}
|
.containsKey("accountingType");
|
||||||
|
if (accountingTypeChanged) {
|
||||||
|
accountMappingWritePlatformService
|
||||||
|
.updateLoanProductToGLAccountMapping(product.getId(),
|
||||||
|
command, accountingTypeChanged);
|
||||||
|
}
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder() //
|
if (!changes.isEmpty()) {
|
||||||
.withCommandId(command.commandId()) //
|
this.loanProductRepository.save(product);
|
||||||
.withEntityId(loanProductId) //
|
}
|
||||||
.with(changes) //
|
|
||||||
.build();
|
return new CommandProcessingResultBuilder() //
|
||||||
}
|
.withCommandId(command.commandId()) //
|
||||||
|
.withEntityId(loanProductId) //
|
||||||
|
.with(changes) //
|
||||||
|
.build();
|
||||||
|
|
||||||
|
} catch (DataIntegrityViolationException dve) {
|
||||||
|
handleDataIntegrityIssues(command, dve);
|
||||||
|
return new CommandProcessingResult(Long.valueOf(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private Set<Charge> assembleSetOfCharges(final JsonCommand command, final String currencyCode) {
|
private Set<Charge> assembleSetOfCharges(final JsonCommand command, final String currencyCode) {
|
||||||
|
|
||||||
@@ -183,5 +222,27 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
|||||||
|
|
||||||
return charges;
|
return charges;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Guaranteed to throw an exception no matter what the data integrity issue
|
||||||
|
* is.
|
||||||
|
*/
|
||||||
|
private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
|
||||||
|
|
||||||
|
Throwable realCause = dve.getMostSpecificCause();
|
||||||
|
if (realCause.getMessage().contains("unq_name")) {
|
||||||
|
|
||||||
|
final String name = command.stringValueOfParameterNamed("name");
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.name", "Loan product with name `" + name
|
||||||
|
+ "` already exists", "name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user