MIFOSX-97: some changes around loan officer reassingment capability for loans.

This commit is contained in:
Keith Woodlock
2012-11-12 17:16:30 +00:00
parent 4cba5ea719
commit 3e16891cfb
6 changed files with 70 additions and 72 deletions
@@ -1,3 +1,4 @@
drop table `m_loan_officer_assignment_history`;
CREATE TABLE `m_loan_officer_assignment_history` ( CREATE TABLE `m_loan_officer_assignment_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT, `id` bigint(20) NOT NULL AUTO_INCREMENT,
`loan_id` bigint(20) NOT NULL , `loan_id` bigint(20) NOT NULL ,
@@ -13,11 +13,12 @@ public class BulkLoanReassignmentCommand {
private final String[] loans; private final String[] loans;
public BulkLoanReassignmentCommand(Long fromLoanOfficerId, public BulkLoanReassignmentCommand(final Long fromLoanOfficerId,
Long toLoanOfficerId, LocalDate assignmentDate, String[] loans) { final Long toLoanOfficerId, final LocalDate assignmentDate,
final String[] loans) {
this.fromLoanOfficerId = fromLoanOfficerId; this.fromLoanOfficerId = fromLoanOfficerId;
this.toLoanOfficerId = toLoanOfficerId; this.toLoanOfficerId = toLoanOfficerId;
this.assignmentDate = assignmentDate; this.assignmentDate = assignmentDate;
this.loans = loans; this.loans = loans;
} }
@@ -149,7 +149,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@LazyCollection(LazyCollectionOption.FALSE) @LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true) @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private Set<LoanOfficerAssignmentHistory> loanOfficerAssignmentHistorySet; private Set<LoanOfficerAssignmentHistory> loanOfficerHistory;
// see // see
// http://stackoverflow.com/questions/4334970/hibernate-cannot-simultaneously-fetch-multiple-bags // http://stackoverflow.com/questions/4334970/hibernate-cannot-simultaneously-fetch-multiple-bags
@@ -188,7 +188,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanProduct = null; this.loanProduct = null;
this.loanRepaymentScheduleDetail = null; this.loanRepaymentScheduleDetail = null;
this.charges = null; this.charges = null;
this.loanOfficerAssignmentHistorySet = null; this.loanOfficerHistory = null;
} }
private Loan( private Loan(
@@ -214,7 +214,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} else { } else {
this.charges = null; this.charges = null;
} }
this.loanOfficerAssignmentHistorySet = null; this.loanOfficerHistory = null;
} }
private Loan( private Loan(
@@ -240,7 +240,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} else { } else {
this.charges = null; this.charges = null;
} }
this.loanOfficerAssignmentHistorySet = null; this.loanOfficerHistory = null;
} }
private BigDecimal deriveSumTotalOfChargesDueAtDisbursement() { private BigDecimal deriveSumTotalOfChargesDueAtDisbursement() {
@@ -669,15 +669,14 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
} }
public void approve(final LocalDate approvedOn, public void approve(final LocalDate approvedOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
LoanLifecycleStateMachine loanLifecycleStateMachine) {
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, LoanStatus.fromInt(this.loanStatus)); final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue(); this.loanStatus = statusEnum.getValue();
this.approvedOnDate = approvedOn.toDateTimeAtCurrentTime().toDate(); this.approvedOnDate = approvedOn.toDateTimeAtCurrentTime().toDate();
LocalDate submittalDate = new LocalDate(this.submittedOnDate); final LocalDate submittalDate = new LocalDate(this.submittedOnDate);
if (approvedOn.isBefore(submittalDate)) { if (approvedOn.isBefore(submittalDate)) {
final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: " final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: "
+ submittalDate.toString(); + submittalDate.toString();
@@ -692,22 +691,20 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
getApprovedOnDate()); getApprovedOnDate());
} }
if (this.loanofficer != null){ if (this.loanofficer != null) {
LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this, this.loanofficer, approvedOn);
LoanOfficerAssignmentHistory.createNew(this, this.loanofficer, approvedOn); this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
this.loanOfficerAssignmentHistorySet.add(loanOfficerAssignmentHistory);
} }
} }
public void undoApproval(LoanLifecycleStateMachine loanLifecycleStateMachine) { public void undoApproval(LoanLifecycleStateMachine loanLifecycleStateMachine) {
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVAL_UNDO, LoanStatus.fromInt(this.loanStatus)); final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVAL_UNDO, LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue(); this.loanStatus = statusEnum.getValue();
this.approvedOnDate = null; this.approvedOnDate = null;
this.loanOfficerAssignmentHistorySet.clear(); this.loanOfficerHistory.clear();
} }
public void disburseWithModifiedRepaymentSchedule( public void disburseWithModifiedRepaymentSchedule(
@@ -1601,31 +1598,35 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return this.loanRepaymentScheduleDetail.getCurrency(); return this.loanRepaymentScheduleDetail.getCurrency();
} }
public void updateLoanOfficer(final Staff newLoanOfficer, final LocalDate assignmentDate) { public void reassignLoanOfficer(final Staff newLoanOfficer, final LocalDate assignmentDate) {
boolean createNewLoanOfficerAssignmentHistoryRecord = true;
for (LoanOfficerAssignmentHistory lastLoanOfficerAssignmentHistoryRecord : this.loanOfficerAssignmentHistorySet){ final LoanOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord();
if (lastLoanOfficerAssignmentHistoryRecord.getEndDate() == null){
if (this.loanofficer.identifiedBy(newLoanOfficer)){ if (this.loanofficer.identifiedBy(newLoanOfficer)) {
lastLoanOfficerAssignmentHistoryRecord.updateStartDate(assignmentDate); latestHistoryRecord.updateStartDate(assignmentDate);
createNewLoanOfficerAssignmentHistoryRecord = false; } else if (latestHistoryRecord.matchesStartDateOf(assignmentDate)) {
} else if (lastLoanOfficerAssignmentHistoryRecord.getStartDate().isEqual(assignmentDate)){ latestHistoryRecord.updateLoanOfficer(newLoanOfficer);
lastLoanOfficerAssignmentHistoryRecord.updateLoanOfficer(newLoanOfficer); this.loanofficer = newLoanOfficer;
createNewLoanOfficerAssignmentHistoryRecord = false; } else if (latestHistoryRecord.hasStartDateBefore(assignmentDate)) {
} else if (assignmentDate.isBefore(lastLoanOfficerAssignmentHistoryRecord.getStartDate())){ throw new LoanOfficerAssignmentException(this.getId(), assignmentDate);
throw new LoanOfficerAssignmentException(this.getId(), assignmentDate); } else {
} else { // loan officer correctly changed from previous loan officer to new loan officer
lastLoanOfficerAssignmentHistoryRecord.updateEndDate(assignmentDate); latestHistoryRecord.updateEndDate(assignmentDate);
}
break; this.loanofficer = newLoanOfficer;
} final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this, this.loanofficer, assignmentDate);
this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
} }
}
if (createNewLoanOfficerAssignmentHistoryRecord){ private LoanOfficerAssignmentHistory findLatestIncompleteHistoryRecord() {
LoanOfficerAssignmentHistory loanOfficerAssignmentHistory =
LoanOfficerAssignmentHistory.createNew(this, this.loanofficer, assignmentDate); LoanOfficerAssignmentHistory latestRecordWithNoEndDate = null;
this.loanOfficerAssignmentHistorySet.add(loanOfficerAssignmentHistory); for (LoanOfficerAssignmentHistory historyRecord : this.loanOfficerHistory){
} if (historyRecord.isCurrentRecord()) {
latestRecordWithNoEndDate = historyRecord;
this.loanofficer = newLoanOfficer; }
}
return latestRecordWithNoEndDate;
} }
} }
@@ -1,6 +1,5 @@
package org.mifosng.platform.loan.domain; package org.mifosng.platform.loan.domain;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom; import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
import org.mifosng.platform.staff.domain.Staff; import org.mifosng.platform.staff.domain.Staff;
@@ -19,7 +18,7 @@ import java.util.Date;
@Table(name = "m_loan_officer_assignment_history") @Table(name = "m_loan_officer_assignment_history")
public class LoanOfficerAssignmentHistory extends AbstractAuditableCustom<AppUser, Long> { public class LoanOfficerAssignmentHistory extends AbstractAuditableCustom<AppUser, Long> {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ManyToOne @ManyToOne
@JoinColumn(name = "loan_id", nullable = false) @JoinColumn(name = "loan_id", nullable = false)
private Loan loan; private Loan loan;
@@ -33,54 +32,50 @@ public class LoanOfficerAssignmentHistory extends AbstractAuditableCustom<AppUse
@Column(name = "start_date") @Column(name = "start_date")
private Date startDate; private Date startDate;
@SuppressWarnings("unused") @Temporal(TemporalType.DATE)
@Temporal(TemporalType.DATE)
@Column(name = "end_date") @Column(name = "end_date")
private Date endDate; private Date endDate;
public static LoanOfficerAssignmentHistory createNew(Loan loan, Staff loanOfficer, public static LoanOfficerAssignmentHistory createNew(final Loan loan, final Staff loanOfficer, final LocalDate startDate) {
LocalDate startDate){
return new LoanOfficerAssignmentHistory(loan, loanOfficer, startDate.toDate(), null); return new LoanOfficerAssignmentHistory(loan, loanOfficer, startDate.toDate(), null);
} }
@SuppressWarnings("unused")
protected LoanOfficerAssignmentHistory() { protected LoanOfficerAssignmentHistory() {
this.loan = null; //
this.loanOfficer = null;
this.startDate = null;
this.endDate = null;
} }
private LoanOfficerAssignmentHistory(Loan loan, Staff loanOfficer, private LoanOfficerAssignmentHistory(final Loan loan, final Staff loanOfficer, final Date startDate, final Date endDate) {
Date startDate, Date endDate) {
this.loan = loan; this.loan = loan;
this.loanOfficer = loanOfficer; this.loanOfficer = loanOfficer;
this.startDate = startDate; this.startDate = startDate;
this.endDate = endDate; this.endDate = endDate;
} }
public Loan getLoan() {
return loan;
}
public LocalDate getStartDate() {
return new LocalDate(startDate);
}
public Date getEndDate() {
return endDate;
}
public void updateLoanOfficer(Staff loanOfficer) { public void updateLoanOfficer(Staff loanOfficer) {
this.loanOfficer = loanOfficer; this.loanOfficer = loanOfficer;
} }
public void updateStartDate(LocalDate startDate){ public void updateStartDate(LocalDate startDate) {
this.startDate = startDate.toDate(); this.startDate = startDate.toDate();
} }
public void updateEndDate(LocalDate endDate){ public void updateEndDate(LocalDate endDate) {
this.endDate = endDate.toDate(); this.endDate = endDate.toDate();
} }
} public boolean matchesStartDateOf(final LocalDate matchingDate) {
return getStartDate().isEqual(matchingDate);
}
private LocalDate getStartDate() {
return new LocalDate(startDate);
}
public boolean hasStartDateBefore(final LocalDate matchingDate) {
return matchingDate.isBefore(getStartDate());
}
public boolean isCurrentRecord() {
return this.endDate == null;
}
}
@@ -754,7 +754,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
throw new LoanOfficerAssignmentException(loan.getId(), fromLoanOfficer.getId()); throw new LoanOfficerAssignmentException(loan.getId(), fromLoanOfficer.getId());
} }
loan.updateLoanOfficer(toLoanOfficer, command.getAssignmentDate()); loan.reassignLoanOfficer(toLoanOfficer, command.getAssignmentDate());
this.loanRepository.save(loan); this.loanRepository.save(loan);
} }
@@ -91,7 +91,7 @@ public class Staff extends AbstractAuditableCustom<AppUser, Long> {
return loanOfficerFlag; return loanOfficerFlag;
} }
public boolean identifiedBy(Staff staff) { public boolean identifiedBy(final Staff staff) {
return getId().equals(staff.getId()); return getId().equals(staff.getId());
} }
} }