MIFOSX-172: wip - support for loan disbursal and undo with reversing transactions.

This commit is contained in:
Keith Woodlock
2013-02-18 15:32:56 +00:00
parent 4030608442
commit bb63d2cb2d
10 changed files with 550 additions and 596 deletions
@@ -1085,7 +1085,6 @@ CREATE TABLE `m_loan_transaction` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`loan_id` bigint(20) NOT NULL,
`transaction_type_enum` smallint(5) NOT NULL,
`contra_id` bigint(20) DEFAULT NULL,
`transaction_date` date NOT NULL,
`amount` decimal(19,6) NOT NULL,
`createdby_id` bigint(20) DEFAULT NULL,
@@ -1096,11 +1095,10 @@ CREATE TABLE `m_loan_transaction` (
`interest_portion_derived` decimal(19,6) DEFAULT NULL,
`fee_charges_portion_derived` decimal(19,6) DEFAULT NULL,
`penalty_charges_portion_derived` decimal(19,6) DEFAULT NULL,
`is_reversed` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `FKCFCEA42640BE0710` (`loan_id`),
KEY `FKCFCEA426FC69F3F1` (`contra_id`),
CONSTRAINT `FKCFCEA42640BE0710` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`),
CONSTRAINT `FKCFCEA426FC69F3F1` FOREIGN KEY (`contra_id`) REFERENCES `m_loan_transaction` (`id`)
CONSTRAINT `FKCFCEA42640BE0710` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1939,4 +1937,4 @@ UNLOCK TABLES;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2013-02-14 23:47:02
-- Dump completed on 2013-02-18 13:20:24
@@ -0,0 +1,11 @@
ALTER TABLE `m_loan_transaction`
ADD COLUMN `is_reversed` TINYINT(1) NOT NULL AFTER `penalty_charges_portion_derived`;
-- === update is_reversed column to true for all fields where contra_id is not null
UPDATE `m_loan_transaction` SET `is_reversed` = 1 WHERE contra_id is not null;
ALTER TABLE `m_loan_transaction` DROP FOREIGN KEY `FKCFCEA426FC69F3F1`;
ALTER TABLE `m_loan_transaction`
DROP COLUMN `contra_id` ,
DROP INDEX `FKCFCEA426FC69F3F1`;
@@ -221,7 +221,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
}
/*** Debit loan Portfolio and credit Fund source for Disbursal **/
if (loanTransactionDTO.isDisbursement()) {
if (loanTransactionDTO.isDisbursement() && !loanTransactionDTO.isContra()) {
final GLAccount loanPortfolioAccount = getLinkedFinAccountForLoanProduct(loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO);
@@ -1106,40 +1106,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
handleDisbursementTransaction(expectedDisbursedOnLocalDate);
}
public Map<String, Object> deriveDisbursementData(final CurrencyData currencyData, final List<Long> existingTransactionIds) {
final Map<String, Object> accountingBridgeData = new LinkedHashMap<String, Object>(20);
accountingBridgeData.put("loanId", this.getId());
accountingBridgeData.put("loanProductId", this.productId());
accountingBridgeData.put("officeId", this.getOfficeId());
accountingBridgeData.put("cashBasedAccountingEnabled", this.isCashBasedAccountingEnabledOnLoanProduct());
accountingBridgeData.put("accrualBasedAccountingEnabled", this.isAccrualBasedAccountingEnabledOnLoanProduct());
final List<Map<String, Object>> newLoanTransactions = new ArrayList<Map<String, Object>>();
final List<LoanTransaction> disbursementTransactions = findDisbursementTransactions();
for (LoanTransaction loanTransaction : disbursementTransactions) {
if (!existingTransactionIds.contains(loanTransaction.getId())) {
newLoanTransactions.add(loanTransaction.toMapData(currencyData));
}
}
accountingBridgeData.put("newLoanTransactions", newLoanTransactions);
return accountingBridgeData;
}
private List<LoanTransaction> findDisbursementTransactions() {
List<LoanTransaction> found = new ArrayList<LoanTransaction>();
for (LoanTransaction transaction : this.loanTransactions) {
if (transaction.isDisbursement() || transaction.isRepaymentAtDisbursement()) {
found.add(transaction);
}
}
return found;
}
private Collection<Long> findExistingTransactionIds() {
Collection<Long> ids = new ArrayList<Long>();
@@ -1151,9 +1117,22 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return ids;
}
private Collection<Long> findExistingReversedTransactionIds() {
final Collection<Long> ids = new ArrayList<Long>();
for (LoanTransaction transaction : this.loanTransactions) {
if (transaction.isReversed()) {
ids.add(transaction.getId());
}
}
return ids;
}
public Map<String, Object> disburse(final AppUser currentUser, final JsonCommand command,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final ApplicationCurrency currency,
final List<Long> existingTransactionIds) {
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
@@ -1161,6 +1140,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, LoanStatus.fromInt(this.loanStatus));
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
@@ -1186,8 +1169,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (isRepaymentScheduleRegenerationRequiredForDisbursement(disbursedOn)) {
regenerateRepaymentSchedule(currency);
}
existingTransactionIds.addAll(findExistingTransactionIds());
}
return actualChanges;
@@ -1276,7 +1257,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
}
public Map<String, Object> undoDisbursal(final LoanLifecycleStateMachine loanLifecycleStateMachine,
final List<Long> existingTransactionIds) {
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
@@ -1291,8 +1272,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
actualChanges.put("disbursedOnDate", "");
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
contraExistingTransactions();
reverseExistingTransactions();
updateLoanToPreDisbursalState();
}
@@ -1300,18 +1282,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return actualChanges;
}
private final void contraExistingTransactions() {
private final void reverseExistingTransactions() {
// final List<LoanTransaction> contras = new
// ArrayList<LoanTransaction>();
for (LoanTransaction transaction : this.loanTransactions) {
if (transaction.isNotContra()) {
LoanTransaction contra = transaction.contra();
// contras.add(contra);
}
transaction.reverse();
}
// this.loanTransactions.addAll(contras);
}
private void updateLoanToPreDisbursalState() {
@@ -1415,7 +1390,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
private List<LoanTransaction> retreiveListOfTransactionsPostDisbursement() {
List<LoanTransaction> repaymentsOrWaivers = new ArrayList<LoanTransaction>();
for (LoanTransaction transaction : this.loanTransactions) {
if (!transaction.isDisbursement() && transaction.isNotContra()) {
if (!transaction.isDisbursement() && transaction.isNotReversed()) {
repaymentsOrWaivers.add(transaction);
}
}
@@ -1462,7 +1437,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
LocalDate currentTransactionDate = loanTransaction.getTransactionDate();
for (LoanTransaction previousTransaction : loanTransactions) {
if (!previousTransaction.isDisbursement() && previousTransaction.isNotContra()) {
if (!previousTransaction.isDisbursement() && previousTransaction.isNotReversed()) {
if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate())
|| currentTransactionDate.isEqual(previousTransaction.getTransactionDate())) {
isChronologicallyLatestRepaymentOrWaiver = false;
@@ -1480,7 +1455,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
LocalDate currentTransactionDate = loanTransaction.getTransactionDate();
for (LoanTransaction previousTransaction : loanTransactions) {
if (previousTransaction.isNotContra()) {
if (previousTransaction.isNotReversed()) {
if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate())
|| currentTransactionDate.isEqual(previousTransaction.getTransactionDate())) {
isChronologicallyLatestRepaymentOrWaiver = false;
@@ -1574,7 +1549,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
throw new InvalidLoanTransactionTypeException("transaction", "adjustment.is.not.allowed.on.closed.loan", errorMessage);
}
transactionForAdjustment.contra();
transactionForAdjustment.reverse();
if (newTransactionDetail.isRepayment() || newTransactionDetail.isInterestWaiver()) {
handleRepaymentOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment);
}
@@ -2137,7 +2112,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return getCurrencyCode().equalsIgnoreCase(matchingCurrencyCode);
}
private String getCurrencyCode() {
public String getCurrencyCode() {
return this.loanRepaymentScheduleDetail.getPrincipal().getCurrencyCode();
}
@@ -2256,7 +2231,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return this.loanOfficer;
}
public Map<String, Object> deriveAccountingBridgeData(final CurrencyData currencyData, final List<Long> existingTransactionIds) {
public Map<String, Object> deriveAccountingBridgeData(final CurrencyData currencyData, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final Map<String, Object> accountingBridgeData = new LinkedHashMap<String, Object>();
accountingBridgeData.put("loanId", this.getId());
@@ -2267,7 +2243,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
final List<Map<String, Object>> newLoanTransactions = new ArrayList<Map<String, Object>>();
for (LoanTransaction transaction : this.loanTransactions) {
if (!existingTransactionIds.contains(transaction.getId())) {
if (transaction.isReversed() && !existingReversedTransactionIds.contains(transaction.getId())) {
newLoanTransactions.add(transaction.toMapData(currencyData));
} else if (!existingTransactionIds.contains(transaction.getId())) {
newLoanTransactions.add(transaction.toMapData(currencyData));
}
}
@@ -28,82 +28,76 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
@ManyToOne(optional = false)
@JoinColumn(name = "loan_id")
private Loan loan;
private Loan loan;
@Column(name = "installment", nullable=false)
@Column(name = "installment", nullable = false)
private final Integer installmentNumber;
@Temporal(TemporalType.DATE)
@Column(name = "fromdate", nullable=true)
private final Date fromDate;
@Column(name = "fromdate", nullable = true)
private final Date fromDate;
@Temporal(TemporalType.DATE)
@Column(name = "duedate", nullable=false)
private final Date dueDate;
@Column(name = "principal_amount", scale=6, precision=19, nullable=true)
private BigDecimal principal;
@Column(name = "principal_completed_derived", scale=6, precision=19, nullable=true)
private BigDecimal principalCompleted;
@Column(name = "principal_writtenoff_derived", scale=6, precision=19, nullable=true)
private BigDecimal principalWrittenOff;
@Column(name = "interest_amount", scale=6, precision=19, nullable=true)
private BigDecimal interest;
@Column(name = "interest_completed_derived", scale = 6, precision = 19, nullable=true)
private BigDecimal interestCompleted;
@Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable=true)
private BigDecimal interestWaived;
@Column(name = "interest_writtenoff_derived", scale=6, precision=19, nullable=true)
private BigDecimal interestWrittenOff;
@Column(name = "fee_charges_amount", scale=6, precision=19, nullable=true)
private BigDecimal feeCharges;
@Column(name = "fee_charges_completed_derived", scale=6, precision=19, nullable=true)
private BigDecimal feeChargesCompleted;
@Column(name = "fee_charges_writtenoff_derived", scale=6, precision=19, nullable=true)
private BigDecimal feeChargesWrittenOff;
@Column(name = "fee_charges_waived_derived", scale=6, precision=19, nullable=true)
private BigDecimal feeChargesWaived;
@Column(name = "penalty_charges_amount", scale=6, precision=19, nullable=true)
private BigDecimal penaltyCharges;
@Column(name = "penalty_charges_completed_derived", scale=6, precision=19, nullable=true)
private BigDecimal penaltyChargesCompleted;
@Column(name = "penalty_charges_writtenoff_derived", scale=6, precision=19, nullable=true)
private BigDecimal penaltyChargesWrittenOff;
@Column(name = "penalty_charges_waived_derived", scale=6, precision=19, nullable=true)
private BigDecimal penaltyChargesWaived;
@Column(name="completed_derived", nullable=false)
@Column(name = "duedate", nullable = false)
private final Date dueDate;
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal principal;
@Column(name = "principal_completed_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal principalCompleted;
@Column(name = "principal_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal principalWrittenOff;
@Column(name = "interest_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal interest;
@Column(name = "interest_completed_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestCompleted;
@Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestWaived;
@Column(name = "interest_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestWrittenOff;
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal feeCharges;
@Column(name = "fee_charges_completed_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal feeChargesCompleted;
@Column(name = "fee_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal feeChargesWrittenOff;
@Column(name = "fee_charges_waived_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal feeChargesWaived;
@Column(name = "penalty_charges_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal penaltyCharges;
@Column(name = "penalty_charges_completed_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal penaltyChargesCompleted;
@Column(name = "penalty_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal penaltyChargesWrittenOff;
@Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal penaltyChargesWaived;
@Column(name = "completed_derived", nullable = false)
private boolean completed;
protected LoanRepaymentScheduleInstallment() {
this.installmentNumber = null;
this.fromDate = null;
this.dueDate = null;
this.installmentNumber = null;
this.fromDate = null;
this.dueDate = null;
this.completed = false;
}
public LoanRepaymentScheduleInstallment(
final Loan loan,
final Integer installmentNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principal,
final BigDecimal interest,
final BigDecimal feeCharges,
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate,
final LocalDate dueDate, final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges,
final BigDecimal penaltyCharges) {
this.loan = loan;
this.installmentNumber = installmentNumber;
@@ -116,15 +110,15 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
this.completed = false;
}
private BigDecimal defaultToNullIfZero(final BigDecimal value) {
BigDecimal result = value;
if (BigDecimal.ZERO.compareTo(value) == 0) {
result = null;
}
return result;
}
private BigDecimal defaultToNullIfZero(final BigDecimal value) {
BigDecimal result = value;
if (BigDecimal.ZERO.compareTo(value) == 0) {
result = null;
}
return result;
}
public Loan getLoan() {
public Loan getLoan() {
return this.loan;
}
@@ -133,324 +127,323 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
}
public LocalDate getFromDate() {
LocalDate fromLocalDate = null;
if (this.fromDate != null) {
fromLocalDate = new LocalDate(this.fromDate);
}
return fromLocalDate;
LocalDate fromLocalDate = null;
if (this.fromDate != null) {
fromLocalDate = new LocalDate(this.fromDate);
}
return fromLocalDate;
}
public LocalDate getDueDate() {
return new LocalDate(this.dueDate);
}
public Money getPrincipal(final MonetaryCurrency currency) {
return Money.of(currency, this.principal);
}
public Money getPrincipalCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.principalCompleted);
}
public Money getPrincipalWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.principalWrittenOff);
}
public Money getPrincipalOutstanding(final MonetaryCurrency currency) {
final Money principalAccountedFor = getPrincipalCompleted(currency).plus(getPrincipalWrittenOff(currency));
return getPrincipal(currency).minus(principalAccountedFor);
}
public Money getInterest(final MonetaryCurrency currency) {
return Money.of(currency, this.interest);
}
public Money getInterestCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.interestCompleted);
}
public Money getInterestWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.interestWaived);
}
public Money getInterestWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.interestWrittenOff);
}
public Money getInterestOutstanding(final MonetaryCurrency currency) {
final Money interestAccountedFor = getInterestCompleted(currency).plus(getInterestWaived(currency)).plus(getInterestWrittenOff(currency));
return getInterest(currency).minus(interestAccountedFor);
}
public Money getFeeCharges(final MonetaryCurrency currency) {
return Money.of(currency, this.feeCharges);
}
public Money getFeeChargesCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesCompleted);
}
public Money getFeeChargesWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesWaived);
}
public Money getFeeChargesWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesWrittenOff);
}
public Money getFeeChargesOutstanding(final MonetaryCurrency currency) {
final Money feeChargesAccountedFor = getFeeChargesCompleted(currency).plus(getFeeChargesWaived(currency)).plus(getFeeChargesWrittenOff(currency));
return getFeeCharges(currency).minus(feeChargesAccountedFor);
}
public Money getPenaltyCharges(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyCharges);
}
public Money getPenaltyChargesCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyChargesCompleted);
}
public Money getPenaltyChargesWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyChargesWaived);
}
public Money getPenaltyChargesWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyChargesWrittenOff);
}
public Money getPenaltyChargesOutstanding(final MonetaryCurrency currency) {
final Money feeChargesAccountedFor = getPenaltyChargesCompleted(currency).plus(getPenaltyChargesWaived(currency)).plus(getPenaltyChargesWrittenOff(currency));
return getPenaltyCharges(currency).minus(feeChargesAccountedFor);
}
public Money getPrincipal(final MonetaryCurrency currency) {
return Money.of(currency, this.principal);
}
public boolean isInterestDue(final MonetaryCurrency currency) {
return getInterestOutstanding(currency).isGreaterThanZero();
}
public Money getTotalPrincipalAndInterest(final MonetaryCurrency currency) {
return getPrincipal(currency).plus(getInterest(currency));
}
public Money getTotalOutstanding(final MonetaryCurrency currency) {
return getPrincipalOutstanding(currency).plus(getInterestOutstanding(currency)).plus(getFeeChargesOutstanding(currency)).plus(getPenaltyChargesOutstanding(currency));
}
public void updateLoan(final Loan loan) {
this.loan = loan;
}
public boolean isFullyCompleted() {
return this.completed;
}
public Money getPrincipalCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.principalCompleted);
}
public boolean isNotFullyCompleted() {
return !this.completed;
}
public boolean isPrincipalNotCompleted(final MonetaryCurrency currency) {
return !isPrincipalCompleted(currency);
}
public boolean isPrincipalCompleted(final MonetaryCurrency currency) {
return getPrincipalOutstanding(currency).isZero();
}
public Money getPrincipalWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.principalWrittenOff);
}
public void resetDerivedComponents() {
this.principalCompleted = null;
this.principalWrittenOff = null;
this.interestCompleted = null;
this.interestWaived = null;
this.interestWrittenOff = null;
this.feeChargesCompleted = null;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.penaltyChargesCompleted = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.completed = false;
}
public Money getPrincipalOutstanding(final MonetaryCurrency currency) {
final Money principalAccountedFor = getPrincipalCompleted(currency).plus(getPrincipalWrittenOff(currency));
return getPrincipal(currency).minus(principalAccountedFor);
}
public Money payPenaltyChargesComponent(final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money penaltyPortionOfTransaction = Money.zero(currency);
final Money penaltyChargesDue = getPenaltyChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(penaltyChargesDue)) {
this.penaltyChargesCompleted = getPenaltyChargesCompleted(currency).plus(penaltyChargesDue).getAmount();
penaltyPortionOfTransaction = penaltyPortionOfTransaction.plus(penaltyChargesDue);
} else {
this.penaltyChargesCompleted = getPenaltyChargesCompleted(currency).plus(transactionAmountRemaining).getAmount();
penaltyPortionOfTransaction = penaltyPortionOfTransaction.plus(transactionAmountRemaining);
}
this.penaltyChargesCompleted = defaultToNullIfZero(this.penaltyChargesCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return penaltyPortionOfTransaction;
}
public Money payFeeChargesComponent(final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money feePortionOfTransaction = Money.zero(currency);
final Money feeChargesDue = getFeeChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(feeChargesDue)) {
this.feeChargesCompleted = getFeeChargesCompleted(currency).plus(feeChargesDue).getAmount();
feePortionOfTransaction = feePortionOfTransaction.plus(feeChargesDue);
} else {
this.feeChargesCompleted = getFeeChargesCompleted(currency).plus(transactionAmountRemaining).getAmount();
feePortionOfTransaction = feePortionOfTransaction.plus(transactionAmountRemaining);
}
this.feeChargesCompleted = defaultToNullIfZero(this.feeChargesCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return feePortionOfTransaction;
}
public Money payInterestComponent(final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money interestPortionOfTransaction = Money.zero(currency);
final Money interestDue = getInterestOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) {
this.interestCompleted = getInterestCompleted(currency).plus(interestDue).getAmount();
interestPortionOfTransaction = interestPortionOfTransaction.plus(interestDue);
} else {
this.interestCompleted = getInterestCompleted(currency).plus(transactionAmountRemaining).getAmount();
interestPortionOfTransaction = interestPortionOfTransaction.plus(transactionAmountRemaining);
}
this.interestCompleted = defaultToNullIfZero(this.interestCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return interestPortionOfTransaction;
}
public Money getInterest(final MonetaryCurrency currency) {
return Money.of(currency, this.interest);
}
public Money payPrincipalComponent(final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money principalPortionOfTransaction = Money.zero(currency);
final Money principalDue = getPrincipalOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(principalDue)) {
this.principalCompleted = getPrincipalCompleted(currency).plus(principalDue).getAmount();
principalPortionOfTransaction = principalPortionOfTransaction.plus(principalDue);
} else {
this.principalCompleted = getPrincipalCompleted(currency).plus(transactionAmountRemaining).getAmount();
principalPortionOfTransaction = principalPortionOfTransaction.plus(transactionAmountRemaining);
}
this.principalCompleted = defaultToNullIfZero(this.principalCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return principalPortionOfTransaction;
}
public Money getInterestCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.interestCompleted);
}
public Money waiveInterestComponent(final Money transactionAmountRemaining) {
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money waivedInterestPortionOfTransaction = Money.zero(currency);
Money interestDue = getInterestOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) {
this.interestWaived = getInterestWaived(currency).plus(interestDue).getAmount();
waivedInterestPortionOfTransaction = waivedInterestPortionOfTransaction.plus(interestDue);
} else {
this.interestWaived = getInterestWaived(currency).plus(transactionAmountRemaining).getAmount();
waivedInterestPortionOfTransaction = waivedInterestPortionOfTransaction.plus(transactionAmountRemaining);
}
this.interestWaived = defaultToNullIfZero(this.interestWaived);
this.completed = getTotalOutstanding(currency).isZero();
return waivedInterestPortionOfTransaction;
}
public Money waivePenaltyChargesComponent(final Money transactionAmountRemaining) {
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money waivedPenaltyChargesPortionOfTransaction = Money.zero(currency);
Money penanltiesDue = getPenaltyChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(penanltiesDue)) {
this.penaltyChargesWaived = getPenaltyChargesWaived(currency).plus(penanltiesDue).getAmount();
waivedPenaltyChargesPortionOfTransaction = waivedPenaltyChargesPortionOfTransaction.plus(penanltiesDue);
} else {
this.penaltyChargesWaived = getPenaltyChargesWaived(currency).plus(transactionAmountRemaining).getAmount();
waivedPenaltyChargesPortionOfTransaction = waivedPenaltyChargesPortionOfTransaction.plus(transactionAmountRemaining);
}
this.penaltyChargesWaived = defaultToNullIfZero(this.penaltyChargesWaived);
this.completed = getTotalOutstanding(currency).isZero();
return waivedPenaltyChargesPortionOfTransaction;
}
public Money waiveFeeChargesComponent(final Money transactionAmountRemaining) {
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money waivedFeeChargesPortionOfTransaction = Money.zero(currency);
Money feesDue = getPenaltyChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(feesDue)) {
this.feeChargesWaived = getFeeChargesWaived(currency).plus(feesDue).getAmount();
waivedFeeChargesPortionOfTransaction = waivedFeeChargesPortionOfTransaction.plus(feesDue);
} else {
this.feeChargesWaived = getFeeChargesWaived(currency).plus(transactionAmountRemaining).getAmount();
waivedFeeChargesPortionOfTransaction = waivedFeeChargesPortionOfTransaction.plus(transactionAmountRemaining);
}
this.feeChargesWaived = defaultToNullIfZero(this.feeChargesWaived);
this.completed = getTotalOutstanding(currency).isZero();
return waivedFeeChargesPortionOfTransaction;
}
public Money writeOffOutstandingPrincipal(final MonetaryCurrency currency) {
final Money principalDue = getPrincipalOutstanding(currency);
this.principalWrittenOff = defaultToNullIfZero(principalDue.getAmount());
this.completed = getTotalOutstanding(currency).isZero();
return principalDue;
}
public Money writeOffOutstandingInterest(final MonetaryCurrency currency) {
final Money interestDue = getInterestOutstanding(currency);
this.interestWrittenOff = defaultToNullIfZero(interestDue.getAmount());
this.completed = getTotalOutstanding(currency).isZero();
return interestDue;
}
public Money getInterestWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.interestWaived);
}
public boolean isOverdueOn(final LocalDate transactionDate) {
return this.getDueDate().isBefore(transactionDate);
}
public Money getInterestWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.interestWrittenOff);
}
public void updateChargePortion(
final Money feeChargesDue,
final Money feeChargesWaived,
final Money feeChargesWrittenOff,
final Money penaltyChargesDue,
final Money penaltyChargesWaived,
final Money penaltyChargesWrittenOff) {
this.feeCharges = defaultToNullIfZero(feeChargesDue.getAmount());
this.feeChargesWaived = defaultToNullIfZero(feeChargesWaived.getAmount());
this.feeChargesWrittenOff = defaultToNullIfZero(feeChargesWrittenOff.getAmount());
this.penaltyCharges = defaultToNullIfZero(penaltyChargesDue.getAmount());
this.penaltyChargesWaived = defaultToNullIfZero(penaltyChargesWaived.getAmount());
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesWrittenOff.getAmount());
}
public Money getInterestOutstanding(final MonetaryCurrency currency) {
final Money interestAccountedFor = getInterestCompleted(currency).plus(getInterestWaived(currency)).plus(
getInterestWrittenOff(currency));
return getInterest(currency).minus(interestAccountedFor);
}
public Money getFeeCharges(final MonetaryCurrency currency) {
return Money.of(currency, this.feeCharges);
}
public Money getFeeChargesCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesCompleted);
}
public Money getFeeChargesWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesWaived);
}
public Money getFeeChargesWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesWrittenOff);
}
public Money getFeeChargesOutstanding(final MonetaryCurrency currency) {
final Money feeChargesAccountedFor = getFeeChargesCompleted(currency).plus(getFeeChargesWaived(currency)).plus(
getFeeChargesWrittenOff(currency));
return getFeeCharges(currency).minus(feeChargesAccountedFor);
}
public Money getPenaltyCharges(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyCharges);
}
public Money getPenaltyChargesCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyChargesCompleted);
}
public Money getPenaltyChargesWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyChargesWaived);
}
public Money getPenaltyChargesWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyChargesWrittenOff);
}
public Money getPenaltyChargesOutstanding(final MonetaryCurrency currency) {
final Money feeChargesAccountedFor = getPenaltyChargesCompleted(currency).plus(getPenaltyChargesWaived(currency)).plus(
getPenaltyChargesWrittenOff(currency));
return getPenaltyCharges(currency).minus(feeChargesAccountedFor);
}
public boolean isInterestDue(final MonetaryCurrency currency) {
return getInterestOutstanding(currency).isGreaterThanZero();
}
public Money getTotalPrincipalAndInterest(final MonetaryCurrency currency) {
return getPrincipal(currency).plus(getInterest(currency));
}
public Money getTotalOutstanding(final MonetaryCurrency currency) {
return getPrincipalOutstanding(currency).plus(getInterestOutstanding(currency)).plus(getFeeChargesOutstanding(currency))
.plus(getPenaltyChargesOutstanding(currency));
}
public void updateLoan(final Loan loan) {
this.loan = loan;
}
public boolean isFullyCompleted() {
return this.completed;
}
public boolean isNotFullyCompleted() {
return !this.completed;
}
public boolean isPrincipalNotCompleted(final MonetaryCurrency currency) {
return !isPrincipalCompleted(currency);
}
public boolean isPrincipalCompleted(final MonetaryCurrency currency) {
return getPrincipalOutstanding(currency).isZero();
}
public void resetDerivedComponents() {
this.principalCompleted = null;
this.principalWrittenOff = null;
this.interestCompleted = null;
this.interestWaived = null;
this.interestWrittenOff = null;
this.feeChargesCompleted = null;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.penaltyChargesCompleted = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.completed = false;
}
public Money payPenaltyChargesComponent(final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money penaltyPortionOfTransaction = Money.zero(currency);
final Money penaltyChargesDue = getPenaltyChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(penaltyChargesDue)) {
this.penaltyChargesCompleted = getPenaltyChargesCompleted(currency).plus(penaltyChargesDue).getAmount();
penaltyPortionOfTransaction = penaltyPortionOfTransaction.plus(penaltyChargesDue);
} else {
this.penaltyChargesCompleted = getPenaltyChargesCompleted(currency).plus(transactionAmountRemaining).getAmount();
penaltyPortionOfTransaction = penaltyPortionOfTransaction.plus(transactionAmountRemaining);
}
this.penaltyChargesCompleted = defaultToNullIfZero(this.penaltyChargesCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return penaltyPortionOfTransaction;
}
public Money payFeeChargesComponent(final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money feePortionOfTransaction = Money.zero(currency);
final Money feeChargesDue = getFeeChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(feeChargesDue)) {
this.feeChargesCompleted = getFeeChargesCompleted(currency).plus(feeChargesDue).getAmount();
feePortionOfTransaction = feePortionOfTransaction.plus(feeChargesDue);
} else {
this.feeChargesCompleted = getFeeChargesCompleted(currency).plus(transactionAmountRemaining).getAmount();
feePortionOfTransaction = feePortionOfTransaction.plus(transactionAmountRemaining);
}
this.feeChargesCompleted = defaultToNullIfZero(this.feeChargesCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return feePortionOfTransaction;
}
public Money payInterestComponent(final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money interestPortionOfTransaction = Money.zero(currency);
final Money interestDue = getInterestOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) {
this.interestCompleted = getInterestCompleted(currency).plus(interestDue).getAmount();
interestPortionOfTransaction = interestPortionOfTransaction.plus(interestDue);
} else {
this.interestCompleted = getInterestCompleted(currency).plus(transactionAmountRemaining).getAmount();
interestPortionOfTransaction = interestPortionOfTransaction.plus(transactionAmountRemaining);
}
this.interestCompleted = defaultToNullIfZero(this.interestCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return interestPortionOfTransaction;
}
public Money payPrincipalComponent(final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money principalPortionOfTransaction = Money.zero(currency);
final Money principalDue = getPrincipalOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(principalDue)) {
this.principalCompleted = getPrincipalCompleted(currency).plus(principalDue).getAmount();
principalPortionOfTransaction = principalPortionOfTransaction.plus(principalDue);
} else {
this.principalCompleted = getPrincipalCompleted(currency).plus(transactionAmountRemaining).getAmount();
principalPortionOfTransaction = principalPortionOfTransaction.plus(transactionAmountRemaining);
}
this.principalCompleted = defaultToNullIfZero(this.principalCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return principalPortionOfTransaction;
}
public Money waiveInterestComponent(final Money transactionAmountRemaining) {
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money waivedInterestPortionOfTransaction = Money.zero(currency);
Money interestDue = getInterestOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) {
this.interestWaived = getInterestWaived(currency).plus(interestDue).getAmount();
waivedInterestPortionOfTransaction = waivedInterestPortionOfTransaction.plus(interestDue);
} else {
this.interestWaived = getInterestWaived(currency).plus(transactionAmountRemaining).getAmount();
waivedInterestPortionOfTransaction = waivedInterestPortionOfTransaction.plus(transactionAmountRemaining);
}
this.interestWaived = defaultToNullIfZero(this.interestWaived);
this.completed = getTotalOutstanding(currency).isZero();
return waivedInterestPortionOfTransaction;
}
public Money waivePenaltyChargesComponent(final Money transactionAmountRemaining) {
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money waivedPenaltyChargesPortionOfTransaction = Money.zero(currency);
Money penanltiesDue = getPenaltyChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(penanltiesDue)) {
this.penaltyChargesWaived = getPenaltyChargesWaived(currency).plus(penanltiesDue).getAmount();
waivedPenaltyChargesPortionOfTransaction = waivedPenaltyChargesPortionOfTransaction.plus(penanltiesDue);
} else {
this.penaltyChargesWaived = getPenaltyChargesWaived(currency).plus(transactionAmountRemaining).getAmount();
waivedPenaltyChargesPortionOfTransaction = waivedPenaltyChargesPortionOfTransaction.plus(transactionAmountRemaining);
}
this.penaltyChargesWaived = defaultToNullIfZero(this.penaltyChargesWaived);
this.completed = getTotalOutstanding(currency).isZero();
return waivedPenaltyChargesPortionOfTransaction;
}
public Money waiveFeeChargesComponent(final Money transactionAmountRemaining) {
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money waivedFeeChargesPortionOfTransaction = Money.zero(currency);
Money feesDue = getPenaltyChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(feesDue)) {
this.feeChargesWaived = getFeeChargesWaived(currency).plus(feesDue).getAmount();
waivedFeeChargesPortionOfTransaction = waivedFeeChargesPortionOfTransaction.plus(feesDue);
} else {
this.feeChargesWaived = getFeeChargesWaived(currency).plus(transactionAmountRemaining).getAmount();
waivedFeeChargesPortionOfTransaction = waivedFeeChargesPortionOfTransaction.plus(transactionAmountRemaining);
}
this.feeChargesWaived = defaultToNullIfZero(this.feeChargesWaived);
this.completed = getTotalOutstanding(currency).isZero();
return waivedFeeChargesPortionOfTransaction;
}
public Money writeOffOutstandingPrincipal(final MonetaryCurrency currency) {
final Money principalDue = getPrincipalOutstanding(currency);
this.principalWrittenOff = defaultToNullIfZero(principalDue.getAmount());
this.completed = getTotalOutstanding(currency).isZero();
return principalDue;
}
public Money writeOffOutstandingInterest(final MonetaryCurrency currency) {
final Money interestDue = getInterestOutstanding(currency);
this.interestWrittenOff = defaultToNullIfZero(interestDue.getAmount());
this.completed = getTotalOutstanding(currency).isZero();
return interestDue;
}
public boolean isOverdueOn(final LocalDate transactionDate) {
return this.getDueDate().isBefore(transactionDate);
}
public void updateChargePortion(final Money feeChargesDue, final Money feeChargesWaived, final Money feeChargesWrittenOff,
final Money penaltyChargesDue, final Money penaltyChargesWaived, final Money penaltyChargesWrittenOff) {
this.feeCharges = defaultToNullIfZero(feeChargesDue.getAmount());
this.feeChargesWaived = defaultToNullIfZero(feeChargesWaived.getAmount());
this.feeChargesWrittenOff = defaultToNullIfZero(feeChargesWrittenOff.getAmount());
this.penaltyCharges = defaultToNullIfZero(penaltyChargesDue.getAmount());
this.penaltyChargesWaived = defaultToNullIfZero(penaltyChargesWaived.getAmount());
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesWrittenOff.getAmount());
}
}
@@ -10,12 +10,10 @@ import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@@ -36,7 +34,7 @@ import org.mifosplatform.useradministration.domain.AppUser;
*/
@Entity
@Table(name = "m_loan_transaction")
public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
public final class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne(optional = false)
@JoinColumn(name = "loan_id", nullable = false)
@@ -64,9 +62,8 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "transaction_type_enum", nullable = false)
private final Integer typeOf;
@OneToOne(optional = true, cascade = { CascadeType.PERSIST })
@JoinColumn(name = "contra_id")
private LoanTransaction contra;
@Column(name = "is_reversed", nullable = false)
private boolean reversed;
protected LoanTransaction() {
this.loan = null;
@@ -98,21 +95,10 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
return waiver;
}
private static LoanTransaction contra(final LoanTransaction originalTransaction) {
LoanTransaction contra = new LoanTransaction(null, LoanTransactionType.CONTRA, originalTransaction.getAmount().negate(),
new LocalDate(originalTransaction.getDateOf()));
contra.updateContra(originalTransaction);
return contra;
}
public static LoanTransaction writeoff(final Loan loan, final LocalDate writeOffDate) {
return new LoanTransaction(loan, LoanTransactionType.WRITEOFF, null, writeOffDate);
}
public void updateContra(final LoanTransaction transaction) {
this.contra = transaction;
}
private LoanTransaction(final Loan loan, final LoanTransactionType type, final BigDecimal amount, final LocalDate date) {
this.loan = loan;
this.typeOf = type.getValue();
@@ -120,108 +106,21 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
this.dateOf = date.toDateMidnight().toDate();
}
public BigDecimal getAmount() {
return this.amount;
public void reverse() {
this.reversed = true;
}
public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount);
public void resetDerivedComponents() {
this.principalPortion = null;
this.interestPortion = null;
this.feeChargesPortion = null;
this.penaltyChargesPortion = null;
}
public LocalDate getTransactionDate() {
return new LocalDate(this.dateOf);
}
public Date getDateOf() {
return dateOf;
}
public LoanTransactionType getTypeOf() {
return LoanTransactionType.fromInt(this.typeOf);
}
public boolean isRepayment() {
return LoanTransactionType.REPAYMENT.equals(getTypeOf()) && isNotContra();
}
public boolean isNotRepayment() {
return !isRepayment();
}
public boolean isNotContra() {
return this.contra == null;
}
public boolean isContra() {
return this.contra != null;
}
public boolean isDisbursement() {
return LoanTransactionType.DISBURSEMENT.equals(getTypeOf());
}
public boolean isRepaymentAtDisbursement() {
return LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.equals(getTypeOf());
}
public boolean isInterestWaiver() {
return LoanTransactionType.WAIVE_INTEREST.equals(getTypeOf()) && isNotContra();
}
public boolean isChargesWaiver() {
return LoanTransactionType.WAIVE_CHARGES.equals(getTypeOf()) && isNotContra();
}
public boolean isNotInterestWaiver() {
return !isInterestWaiver();
}
public boolean isWaiver() {
return isInterestWaiver() || isChargesWaiver();
}
public boolean isNotWaiver() {
return !isInterestWaiver() && !isChargesWaiver();
}
public boolean isWriteOff() {
return getTypeOf().isWriteOff() && isNotContra();
}
public boolean isIdentifiedBy(final Long identifier) {
return this.getId().equals(identifier);
}
public boolean isBelongingToLoanOf(final Loan check) {
return this.loan.getId().equals(check.getId());
}
public boolean isNotBelongingToLoanOf(final Loan check) {
return !isBelongingToLoanOf(check);
}
public LoanTransaction contra() {
this.contra = LoanTransaction.contra(this);
contra.updateLoan(this.loan);
return this.contra;
}
// public LoanTransaction newContra() {
// // should update some flag old entry being contra'd
// LoanTransaction contra = LoanTransaction.contra(this);
// contra.updateLoan(this.loan);
// return contra;
// }
public void updateLoan(final Loan loan) {
this.loan = loan;
}
public boolean isNonZero() {
return this.amount.subtract(BigDecimal.ZERO).doubleValue() > 0;
}
/**
* This updates the derived fields of a loan transaction for the principal,
* interest and interest waived portions.
@@ -282,11 +181,92 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
return this.penaltyChargesPortion;
}
public void resetDerivedComponents() {
this.principalPortion = null;
this.interestPortion = null;
this.feeChargesPortion = null;
this.penaltyChargesPortion = null;
public BigDecimal getAmount() {
return this.amount;
}
public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount);
}
public LocalDate getTransactionDate() {
return new LocalDate(this.dateOf);
}
public Date getDateOf() {
return dateOf;
}
public LoanTransactionType getTypeOf() {
return LoanTransactionType.fromInt(this.typeOf);
}
public boolean isReversed() {
return this.reversed;
}
public boolean isNotReversed() {
return !isReversed();
}
public boolean isRepayment() {
return LoanTransactionType.REPAYMENT.equals(getTypeOf()) && isNotReversed();
}
public boolean isNotRepayment() {
return !isRepayment();
}
public boolean isDisbursement() {
return LoanTransactionType.DISBURSEMENT.equals(getTypeOf()) && isNotReversed();
}
public boolean isRepaymentAtDisbursement() {
return LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.equals(getTypeOf()) && isNotReversed();
}
private boolean isRecoveryRepayment() {
return LoanTransactionType.RECOVERY_REPAYMENT.equals(getTypeOf()) && isNotReversed();
}
public boolean isInterestWaiver() {
return LoanTransactionType.WAIVE_INTEREST.equals(getTypeOf()) && isNotReversed();
}
public boolean isChargesWaiver() {
return LoanTransactionType.WAIVE_CHARGES.equals(getTypeOf()) && isNotReversed();
}
public boolean isNotInterestWaiver() {
return !isInterestWaiver();
}
public boolean isWaiver() {
return isInterestWaiver() || isChargesWaiver();
}
public boolean isNotWaiver() {
return !isInterestWaiver() && !isChargesWaiver();
}
public boolean isWriteOff() {
return getTypeOf().isWriteOff() && isNotReversed();
}
public boolean isIdentifiedBy(final Long identifier) {
return this.getId().equals(identifier);
}
public boolean isBelongingToLoanOf(final Loan check) {
return this.loan.getId().equals(check.getId());
}
public boolean isNotBelongingToLoanOf(final Loan check) {
return !isBelongingToLoanOf(check);
}
public boolean isNonZero() {
return this.amount.subtract(BigDecimal.ZERO).doubleValue() > 0;
}
public boolean isGreaterThan(final Money monetaryAmount) {
@@ -318,19 +298,18 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
public Map<String, Object> toMapData(final CurrencyData currencyData) {
final Map<String, Object> thisTransactionData = new LinkedHashMap<String, Object>();
LoanTransactionType type = LoanTransactionType.fromInt(this.typeOf);
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(this.typeOf);
thisTransactionData.put("id", this.getId());
thisTransactionData.put("type", transactionType);
thisTransactionData.put("disbursement", type.isDisbursement());
thisTransactionData.put("repaymentAtDisbursement", type.isRepaymentAtDisbursement());
thisTransactionData.put("repayment", type.isRepayment());
thisTransactionData.put("recoveryRepayment", type.isRecoveryRepayment());
thisTransactionData.put("contra", type.isContra());
thisTransactionData.put("waiveInterest", type.isWaiveInterest());
thisTransactionData.put("waiveCharges", type.isWaiveCharges());
thisTransactionData.put("writeOff", type.isWriteOff());
thisTransactionData.put("disbursement", Boolean.valueOf(this.isDisbursement()));
thisTransactionData.put("repaymentAtDisbursement", Boolean.valueOf(this.isRepaymentAtDisbursement()));
thisTransactionData.put("repayment", Boolean.valueOf(this.isRepayment()));
thisTransactionData.put("recoveryRepayment", Boolean.valueOf(this.isRecoveryRepayment()));
thisTransactionData.put("contra", Boolean.valueOf(this.isReversed()));
thisTransactionData.put("waiveInterest", Boolean.valueOf(this.isInterestWaiver()));
thisTransactionData.put("waiveCharges", Boolean.valueOf(this.isChargesWaiver()));
thisTransactionData.put("writeOff", Boolean.valueOf(this.isWriteOff()));
thisTransactionData.put("date", this.getTransactionDate());
thisTransactionData.put("currency", currencyData);
@@ -77,35 +77,31 @@ public enum LoanTransactionType {
return loanTransactionType;
}
public Boolean isDisbursement() {
public boolean isDisbursement() {
return this.value.equals(LoanTransactionType.DISBURSEMENT.getValue());
}
public Boolean isRepaymentAtDisbursement() {
public boolean isRepaymentAtDisbursement() {
return this.value.equals(LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue());
}
public Boolean isRepayment() {
public boolean isRepayment() {
return this.value.equals(LoanTransactionType.REPAYMENT.getValue());
}
public Boolean isRecoveryRepayment() {
public boolean isRecoveryRepayment() {
return this.value.equals(LoanTransactionType.RECOVERY_REPAYMENT.getValue());
}
public Boolean isContra() {
return this.value.equals(LoanTransactionType.CONTRA.getValue());
}
public Boolean isWaiveInterest() {
public boolean isWaiveInterest() {
return this.value.equals(LoanTransactionType.WAIVE_INTEREST.getValue());
}
public Boolean isWaiveCharges() {
public boolean isWaiveCharges() {
return this.value.equals(LoanTransactionType.WAIVE_CHARGES.getValue());
}
public Boolean isWriteOff() {
public boolean isWriteOff() {
return this.value.equals(LoanTransactionType.WRITEOFF.getValue());
}
}
@@ -12,7 +12,7 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceN
*/
public class LoanNotFoundException extends AbstractPlatformResourceNotFoundException {
public LoanNotFoundException(Long id) {
super("error.msg.loan.id.invalid", "Loan with identifier " + id + " does not exist", id);
}
public LoanNotFoundException(final Long id) {
super("error.msg.loan.id.invalid", "Loan with identifier " + id + " does not exist", id);
}
}
@@ -200,7 +200,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
// repayments at time of disbursement (e.g. charges)
String sql = "select "
+ rm.LoanPaymentsSchema()
+ " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) and tr.contra_id is null order by tr.transaction_date ASC";
+ " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) and tr.is_reversed=0 order by tr.transaction_date ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
} catch (EmptyResultDataAccessException e) {
return null;
@@ -130,13 +130,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getPrincpal().getCurrencyCode());
/***
* In cases where an "Undo disbursement is done and the loan is
* disbursed again, we need to ensure the old transactions are entered
* again in accounting
***/
final List<Long> existingTransactionIds = new ArrayList<Long>();
final Map<String, Object> changes = loan.disburse(currentUser, command, defaultLoanLifecycleStateMachine(), currency, existingTransactionIds);
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Map<String, Object> changes = loan.disburse(currentUser, command, defaultLoanLifecycleStateMachine(), currency,
existingTransactionIds, existingReversedTransactionIds);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
@@ -144,8 +141,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
final Map<String, Object> accountingBridgeData = loan.deriveDisbursementData(currency.toData(), existingTransactionIds);
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
@@ -169,7 +167,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Loan loan = retrieveLoanBy(loanId);
final List<Long> existingTransactionIds = new ArrayList<Long>();
final Map<String, Object> changes = loan.undoDisbursal(defaultLoanLifecycleStateMachine(), existingTransactionIds);
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Map<String, Object> changes = loan.undoDisbursal(defaultLoanLifecycleStateMachine(), existingTransactionIds,
existingReversedTransactionIds);
if (!changes.isEmpty()) {
this.loanRepository.saveAndFlush(loan);
@@ -179,10 +179,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note);
}
final Loan loanRefetch = retrieveLoanBy(loanId);
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getPrincpal().getCurrencyCode());
final Map<String, Object> accountingBridgeData = loanRefetch.deriveAccountingBridgeData(currency.toData(), existingTransactionIds);
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
@@ -736,7 +735,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LoanTransactionDTO loanTransactionDTO = new LoanTransactionDTO(loanTransaction.getId().toString(), loanTransaction.getDateOf(),
loanTransaction.getAmount(), loanTransaction.getPrincipalPortion(), loanTransaction.getInterestPortion(),
loanTransaction.getFeePortion(), loanTransaction.getPenaltyChargesPortion(), loanTransaction.isDisbursement(),
loanTransaction.isRepayment(), loanTransaction.isRepaymentAtDisbursement(), loanTransaction.isContra(),
loanTransaction.isRepayment(), loanTransaction.isRepaymentAtDisbursement(), loanTransaction.isReversed(),
loanTransaction.isWriteOff());
loanTransactionDTOs.add(loanTransactionDTO);
}