Adding unique constraint on Loan Product name

This commit is contained in:
Nayan Ambali
2013-02-06 00:23:29 +05:30
parent e7819f6c25
commit 22ea9d4dff
3 changed files with 120 additions and 55 deletions
@@ -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);
@@ -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")
@@ -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);
}
} }