mifosx-172: wip - support adjust transaction for accounting.

This commit is contained in:
Keith Woodlock
2013-02-19 00:45:33 +00:00
parent bb63d2cb2d
commit 87eb0a6fe8
6 changed files with 163 additions and 105 deletions
@@ -1087,10 +1087,6 @@ CREATE TABLE `m_loan_transaction` (
`transaction_type_enum` smallint(5) NOT NULL, `transaction_type_enum` smallint(5) NOT NULL,
`transaction_date` date NOT NULL, `transaction_date` date NOT NULL,
`amount` decimal(19,6) NOT NULL, `amount` decimal(19,6) NOT NULL,
`createdby_id` bigint(20) DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
`lastmodifiedby_id` bigint(20) DEFAULT NULL,
`principal_portion_derived` decimal(19,6) DEFAULT NULL, `principal_portion_derived` decimal(19,6) DEFAULT NULL,
`interest_portion_derived` decimal(19,6) DEFAULT NULL, `interest_portion_derived` decimal(19,6) DEFAULT NULL,
`fee_charges_portion_derived` decimal(19,6) DEFAULT NULL, `fee_charges_portion_derived` decimal(19,6) DEFAULT NULL,
@@ -1937,4 +1933,4 @@ UNLOCK TABLES;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2013-02-18 13:20:24 -- Dump completed on 2013-02-19 0:44:13
@@ -9,3 +9,10 @@ ALTER TABLE `m_loan_transaction` DROP FOREIGN KEY `FKCFCEA426FC69F3F1`;
ALTER TABLE `m_loan_transaction` ALTER TABLE `m_loan_transaction`
DROP COLUMN `contra_id` , DROP COLUMN `contra_id` ,
DROP INDEX `FKCFCEA426FC69F3F1`; DROP INDEX `FKCFCEA426FC69F3F1`;
ALTER TABLE `m_loan_transaction`
DROP COLUMN `lastmodifiedby_id`,
DROP COLUMN `lastmodified_date`,
DROP COLUMN `created_date`,
DROP COLUMN `createdby_id`;
@@ -165,11 +165,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@Column(name = "expected_disbursedon_date") @Column(name = "expected_disbursedon_date")
private Date expectedDisbursedOnDate; private Date expectedDisbursementDate;
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@Column(name = "disbursedon_date") @Column(name = "disbursedon_date")
private Date disbursedOnDate; private Date actualDisbursementDate;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ManyToOne(optional = true) @ManyToOne(optional = true)
@@ -199,7 +199,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@Column(name = "maturedon_date") @Column(name = "maturedon_date")
private Date maturedOnDate; private Date actualMaturityDate;
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@Column(name = "expected_firstrepaymenton_date") @Column(name = "expected_firstrepaymenton_date")
@@ -387,12 +387,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategy);
if (!loanCharge.isDueAtDisbursement()) { // TODO - only need to // TODO - only need to reprocess transactions against loan schedule
// reprocess transactions // if loan charge is added with due date before latest transaction.
// against loan schedule if if (!loanCharge.isDueAtDisbursement()) {
// loan charge is added with
// due date before latest
// transaction.
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
getCurrency(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges()); getCurrency(), this.repaymentScheduleInstallments, getNullPointerSafeLoanCharges());
@@ -640,6 +637,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public void updateLoanScheduleDependentDerivedFields() { public void updateLoanScheduleDependentDerivedFields() {
this.expectedMaturityDate = determineExpectedMaturityDate().toDate(); this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
this.actualMaturityDate = determineExpectedMaturityDate().toDate();
} }
public Map<String, Object> loanApplicationModification(final JsonCommand command, final Set<LoanCharge> possiblyModifedLoanCharges, public Map<String, Object> loanApplicationModification(final JsonCommand command, final Set<LoanCharge> possiblyModifedLoanCharges,
@@ -737,7 +735,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
actualChanges.put("recalculateLoanSchedule", true); actualChanges.put("recalculateLoanSchedule", true);
final LocalDate newValue = command.localDateValueOfParameterNamed(expectedDisbursementDateParamName); final LocalDate newValue = command.localDateValueOfParameterNamed(expectedDisbursementDateParamName);
this.expectedDisbursedOnDate = newValue.toDate(); this.expectedDisbursementDate = newValue.toDate();
removeFirstDisbursementTransaction(); removeFirstDisbursementTransaction();
disburse(getExpectedDisbursedOnLocalDate()); disburse(getExpectedDisbursedOnLocalDate());
} }
@@ -894,7 +892,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
// Have to set expectedDisbursementDate to avoid nullPointer so should // Have to set expectedDisbursementDate to avoid nullPointer so should
// be passed down to updateLoanSchedule method // be passed down to updateLoanSchedule method
if (loanSchedule.getDisbursementDate() != null) { if (loanSchedule.getDisbursementDate() != null) {
this.expectedDisbursedOnDate = loanSchedule.getDisbursementDate().toDate(); this.expectedDisbursementDate = loanSchedule.getDisbursementDate().toDate();
} }
updateLoanSchedule(loanScheduleData); updateLoanSchedule(loanScheduleData);
@@ -914,7 +912,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.submittedOnDate = submittedOn.toDate(); this.submittedOnDate = submittedOn.toDate();
this.submittedBy = currentUser; this.submittedBy = currentUser;
this.expectedMaturityDate = determineExpectedMaturityDate().toDate(); updateLoanScheduleDependentDerivedFields();
if (loanSchedule.getRepaymentStartFromDate() != null) { if (loanSchedule.getRepaymentStartFromDate() != null) {
this.expectedFirstRepaymentOnDate = loanSchedule.getRepaymentStartFromDate().toDate(); this.expectedFirstRepaymentOnDate = loanSchedule.getRepaymentStartFromDate().toDate();
@@ -1101,8 +1099,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
private void disburse(final LocalDate expectedDisbursedOnLocalDate) { private void disburse(final LocalDate expectedDisbursedOnLocalDate) {
this.disbursedOnDate = expectedDisbursedOnLocalDate.toDate(); this.actualDisbursementDate = expectedDisbursedOnLocalDate.toDate();
this.expectedMaturityDate = determineExpectedMaturityDate().toDate(); updateLoanScheduleDependentDerivedFields();
handleDisbursementTransaction(expectedDisbursedOnLocalDate); handleDisbursementTransaction(expectedDisbursedOnLocalDate);
} }
@@ -1147,26 +1145,19 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanStatus = statusEnum.getValue(); this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
LocalDate disbursedOn = command.localDateValueOfParameterNamed("disbursedOnDate"); final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
if (disbursedOn == null) {
disbursedOn = command.localDateValueOfParameterNamed("eventDate");
}
final Locale locale = new Locale(command.locale()); this.actualDisbursementDate = actualDisbursementDate.toDate();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
this.disbursedOnDate = disbursedOn.toDate();
this.disbursedBy = currentUser; this.disbursedBy = currentUser;
this.expectedMaturityDate = determineExpectedMaturityDate().toDate(); updateLoanScheduleDependentDerivedFields();
actualChanges.put("locale", command.locale()); actualChanges.put("locale", command.locale());
actualChanges.put("dateFormat", command.dateFormat()); actualChanges.put("dateFormat", command.dateFormat());
actualChanges.put("disbursedOnDate", disbursedOn.toString(fmt)); actualChanges.put("actualDisbursementDate", command.stringValueOfParameterNamed("actualDisbursementDate"));
actualChanges.put("expectedMaturityDate", LocalDate.fromDateFields(this.expectedDisbursedOnDate).toString(fmt));
handleDisbursementTransaction(disbursedOn); handleDisbursementTransaction(actualDisbursementDate);
if (isRepaymentScheduleRegenerationRequiredForDisbursement(disbursedOn)) { if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) {
regenerateRepaymentSchedule(currency); regenerateRepaymentSchedule(currency);
} }
} }
@@ -1267,9 +1258,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanStatus = statusEnum.getValue(); this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
this.disbursedOnDate = null; this.actualDisbursementDate = null;
this.disbursedBy = null; this.disbursedBy = null;
actualChanges.put("disbursedOnDate", ""); actualChanges.put("actualDisbursementDate", "");
existingTransactionIds.addAll(findExistingTransactionIds()); existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
@@ -1290,7 +1281,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
private void updateLoanToPreDisbursalState() { private void updateLoanToPreDisbursalState() {
this.disbursedOnDate = null; this.actualDisbursementDate = null;
for (LoanCharge charge : getNullPointerSafeLoanCharges()) { for (LoanCharge charge : getNullPointerSafeLoanCharges()) {
charge.resetToOriginal(loanCurrency()); charge.resetToOriginal(loanCurrency());
@@ -1318,11 +1309,15 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
public LoanTransaction makeRepayment(final LocalDate transactionDate, final BigDecimal transactionAmount, public LoanTransaction makeRepayment(final LocalDate transactionDate, final BigDecimal transactionAmount,
final LoanLifecycleStateMachine loanLifecycleStateMachine) { final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final Money repayment = Money.of(loanCurrency(), transactionAmount); final Money repayment = Money.of(loanCurrency(), transactionAmount);
final LoanTransaction loanTransaction = LoanTransaction.repayment(repayment, transactionDate); final LoanTransaction loanTransaction = LoanTransaction.repayment(repayment, transactionDate);
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
handleRepaymentOrWaiverTransaction(loanTransaction, loanLifecycleStateMachine, null); handleRepaymentOrWaiverTransaction(loanTransaction, loanLifecycleStateMachine, null);
return loanTransaction; return loanTransaction;
@@ -1418,7 +1413,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanStatus = statusEnum.getValue(); this.loanStatus = statusEnum.getValue();
this.closedOnDate = transactionDate.toDate(); this.closedOnDate = transactionDate.toDate();
this.maturedOnDate = transactionDate.toDate(); this.actualMaturityDate = transactionDate.toDate();
} }
private void handleLoanOverpayment(final LoanLifecycleStateMachine loanLifecycleStateMachine) { private void handleLoanOverpayment(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
@@ -1427,7 +1422,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanStatus = statusEnum.getValue(); this.loanStatus = statusEnum.getValue();
this.closedOnDate = null; this.closedOnDate = null;
this.maturedOnDate = null; this.actualMaturityDate = null;
} }
private boolean isChronologicallyLatestRepaymentOrWaiver(final LoanTransaction loanTransaction, private boolean isChronologicallyLatestRepaymentOrWaiver(final LoanTransaction loanTransaction,
@@ -1530,7 +1525,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
public LoanTransaction adjustExistingTransaction(final LocalDate transactionDate, final BigDecimal transactionAmountValue, public LoanTransaction adjustExistingTransaction(final LocalDate transactionDate, final BigDecimal transactionAmountValue,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment) { final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment,
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final Money transactionAmount = Money.of(loanCurrency(), transactionAmountValue); final Money transactionAmount = Money.of(loanCurrency(), transactionAmountValue);
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, transactionDate); LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, transactionDate);
@@ -1914,8 +1913,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public LocalDate getDisbursedOnDate() { public LocalDate getDisbursedOnDate() {
LocalDate date = null; LocalDate date = null;
if (this.disbursedOnDate != null) { if (this.actualDisbursementDate != null) {
date = new LocalDate(this.disbursedOnDate); date = new LocalDate(this.actualDisbursementDate);
} }
return date; return date;
} }
@@ -1937,13 +1936,13 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
public Date getExpectedDisbursedOnDate() { public Date getExpectedDisbursedOnDate() {
return this.expectedDisbursedOnDate; return this.expectedDisbursementDate;
} }
public LocalDate getExpectedDisbursedOnLocalDate() { public LocalDate getExpectedDisbursedOnLocalDate() {
LocalDate expectedDisbursementDate = null; LocalDate expectedDisbursementDate = null;
if (this.expectedDisbursedOnDate != null) { if (this.expectedDisbursementDate != null) {
expectedDisbursementDate = new LocalDate(this.expectedDisbursedOnDate); expectedDisbursementDate = new LocalDate(this.expectedDisbursementDate);
} }
return expectedDisbursementDate; return expectedDisbursementDate;
} }
@@ -1958,8 +1957,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public LocalDate getDisbursementDate() { public LocalDate getDisbursementDate() {
LocalDate disbursementDate = getExpectedDisbursedOnLocalDate(); LocalDate disbursementDate = getExpectedDisbursedOnLocalDate();
if (this.disbursedOnDate != null) { if (this.actualDisbursementDate != null) {
disbursementDate = new LocalDate(this.disbursedOnDate); disbursementDate = new LocalDate(this.actualDisbursementDate);
} }
return disbursementDate; return disbursementDate;
} }
@@ -1974,8 +1973,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public LocalDate getActualMaturityDate() { public LocalDate getActualMaturityDate() {
LocalDate possibleMaturityDate = null; LocalDate possibleMaturityDate = null;
if (this.maturedOnDate != null) { if (this.actualMaturityDate != null) {
possibleMaturityDate = new LocalDate(this.maturedOnDate); possibleMaturityDate = new LocalDate(this.actualMaturityDate);
} }
return possibleMaturityDate; return possibleMaturityDate;
} }
@@ -1986,8 +1985,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (this.expectedMaturityDate != null) { if (this.expectedMaturityDate != null) {
possibleMaturityDate = new LocalDate(this.expectedMaturityDate); possibleMaturityDate = new LocalDate(this.expectedMaturityDate);
} }
if (this.maturedOnDate != null) { if (this.actualMaturityDate != null) {
possibleMaturityDate = new LocalDate(this.maturedOnDate); possibleMaturityDate = new LocalDate(this.actualMaturityDate);
} }
return possibleMaturityDate; return possibleMaturityDate;
} }
@@ -2006,7 +2005,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
public boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) { public boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) {
return !new LocalDate(this.expectedDisbursedOnDate).isEqual(actualDisbursedOnDate); return !new LocalDate(this.expectedDisbursementDate).isEqual(actualDisbursedOnDate);
} }
public boolean isRepaymentScheduleRegenerationRequiredForDisbursement(final LocalDate actualDisbursementDate) { public boolean isRepaymentScheduleRegenerationRequiredForDisbursement(final LocalDate actualDisbursementDate) {
@@ -2090,7 +2089,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
if (isDisbursed()) { if (isDisbursed()) {
statusSinceDate = new LocalDate(this.disbursedOnDate); statusSinceDate = new LocalDate(this.actualDisbursementDate);
} }
if (isClosed()) { if (isClosed()) {
@@ -19,14 +19,13 @@ import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.data.jpa.domain.AbstractPersistable;
/** /**
* All monetary transactions against a loan are modelled through this entity. * All monetary transactions against a loan are modelled through this entity.
@@ -34,12 +33,19 @@ import org.mifosplatform.useradministration.domain.AppUser;
*/ */
@Entity @Entity
@Table(name = "m_loan_transaction") @Table(name = "m_loan_transaction")
public final class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> { public final class LoanTransaction extends AbstractPersistable<Long> {
@ManyToOne(optional = false) @ManyToOne(optional = false)
@JoinColumn(name = "loan_id", nullable = false) @JoinColumn(name = "loan_id", nullable = false)
private Loan loan; private Loan loan;
@Column(name = "transaction_type_enum", nullable = false)
private final Integer typeOf;
@Temporal(TemporalType.DATE)
@Column(name = "transaction_date", nullable = false)
private final Date dateOf;
@Column(name = "amount", scale = 6, precision = 19, nullable = false) @Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount; private BigDecimal amount;
@@ -55,13 +61,6 @@ public final class LoanTransaction extends AbstractAuditableCustom<AppUser, Long
@Column(name = "penalty_charges_portion_derived", scale = 6, precision = 19, nullable = true) @Column(name = "penalty_charges_portion_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal penaltyChargesPortion = BigDecimal.ZERO; private BigDecimal penaltyChargesPortion = BigDecimal.ZERO;
@Temporal(TemporalType.DATE)
@Column(name = "transaction_date", nullable = false)
private final Date dateOf;
@Column(name = "transaction_type_enum", nullable = false)
private final Integer typeOf;
@Column(name = "is_reversed", nullable = false) @Column(name = "is_reversed", nullable = false)
private boolean reversed; private boolean reversed;
@@ -0,0 +1,66 @@
/**
* 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.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
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.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public final class LoanEventApiJsonValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public LoanEventApiJsonValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateDisbursement(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("actualDisbursementDate", "note", "locale",
"dateFormat"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.disbursement");
final JsonElement element = fromApiJsonHelper.parse(json);
final LocalDate actualDisbursementDate = fromApiJsonHelper.extractLocalDateNamed("actualDisbursementDate", element);
baseDataValidator.reset().parameter("actualDisbursementDate").value(actualDisbursementDate).notNull();
final String note = fromApiJsonHelper.extractStringNamed("note", element);
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -34,7 +34,6 @@ import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
import org.mifosplatform.portfolio.client.domain.Note; import org.mifosplatform.portfolio.client.domain.Note;
import org.mifosplatform.portfolio.client.domain.NoteRepository; import org.mifosplatform.portfolio.client.domain.NoteRepository;
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
import org.mifosplatform.portfolio.loanaccount.command.LoanStateTransitionCommand;
import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand;
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
@@ -51,7 +50,7 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentEx
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException; import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanChargeCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanChargeCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanStateTransitionCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanTransactionCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanTransactionCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException; import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
@@ -64,7 +63,7 @@ import org.springframework.transaction.annotation.Transactional;
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService { public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer; private final LoanEventApiJsonValidator loanEventApiJsonValidator;
private final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer; private final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer;
private final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer; private final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer;
private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer; private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer;
@@ -79,7 +78,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Autowired @Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer, final LoanEventApiJsonValidator loanEventApiJsonValidator,
final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer, final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer,
final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer, final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer,
final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanAssembler loanAssembler, final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanAssembler loanAssembler,
@@ -88,7 +87,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final ApplicationCurrencyRepository applicationCurrencyRepository, final ApplicationCurrencyRepository applicationCurrencyRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService) { final JournalEntryWritePlatformService journalEntryWritePlatformService) {
this.context = context; this.context = context;
this.loanStateTransitionCommandFromApiJsonDeserializer = loanStateTransitionCommandFromApiJsonDeserializer; this.loanEventApiJsonValidator = loanEventApiJsonValidator;
this.loanTransactionCommandFromApiJsonDeserializer = loanTransactionCommandFromApiJsonDeserializer; this.loanTransactionCommandFromApiJsonDeserializer = loanTransactionCommandFromApiJsonDeserializer;
this.loanChargeCommandFromApiJsonDeserializer = loanChargeCommandFromApiJsonDeserializer; this.loanChargeCommandFromApiJsonDeserializer = loanChargeCommandFromApiJsonDeserializer;
this.loanAssembler = loanAssembler; this.loanAssembler = loanAssembler;
@@ -117,18 +116,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final AppUser currentUser = context.authenticatedUser(); final AppUser currentUser = context.authenticatedUser();
final LoanStateTransitionCommand disburseLoan = this.loanStateTransitionCommandFromApiJsonDeserializer.commandFromApiJson(command this.loanEventApiJsonValidator.validateDisbursement(command.json());
.json());
disburseLoan.validate();
final Loan loan = retrieveLoanBy(loanId); final Loan loan = retrieveLoanBy(loanId);
final String noteText = command.stringValueOfParameterNamed("note"); final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
final LocalDate actualDisbursementDate = disburseLoan.getDisbursedOnDate();
if (this.isBeforeToday(actualDisbursementDate) && currentUser.canNotDisburseLoanInPast()) { throw new NoAuthorizationException(
"User has no authority to disburse loan with a date in the past."); }
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getPrincpal().getCurrencyCode());
final List<Long> existingTransactionIds = new ArrayList<Long>(); final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(); final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
@@ -137,6 +129,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.loanRepository.save(loan); this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) { if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText); Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note); this.noteRepository.save(note);
@@ -171,7 +164,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> changes = loan.undoDisbursal(defaultLoanLifecycleStateMachine(), existingTransactionIds, final Map<String, Object> changes = loan.undoDisbursal(defaultLoanLifecycleStateMachine(), existingTransactionIds,
existingReversedTransactionIds); existingReversedTransactionIds);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.loanRepository.saveAndFlush(loan); this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) { if (StringUtils.isNotBlank(noteText)) {
@@ -218,7 +211,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (this.isBeforeToday(transactionDate) && currentUser.canNotMakeRepaymentOnLoanInPast()) { throw new NoAuthorizationException( if (this.isBeforeToday(transactionDate) && currentUser.canNotMakeRepaymentOnLoanInPast()) { throw new NoAuthorizationException(
"error.msg.no.permission.to.make.repayment.on.loan.in.past"); } "error.msg.no.permission.to.make.repayment.on.loan.in.past"); }
final LoanTransaction loanRepayment = loan.makeRepayment(transactionDate, transactionAmount, defaultLoanLifecycleStateMachine()); final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final LoanTransaction loanRepayment = loan.makeRepayment(transactionDate, transactionAmount, defaultLoanLifecycleStateMachine(),
existingTransactionIds, existingReversedTransactionIds);
this.loanTransactionRepository.save(loanRepayment); this.loanTransactionRepository.save(loanRepayment);
this.loanRepository.save(loan); this.loanRepository.save(loan);
@@ -232,11 +229,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("locale", command.locale()); changes.put("locale", command.locale());
changes.put("dateFormat", command.dateFormat()); changes.put("dateFormat", command.dateFormat());
// make a call to accounting final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
if (loan.isAccountingEnabledOnLoanProduct()) { final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
LoanDTO loanDTO = populateLoanData(loan, loanRepayment); existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO); journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
@@ -271,15 +267,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount")); changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount"));
boolean transactionIsRepayment = transactionToAdjust.isRepayment(); final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final LoanTransaction newTransactionDetail = loan.adjustExistingTransaction(transactionDate, transactionAmount, final LoanTransaction newTransactionDetail = loan.adjustExistingTransaction(transactionDate, transactionAmount,
defaultLoanLifecycleStateMachine(), transactionToAdjust); defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds);
if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) { if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
this.loanTransactionRepository.saveAndFlush(newTransactionDetail); this.loanTransactionRepository.save(newTransactionDetail);
} }
this.loanRepository.saveAndFlush(loan); this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) { if (StringUtils.isNotBlank(noteText)) {
@@ -291,17 +289,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("locale", command.locale()); changes.put("locale", command.locale());
changes.put("dateFormat", command.dateFormat()); changes.put("dateFormat", command.dateFormat());
// make a call to accounting only if transaction being adjusted is a final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
// repayment (not waiver) final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
if (loan.isAccountingEnabledOnLoanProduct() && transactionIsRepayment) { existingReversedTransactionIds);
List<LoanTransaction> newLoanTransactions = new ArrayList<LoanTransaction>(); journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
if (newTransactionDetail.getId() != null) {
newLoanTransactions.add(newTransactionDetail);
}
newLoanTransactions.add(transactionToAdjust);
LoanDTO loanDTO = populateLoanDTO(loan, newLoanTransactions);
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO);
}
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
@@ -655,7 +646,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.reassignLoanOfficer(toLoanOfficer, dateOfLoanOfficerAssignment); loan.reassignLoanOfficer(toLoanOfficer, dateOfLoanOfficerAssignment);
this.loanRepository.saveAndFlush(loan); this.loanRepository.save(loan);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
@@ -717,7 +708,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.removeLoanOfficer(dateOfLoanOfficerunAssigned); loan.removeLoanOfficer(dateOfLoanOfficerunAssigned);
this.loanRepository.saveAndFlush(loan); this.loanRepository.save(loan);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //