diff --git a/mifosng-db/multi-tenant-demo-backups/ceda/bk_ceda_trial.sql b/mifosng-db/multi-tenant-demo-backups/ceda/bk_ceda_trial.sql index 5f47c45b6..4a4de8ac0 100644 --- a/mifosng-db/multi-tenant-demo-backups/ceda/bk_ceda_trial.sql +++ b/mifosng-db/multi-tenant-demo-backups/ceda/bk_ceda_trial.sql @@ -732,7 +732,10 @@ DROP TABLE IF EXISTS `m_group`; CREATE TABLE `m_group` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `office_id` bigint(20) NOT NULL, - `loan_officer_id` bigint(20) DEFAULT NULL, + `level_Id` int(11) NOT NULL, + `parent_id` bigint(20) DEFAULT NULL, + `hierarchy` varchar(100) DEFAULT NULL, + `staff_id` bigint(20) DEFAULT NULL, `name` varchar(100) DEFAULT NULL, `external_id` varchar(100) DEFAULT NULL, `is_deleted` tinyint(1) NOT NULL DEFAULT '0', @@ -740,9 +743,13 @@ CREATE TABLE `m_group` ( UNIQUE KEY `name` (`name`), UNIQUE KEY `external_id` (`external_id`), KEY `office_id` (`office_id`), - KEY `loan_officer_id` (`loan_officer_id`), + KEY `loan_officer_id` (`staff_id`), + KEY `Parent_Id_reference` (`parent_id`), + KEY `FK_m_group_level` (`level_Id`), + CONSTRAINT `FK_m_group_level` FOREIGN KEY (`level_Id`) REFERENCES `m_group_level` (`id`), + CONSTRAINT `FK_m_group_m_staff` FOREIGN KEY (`staff_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `m_group_ibfk_1` FOREIGN KEY (`office_id`) REFERENCES `m_office` (`id`), - CONSTRAINT `FK_m_group_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`) + CONSTRAINT `Parent_Id_reference` FOREIGN KEY (`parent_id`) REFERENCES `m_group` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -781,6 +788,35 @@ LOCK TABLES `m_group_client` WRITE; /*!40000 ALTER TABLE `m_group_client` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `m_group_level` +-- + +DROP TABLE IF EXISTS `m_group_level`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `m_group_level` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `parent_id` int(11) DEFAULT NULL, + `level_name` varchar(100) NOT NULL, + `recursable` tinyint(1) NOT NULL, + `can_have_clients` tinyint(1) NOT NULL, + PRIMARY KEY (`id`), + KEY `Parent_levelId_reference` (`parent_id`), + CONSTRAINT `Parent_levelId_reference` FOREIGN KEY (`parent_id`) REFERENCES `m_group_level` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `m_group_level` +-- + +LOCK TABLES `m_group_level` WRITE; +/*!40000 ALTER TABLE `m_group_level` DISABLE KEYS */; +INSERT INTO `m_group_level` VALUES (1,NULL,'Center',1,0),(2,NULL,'Group',0,1); +/*!40000 ALTER TABLE `m_group_level` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `m_guarantor` -- @@ -1933,4 +1969,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2013-02-19 0:44:13 +-- Dump completed on 2013-02-20 9:57:08 diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeRepositoryWrapper.java new file mode 100644 index 000000000..12fd1dcde --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeRepositoryWrapper.java @@ -0,0 +1,43 @@ +/** + * 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.charge.domain; + +import org.mifosplatform.portfolio.charge.exception.ChargeIsNotActiveException; +import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * Wrapper for {@link ChargeRepository} that is responsible for checking if + * {@link Charge} is returned when using findOne repository method + * and throwing an appropriate not found exception. + *

+ * + *

+ * This is to avoid need for checking and throwing in multiple areas of code + * base where {@link ChargeRepository} is required. + *

+ */ +@Service +public class ChargeRepositoryWrapper { + + private final ChargeRepository repository; + + @Autowired + public ChargeRepositoryWrapper(final ChargeRepository repository) { + this.repository = repository; + } + + public Charge findOneWithNotFoundDetection(final Long id) { + + final Charge chargeDefinition = this.repository.findOne(id); + if (chargeDefinition == null || chargeDefinition.isDeleted()) { throw new ChargeNotFoundException(id); } + if (!chargeDefinition.isActive()) { throw new ChargeIsNotActiveException(id, chargeDefinition.getName()); } + + return chargeDefinition; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanChargeCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanChargeCommand.java index 9622c1e07..19a02235a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanChargeCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanChargeCommand.java @@ -6,16 +6,12 @@ package org.mifosplatform.portfolio.loanaccount.command; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; import org.joda.time.LocalDate; -import org.mifosplatform.infrastructure.core.data.ApiParameterError; -import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; -import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; /** - * Immutable command for creating and updating loan charges. + * Java object representation of {@link LoanCharge} API JSON. */ public class LoanChargeCommand implements Comparable { @@ -23,10 +19,12 @@ public class LoanChargeCommand implements Comparable { private final Long id; private final Long chargeId; private final BigDecimal amount; + @SuppressWarnings("unused") private final Integer chargeTimeType; + @SuppressWarnings("unused") private final Integer chargeCalculationType; @SuppressWarnings("unused") - private final LocalDate specifiedDueDate; + private final LocalDate dueDate; public LoanChargeCommand(final Long id, final Long chargeId, final BigDecimal amount, final Integer chargeTimeType, final Integer chargeCalculationType, final LocalDate specifiedDueDate) { @@ -35,7 +33,7 @@ public class LoanChargeCommand implements Comparable { this.amount = amount; this.chargeTimeType = chargeTimeType; this.chargeCalculationType = chargeCalculationType; - this.specifiedDueDate = specifiedDueDate; + this.dueDate = specifiedDueDate; } @Override @@ -46,18 +44,4 @@ public class LoanChargeCommand implements Comparable { } return comparison; } - - @Deprecated - public void validateForUpdate() { - List dataValidationErrors = new ArrayList(); - - DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge"); - - baseDataValidator.reset().parameter("amount").value(this.amount).ignoreIfNull().positiveAmount(); - baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTimeType).ignoreIfNull().inMinMaxRange(1, 2); - baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculationType).ignoreIfNull().inMinMaxRange(1, 4); - - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", - "Validation errors exist.", dataValidationErrors); } - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanChargeData.java index 0e4657a6f..c50ba2b73 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanChargeData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanChargeData.java @@ -31,7 +31,7 @@ public class LoanChargeData { private final EnumOptionData chargeTimeType; @SuppressWarnings("unused") - private final LocalDate dueAsOfDate; + private final LocalDate dueDate; @SuppressWarnings("unused") private final EnumOptionData chargeCalculationType; @@ -81,7 +81,7 @@ public class LoanChargeData { public LoanChargeData(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived, final BigDecimal amountWrittenOff, - final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueAsOfDate, + final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueDate, final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo, final boolean penalty) { this.id = id; @@ -94,7 +94,7 @@ public class LoanChargeData { this.amountWrittenOff = amountWrittenOff; this.amountOutstanding = amountOutstanding; this.chargeTimeType = chargeTimeType; - this.dueAsOfDate = dueAsOfDate; + this.dueDate = dueDate; this.chargeCalculationType = chargeCalculationType; this.percentage = percentage; this.amountPercentageAppliedTo = amountPercentageAppliedTo; @@ -122,7 +122,7 @@ public class LoanChargeData { this.amountWrittenOff = BigDecimal.ZERO; this.amountOutstanding = amount; this.chargeTimeType = chargeTimeType; - this.dueAsOfDate = null; + this.dueDate = null; this.chargeCalculationType = chargeCalculationType; this.percentage = null; this.amountPercentageAppliedTo = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java index ef9d4a496..d12e26df2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -150,7 +150,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen for (LoanCharge loanCharge : charges) { if (loanCharge.isNotFullyPaid() && !loanCharge.isDueAtDisbursement()) { if (earliestUnpaidCharge == null - || loanCharge.getDueForCollectionAsOfLocalDate().isBefore(earliestUnpaidCharge.getDueForCollectionAsOfLocalDate())) { + || loanCharge.getDueLocalDate().isBefore(earliestUnpaidCharge.getDueLocalDate())) { earliestUnpaidCharge = loanCharge; } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index a0d002847..ebad29111 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -322,7 +322,7 @@ public class Loan extends AbstractAuditableCustom { Money chargesDue = Money.of(getCurrency(), BigDecimal.ZERO); - for (LoanCharge charge : getNullPointerSafeLoanCharges()) { + for (LoanCharge charge : setOfLoanCharges()) { if (charge.isDueAtDisbursement()) { chargesDue = chargesDue.plus(charge.amount()); } @@ -362,7 +362,8 @@ public class Loan extends AbstractAuditableCustom { validateLoanIsNotClosed(loanCharge); if (isDisbursed() && loanCharge.isDueAtDisbursement()) { - // Note: added this constraint to restrict adding charges to a loan + // Note: added this constraint to restrict adding disbursement + // charges to a loan // after it is disbursed // if the loan charge payment type is 'Disbursement'. // To undo this constraint would mean resolving how charges due are @@ -380,7 +381,7 @@ public class Loan extends AbstractAuditableCustom { validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); loanCharge.update(this); - getNullPointerSafeLoanCharges().add(loanCharge); + setOfLoanCharges().add(loanCharge); updateTotalChargesDueAtDisbursement(); @@ -390,11 +391,11 @@ public class Loan extends AbstractAuditableCustom { if (!loanCharge.isDueAtDisbursement()) { final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, - getCurrency(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); } else { // just reprocess the loan schedule only for now. LoanScheduleWrapper wrapper = new LoanScheduleWrapper(); - wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges()); } } @@ -435,7 +436,7 @@ public class Loan extends AbstractAuditableCustom { // if you want ability to remove loan charges that are waived. validateLoanChargeIsNotWaived(loanCharge); - boolean removed = getNullPointerSafeLoanCharges().remove(loanCharge); + boolean removed = setOfLoanCharges().remove(loanCharge); if (removed) { updateTotalChargesDueAtDisbursement(); } @@ -447,7 +448,7 @@ public class Loan extends AbstractAuditableCustom { if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) { final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, - getCurrency(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); } } @@ -484,7 +485,7 @@ public class Loan extends AbstractAuditableCustom { validateLoanIsNotClosed(loanCharge); - if (getNullPointerSafeLoanCharges().contains(loanCharge)) { + if (setOfLoanCharges().contains(loanCharge)) { final Map loanChargeChanges = loanCharge.update(command, getPrincpal().getAmount()); actualChanges.putAll(loanChargeChanges); updateTotalChargesDueAtDisbursement(); @@ -495,11 +496,11 @@ public class Loan extends AbstractAuditableCustom { if (!loanCharge.isDueAtDisbursement()) { final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, - getCurrency(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); } else { // reprocess loan schedule based on charge been waived. LoanScheduleWrapper wrapper = new LoanScheduleWrapper(); - wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges()); } return actualChanges; @@ -523,7 +524,7 @@ public class Loan extends AbstractAuditableCustom { LocalDate transactionDate = getDisbursementDate(); if (loanCharge.isSpecifiedDueDate()) { - transactionDate = loanCharge.getDueForCollectionAsOfLocalDate(); + transactionDate = loanCharge.getDueLocalDate(); } updateTotalChargesDueAtDisbursement(); @@ -538,11 +539,11 @@ public class Loan extends AbstractAuditableCustom { if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) { final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, - getCurrency(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); } else { // reprocess loan schedule based on charge been waived. LoanScheduleWrapper wrapper = new LoanScheduleWrapper(); - wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges()); } doPostLoanTransactionChecks(waiveLoanChargeTransaction.getTransactionDate(), loanLifecycleStateMachine); @@ -595,8 +596,8 @@ public class Loan extends AbstractAuditableCustom { } public void updateLoanCharges(final Set loanCharges) { - getNullPointerSafeLoanCharges().clear(); - getNullPointerSafeLoanCharges().addAll(associateChargesWithThisLoan(loanCharges)); + setOfLoanCharges().clear(); + setOfLoanCharges().addAll(associateChargesWithThisLoan(loanCharges)); this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement(); } @@ -630,7 +631,7 @@ public class Loan extends AbstractAuditableCustom { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor .determineProcessor(this.transactionProcessingStrategy); loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), repaymentsOrWaivers, getCurrency(), - this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + this.repaymentScheduleInstallments, setOfLoanCharges()); } public void updateLoanScheduleDependentDerivedFields() { @@ -785,7 +786,7 @@ public class Loan extends AbstractAuditableCustom { final String chargesParamName = "charges"; if (command.parameterExists(chargesParamName)) { - final Set existingLoanCharges = getNullPointerSafeLoanCharges(); + final Set existingLoanCharges = setOfLoanCharges(); if (!possiblyModifedLoanCharges.equals(existingLoanCharges)) { actualChanges.put(chargesParamName, getLoanCharges(possiblyModifedLoanCharges)); @@ -797,20 +798,6 @@ public class Loan extends AbstractAuditableCustom { validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); } } - - // final ImmutableSet difference = - // Sets.symmetricDifference(existingLoanCharges, - // possiblyModifedLoanCharges) - // .immutableCopy(); - // if (!difference.isEmpty()) { - // actualChanges.put(chargesParamName, getLoanCharges(difference)); - // actualChanges.put("recalculateLoanSchedule", true); - // - // for (LoanCharge loanCharge : possiblyModifedLoanCharges) { - // validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, - // getDisbursementDate(), getLastRepaymentPeriodDueDate()); - // } - // } } final String collateralParamName = "collateral"; @@ -819,22 +806,12 @@ public class Loan extends AbstractAuditableCustom { if (!possiblyModifedLoanCollateralItems.equals(this.collateral)) { actualChanges.put(collateralParamName, listOfLoanCollateralData(possiblyModifedLoanCollateralItems)); } - - // - // final ImmutableSet difference = - // Sets.symmetricDifference(this.collateral, - // possiblyModifedLoanCollateralItems) - // .immutableCopy(); - // if (!difference.isEmpty()) { - // actualChanges.put(collateralParamName, - // listOfLoanCollateralData(difference)); - // } } return actualChanges; } - private Set getNullPointerSafeLoanCharges() { + private Set setOfLoanCharges() { Set loanCharges = this.charges; if (this.charges == null) { loanCharges = new HashSet(); @@ -933,7 +910,7 @@ public class Loan extends AbstractAuditableCustom { } // charges are optional - for (LoanCharge loanCharge : getNullPointerSafeLoanCharges()) { + for (LoanCharge loanCharge : setOfLoanCharges()) { validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); } } @@ -1190,7 +1167,7 @@ public class Loan extends AbstractAuditableCustom { final LoanSchedule loanSchedule = new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, interestRatePerPeriod, interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, loanTermFrequency, - loanTermPeriodFrequencyType, getNullPointerSafeLoanCharges(), this.getDisbursementDate(), + loanTermPeriodFrequencyType, setOfLoanCharges(), this.getDisbursementDate(), this.getExpectedFirstRepaymentOnDate(), this.getInterestChargedFromDate(), inArrearsTolerance); final LoanScheduleData generatedData = loanSchedule.generate(); @@ -1215,7 +1192,7 @@ public class Loan extends AbstractAuditableCustom { chargesPayment.updateLoan(this); this.loanTransactions.add(chargesPayment); - for (LoanCharge charge : getNullPointerSafeLoanCharges()) { + for (LoanCharge charge : setOfLoanCharges()) { if (charge.isDueAtDisbursement()) { charge.markAsFullyPaid(); } @@ -1281,7 +1258,7 @@ public class Loan extends AbstractAuditableCustom { private void updateLoanToPreDisbursalState() { this.actualDisbursementDate = null; - for (LoanCharge charge : getNullPointerSafeLoanCharges()) { + for (LoanCharge charge : setOfLoanCharges()) { charge.resetToOriginal(loanCurrency()); } @@ -1290,7 +1267,7 @@ public class Loan extends AbstractAuditableCustom { } LoanScheduleWrapper wrapper = new LoanScheduleWrapper(); - wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges()); } public LoanTransaction waiveInterest(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine) { @@ -1370,11 +1347,11 @@ public class Loan extends AbstractAuditableCustom { .determineProcessor(this.transactionProcessingStrategy); if (isTransactionChronologicallyLatest && adjustedTransaction == null) { loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), this.repaymentScheduleInstallments, - getNullPointerSafeLoanCharges()); + setOfLoanCharges()); } else { final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, - getCurrency(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); + getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); } doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java index 13ad1d177..cb2c4ccdb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java @@ -50,7 +50,7 @@ public class LoanCharge extends AbstractPersistable { @Temporal(TemporalType.DATE) @Column(name = "due_for_collection_as_of_date") - private Date dueForCollectionAsOfDate; + private Date dueDate; @Column(name = "charge_calculation_enum") private Integer chargeCalculation; @@ -85,29 +85,32 @@ public class LoanCharge extends AbstractPersistable { public static LoanCharge createNewFromJson(final Loan loan, final Charge chargeDefinition, final JsonCommand command) { final BigDecimal amount = command.bigDecimalValueOfParameterNamed("amount"); - final Integer chargeTimeType = command.integerValueOfParameterNamed("chargeTimeType"); - final Integer chargeCalculationType = command.integerValueOfParameterNamed("chargeCalculationType"); - final LocalDate specifiedDueDate = command.localDateValueOfParameterNamed("specifiedDueDate"); + // final Integer chargeTimeType = + // command.integerValueOfParameterNamed("chargeTimeType"); + // final Integer chargeCalculationType = + // command.integerValueOfParameterNamed("chargeCalculationType"); + final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate"); ChargeTimeType chargeTime = null; - if (chargeTimeType != null) { - chargeTime = ChargeTimeType.fromInt(chargeTimeType); - } + // if (chargeTimeType != null) { + // chargeTime = ChargeTimeType.fromInt(chargeTimeType); + // } ChargeCalculationType chargeCalculation = null; - if (chargeCalculationType != null) { - chargeCalculation = ChargeCalculationType.fromInt(chargeCalculationType); - } + // if (chargeCalculationType != null) { + // chargeCalculation = + // ChargeCalculationType.fromInt(chargeCalculationType); + // } return new LoanCharge(loan, chargeDefinition, loan.getPrincpal().getAmount(), amount, chargeTime, chargeCalculation, - specifiedDueDate); + dueDate); } /* * loanPrincipal is required for charges that are percentage based */ public static LoanCharge createNewWithoutLoan(final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount, - final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate specifiedDueDate) { - return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, specifiedDueDate); + final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate) { + return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, dueDate); } protected LoanCharge() { @@ -115,7 +118,7 @@ public class LoanCharge extends AbstractPersistable { } public LoanCharge(final Loan loan, final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount, - final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate specifiedDueDate) { + final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate) { this.loan = loan; this.charge = chargeDefinition; this.penaltyCharge = chargeDefinition.isPenalty(); @@ -127,15 +130,15 @@ public class LoanCharge extends AbstractPersistable { if (ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.SPECIFIED_DUE_DATE)) { - if (specifiedDueDate == null) { - final String defaultUserMessage = "Loan charge is missing specified due date"; - throw new LoanChargeWithoutMandatoryFieldException("loancharge", "specifiedDueDate", defaultUserMessage, + if (dueDate == null) { + final String defaultUserMessage = "Loan charge is missing due date."; + throw new LoanChargeWithoutMandatoryFieldException("loanCharge", "dueDate", defaultUserMessage, chargeDefinition.getId(), chargeDefinition.getName()); } - this.dueForCollectionAsOfDate = specifiedDueDate.toDate(); + this.dueDate = dueDate.toDate(); } else { - this.dueForCollectionAsOfDate = null; + this.dueDate = null; } this.chargeCalculation = chargeDefinition.getChargeCalculation(); @@ -240,7 +243,7 @@ public class LoanCharge extends AbstractPersistable { public void update(final BigDecimal amount, final LocalDate specifiedDueDate, final BigDecimal loanPrincipal) { if (specifiedDueDate != null) { - this.dueForCollectionAsOfDate = specifiedDueDate.toDate(); + this.dueDate = specifiedDueDate.toDate(); } if (amount != null) { @@ -279,31 +282,37 @@ public class LoanCharge extends AbstractPersistable { final String dateFormatAsInput = command.dateFormat(); final String localeAsInput = command.locale(); - final String chargeTimeParamName = "chargeTime"; - if (command.isChangeInIntegerParameterNamed(chargeTimeParamName, this.chargeTime)) { - final Integer newValue = command.integerValueOfParameterNamed(chargeTimeParamName); - actualChanges.put(chargeTimeParamName, newValue); - actualChanges.put("locale", localeAsInput); - this.chargeTime = ChargeTimeType.fromInt(newValue).getValue(); - } + // final String chargeTimeParamName = "chargeTime"; + // if (command.isChangeInIntegerParameterNamed(chargeTimeParamName, + // this.chargeTime)) { + // final Integer newValue = + // command.integerValueOfParameterNamed(chargeTimeParamName); + // actualChanges.put(chargeTimeParamName, newValue); + // actualChanges.put("locale", localeAsInput); + // this.chargeTime = ChargeTimeType.fromInt(newValue).getValue(); + // } + // + // final String chargeCalculationParamName = "chargeCalculation"; + // if + // (command.isChangeInIntegerParameterNamed(chargeCalculationParamName, + // this.chargeCalculation)) { + // final Integer newValue = + // command.integerValueOfParameterNamed(chargeCalculationParamName); + // actualChanges.put(chargeCalculationParamName, newValue); + // actualChanges.put("locale", localeAsInput); + // this.chargeCalculation = + // ChargeCalculationType.fromInt(newValue).getValue(); + // } - final String chargeCalculationParamName = "chargeCalculation"; - if (command.isChangeInIntegerParameterNamed(chargeCalculationParamName, this.chargeCalculation)) { - final Integer newValue = command.integerValueOfParameterNamed(chargeCalculationParamName); - actualChanges.put(chargeCalculationParamName, newValue); - actualChanges.put("locale", localeAsInput); - this.chargeCalculation = ChargeCalculationType.fromInt(newValue).getValue(); - } - - final String specifiedDueDateParamName = "specifiedDueDate"; - if (command.isChangeInLocalDateParameterNamed(specifiedDueDateParamName, getDueForCollectionAsOfLocalDate())) { + final String specifiedDueDateParamName = "dueDate"; + if (command.isChangeInLocalDateParameterNamed(specifiedDueDateParamName, getDueLocalDate())) { final String valueAsInput = command.stringValueOfParameterNamed(specifiedDueDateParamName); actualChanges.put(specifiedDueDateParamName, valueAsInput); actualChanges.put("dateFormat", dateFormatAsInput); actualChanges.put("locale", localeAsInput); final LocalDate newValue = command.localDateValueOfParameterNamed(specifiedDueDateParamName); - this.dueForCollectionAsOfDate = newValue.toDate(); + this.dueDate = newValue.toDate(); } final String amountParamName = "amount"; @@ -355,13 +364,13 @@ public class LoanCharge extends AbstractPersistable { public LoanChargeCommand toCommand() { return new LoanChargeCommand(this.getId(), this.charge.getId(), this.amount, this.chargeTime, this.chargeCalculation, - getDueForCollectionAsOfLocalDate()); + getDueLocalDate()); } - public LocalDate getDueForCollectionAsOfLocalDate() { + public LocalDate getDueLocalDate() { LocalDate specifiedDueDate = null; - if (this.dueForCollectionAsOfDate != null) { - specifiedDueDate = new LocalDate(this.dueForCollectionAsOfDate); + if (this.dueDate != null) { + specifiedDueDate = new LocalDate(this.dueDate); } return specifiedDueDate; } @@ -418,7 +427,7 @@ public class LoanCharge extends AbstractPersistable { } public boolean isDueForCollectionFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive) { - final LocalDate specifiedDueDate = getDueForCollectionAsOfLocalDate(); + final LocalDate specifiedDueDate = getDueLocalDate(); return occursOnDayFromAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, specifiedDueDate); } @@ -508,7 +517,7 @@ public class LoanCharge extends AbstractPersistable { .append(this.getId(), rhs.getId()) // .append(this.charge.getId(), rhs.charge.getId()) // .append(this.amount, rhs.amount) // - .append(this.getDueForCollectionAsOfLocalDate(), rhs.getDueForCollectionAsOfLocalDate()) // + .append(this.getDueLocalDate(), rhs.getDueLocalDate()) // .isEquals(); } @@ -517,7 +526,7 @@ public class LoanCharge extends AbstractPersistable { return new HashCodeBuilder(3, 5) // .append(this.getId()) // .append(this.charge.getId()) // - .append(this.amount).append(this.getDueForCollectionAsOfLocalDate()) // + .append(this.amount).append(this.getDueLocalDate()) // .toHashCode(); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanChargeCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanChargeCommandFromApiJsonDeserializer.java deleted file mode 100644 index d15a2c697..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanChargeCommandFromApiJsonDeserializer.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * 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.loanaccount.serialization; - -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.lang.StringUtils; -import org.joda.time.LocalDate; -import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; -import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer; -import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer; -import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; -import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.google.gson.JsonElement; -import com.google.gson.reflect.TypeToken; - -/** - * Implementation of {@link FromApiJsonDeserializer} for - * {@link LoanChargeCommand}'s. - */ -@Component -public final class LoanChargeCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer { - - /** - * The parameters supported for this command. - */ - final Set supportedParameters = new HashSet(Arrays.asList("id", "chargeId", "amount", "chargeTimeType", - "chargeCalculationType", "specifiedDueDate", "locale", "dateFormat")); - - private final FromJsonHelper fromApiJsonHelper; - - @Autowired - public LoanChargeCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) { - this.fromApiJsonHelper = fromApiJsonHelper; - } - - @Override - public LoanChargeCommand commandFromApiJson(final String json) { - - if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } - - final Type typeOfMap = new TypeToken>() {}.getType(); - fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); - - final JsonElement element = fromApiJsonHelper.parse(json); - final Long id = fromApiJsonHelper.extractLongNamed("id", element); - final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", element); - final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element); - final Integer chargeTimeType = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeTimeType", element); - final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeCalculationType", element); - final LocalDate dueAsOfDate = fromApiJsonHelper.extractLocalDateNamed("specifiedDueDate", element); - - return new LoanChargeCommand(id, chargeId, amount, chargeTimeType, chargeCalculationType, dueAsOfDate); - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index e473668c3..2817ecf8f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -111,4 +111,53 @@ public final class LoanEventApiJsonValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } + + public void validateAddLoanCharge(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Set disbursementParameters = new HashSet(Arrays.asList("chargeId", "amount", "dueDate", "locale", + "dateFormat")); + + final Type typeOfMap = new TypeToken>() {}.getType(); + fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanCharge"); + + final JsonElement element = fromApiJsonHelper.parse(json); + final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", element); + baseDataValidator.reset().parameter("chargeId").value(chargeId).notNull().integerGreaterThanZero(); + + final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element); + baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount(); + + if (fromApiJsonHelper.parameterExists("dueDate", element)) { + fromApiJsonHelper.extractLocalDateNamed("dueDate", element); + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateUpdateOfLoanCharge(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Set disbursementParameters = new HashSet(Arrays.asList("amount", "dueDate", "locale", "dateFormat")); + + final Type typeOfMap = new TypeToken>() {}.getType(); + fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanCharge"); + + final JsonElement element = fromApiJsonHelper.parse(json); + + final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element); + baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount(); + + if (fromApiJsonHelper.parameterExists("dueDate", element)) { + fromApiJsonHelper.extractLocalDateNamed("dueDate", element); + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java index dfd5786fd..0aab3ffcd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java @@ -14,10 +14,8 @@ import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.portfolio.charge.domain.Charge; import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType; -import org.mifosplatform.portfolio.charge.domain.ChargeRepository; +import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper; import org.mifosplatform.portfolio.charge.domain.ChargeTimeType; -import org.mifosplatform.portfolio.charge.exception.ChargeIsNotActiveException; -import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException; import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException; import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository; @@ -32,11 +30,11 @@ import com.google.gson.JsonObject; public class LoanChargeAssembler { private final FromJsonHelper fromApiJsonHelper; - private final ChargeRepository chargeRepository; + private final ChargeRepositoryWrapper chargeRepository; private final LoanChargeRepository loanChargeRepository; @Autowired - public LoanChargeAssembler(final FromJsonHelper fromApiJsonHelper, final ChargeRepository chargeRepository, + public LoanChargeAssembler(final FromJsonHelper fromApiJsonHelper, final ChargeRepositoryWrapper chargeRepository, final LoanChargeRepository loanChargeRepository) { this.fromApiJsonHelper = fromApiJsonHelper; this.chargeRepository = chargeRepository; @@ -69,10 +67,7 @@ public class LoanChargeAssembler { dateFormat, locale); if (id == null) { - final Charge chargeDefinition = this.chargeRepository.findOne(chargeId); - if (chargeDefinition == null || chargeDefinition.isDeleted()) { throw new ChargeNotFoundException(chargeId); } - - if (!chargeDefinition.isActive()) { throw new ChargeIsNotActiveException(chargeId, chargeDefinition.getName()); } + final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId); ChargeTimeType chargeTime = null; if (chargeTimeType != null) { ChargeTimeType.fromInt(chargeTimeType); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 07e40e96a..85dbee061 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -23,13 +23,10 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepository; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.portfolio.charge.domain.Charge; -import org.mifosplatform.portfolio.charge.domain.ChargeRepository; -import org.mifosplatform.portfolio.charge.exception.ChargeIsNotActiveException; -import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException; +import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper; import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException; import org.mifosplatform.portfolio.client.domain.Note; import org.mifosplatform.portfolio.client.domain.NoteRepository; -import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand; import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.Loan; @@ -44,7 +41,6 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException; -import org.mifosplatform.portfolio.loanaccount.serialization.LoanChargeCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator; import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException; @@ -58,13 +54,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final PlatformSecurityContext context; private final LoanEventApiJsonValidator loanEventApiJsonValidator; - private final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer; private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer; private final LoanRepository loanRepository; private final NoteRepository noteRepository; private final LoanTransactionRepository loanTransactionRepository; private final LoanAssembler loanAssembler; - private final ChargeRepository chargeRepository; + private final ChargeRepositoryWrapper chargeRepository; private final LoanChargeRepository loanChargeRepository; private final ApplicationCurrencyRepository applicationCurrencyRepository; private final JournalEntryWritePlatformService journalEntryWritePlatformService; @@ -72,15 +67,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Autowired public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanEventApiJsonValidator loanEventApiJsonValidator, - final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer, final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanAssembler loanAssembler, final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository, - final NoteRepository noteRepository, final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository, + final NoteRepository noteRepository, final ChargeRepositoryWrapper chargeRepository, + final LoanChargeRepository loanChargeRepository, final ApplicationCurrencyRepository applicationCurrencyRepository, final JournalEntryWritePlatformService journalEntryWritePlatformService) { this.context = context; this.loanEventApiJsonValidator = loanEventApiJsonValidator; - this.loanChargeCommandFromApiJsonDeserializer = loanChargeCommandFromApiJsonDeserializer; this.loanAssembler = loanAssembler; this.loanRepository = loanRepository; this.loanTransactionRepository = loanTransactionRepository; @@ -472,24 +466,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Transactional @Override public CommandProcessingResult addLoanCharge(final Long loanId, final JsonCommand command) { + this.context.authenticatedUser(); - // FIXME - KW - update loan charge api validation - final LoanChargeCommand loanChargeCommand = this.loanChargeCommandFromApiJsonDeserializer.commandFromApiJson(command.json()); - // loanChargeCommand.validateForCreate(); + this.loanEventApiJsonValidator.validateAddLoanCharge(command.json()); final Loan loan = retrieveLoanBy(loanId); final Long chargeDefinitionId = command.longValueOfParameterNamed("chargeId"); - final Charge chargeDefinition = this.chargeRepository.findOne(chargeDefinitionId); - if (chargeDefinition == null || chargeDefinition.isDeleted()) { throw new ChargeNotFoundException(chargeDefinitionId); } - if (!chargeDefinition.isActive()) { throw new ChargeIsNotActiveException(chargeDefinition.getId(), chargeDefinition.getName()); } + final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId); final LoanCharge loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command); if (!loan.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) { - String errorMessage = "Charge and Loan must have the same currency."; - throw new InvalidCurrencyException("charge", "attach.to.loan", errorMessage); + final String errorMessage = "Charge and Loan must have the same currency."; + throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage); } this.loanChargeRepository.save(loanCharge); @@ -513,8 +504,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.context.authenticatedUser(); - final LoanChargeCommand loanChargeCommand = this.loanChargeCommandFromApiJsonDeserializer.commandFromApiJson(command.json()); - loanChargeCommand.validateForUpdate(); + this.loanEventApiJsonValidator.validateUpdateOfLoanCharge(command.json()); final Loan loan = retrieveLoanBy(loanId); final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId); @@ -540,9 +530,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.context.authenticatedUser(); - final LoanChargeCommand loanChargeCommand = this.loanChargeCommandFromApiJsonDeserializer.commandFromApiJson(command.json()); - loanChargeCommand.validateForUpdate(); - final Loan loan = retrieveLoanBy(loanId); final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId); @@ -553,12 +540,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanTransactionRepository.save(waiveTransaction); this.loanRepository.save(loan); - final String noteText = ""; // command.getNote(); - if (StringUtils.isNotBlank(noteText)) { - final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText); - this.noteRepository.save(note); - } - return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loanChargeId) // diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/DepositAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/DepositAccount.java index 3065ebc20..4b1516dcd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/DepositAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/DepositAccount.java @@ -187,10 +187,10 @@ public class DepositAccount extends AbstractAuditableCustom { if (interestCompoundingAllowed) { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityFor(deposit, tenureInMonths, - maturityInterestRate, interestCompoundedEvery, interestCompoundedFrequencyPeriodType); + maturityInterestRate, interestCompoundedEvery); } else { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityForSimpleInterest(deposit, - tenureInMonths, maturityInterestRate, interestCompoundedEvery, interestCompoundedFrequencyPeriodType); + tenureInMonths, maturityInterestRate, interestCompoundedEvery); } DepositAccountStatus from = null; @@ -278,11 +278,12 @@ public class DepositAccount extends AbstractAuditableCustom { this.externalId = this.getId() + "_" + this.externalId; } - public Map approve(final LocalDate actualCommencementDate, final DepositLifecycleStateMachine depositLifecycleStateMachine, - final JsonCommand command, final FixedTermDepositInterestCalculator calculator) { - - final Map actualChanges = new LinkedHashMap(20); - final String localeAsInput = command.locale(); + public Map approve(final LocalDate actualCommencementDate, + final DepositLifecycleStateMachine depositLifecycleStateMachine, final JsonCommand command, + final FixedTermDepositInterestCalculator calculator) { + + final Map actualChanges = new LinkedHashMap(20); + final String localeAsInput = command.locale(); DepositAccountStatus statusEnum = depositLifecycleStateMachine.transition(DepositAccountEvent.DEPOSIT_APPROVED, DepositAccountStatus.fromInt(this.depositStatus)); @@ -295,7 +296,7 @@ public class DepositAccount extends AbstractAuditableCustom { actualChanges.put("locale", localeAsInput); this.tenureInMonths = newValue; } - + final String maturityInterestRateParamName = "maturityInterestRate"; if (command.isChangeInBigDecimalParameterNamed(maturityInterestRateParamName, this.interestRate)) { final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(maturityInterestRateParamName); @@ -313,7 +314,7 @@ public class DepositAccount extends AbstractAuditableCustom { this.depositAmount = newValue; } this.product.validateDepositInRange(this.depositAmount); - + final String interestCompoundedEveryParamName = "interestCompoundedEvery"; if (command.isChangeInIntegerParameterNamed(interestCompoundedEveryParamName, this.interestCompoundedEvery)) { final Integer newValue = command.integerValueOfParameterNamed(interestCompoundedEveryParamName); @@ -337,15 +338,16 @@ public class DepositAccount extends AbstractAuditableCustom { if (this.interestCompoundingAllowed) { futureValueOnMaturity = calculator.calculateInterestOnMaturityFor(getDeposit(), this.tenureInMonths, this.interestRate, - this.interestCompoundedEvery, getInterestCompoundedFrequencyType()); + this.interestCompoundedEvery); } else { futureValueOnMaturity = calculator.calculateInterestOnMaturityForSimpleInterest(getDeposit(), this.tenureInMonths, - this.interestRate, this.interestCompoundedEvery, getInterestCompoundedFrequencyType()); + this.interestRate, this.interestCompoundedEvery); } this.interestAccrued = futureValueOnMaturity.minus(getDeposit()).getAmount(); this.total = futureValueOnMaturity.getAmount(); - DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.deposit(getDeposit(), getActualCommencementDate(), null); + DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.deposit(getDeposit(), getActualCommencementDate(), + null); depositaccountTransaction.updateAccount(this); this.depositaccountTransactions.add(depositaccountTransaction); @@ -368,7 +370,7 @@ public class DepositAccount extends AbstractAuditableCustom { throw new InvalidDepositStateTransitionException("approval", "cannot.be.a.future.date", errorMessage, getActualCommencementDate()); } - return actualChanges; + return actualChanges; } public LocalDate getActualCommencementDate() { @@ -592,8 +594,11 @@ public class DepositAccount extends AbstractAuditableCustom { preClosureInterestRate); this.interestAccrued = this.interestAccrued.add(accuredInterestAfterMaturityToTillDate.getAmount()); this.availableInterest = this.availableInterest.add(accuredInterestAfterMaturityToTillDate.getAmount()); - DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.withdraw(getDeposit(), eventDate,Money.of(currency, this.availableInterest),getDeposit().getAmount().add(this.availableInterest)); - //DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.withdraw(getDeposit(), eventDate, getAccuredInterest()); + DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.withdraw(getDeposit(), eventDate, + Money.of(currency, this.availableInterest), getDeposit().getAmount().add(this.availableInterest)); + // DepositAccountTransaction depositaccountTransaction = + // DepositAccountTransaction.withdraw(getDeposit(), eventDate, + // getAccuredInterest()); depositaccountTransaction.updateAccount(this); this.depositaccountTransactions.add(depositaccountTransaction); @@ -651,8 +656,11 @@ public class DepositAccount extends AbstractAuditableCustom { @SuppressWarnings("unused") boolean statustest = isActive(); if (isActive()) { - //DepositAccountTransaction depositAccountTransaction = DepositAccountTransaction.withdraw(null, new LocalDate(), interest); - DepositAccountTransaction depositAccountTransaction = DepositAccountTransaction.withdraw(null, new LocalDate(), interest,getDeposit().getAmount().add(this.availableInterest).subtract(interest.getAmount())); + // DepositAccountTransaction depositAccountTransaction = + // DepositAccountTransaction.withdraw(null, new LocalDate(), + // interest); + DepositAccountTransaction depositAccountTransaction = DepositAccountTransaction.withdraw(null, new LocalDate(), interest, + getDeposit().getAmount().add(this.availableInterest).subtract(interest.getAmount())); depositAccountTransaction.updateAccount(this); this.depositaccountTransactions.add(depositAccountTransaction); this.interstPaid = this.interstPaid.add(interest.getAmount()); @@ -667,9 +675,13 @@ public class DepositAccount extends AbstractAuditableCustom { DepositAccountStatus.fromInt(this.depositStatus)); this.depositStatus = statusEnumForClose.getValue(); - //DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.withdraw(getDeposit(), new LocalDate(),Money.of(getDeposit().getCurrency(), getTotal()).minus(getDeposit())); - DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.withdraw(getDeposit(), new LocalDate(),Money.of(getDeposit().getCurrency(), getTotal()).minus(getDeposit()), - getDeposit().plus(Money.of(getDeposit().getCurrency(), getTotal()).minus(getDeposit())).getAmount()); + // DepositAccountTransaction depositaccountTransaction = + // DepositAccountTransaction.withdraw(getDeposit(), new + // LocalDate(),Money.of(getDeposit().getCurrency(), + // getTotal()).minus(getDeposit())); + DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.withdraw(getDeposit(), new LocalDate(), + Money.of(getDeposit().getCurrency(), getTotal()).minus(getDeposit()), + getDeposit().plus(Money.of(getDeposit().getCurrency(), getTotal()).minus(getDeposit())).getAmount()); depositaccountTransaction.updateAccount(this); this.depositaccountTransactions.add(depositaccountTransaction); @@ -687,10 +699,10 @@ public class DepositAccount extends AbstractAuditableCustom { if (isInterestCompoundingAllowed) { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityFor(deposit, tenureInMonths, - maturityInterestRate, interestCompoundedEvery, compoundingInterestFrequency); + maturityInterestRate, interestCompoundedEvery); } else { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityForSimpleInterest(deposit, - tenureInMonths, maturityInterestRate, interestCompoundedEvery, compoundingInterestFrequency); + tenureInMonths, maturityInterestRate, interestCompoundedEvery); } setExternalId(externalId); @@ -747,18 +759,17 @@ public class DepositAccount extends AbstractAuditableCustom { if (lastInterestPostedDate.isBefore(getMaturityDate())) { if (nextInterestPostingDate.isAfter(getMaturityDate())) { Integer noofRemainMonthsAfterMaturity = Months.monthsBetween(getMaturityDate(), nextInterestPostingDate).getMonths(); - Integer noOfMonthsBeforeMaturity = Months.monthsBetween(lastInterestPostedDate, getMaturityDate().plusDays(1)).getMonths(); - Money futureValueOnMaturity = null; + Integer noOfMonthsBeforeMaturity = Months.monthsBetween(lastInterestPostedDate, getMaturityDate().plusDays(1)) + .getMonths(); + Money futureValueOnMaturity = Money.zero(this.currency); if (noOfMonthsBeforeMaturity > 0) { if (this.interestCompoundingAllowed) { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityFor(deposit, - noOfMonthsBeforeMaturity, this.interestRate, this.interestCompoundedEvery, - getInterestCompoundedFrequencyType()); + noOfMonthsBeforeMaturity, this.interestRate, this.interestCompoundedEvery); } else { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityForSimpleInterest( - deposit, noOfMonthsBeforeMaturity, this.interestRate, this.interestCompoundedEvery, - getInterestCompoundedFrequencyType()); + deposit, noOfMonthsBeforeMaturity, this.interestRate, this.interestCompoundedEvery); } this.interestPostedAmount = this.interestPostedAmount.add(futureValueOnMaturity.minus(deposit).getAmount()); this.availableInterest = this.availableInterest.add(futureValueOnMaturity.minus(deposit).getAmount()); @@ -770,12 +781,10 @@ public class DepositAccount extends AbstractAuditableCustom { if (this.interestCompoundingAllowed) { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityFor(deposit, - noofRemainMonthsAfterMaturity, this.preClosureInterestRate, this.interestCompoundedEvery, - getInterestCompoundedFrequencyType()); + noofRemainMonthsAfterMaturity, this.preClosureInterestRate, this.interestCompoundedEvery); } else { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityForSimpleInterest( - deposit, noofRemainMonthsAfterMaturity, this.preClosureInterestRate, this.interestCompoundedEvery, - getInterestCompoundedFrequencyType()); + deposit, noofRemainMonthsAfterMaturity, this.preClosureInterestRate, this.interestCompoundedEvery); } this.interestPostedAmount = this.interestPostedAmount.add(futureValueOnMaturity.minus(deposit).getAmount()); this.availableInterest = this.availableInterest.add(futureValueOnMaturity.minus(deposit).getAmount()); @@ -784,8 +793,8 @@ public class DepositAccount extends AbstractAuditableCustom { } // add transaction entry for each interest posting - DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.postInterest(null, nextInterestPostingDate,futureValueOnMaturity.minus(deposit),deposit.getAmount().add(this.availableInterest)); - //DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.postInterest(null, nextInterestPostingDate, futureValueOnMaturity.minus(deposit)); + DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.postInterest(null, + nextInterestPostingDate, futureValueOnMaturity.minus(deposit), deposit.getAmount().add(this.availableInterest)); depositaccountTransaction.updateAccount(this); this.depositaccountTransactions.add(depositaccountTransaction); @@ -793,12 +802,10 @@ public class DepositAccount extends AbstractAuditableCustom { Money futureValueOnMaturity = null; if (this.interestCompoundingAllowed) { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityFor(deposit, - this.interestCompoundedEvery, this.interestRate, this.interestCompoundedEvery, - getInterestCompoundedFrequencyType()); + this.interestCompoundedEvery, this.interestRate, this.interestCompoundedEvery); } else { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityForSimpleInterest(deposit, - this.interestCompoundedEvery, this.interestRate, this.interestCompoundedEvery, - getInterestCompoundedFrequencyType()); + this.interestCompoundedEvery, this.interestRate, this.interestCompoundedEvery); } this.interestPostedAmount = this.interestPostedAmount.add(futureValueOnMaturity.minus(deposit).getAmount()); this.availableInterest = this.availableInterest.add(futureValueOnMaturity.minus(deposit).getAmount()); @@ -806,8 +813,8 @@ public class DepositAccount extends AbstractAuditableCustom { this.nextInterestPostingDate = nextInterestPostingDate.plusMonths(this.interestCompoundedEvery).toDate(); // add transaction entry for each interest posting - // DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.postInterest(null, nextInterestPostingDate, futureValueOnMaturity.minus(deposit)); - DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.postInterest(null, nextInterestPostingDate,futureValueOnMaturity.minus(deposit),deposit.getAmount().add(this.availableInterest)); + DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.postInterest(null, + nextInterestPostingDate, futureValueOnMaturity.minus(deposit), deposit.getAmount().add(this.availableInterest)); depositaccountTransaction.updateAccount(this); this.depositaccountTransactions.add(depositaccountTransaction); } @@ -815,12 +822,10 @@ public class DepositAccount extends AbstractAuditableCustom { Money futureValueOnMaturity = null; if (this.interestCompoundingAllowed) { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityFor(deposit, - this.interestCompoundedEvery, this.preClosureInterestRate, this.interestCompoundedEvery, - getInterestCompoundedFrequencyType()); + this.interestCompoundedEvery, this.preClosureInterestRate, this.interestCompoundedEvery); } else { futureValueOnMaturity = fixedTermDepositInterestCalculator.calculateInterestOnMaturityForSimpleInterest(deposit, - this.interestCompoundedEvery, this.preClosureInterestRate, this.interestCompoundedEvery, - getInterestCompoundedFrequencyType()); + this.interestCompoundedEvery, this.preClosureInterestRate, this.interestCompoundedEvery); } this.interestPostedAmount = this.interestPostedAmount.add(futureValueOnMaturity.minus(deposit).getAmount()); this.availableInterest = this.availableInterest.add(futureValueOnMaturity.minus(deposit).getAmount()); @@ -828,7 +833,8 @@ public class DepositAccount extends AbstractAuditableCustom { this.nextInterestPostingDate = nextInterestPostingDate.plusMonths(this.interestCompoundedEvery).toDate(); // add transaction entry for each interest posting - DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.postInterest(null, nextInterestPostingDate, futureValueOnMaturity.minus(deposit),deposit.getAmount().add(this.availableInterest)); + DepositAccountTransaction depositaccountTransaction = DepositAccountTransaction.postInterest(null, nextInterestPostingDate, + futureValueOnMaturity.minus(deposit), deposit.getAmount().add(this.availableInterest)); depositaccountTransaction.updateAccount(this); this.depositaccountTransactions.add(depositaccountTransaction); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/FixedTermDepositInterestCalculator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/FixedTermDepositInterestCalculator.java index cd3b73300..48114a839 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/FixedTermDepositInterestCalculator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savingsdepositaccount/domain/FixedTermDepositInterestCalculator.java @@ -10,25 +10,14 @@ import java.math.MathContext; import java.math.RoundingMode; import org.mifosplatform.organisation.monetary.domain.Money; -import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType; import org.springframework.stereotype.Service; @Service public class FixedTermDepositInterestCalculator { - // FIXME - HUGO - Are the unused field here needed? public Money calculateInterestOnMaturityFor(final Money deposit, final Integer tenureInMonths, final BigDecimal maturityInterestRate, - final Integer interestCompoundedEvery, final PeriodFrequencyType interestCompoundedFrequencyPeriodType) { + final Integer interestCompoundedEvery) { - // TODO - HUGO - Can change 'interest compounding' fields into something - // simpler like interestCompoundedFrequency {None (Simple Interest), - // Monthly, Quarterly, Half-Yearly, Annually} - // - the 'interestCompoundedEvery' would then be inferred form the - // selected type. - // - the special case of 'simple interest' or 'no compounding' would use - // simple formula rather than 'future value' function. - - // FIXME - HUGO - assuming only monthly compounding period for now MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN); Integer monthsInYear = 12; @@ -72,8 +61,8 @@ public class FixedTermDepositInterestCalculator { return retval; } - public Money calculateInterestOnMaturityForSimpleInterest(final Money deposit, final Integer tenureInMonths, final BigDecimal maturityInterestRate, - final Integer interestCompoundedEvery, final PeriodFrequencyType interestCompoundedFrequencyPeriodType) { + public Money calculateInterestOnMaturityForSimpleInterest(final Money deposit, final Integer tenureInMonths, + final BigDecimal maturityInterestRate, final Integer interestCompoundedEvery) { MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN); Integer monthsInYear = 12;