diff --git a/mifosng-db/patches/patch-0001-deposit-account-table.sql b/mifosng-db/patches/patch-0001-deposit-account-table.sql index ea5693489..9fab5e717 100644 --- a/mifosng-db/patches/patch-0001-deposit-account-table.sql +++ b/mifosng-db/patches/patch-0001-deposit-account-table.sql @@ -18,8 +18,8 @@ CREATE TABLE `portfolio_deposit_account` ( `actual_maturity_date` DATE DEFAULT NULL, `projected_interest_accrued_on_maturity` decimal(19,6) NOT NULL, `actual_interest_accrued` decimal(19,6) DEFAULT NULL, - `projected_total_return_on_maturity_amount` decimal(19,6) NOT NULL, - `actual_total_return` decimal(19,6) DEFAULT NULL, + `projected_total_maturity_amount` decimal(19,6) NOT NULL, + `actual_total_amount` decimal(19,6) DEFAULT NULL, `can_renew` tinyint(1) NOT NULL DEFAULT '0', `renewed_account_id` bigint(20) DEFAULT NULL, `can_pre_close` tinyint(1) NOT NULL DEFAULT '0', diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/DepositAccountCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/DepositAccountCommand.java index abcc747a6..248fe7481 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/DepositAccountCommand.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/DepositAccountCommand.java @@ -3,6 +3,8 @@ package org.mifosng.platform.api.commands; import java.math.BigDecimal; import java.util.Set; +import org.joda.time.LocalDate; + /** * Immutable command used when create/renewing deposit accounts */ @@ -13,12 +15,14 @@ public class DepositAccountCommand { private final Long productId; private final String externalId; - private final String currencyCode; - private final Integer digitsAfterDecimal; private final BigDecimal depositAmount; private final BigDecimal maturityInterestRate; private final Integer termInMonths; + private final Integer interestCompoundedEvery; + private final Integer interestCompoundedEveryPeriodType; + private final LocalDate commencementDate; + private final Set modifiedParameters; public DepositAccountCommand(final Set modifiedParameters, @@ -26,23 +30,22 @@ public class DepositAccountCommand { final Long clientId, final Long productId, final String externalId, - final String currencyCode, - final Integer digitsAfterDecimal, final BigDecimal depositAmount, final BigDecimal interestRate, - final Integer termInMonths) { + final Integer termInMonths, final Integer compoundingInterestFrequency, final Integer compoundingInterestFrequencyType, final LocalDate commencementDate) { this.id = id; this.clientId = clientId; this.productId = productId; this.externalId = externalId; - this.currencyCode = currencyCode; - this.digitsAfterDecimal = digitsAfterDecimal; this.depositAmount = depositAmount; this.maturityInterestRate = interestRate; this.termInMonths = termInMonths; this.modifiedParameters = modifiedParameters; + this.interestCompoundedEvery = compoundingInterestFrequency; + this.interestCompoundedEveryPeriodType = compoundingInterestFrequencyType; + this.commencementDate = commencementDate; } public Long getId() { @@ -61,14 +64,6 @@ public class DepositAccountCommand { return externalId; } - public String getCurrencyCode() { - return currencyCode; - } - - public Integer getDigitsAfterDecimal() { - return digitsAfterDecimal; - } - public BigDecimal getDepositAmount() { return depositAmount; } @@ -80,6 +75,22 @@ public class DepositAccountCommand { public Integer getTermInMonths() { return termInMonths; } + + public Integer getInterestCompoundedEvery() { + return interestCompoundedEvery; + } + + public Integer getInterestCompoundedEveryPeriodType() { + return interestCompoundedEveryPeriodType; + } + + public LocalDate getCommencementDate() { + return commencementDate; + } + + public boolean isNoFieldChanged() { + return this.modifiedParameters.isEmpty(); + } public boolean isCurrencyCodeChanged() { return this.modifiedParameters.contains("currencyCode"); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java index 83762f12f..bb4b2d2d3 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java @@ -1493,7 +1493,9 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { Map requestMap = gsonConverter.fromJson(json, typeOfMap); Set supportedParams = new HashSet( - Arrays.asList("clientId", "productId", "externalId", "currencyCode", "digitsAfterDecimal", "depositAmount", "maturityInterestRate", "termInMonths", "locale") + Arrays.asList("clientId", "productId", "externalId", "depositAmount", "maturityInterestRate", + "termInMonths", "interestCompoundedEvery", "interestCompoundedEveryPeriodType", "commencementDate", + "locale", "dateFormat") ); checkForUnsupportedParameters(requestMap, supportedParams); Set modifiedParameters = new HashSet(); @@ -1501,15 +1503,19 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { Long clientId = extractLongParameter("clientId", requestMap, modifiedParameters); Long productId = extractLongParameter("productId", requestMap, modifiedParameters); String externalId = extractStringParameter("externalId", requestMap,modifiedParameters); - String currencyCode=extractStringParameter("currencyCode", requestMap,modifiedParameters); - Integer digitsAfterDecimalValue = extractIntegerParameter("digitsAfterDecimal", requestMap, modifiedParameters); BigDecimal depositAmount=extractBigDecimalParameter("depositAmount", requestMap, modifiedParameters); BigDecimal interestRate = extractBigDecimalParameter("maturityInterestRate", requestMap, modifiedParameters); Integer termInMonths = extractIntegerParameter("termInMonths", requestMap, modifiedParameters); - // isRenewable, isPreClosureAllowed, expectedDepositDate, pojectMaturityDate, projectedInterestAccrued + Integer interestCompoundedEvery = extractIntegerParameter("interestCompoundedEvery", requestMap, modifiedParameters); + Integer interestCompoundedEveryPeriodType = extractIntegerParameter("interestCompoundedEveryPeriodType", requestMap, modifiedParameters); + LocalDate commencementDate = extractLocalDateParameter("commencementDate", requestMap, modifiedParameters); - return new DepositAccountCommand(modifiedParameters, resourceIdentifier, clientId, productId, externalId, currencyCode, digitsAfterDecimalValue, depositAmount, interestRate, termInMonths); + // TODO - isRenewable, isPreClosureAllowed + + return new DepositAccountCommand(modifiedParameters, resourceIdentifier, clientId, productId, + externalId, depositAmount, interestRate, termInMonths, + interestCompoundedEvery, interestCompoundedEveryPeriodType, commencementDate); } @Override diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/deposit/domain/DepositProduct.java b/mifosng-provider/src/main/java/org/mifosng/platform/deposit/domain/DepositProduct.java index f4d61ac69..209d61775 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/deposit/domain/DepositProduct.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/deposit/domain/DepositProduct.java @@ -113,4 +113,8 @@ public class DepositProduct extends AbstractAuditableCustom { public void validateInterestRateInRange(final BigDecimal interestRate) { this.depositProductRelatedDetail.validateInterestRateInRange(interestRate); } + + public void validateDepositInRange(final BigDecimal depositAmount) { + this.depositProductRelatedDetail.validateDepositInRange(depositAmount); + } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/deposit/domain/DepositProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosng/platform/deposit/domain/DepositProductRelatedDetail.java index 6cff1f263..656322216 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/deposit/domain/DepositProductRelatedDetail.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/deposit/domain/DepositProductRelatedDetail.java @@ -8,7 +8,7 @@ import javax.persistence.Embedded; import org.mifosng.platform.api.commands.DepositProductCommand; import org.mifosng.platform.currency.domain.MonetaryCurrency; -import org.mifosng.platform.exceptions.InterestRateOutsideRangeException; +import org.mifosng.platform.exceptions.ValueOutsideRangeException; @Embeddable public class DepositProductRelatedDetail { @@ -169,7 +169,28 @@ public class DepositProductRelatedDetail { } if (!inRange) { - throw new InterestRateOutsideRangeException(interestRate, this.maturityMinInterestRate, this.maturityMaxInterestRate); + final String actualValue = interestRate.toPlainString(); + final String minValue = (this.maturityMinInterestRate == null) ? "" : this.maturityMinInterestRate.toPlainString(); + final String maxValue = (this.maturityMaxInterestRate == null) ? "" : this.maturityMaxInterestRate.toPlainString(); + throw new ValueOutsideRangeException(actualValue, minValue, maxValue, "deposit.account.maturityInterestRate"); + } + } + + public void validateDepositInRange(final BigDecimal depositAmount) { + boolean inRange = true; + if (depositAmount.compareTo(this.minimumBalance) < 0) { + inRange = false; + } + + if (this.maximumBalance != null && this.maximumBalance.compareTo(depositAmount) < 0) { + inRange = false; + } + + if (!inRange) { + final String actualValue = depositAmount.toPlainString(); + final String minValue = (this.minimumBalance == null) ? "" : this.minimumBalance.toPlainString(); + final String maxValue = (this.minimumBalance == null) ? "" : this.minimumBalance.toPlainString(); + throw new ValueOutsideRangeException(actualValue, minValue, maxValue, "deposit.account.deposit.amount"); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InterestRateOutsideRangeException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InterestRateOutsideRangeException.java deleted file mode 100644 index ca7d06b57..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InterestRateOutsideRangeException.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.mifosng.platform.exceptions; - -import java.math.BigDecimal; - -/** - * Exception thrown when an attempt is made update the parent of a root office. - */ -public class InterestRateOutsideRangeException extends AbstractPlatformDomainRuleException { - - public InterestRateOutsideRangeException(final BigDecimal interestRate, final BigDecimal rangeMin, final BigDecimal rangeMax) { - super("error.msg.deposit.account.maturityInterestRate.outside.of.allowed.range", "Interest rate of " + interestRate.toPlainString() + " is outside of allowed range of [" - + rangeMin.toPlainString() + ", " + rangeMax.toPlainString() + "].", interestRate, rangeMin, rangeMax); - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ValueOutsideRangeException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ValueOutsideRangeException.java new file mode 100644 index 000000000..e255e117a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ValueOutsideRangeException.java @@ -0,0 +1,13 @@ +package org.mifosng.platform.exceptions; + + +/** + * Exception thrown when an attempt is made update the parent of a root office. + */ +public class ValueOutsideRangeException extends AbstractPlatformDomainRuleException { + + public ValueOutsideRangeException(final String actualValue, final String rangeMin, final String rangeMax, final String fieldCode) { + super("error.msg." + fieldCode + ".outside.of.allowed.range", actualValue + " is outside of allowed range of [" + + rangeMin + ", " + rangeMax + "].", actualValue, rangeMin, rangeMax); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccount.java b/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccount.java index b2cf5484c..29c069c6d 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccount.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/saving/domain/DepositAccount.java @@ -1,78 +1,164 @@ package org.mifosng.platform.saving.domain; import java.math.BigDecimal; +import java.util.Date; import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import javax.persistence.UniqueConstraint; +import org.joda.time.LocalDate; import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.client.domain.Client; import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.deposit.domain.DepositProduct; import org.mifosng.platform.infrastructure.AbstractAuditableCustom; +import org.mifosng.platform.loan.domain.PeriodFrequencyType; import org.mifosng.platform.user.domain.AppUser; @Entity @Table(name = "portfolio_deposit_account", uniqueConstraints = @UniqueConstraint(name="deposit_acc_external_id", columnNames = { "external_id" })) public class DepositAccount extends AbstractAuditableCustom { + @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "client_id", nullable = false) private Client client; + @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "product_id") private final DepositProduct product; + @SuppressWarnings("unused") @Column(name = "external_id") private String externalId; + @SuppressWarnings("unused") @Embedded private MonetaryCurrency currency; @Column(name = "deposit_amount", scale = 6, precision = 19, nullable = false) private BigDecimal depositAmount; - @Column(name = "maturity_interest_rate", scale = 6, precision = 19, nullable = false) + @SuppressWarnings("unused") + @Column(name = "maturity_nominal_interest_rate", scale = 6, precision = 19, nullable = false) private BigDecimal interestRate; @Column(name = "tenure_months", nullable = false) private Integer tenureInMonths; + @SuppressWarnings("unused") + @Column(name = "interest_compounding_frequency", nullable = false) + private Integer interestCompoundedEvery; + + @SuppressWarnings("unused") + @Enumerated(EnumType.ORDINAL) + @Column(name = "interest_compounding_frequency_enum", nullable = false) + private PeriodFrequencyType interestCompoundedFrequencyType; + + @SuppressWarnings("unused") + @Temporal(TemporalType.DATE) + @Column(name = "projected_commencement_date") + private Date projectedCommencementDate; + + @SuppressWarnings("unused") + @Temporal(TemporalType.DATE) + @Column(name = "actual_commencement_date") + private Date actualCommencementDate; + + @SuppressWarnings("unused") + @Temporal(TemporalType.DATE) + @Column(name = "projected_maturity_date") + private Date projectedMaturityDate; + + @SuppressWarnings("unused") + @Temporal(TemporalType.DATE) + @Column(name = "actual_maturity_date") + private Date actualMaturityDate; + + @SuppressWarnings("unused") + @Column(name = "projected_interest_accrued_on_maturity", scale = 6, precision = 19, nullable = false) + private BigDecimal projectedInterestAccruedOnMaturity; + + @SuppressWarnings("unused") + @Column(name = "actual_interest_accrued", scale = 6, precision = 19, nullable = false) + private BigDecimal interestAccrued; + + @SuppressWarnings("unused") + @Column(name = "projected_total_maturity_amount", scale = 6, precision = 19, nullable = false) + private BigDecimal projectedTotalOnMaturity; + + @SuppressWarnings("unused") + @Column(name = "actual_total_amount", scale = 6, precision = 19, nullable = false) + private BigDecimal total; + + @SuppressWarnings("unused") @Column(name = "pre_closure_interest_rate", scale = 6, precision = 19, nullable = false) private BigDecimal preClosureInterestRate; + @SuppressWarnings("unused") @Column(name = "can_renew", nullable = false) - private boolean renewalAllowed; + private boolean renewalAllowed = false; + @SuppressWarnings("unused") @Column(name = "can_pre_close", nullable = false) - private boolean preClosureAllowed; + private boolean preClosureAllowed = false; @Column(name = "is_deleted", nullable=false) private boolean deleted = false; + + public static DepositAccount openNew( + final Client client, final DepositProduct product, + final String externalId, final Money deposit, final BigDecimal interestRate, final Integer termInMonths, + final Integer interestCompoundedEvery, + final PeriodFrequencyType interestCompoundedFrequencyPeriodType, + final LocalDate commencementDate) { + return new DepositAccount(client, product, externalId, deposit, interestRate, termInMonths, + interestCompoundedEvery, interestCompoundedFrequencyPeriodType, commencementDate); + } protected DepositAccount() { this.product = null; } - public DepositAccount( + private DepositAccount( final Client client, final DepositProduct product, - final String externalId, final Money deposit, final BigDecimal interestRate, final Integer termInMonths) { + final String externalId, final Money deposit, final BigDecimal interestRate, final Integer termInMonths, + final Integer interestCompoundedEvery, + final PeriodFrequencyType interestCompoundedFrequencyPeriodType, + final LocalDate commencementDate) { this.client = client; this.product = product; this.externalId = externalId; this.currency = deposit.getCurrency(); this.depositAmount = deposit.getAmount(); + product.validateDepositInRange(this.depositAmount); this.interestRate = interestRate; product.validateInterestRateInRange(interestRate); this.tenureInMonths = termInMonths; + + this.interestCompoundedEvery = interestCompoundedEvery; + this.interestCompoundedFrequencyType = interestCompoundedFrequencyPeriodType; + if (commencementDate != null) { + this.projectedCommencementDate = commencementDate.toDate(); + this.projectedMaturityDate = commencementDate.plusMonths(this.tenureInMonths).toDate(); + } + this.preClosureInterestRate = BigDecimal.ZERO; + + // derived fields + this.projectedInterestAccruedOnMaturity = BigDecimal.ZERO; + this.projectedTotalOnMaturity = BigDecimal.ZERO; } public boolean isDeleted() { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountAssembler.java index 62651d5bb..506893f1c 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountAssembler.java @@ -3,12 +3,12 @@ package org.mifosng.platform.savingproduct.service; import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.client.domain.Client; import org.mifosng.platform.client.domain.ClientRepository; -import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.deposit.domain.DepositProduct; import org.mifosng.platform.deposit.domain.DepositProductRepository; import org.mifosng.platform.exceptions.ClientNotFoundException; import org.mifosng.platform.exceptions.DepositProductNotFoundException; +import org.mifosng.platform.loan.domain.PeriodFrequencyType; import org.mifosng.platform.saving.domain.DepositAccount; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -40,9 +40,21 @@ public class DepositAccountAssembler { throw new DepositProductNotFoundException(command.getProductId()); } - MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal()); - Money deposit = Money.of(currency, command.getDepositAmount()); + // currency details inherited from product setting + Money deposit = Money.of(product.getCurrency(), command.getDepositAmount()); - return new DepositAccount(client, product, command.getExternalId(), deposit, command.getMaturityInterestRate(), command.getTermInMonths()); + // default to months for now. + Integer interestCompoundingPeriodType = PeriodFrequencyType.MONTHS.getValue(); + if (command.getInterestCompoundedEveryPeriodType() != null) { + interestCompoundingPeriodType = command.getInterestCompoundedEveryPeriodType(); + } + PeriodFrequencyType compoundingInterestFrequency = PeriodFrequencyType.fromInt(interestCompoundingPeriodType); + +// boolean renewalAllowed = product.isr + + DepositAccount account = DepositAccount.openNew(client, product, command.getExternalId(), deposit, command.getMaturityInterestRate(), + command.getTermInMonths(), command.getInterestCompoundedEvery(), compoundingInterestFrequency, command.getCommencementDate()); + + return account; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountCommandValidator.java index dfc2a8b98..317cb1bda 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountCommandValidator.java @@ -27,16 +27,20 @@ public class DepositAccountCommandValidator { baseDataValidator.reset().parameter("productId").value(command.getProductId()).ignoreIfNull().notNull(); baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).ignoreIfNull().notExceedingLengthOf(100); - baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).ignoreIfNull().notBlank(); - baseDataValidator.reset().parameter("digitsAfterDecimal").value(command.getDigitsAfterDecimal()).ignoreIfNull().notNull().inMinMaxRange(0, 6); baseDataValidator.reset().parameter("depositAmount").value(command.getDepositAmount()).ignoreIfNull().notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter("maturityInterestRate").value(command.getMaturityInterestRate()).ignoreIfNull().notNull().zeroOrPositiveAmount(); baseDataValidator.reset().parameter("termInMonths").value(command.getTermInMonths()).ignoreIfNull().notNull().integerGreaterThanZero(); - baseDataValidator.reset().anyOfNotNull( - command.getClientId(), command.getProductId(), command.getExternalId(), - command.getCurrencyCode(), command.getDigitsAfterDecimal(), - command.getDepositAmount(), command.getMaturityInterestRate(), command.getTermInMonths()); + baseDataValidator.reset().parameter("interestCompoundedEvery").value(command.getInterestCompoundedEvery()).ignoreIfNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("interestCompoundedEveryPeriodType").value(command.getInterestCompoundedEveryPeriodType()).ignoreIfNull().inMinMaxRange(2, 2); + baseDataValidator.reset().parameter("commencementDate").value(command.getCommencementDate()).ignoreIfNull(); + + if (command.isNoFieldChanged()) { + StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append("deposit.account").append(".no.parameters.for.update"); + StringBuilder defaultEnglishMessage = new StringBuilder("No parameters passed for update."); + ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), "id"); + dataValidationErrors.add(error); + } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); @@ -53,12 +57,14 @@ public class DepositAccountCommandValidator { baseDataValidator.reset().parameter("productId").value(command.getProductId()).notNull(); baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).ignoreIfNull().notExceedingLengthOf(100); - baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).notBlank(); - baseDataValidator.reset().parameter("digitsAfterDecimal").value(command.getDigitsAfterDecimal()).notNull().inMinMaxRange(0, 6); baseDataValidator.reset().parameter("depositAmount").value(command.getDepositAmount()).notNull().zeroOrPositiveAmount(); baseDataValidator.reset().parameter("maturityInterestRate").value(command.getMaturityInterestRate()).notNull().zeroOrPositiveAmount(); baseDataValidator.reset().parameter("termInMonths").value(command.getTermInMonths()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("interestCompoundedEvery").value(command.getInterestCompoundedEvery()).notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter("interestCompoundedEveryPeriodType").value(command.getInterestCompoundedEveryPeriodType()).notNull().inMinMaxRange(2, 2); + baseDataValidator.reset().parameter("commencementDate").value(command.getCommencementDate()).notNull(); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountReadPlatformServiceImpl.java index aa8fd6f74..3033f773f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountReadPlatformServiceImpl.java @@ -73,7 +73,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead return "da.id as id, da.client_id as clientId, da.product_id as productId," + " da.currency_code as currencyCode, da.currency_digits as currencyDigits, " + " da.deposit_amount as depositAmount, " - + " da.maturity_interest_rate as interestRate, " + + " da.maturity_nominal_interest_rate as interestRate, " + " da.created_date as createdon, da.lastmodified_date as modifiedon, " + " c.firstname as firstname, c.lastname as lastname, pd.name as productName," + " curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol" diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index 3c0c98853..073df599a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/savingproduct/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -3,17 +3,23 @@ package org.mifosng.platform.savingproduct.service; import org.mifosng.platform.api.commands.DepositAccountCommand; import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.exceptions.DepositAccountNotFoundException; +import org.mifosng.platform.exceptions.PlatformDataIntegrityException; import org.mifosng.platform.exceptions.ProductNotFoundException; import org.mifosng.platform.saving.domain.DepositAccount; import org.mifosng.platform.saving.domain.DepositAccountRepository; import org.mifosng.platform.security.PlatformSecurityContext; +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; @Service public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements DepositAccountWritePlatformService { + private final static Logger logger = LoggerFactory.getLogger(DepositAccountWritePlatformServiceJpaRepositoryImpl.class); + private final PlatformSecurityContext context; private final DepositAccountRepository depositAccountRepository; private final DepositAccountAssembler depositAccountAssembler; @@ -28,40 +34,66 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo this.depositAccountRepository = depositAccountRepository; this.depositAccountAssembler = depositAccountAssembler; } + + /* + * Guaranteed to throw an exception no matter what the data integrity issue is. + */ + private void handleDataIntegrityIssues(final DepositAccountCommand command, DataIntegrityViolationException dve) { + + Throwable realCause = dve.getMostSpecificCause(); + if (realCause.getMessage().contains("deposit_acc_external_id")) { + throw new PlatformDataIntegrityException("error.msg.desposit.account.duplicate.externalId", "Deposit account with externalId " + command.getExternalId() + " already exists", "externalId", command.getExternalId()); + } + + logger.error(dve.getMessage(), dve); + throw new PlatformDataIntegrityException("error.msg.deposit.account.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); + } @Transactional @Override public EntityIdentifier createDepositAccount(final DepositAccountCommand command) { - this.context.authenticatedUser(); - - DepositAccountCommandValidator validator = new DepositAccountCommandValidator(command); - validator.validateForCreate(); - - final DepositAccount account = this.depositAccountAssembler.assembleFrom(command); - this.depositAccountRepository.save(account); - - return new EntityIdentifier(account.getId()); + try { + this.context.authenticatedUser(); + + DepositAccountCommandValidator validator = new DepositAccountCommandValidator(command); + validator.validateForCreate(); + + final DepositAccount account = this.depositAccountAssembler.assembleFrom(command); + this.depositAccountRepository.save(account); + + return new EntityIdentifier(account.getId()); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve); + return new EntityIdentifier(Long.valueOf(-1)); + } } - + @Transactional @Override public EntityIdentifier updateDepositAccount(final DepositAccountCommand command) { - this.context.authenticatedUser(); - - DepositAccountCommandValidator validator = new DepositAccountCommandValidator(command); - validator.validateForUpdate(); - - final DepositAccount account = this.depositAccountAssembler.assembleFrom(command); - if (account == null || account.isDeleted()) { - throw new DepositAccountNotFoundException(command.getId()); + try { + + this.context.authenticatedUser(); + + DepositAccountCommandValidator validator = new DepositAccountCommandValidator(command); + validator.validateForUpdate(); + + // FIXME - update scenarios to be completely done + final DepositAccount account = this.depositAccountAssembler.assembleFrom(command); + if (account == null || account.isDeleted()) { + throw new DepositAccountNotFoundException(command.getId()); + } + + account.update(command); + this.depositAccountRepository.save(account); + + return new EntityIdentifier(account.getId()); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve); + return new EntityIdentifier(Long.valueOf(-1)); } - - account.update(command); - this.depositAccountRepository.save(account); - - return new EntityIdentifier(Long.valueOf(1)); } @Transactional