Mifosx 54, associating relations to staff entity

This commit is contained in:
Vishwas Babu AJ
2012-09-09 14:34:37 +01:00
committed by Keith Woodlock
parent 1585b33f42
commit fa6370d31a
22 changed files with 390 additions and 59 deletions
+1
View File
@@ -10,3 +10,4 @@ repos
*.jar
*.war
*.ear
*.bak
+3
View File
@@ -0,0 +1,3 @@
ALTER TABLE `m_loan`
ADD COLUMN `loan_officer_id` BIGINT(20) NULL AFTER `fund_id`,
ADD CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`);
+9
View File
@@ -0,0 +1,9 @@
ALTER TABLE `m_staff`
ADD COLUMN `office_id` BIGINT(20) NULL AFTER `id`,
ADD CONSTRAINT `FK_m_staff_m_office` FOREIGN KEY (`office_id`) REFERENCES `m_office` (`id`);
ALTER TABLE `m_staff`
ADD COLUMN `is_loan_officer` TINYINT(1) NOT NULL DEFAULT 0 AFTER `id`;
update m_staff set office_id=1;
@@ -40,6 +40,7 @@ import org.mifosng.platform.api.data.LoanRepaymentPeriodData;
import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
import org.mifosng.platform.api.data.LoanSchedule;
import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.StaffData;
import org.mifosng.platform.api.data.TransactionProcessingStrategyData;
import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
import org.mifosng.platform.api.infrastructure.ApiJsonSerializerService;
@@ -52,6 +53,7 @@ import org.mifosng.platform.loan.service.LoanReadPlatformService;
import org.mifosng.platform.loan.service.LoanWritePlatformService;
import org.mifosng.platform.loanproduct.service.LoanDropdownReadPlatformService;
import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService;
import org.mifosng.platform.staff.service.StaffReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -88,6 +90,9 @@ public class LoansApiResource {
@Autowired
private ApiJsonSerializerService apiJsonSerializerService;
@Autowired
private StaffReadPlatformService staffReadPlatformService;
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@@ -118,6 +123,9 @@ public class LoansApiResource {
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}else if (responseParameters.contains("loanOfficerOptions") && (responseParameters.size() == 1)) {
// TODO Vishwas: check if response parameters should be additive
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
@@ -146,10 +154,19 @@ public class LoansApiResource {
final boolean convenienceDataRequired = false;
Collection<ChargeData> charges = loanBasicDetails.getCharges();
// populate loan officers if query param is passed in
Collection<StaffData> allowedLoanOfficers = null;
if (responseParameters.contains("loanOfficerOptions")) {
// get applicable loan officers
allowedLoanOfficers = this.staffReadPlatformService
.retrieveAllStaff(" office_id = "
+ loanBasicDetails.getClientOfficeId() + " and is_loan_officer = 1");
}
final LoanAccountData newLoanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, null, null, null, null, charges,
productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions,
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions);
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, allowedLoanOfficers);
return this.apiJsonSerializerService.serialzieLoanAccountDataToJson(prettyPrint, responseParameters, newLoanAccount);
}
@@ -176,6 +193,10 @@ public class LoansApiResource {
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
} else if ((responseParameters.contains("loanOfficerName") || responseParameters
.contains("loanOfficerId")) && (responseParameters.size() <= 2)) {
// TODO Vishwas: check if response parameters should be additive
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
@@ -241,11 +262,19 @@ public class LoansApiResource {
chargeOptions.removeAll(charges);
}
}
Collection<StaffData> allowedLoanOfficers = null;
if (responseParameters.contains("loanOfficerOptions")) {
// get applicable loan officers
allowedLoanOfficers = this.staffReadPlatformService
.retrieveAllStaff(" office_id = "
+ loanBasicDetails.getClientOfficeId() + " and is_loan_officer = 1");
}
final LoanAccountData loanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, summary, repaymentSchedule, loanRepayments, permissions, charges,
productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions,
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions);
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, allowedLoanOfficers);
return this.apiJsonSerializerService.serialzieLoanAccountDataToJson(prettyPrint, responseParameters, loanAccount);
}
@@ -1,5 +1,6 @@
package org.mifosng.platform.api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
@@ -10,19 +11,25 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.StaffCommand;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.OfficeLookup;
import org.mifosng.platform.api.data.StaffData;
import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
import org.mifosng.platform.api.infrastructure.ApiJsonSerializerService;
import org.mifosng.platform.api.infrastructure.ApiParameterHelper;
import org.mifosng.platform.organisation.service.OfficeReadPlatformService;
import org.mifosng.platform.staff.service.StaffReadPlatformService;
import org.mifosng.platform.staff.service.StaffWritePlatformService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -44,10 +51,18 @@ public class StaffApiResource {
@Autowired
private ApiJsonSerializerService apiJsonSerializerService;
@Autowired
private OfficeReadPlatformService officeReadPlatformService;
private final static Logger logger = LoggerFactory
.getLogger(StaffApiResource.class);
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveStaffs(@Context final UriInfo uriInfo) {
public String retrieveStaff(@Context final UriInfo uriInfo,
@QueryParam("sqlSearch") final String sqlSearch,
@QueryParam("officeId") final Integer officeId) {
Set<String> responseParameters = ApiParameterHelper
.extractFieldsForResponseIfProvided(uriInfo
@@ -55,8 +70,9 @@ public class StaffApiResource {
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo
.getQueryParameters());
final String extraCriteria = getStaffCriteria(sqlSearch, officeId);
final Collection<StaffData> staff = this.readPlatformService
.retrieveAllStaff();
.retrieveAllStaff(extraCriteria);
return this.apiJsonSerializerService.serializeStaffDataToJson(
prettyPrint, responseParameters, staff);
@@ -87,8 +103,14 @@ public class StaffApiResource {
.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo
.getQueryParameters());
boolean template = ApiParameterHelper.template(uriInfo
.getQueryParameters());
final StaffData staff = this.readPlatformService.retrieveStaff(staffId);
if (template) {
staff.setAllowedOffices(new ArrayList<OfficeLookup>(
officeReadPlatformService.retrieveAllOfficesForLookup()));
}
return this.apiJsonSerializerService.serializeStaffDataToJson(
prettyPrint, responseParameters, staff);
@@ -108,4 +130,24 @@ public class StaffApiResource {
return Response.ok().entity(new EntityIdentifier(entityId)).build();
}
private String getStaffCriteria(String sqlSearch, Integer officeId) {
String extraCriteria = "";
if (sqlSearch != null) {
extraCriteria = " and (" + sqlSearch + ")";
}
if (officeId != null) {
extraCriteria += " and office_id = " + officeId;
}
if (StringUtils.isNotBlank(extraCriteria)) {
extraCriteria = extraCriteria.substring(4);
}
logger.info("extraCriteria; " + extraCriteria);
return extraCriteria;
}
}
@@ -17,6 +17,7 @@ public class LoanApplicationCommand {
private final String externalId;
private final Long fundId;
private final Long loanOfficerId;
private final Long transactionProcessingStrategyId;
private final BigDecimal principal;
@@ -63,13 +64,15 @@ public class LoanApplicationCommand {
final Integer interestCalculationPeriodMethod,
final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod,
final Integer loanTermFrequency, final Integer loanTermFrequencyType,
final BigDecimal toleranceAmount, final LoanChargeCommand[] charges) {
final BigDecimal toleranceAmount, final LoanChargeCommand[] charges,
final Long loanOfficerId) {
this.modifiedParameters = modifiedParameters;
this.loanId = loanId;
this.clientId = clientId;
this.productId = productId;
this.externalId = externalId;
this.fundId = fundId;
this.loanOfficerId = loanOfficerId;
this.transactionProcessingStrategyId = transactionProcessingStrategyId;
this.submittedOnDate = submittedOnDate;
@@ -231,6 +234,10 @@ public class LoanApplicationCommand {
public boolean isFundChanged() {
return this.modifiedParameters.contains("fundId");
}
public boolean isLoanOfficerChanged() {
return this.modifiedParameters.contains("loanOfficerId");
}
public boolean isTransactionStrategyChanged() {
return this.modifiedParameters.contains("transactionProcessingStrategyId");
@@ -259,4 +266,8 @@ public class LoanApplicationCommand {
public boolean isInterestChargedFromDateChanged() {
return this.modifiedParameters.contains("interestChargedFromDate");
}
public Long getLoanOfficerId() {
return loanOfficerId;
}
}
@@ -10,16 +10,20 @@ public class StaffCommand {
private final Long id;
private final String firstName;
private final String lastName;
private final Long officeId;
private final boolean loanOfficerFlag;
private final Set<String> modifiedParameters;
public StaffCommand(final Set<String> modifiedParameters, final Long id,
final String firstName, final String lastName) {
final Long officeId, final String firstName, final String lastName,
final boolean loanOfficerFlag) {
this.modifiedParameters = modifiedParameters;
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.officeId = officeId;
this.loanOfficerFlag = loanOfficerFlag;
}
public Long getId() {
@@ -34,6 +38,14 @@ public class StaffCommand {
return lastName;
}
public Long getOfficeId() {
return officeId;
}
public boolean isLoanOfficerFlag() {
return loanOfficerFlag;
}
public boolean isFirstNameChanged() {
return this.modifiedParameters.contains("firstname");
}
@@ -42,4 +54,12 @@ public class StaffCommand {
return this.modifiedParameters.contains("lastname");
}
public boolean isLoanOfficerFlagChanged() {
return this.modifiedParameters.contains("loanOfficerFlag");
}
public boolean isOfficeChanged() {
return this.modifiedParameters.contains("officeId");
}
}
@@ -1,7 +1,9 @@
package org.mifosng.platform.api.data;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.joda.time.LocalDate;
@@ -20,6 +22,8 @@ public class LoanAccountData {
private final EnumOptionData status;
private final Long fundId;
private final String fundName;
private Long loanOfficerId;
private String loanOfficerName;
private final CurrencyData currency;
private final BigDecimal principal;
private final BigDecimal inArrearsTolerance;
@@ -66,6 +70,7 @@ public class LoanAccountData {
private final Collection<EnumOptionData> interestCalculationPeriodTypeOptions;
private final Collection<FundData> fundOptions;
private final Collection<ChargeData> chargeOptions;
private final Collection<StaffData> loanOfficerOptions;
public LoanAccountData(
final LoanBasicDetailsData basicDetails,
@@ -84,7 +89,8 @@ public class LoanAccountData {
final Collection<EnumOptionData> interestTypeOptions,
final Collection<EnumOptionData> interestCalculationPeriodTypeOptions,
final Collection<FundData> fundOptions,
final Collection<ChargeData> chargeOptions) {
final Collection<ChargeData> chargeOptions,
final Collection<StaffData> loanOfficerOptions) {
this.summary = summary;
this.repaymentSchedule = repaymentSchedule;
this.loanRepayments = loanRepayments;
@@ -100,6 +106,7 @@ public class LoanAccountData {
this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions;
this.fundOptions = fundOptions;
this.chargeOptions = chargeOptions;
this.loanOfficerOptions = loanOfficerOptions;
if (convenienceDataRequired) {
int maxSubmittedOnOffsetFromToday = basicDetails
@@ -131,6 +138,9 @@ public class LoanAccountData {
this.loanProductDescription = basicDetails.getLoanProductDescription();
this.fundId = basicDetails.getFundId();
this.fundName = basicDetails.getFundName();
this.loanOfficerId = basicDetails.getLoanOfficerId();
this.loanOfficerName = basicDetails.getLoanOfficerName();
this.submittedOnDate = basicDetails.getSubmittedOnDate();
this.approvedOnDate = basicDetails.getApprovedOnDate();
@@ -364,4 +374,20 @@ public class LoanAccountData {
public Collection<ChargeData> getChargeOptions() {
return chargeOptions;
}
public Long getLoanOfficerId() {
return loanOfficerId;
}
public void setLoanOfficerId(Long loanOfficerId) {
this.loanOfficerId = loanOfficerId;
}
public String getLoanOfficerName() {
return loanOfficerName;
}
public void setLoanOfficerName(String loanOfficerName) {
this.loanOfficerName = loanOfficerName;
}
}
@@ -13,9 +13,14 @@ import org.joda.time.Months;
*/
public class LoanBasicDetailsData {
public Long getClientOfficeId() {
return clientOfficeId;
}
private final Long id;
private final String externalId;
private final Long clientId;
private final Long clientOfficeId;
private final String clientName;
private final Long loanProductId;
private final String loanProductName;
@@ -23,6 +28,8 @@ public class LoanBasicDetailsData {
private final EnumOptionData status;
private final Long fundId;
private final String fundName;
private Long loanOfficerId;
private String loanOfficerName;
private final CurrencyData currency;
private final BigDecimal principal;
private final BigDecimal inArrearsTolerance;
@@ -52,8 +59,9 @@ public class LoanBasicDetailsData {
private final Collection<ChargeData> charges;
public static LoanBasicDetailsData populateForNewLoanCreation(final Long clientId, final String clientName, final LocalDate expectedDisbursementDate) {
return new LoanBasicDetailsData(clientId, clientName, expectedDisbursementDate);
public static LoanBasicDetailsData populateForNewLoanCreation(final Long clientId, final String clientName, final LocalDate expectedDisbursementDate,
final Long clientOfficeId) {
return new LoanBasicDetailsData(clientId, clientName, expectedDisbursementDate, clientOfficeId);
}
public static LoanBasicDetailsData populateForNewLoanCreation(final LoanBasicDetailsData loanAccount, final LoanProductData product) {
@@ -61,7 +69,7 @@ public class LoanBasicDetailsData {
final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery();
final EnumOptionData termPeriodFrequencyType = product.getRepaymentFrequencyType();
return new LoanBasicDetailsData(
loanAccount.clientId, loanAccount.clientName,
loanAccount.clientId, loanAccount.clientName, loanAccount.clientOfficeId,
product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(),
product.getCurrency(), product.getPrincipal(), product.getInArrearsTolerance(),
termFrequency, termPeriodFrequencyType, product.getNumberOfRepayments(), product.getRepaymentEvery(), product.getRepaymentFrequencyType(),
@@ -71,7 +79,7 @@ public class LoanBasicDetailsData {
}
private LoanBasicDetailsData(
final Long clientId, final String clientName,
final Long clientId, final String clientName, final Long clientOfficeId,
final Long productId, final String productName, final String productDescription, final Long fundId, final String fundName,
final CurrencyData currency, final BigDecimal principal, final BigDecimal inArrearsTolerance,
final Integer termFrequency,
@@ -90,6 +98,7 @@ public class LoanBasicDetailsData {
this.externalId = null;
this.clientId = clientId;
this.clientName = clientName;
this.clientOfficeId = clientOfficeId;
this.loanProductId = productId;
this.loanProductName = productName;
this.loanProductDescription= productDescription;
@@ -134,18 +143,20 @@ public class LoanBasicDetailsData {
this.charges = charges;
}
private LoanBasicDetailsData(final Long clientId, final String clientName, final LocalDate expectedDisbursementDate) {
private LoanBasicDetailsData(final Long clientId, final String clientName, final LocalDate expectedDisbursementDate, final Long clientOfficeId) {
this.id = null;
this.externalId = null;
this.clientId = clientId;
this.clientName = clientName;
this.clientOfficeId = clientOfficeId;
this.loanProductId = null;
this.loanProductName = null;
this.loanProductDescription= null;
this.status = null;
this.fundId = null;
this.fundName = null;
this.loanOfficerId = null;
this.loanOfficerName = null;
this.currency = null;
this.principal = null;
this.inArrearsTolerance = null;
@@ -181,7 +192,7 @@ public class LoanBasicDetailsData {
final Long id,
final String externalId,
final Long clientId, final String clientName,
final Long loanProductId,
final Long clientOfficeId, final Long loanProductId,
final String loanProductName,
final String loanProductDescription,
final Long fundId, String fundName,
@@ -210,16 +221,20 @@ public class LoanBasicDetailsData {
final Integer termFrequency,
final EnumOptionData termPeriodFrequencyType,
final Integer transactionStrategyId,
final Collection<ChargeData> charges) {
final Collection<ChargeData> charges,
final Long loanOfficerId, String loanOfficerName) {
this.id = id;
this.externalId = externalId;
this.clientId = clientId;
this.clientName = clientName;
this.clientOfficeId = clientOfficeId;
this.loanProductId = loanProductId;
this.loanProductName = loanProductName;
this.loanProductDescription = loanProductDescription;
this.fundId = fundId;
this.fundName = fundName;
this.loanOfficerId = loanOfficerId;
this.loanOfficerName = loanOfficerName;
this.closedOnDate = closedOnDate;
this.submittedOnDate = submittedOnDate;
this.approvedOnDate = approvedOnDate;
@@ -476,4 +491,20 @@ public class LoanBasicDetailsData {
public Collection<ChargeData> getCharges() {
return charges;
}
public Long getLoanOfficerId() {
return loanOfficerId;
}
public void setLoanOfficerId(Long loanOfficerId) {
this.loanOfficerId = loanOfficerId;
}
public String getLoanOfficerName() {
return loanOfficerName;
}
public void setLoanOfficerName(String loanOfficerName) {
this.loanOfficerName = loanOfficerName;
}
}
@@ -1,6 +1,8 @@
package org.mifosng.platform.api.data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class StaffData implements Serializable {
@@ -8,17 +10,23 @@ public class StaffData implements Serializable {
private String firstname;
private String lastname;
private String displayName;
private final Long officeId;
private final String officeName;
private final Boolean loanOfficerFlag;
public StaffData() {
//
}
private List<OfficeLookup> allowedOffices = new ArrayList<OfficeLookup>();
public StaffData(final Long id, final String firstname,
final String lastname,final String displayName) {
final String lastname, final String displayName,
final Long officeId, final String officeName,
final Boolean loanOfficerFlag) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.displayName= displayName;
this.displayName = displayName;
this.officeName = officeName;
this.loanOfficerFlag = loanOfficerFlag;
this.officeId = officeId;
}
public Long getId() {
@@ -53,5 +61,24 @@ public class StaffData implements Serializable {
this.displayName = displayName;
}
public Long getOfficeId() {
return officeId;
}
public String getOfficeName() {
return officeName;
}
public Boolean getLoanOfficerFlag() {
return loanOfficerFlag;
}
public List<OfficeLookup> getAllowedOffices() {
return allowedOffices;
}
public void setAllowedOffices(List<OfficeLookup> allowedOffices) {
this.allowedOffices = allowedOffices;
}
}
@@ -134,7 +134,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
Set<String> supportedParams = new HashSet<String>(
Arrays.asList("firstname", "lastname")
Arrays.asList("firstname", "lastname","officeId", "loanOfficerFlag")
);
checkForUnsupportedParameters(requestMap, supportedParams);
@@ -143,8 +143,10 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
String firstname = extractStringParameter("firstname", requestMap, modifiedParameters);
String lastname = extractStringParameter("lastname", requestMap, modifiedParameters);
Boolean isLoanOfficer = extractBooleanParameter("loanOfficerFlag", requestMap, modifiedParameters);
Long officeId = extractLongParameter("officeId", requestMap, modifiedParameters);
return new StaffCommand(modifiedParameters, resourceIdentifier, firstname, lastname);
return new StaffCommand(modifiedParameters, resourceIdentifier, officeId,firstname, lastname, isLoanOfficer);
}
@Override
@@ -445,7 +447,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
"loanTermFrequency", "loanTermFrequencyType", "charges",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote",
"locale", "dateFormat")
"locale", "dateFormat", "loanOfficerId")
);
checkForUnsupportedParameters(requestMap, supportedParams);
@@ -455,6 +457,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
Long clientId = extractLongParameter("clientId", requestMap, modifiedParameters);
Long productId = extractLongParameter("productId", requestMap, modifiedParameters);
Long fundId = extractLongParameter("fundId", requestMap, modifiedParameters);
Long loanOfficerId = extractLongParameter("loanOfficerId", requestMap, modifiedParameters);
Long transactionProcessingStrategyId = extractLongParameter("transactionProcessingStrategyId", requestMap, modifiedParameters);
String externalId = extractStringParameter("externalId", requestMap, modifiedParameters);
@@ -523,7 +526,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
principal, interestRatePerPeriod, interestRateFrequencyTypeValue, interestTypeValue, interestCalculationPeriodTypeValue,
repaymentEvery, repaymentFrequencyType, numberOfRepayments, amortizationTypeValue,
loanTermFrequency, loanTermFrequencyType,
inArrearsToleranceValue, charges);
inArrearsToleranceValue, charges,loanOfficerId);
}
@Override
@@ -63,7 +63,8 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
private static final Set<String> FUND_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "name", "externalId"));
private static final Set<String> STAFF_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "firstname", "lastname","displayName"));
Arrays.asList("id", "firstname", "lastname","displayName", "officeId", "officeName",
"loanOfficerFlag","allowedOffices"));
private static final Set<String> LOAN_PRODUCT_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "name", "description", "fundId", "fundName",
"transactionProcessingStrategyId",
@@ -149,7 +150,8 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
"lifeCycleStatusDate", "summary", "repaymentSchedule",
"loanRepayments", "permissions", "convenienceData", "charges",
"productOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions", "chargeOptions"));
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions", "chargeOptions",
"loanOfficerId", "loanOfficerName", "loanOfficerOptions"));
private static final Set<String> LOAN_TRANSACTION_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "transactionType", "date", "principal",
@@ -7,7 +7,7 @@ public class StaffNotFoundException extends
AbstractPlatformResourceNotFoundException {
public StaffNotFoundException(final Long id) {
super("error.msg.fund.id.invalid", "Staff with identifier " + id
super("error.msg.staff.id.invalid", "Staff with identifier " + id
+ " does not exist", id);
}
}
@@ -0,0 +1,21 @@
package org.mifosng.platform.exceptions;
/**
* A {@link RuntimeException} thrown when staff resources are not found.
*/
public class StaffRoleException extends
AbstractPlatformResourceNotFoundException {
public static enum STAFF_ROLE {
LOAN_OFFICER, BRANCH_MANAGER;
@Override
public String toString() {
return name().toString().replaceAll("-", " ").toLowerCase();
}
}
public StaffRoleException(final Long id, STAFF_ROLE role) {
super("error.msg.staff.id.invalid.role", "Staff with identifier " + id
+ " is not a " + role.toString(), id);
}
}
@@ -38,6 +38,7 @@ import org.mifosng.platform.exceptions.InvalidLoanStateTransitionException;
import org.mifosng.platform.exceptions.InvalidLoanTransactionTypeException;
import org.mifosng.platform.fund.domain.Fund;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
import org.mifosng.platform.staff.domain.Staff;
import org.mifosng.platform.user.domain.AppUser;
@Entity
@@ -57,6 +58,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@JoinColumn(name = "fund_id", nullable = true)
private Fund fund;
@ManyToOne
@JoinColumn(name = "loan_officer_id", nullable = true)
private Staff loanofficer;
@ManyToOne
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@@ -157,10 +162,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@Transient
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessor = new LoanRepaymentScheduleTransactionProcessorFactory();
public static Loan createNew(final Fund fund, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
public static Loan createNew(final Fund fund,final Staff loanOfficer, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProduct loanProduct, final Client client,
final LoanProductRelatedDetail loanRepaymentScheduleDetail) {
return new Loan(client, fund, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null);
return new Loan(client, fund,loanOfficer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null);
}
protected Loan() {
@@ -170,11 +175,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.charges = null;
}
public Loan(final Client client, Fund fund, LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProduct loanProduct,
public Loan(final Client client, Fund fund, Staff loanOfficer, LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProduct loanProduct,
final LoanProductRelatedDetail loanRepaymentScheduleDetail,
final LoanStatus loanStatus) {
this.client = client;
this.fund = fund;
this.loanofficer = loanOfficer;
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.loanProduct = loanProduct;
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
@@ -194,6 +200,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return this.loanProduct;
}
public Staff getLoanofficer() {
return loanofficer;
}
public LoanProductRelatedDetail repaymentScheduleDetail() {
return this.loanRepaymentScheduleDetail;
}
@@ -23,6 +23,8 @@ import org.mifosng.platform.exceptions.FundNotFoundException;
import org.mifosng.platform.exceptions.InvalidCurrencyException;
import org.mifosng.platform.exceptions.LoanProductNotFoundException;
import org.mifosng.platform.exceptions.LoanTransactionProcessingStrategyNotFoundException;
import org.mifosng.platform.exceptions.StaffNotFoundException;
import org.mifosng.platform.exceptions.StaffRoleException;
import org.mifosng.platform.fund.domain.Fund;
import org.mifosng.platform.fund.domain.FundRepository;
import org.mifosng.platform.loan.domain.AmortizationMethod;
@@ -41,6 +43,8 @@ import org.mifosng.platform.loan.domain.LoanTransactionProcessingStrategy;
import org.mifosng.platform.loan.domain.LoanTransactionProcessingStrategyRepository;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.loanschedule.domain.AprCalculator;
import org.mifosng.platform.staff.domain.Staff;
import org.mifosng.platform.staff.domain.StaffRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
@@ -54,6 +58,7 @@ public class LoanAssembler {
private final FundRepository fundRepository;
private final ChargeRepository chargeRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
private final StaffRepository staffRepository;
@Autowired
public LoanAssembler(
@@ -61,12 +66,14 @@ public class LoanAssembler {
final ClientRepository clientRepository,
final FundRepository fundRepository,
final ChargeRepository chargeRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository) {
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final StaffRepository staffRepository) {
this.loanProductRepository = loanProductRepository;
this.clientRepository = clientRepository;
this.fundRepository = fundRepository;
this.chargeRepository = chargeRepository;
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
this.staffRepository = staffRepository;
}
public Loan assembleFrom(final LoanApplicationCommand command) {
@@ -94,7 +101,10 @@ public class LoanAssembler {
Fund fund = findFundByIdIfProvided(command.getFundId());
LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
Loan loan = Loan.createNew(fund, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail);
//optionally associate a loan officer to the loan
Staff loanOfficer= findLoanOfficerByIdIfProvided(command.getLoanOfficerId());
Loan loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail);
loan.setExternalId(command.getExternalId());
final Set<LoanCharge> charges = this.assembleSetOfCharges(command, loan, loanProduct, currency.getCode());
@@ -159,6 +169,20 @@ public class LoanAssembler {
return fund;
}
private Staff findLoanOfficerByIdIfProvided(final Long loanOfficerId) {
Staff staff = null;
if (loanOfficerId != null) {
staff = this.staffRepository.findOne(loanOfficerId);
if (staff == null) {
throw new StaffNotFoundException(loanOfficerId);
} else if (!staff.getLoanOfficerFlag()) {
throw new StaffRoleException(loanOfficerId,
StaffRoleException.STAFF_ROLE.LOAN_OFFICER);
}
}
return staff;
}
public LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
LoanTransactionProcessingStrategy strategy = null;
if (transactionProcessingStrategyId != null) {
@@ -234,7 +234,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LocalDate expectedDisbursementDate = new LocalDate();
final ClientData clientAccount = this.clientReadPlatformService.retrieveIndividualClient(clientId);
LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewLoanCreation(clientAccount.getId(), clientAccount.getDisplayName(), expectedDisbursementDate);
LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewLoanCreation(clientAccount.getId(), clientAccount.getDisplayName(), expectedDisbursementDate,
clientAccount.getOfficeId());
if (productId != null) {
LoanProductData selectedProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
@@ -371,7 +372,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
public String loanSchema() {
return "l.id as id, l.external_id as externalId, l.fund_id as fundId, f.name as fundName, "
+ " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription, c.id as clientId, c.display_name as clientName, "
+ " l.submittedon_date as submittedOnDate,"
+ " c.office_id as clientOfficeId,l.submittedon_date as submittedOnDate,"
+ " l.approvedon_date as approvedOnDate, l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate,"
+ " l.interest_calculated_from_date as interestChargedFromDate, l.closedon_date as closedOnDate, l.expected_maturedon_date as expectedMaturityDate, "
+ " l.principal_amount as principal, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery,"
@@ -380,12 +381,14 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " l.term_frequency as termFrequency, l.term_period_frequency_enum as termPeriodFrequencyType, "
+ " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.interest_calculated_in_period_enum as interestCalculationPeriodType,"
+ " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, "
+ " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode"
+ " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "
+ " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName"
+ " from m_loan l"
+ " join m_client c on c.id = l.client_id"
+ " join m_product_loan lp on lp.id = l.product_id"
+ " join m_currency rc on rc.`code` = l.currency_code"
+ " left join m_fund f on f.id = l.fund_id";
+ " left join m_fund f on f.id = l.fund_id"
+ " left join m_staff s on s.id = l.loan_officer_id";
}
@Override
@@ -404,9 +407,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
Long id = rs.getLong("id");
String externalId = rs.getString("externalId");
Long clientId = JdbcSupport.getLong(rs, "clientId");
Long clientOfficeId = JdbcSupport.getLong(rs, "clientOfficeId");
String clientName = rs.getString("clientName");
Long fundId = JdbcSupport.getLong(rs, "fundId");
String fundName = rs.getString("fundName");
Long loanOfficerId = JdbcSupport.getLong(rs, "loanOfficerId");
String loanOfficerName = rs.getString("loanOfficerName");
Long loanProductId = JdbcSupport.getLong(rs, "loanProductId");
String loanProductName = rs.getString("loanProductName");
String loanProductDescription = rs.getString("loanProductDescription");
@@ -471,7 +477,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
Collection<ChargeData> charges = null;
return new LoanBasicDetailsData(id, externalId, clientId, clientName,
return new LoanBasicDetailsData(id, externalId, clientId, clientName, clientOfficeId,
loanProductId, loanProductName, loanProductDescription,
fundId, fundName,
closedOnDate,
@@ -485,7 +491,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
amortizationType, interestType,
interestCalculationPeriodType,
status,
lifeCycleStatusDate, termFrequency, termPeriodFrequencyType, transactionStrategyId, charges);
lifeCycleStatusDate, termFrequency, termPeriodFrequencyType, transactionStrategyId, charges,
loanOfficerId, loanOfficerName);
}
}
@@ -2,17 +2,19 @@ package org.mifosng.platform.staff.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.StaffCommand;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
import org.mifosng.platform.organisation.domain.Office;
import org.mifosng.platform.user.domain.AppUser;
@Entity
@Table(name = "m_staff", uniqueConstraints = {
@UniqueConstraint(columnNames = { "display_name" }, name = "display_name")})
@Table(name = "m_staff", uniqueConstraints = { @UniqueConstraint(columnNames = { "display_name" }, name = "display_name") })
public class Staff extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "firstname", length = 50)
@@ -25,21 +27,39 @@ public class Staff extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "display_name", length = 100)
private String displayName;
public static Staff createNew(final String firstname, final String lastname) {
return new Staff(firstname, lastname);
// Office to which this employee belongs
@SuppressWarnings("unused")
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
// Flag determines if employee is a loan Officer
@SuppressWarnings("unused")
@Column(name = "is_loan_officer ", nullable = false)
private Boolean loanOfficerFlag;
public static Staff createNew(Office staffOffice, final String firstname,
final String lastname, boolean isLoanOfficer) {
return new Staff(staffOffice, firstname, lastname, isLoanOfficer);
}
protected Staff() {
//
}
private Staff(final String firstname, final String lastname) {
private Staff(final Office staffOffice, final String firstname,
final String lastname, final boolean isLoanOfficer) {
this.office = staffOffice;
this.firstname = StringUtils.defaultIfEmpty(firstname, null);
this.lastname = StringUtils.defaultIfEmpty(lastname, null);
this.loanOfficerFlag = isLoanOfficer;
deriveDisplayName(firstname);
}
public void update(final StaffCommand command) {
public void update(final StaffCommand command, final Office staffOffice) {
if (command.isOfficeChanged()) {
this.office = staffOffice;
}
if (command.isFirstNameChanged()) {
this.firstname = StringUtils.defaultIfEmpty(command.getFirstName(),
@@ -54,6 +74,10 @@ public class Staff extends AbstractAuditableCustom<AppUser, Long> {
if (command.isLastNameChanged() || command.isFirstNameChanged()) {
deriveDisplayName(firstname);
}
if (command.isLoanOfficerFlagChanged()) {
this.loanOfficerFlag = command.isLoanOfficerFlag();
}
}
private void deriveDisplayName(final String firstname) {
@@ -64,4 +88,8 @@ public class Staff extends AbstractAuditableCustom<AppUser, Long> {
}
}
public Boolean getLoanOfficerFlag() {
return loanOfficerFlag;
}
}
@@ -29,6 +29,10 @@ public class StaffCommandValidator {
baseDataValidator.reset().parameter("lastname")
.value(command.getLastName()).notBlank()
.notExceedingLengthOf(50);
baseDataValidator.reset().parameter("officeId")
.value(command.getOfficeId()).notNull()
.integerGreaterThanZero();
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(
@@ -8,5 +8,5 @@ public interface StaffReadPlatformService {
StaffData retrieveStaff(Long staffId);
Collection<StaffData> retrieveAllStaff();
Collection<StaffData> retrieveAllStaff(final String extraCriteria);
}
@@ -4,6 +4,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.data.StaffData;
import org.mifosng.platform.exceptions.StaffNotFoundException;
import org.mifosng.platform.infrastructure.TenantAwareRoutingDataSource;
@@ -30,7 +31,9 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService {
private static final class StaffMapper implements RowMapper<StaffData> {
public String schema() {
return " s.id as id, s.firstname as firstname, s.lastname as lastname, s.display_name as displayName from m_staff s ";
return " s.id as id,s.office_id as officeId, o.name as officeName, s.firstname as firstname, s.lastname as lastname,"
+ " s.display_name as displayName, s.is_loan_officer as isLoanOfficer from m_staff s "
+ " join m_office o on o.id = s.office_id";
}
@Override
@@ -41,19 +44,27 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService {
String firstname = rs.getString("firstname");
String lastname = rs.getString("lastname");
String displayName = rs.getString("displayName");
return new StaffData(id, firstname, lastname,displayName);
Long officeId = rs.getLong("officeId");
boolean isLoanOfficer = rs.getBoolean("isLoanOfficer");
String officeName = rs.getString("officeName");
return new StaffData(id, firstname, lastname, displayName,
officeId, officeName, isLoanOfficer);
}
}
@Override
public Collection<StaffData> retrieveAllStaff() {
public Collection<StaffData> retrieveAllStaff(final String extraCriteria) {
context.authenticatedUser();
StaffMapper rm = new StaffMapper();
String sql = "select " + rm.schema() + " order by s.lastname";
String sql = "select " + rm.schema();
if (StringUtils.isNotBlank(extraCriteria)) {
sql += " where " + extraCriteria;
}
sql = sql + " order by s.lastname";
return this.jdbcTemplate.query(sql, rm, new Object[] {});
}
@@ -2,11 +2,14 @@ package org.mifosng.platform.staff.service;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.StaffCommand;
import org.mifosng.platform.exceptions.StaffNotFoundException;
import org.mifosng.platform.exceptions.OfficeNotFoundException;
import org.mifosng.platform.exceptions.PlatformDataIntegrityException;
import org.mifosng.platform.exceptions.StaffNotFoundException;
import org.mifosng.platform.organisation.domain.Office;
import org.mifosng.platform.organisation.domain.OfficeRepository;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.staff.domain.Staff;
import org.mifosng.platform.staff.domain.StaffRepository;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -23,13 +26,16 @@ public class StaffWritePlatformServiceJpaRepositoryImpl implements
private final PlatformSecurityContext context;
private final StaffRepository staffRepository;
private final OfficeRepository officeRepository;
@Autowired
public StaffWritePlatformServiceJpaRepositoryImpl(
final PlatformSecurityContext context,
final StaffRepository staffRepository) {
final StaffRepository staffRepository,
final OfficeRepository officeRepository) {
this.context = context;
this.staffRepository = staffRepository;
this.officeRepository = officeRepository;
}
@Transactional
@@ -42,8 +48,14 @@ public class StaffWritePlatformServiceJpaRepositoryImpl implements
StaffCommandValidator validator = new StaffCommandValidator(command);
validator.validateForCreate();
Staff staff = Staff.createNew(command.getFirstName(),
command.getLastName());
Office staffOffice = this.officeRepository.findOne(command
.getOfficeId());
if (staffOffice == null) {
throw new OfficeNotFoundException(command.getOfficeId());
}
Staff staff = Staff.createNew(staffOffice, command.getFirstName(),
command.getLastName(), command.isLoanOfficerFlag());
this.staffRepository.saveAndFlush(staff);
@@ -69,7 +81,17 @@ public class StaffWritePlatformServiceJpaRepositoryImpl implements
if (staff == null) {
throw new StaffNotFoundException(staffId);
}
staff.update(command);
Office staffOffice = null;
Long officeId = command.getOfficeId();
if (command.isOfficeChanged() && officeId != null) {
staffOffice = this.officeRepository.findOne(officeId);
if (staffOffice == null) {
throw new OfficeNotFoundException(command.getOfficeId());
}
}
staff.update(command, staffOffice);
this.staffRepository.saveAndFlush(staff);