MIFOSX 26 Solidarity group loan

This commit is contained in:
Michal Dudzinski
2012-10-01 15:00:48 +02:00
parent 2855e56e0f
commit 98ca889938
19 changed files with 456 additions and 39 deletions
@@ -0,0 +1,3 @@
alter table m_loan add column group_id bigint(20) after client_id;
alter table m_loan modify client_id bigint(20);
alter table m_loan add constraint foreign key (`group_id`) references `m_group` (`id`);
@@ -20,8 +20,10 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.mifosng.platform.api.commands.GroupCommand;
import org.mifosng.platform.api.data.ClientAccountSummaryCollectionData;
import org.mifosng.platform.api.data.ClientLookup;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData;
import org.mifosng.platform.api.data.GroupData;
import org.mifosng.platform.api.data.OfficeLookup;
import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
@@ -167,4 +169,28 @@ public class GroupsApiResource {
return Response.ok().entity(entityIdentifier).build();
}
@GET
@Path("{groupId}/loans")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public String retrieveGroupAccount(@PathParam("groupId") final Long groupId,
@Context final UriInfo uriInfo) {
Set<String> typicalResponseParameters = new HashSet<String>(
Arrays.asList("pendingApprovalLoans", "awaitingDisbursalLoans", "openLoans", "closedLoans",
"anyLoanCount", "pendingApprovalLoanCount", "awaitingDisbursalLoanCount",
"activeLoanCount", "closedLoanCount")
);
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
GroupAccountSummaryCollectionData clientAccount = this.groupReadPlatformService.retrieveGroupAccountDetails(groupId);
return this.apiJsonSerializerService.serializeGroupAccountSummaryCollectionDataToJson(prettyPrint, responseParameters, clientAccount);
}
}
@@ -96,7 +96,7 @@ public class LoansApiResource {
private StaffReadPlatformService staffReadPlatformService;
private final static Set<String> typicalResponseParameters = new HashSet<String>(
Arrays.asList("id", "externalId", "clientId", "clientName", "fundId", "fundName",
Arrays.asList("id", "externalId", "clientId", "groupId", "clientName", "groupName", "fundId", "fundName",
"loanProductId", "loanProductName", "loanProductDescription",
"loanOfficerName", "loanOfficerId",
"currency", "principal",
@@ -118,6 +118,7 @@ public class LoansApiResource {
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveDetailsForNewLoanApplicationStepOne(
@QueryParam("clientId") final Long clientId,
@QueryParam("groupId") final Long groupId,
@QueryParam("productId") final Long productId,
@Context final UriInfo uriInfo) {
@@ -145,17 +146,26 @@ public class LoansApiResource {
Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges();
ChargeData chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate();
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges();
ChargeData chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate();
LoanBasicDetailsData loanBasicDetails;
Long officeId;
if ( clientId != null ){
loanBasicDetails = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId);
officeId = loanBasicDetails.getClientOfficeId();
} else {
loanBasicDetails = this.loanReadPlatformService.retrieveGroupAndProductDetails(groupId, productId);
officeId = loanBasicDetails.getGroupOfficeId();
}
LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId);
final boolean convenienceDataRequired = false;
Collection<LoanChargeData> charges = loanBasicDetails.getCharges();
Collection<StaffData> allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.getClientOfficeId());
final LoanAccountData newLoanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, null, null, null, charges,
Collection<StaffData> allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId);
final LoanAccountData newLoanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, null, null, null, charges,
productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions,
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers);
@@ -12,6 +12,7 @@ public class LoanApplicationCommand {
private final Long loanId;
private final Long clientId;
private final Long groupId;
private final Long productId;
private final String externalId;
@@ -48,7 +49,7 @@ public class LoanApplicationCommand {
public LoanApplicationCommand(
final Set<String> modifiedParameters,
final Long loanId,
final Long clientId, final Long productId, final String externalId,
final Long clientId, final Long groupId, final Long productId, final String externalId,
final Long fundId, final Long transactionProcessingStrategyId,
final LocalDate submittedOnDate, final String submittedOnNote,
final LocalDate expectedDisbursementDate,
@@ -66,6 +67,7 @@ public class LoanApplicationCommand {
this.modifiedParameters = modifiedParameters;
this.loanId = loanId;
this.clientId = clientId;
this.groupId = groupId;
this.productId = productId;
this.externalId = externalId;
this.fundId = fundId;
@@ -112,6 +114,10 @@ public class LoanApplicationCommand {
return clientId;
}
public Long getGroupId() {
return groupId;
}
public Long getProductId() {
return productId;
}
@@ -0,0 +1,73 @@
package org.mifosng.platform.api.data;
import java.util.List;
public class GroupAccountSummaryCollectionData {
private final int anyLoanCount;
private final int pendingApprovalLoanCount;
private final List<GroupAccountSummaryData> pendingApprovalLoans;
private final int awaitingDisbursalLoanCount;
private final List<GroupAccountSummaryData> awaitingDisbursalLoans;
private final int activeLoanCount;
private final List<GroupAccountSummaryData> openLoans;
private final int closedLoanCount;
private final List<GroupAccountSummaryData> closedLoans;
public GroupAccountSummaryCollectionData(List<GroupAccountSummaryData> pendingApprovalLoans,
List<GroupAccountSummaryData> awaitingDisbursalLoans,
List<GroupAccountSummaryData> openLoans,
List<GroupAccountSummaryData> closedLoans) {
this.pendingApprovalLoans = pendingApprovalLoans;
this.pendingApprovalLoanCount = pendingApprovalLoans.size();
this.awaitingDisbursalLoans = awaitingDisbursalLoans;
this.awaitingDisbursalLoanCount = awaitingDisbursalLoans.size();
this.openLoans = openLoans;
this.activeLoanCount = openLoans.size();
this.closedLoans = closedLoans;
this.closedLoanCount = closedLoans.size();
this.anyLoanCount = pendingApprovalLoanCount + awaitingDisbursalLoanCount + activeLoanCount + closedLoanCount;
}
public int getAnyLoanCount() {
return anyLoanCount;
}
public int getPendingApprovalLoanCount() {
return pendingApprovalLoanCount;
}
public List<GroupAccountSummaryData> getPendingApprovalLoans() {
return pendingApprovalLoans;
}
public int getAwaitingDisbursalLoanCount() {
return awaitingDisbursalLoanCount;
}
public List<GroupAccountSummaryData> getAwaitingDisbursalLoans() {
return awaitingDisbursalLoans;
}
public int getActiveLoanCount() {
return activeLoanCount;
}
public List<GroupAccountSummaryData> getOpenLoans() {
return openLoans;
}
public int getClosedLoanCount() {
return closedLoanCount;
}
public List<GroupAccountSummaryData> getClosedLoans() {
return closedLoans;
}
}
@@ -0,0 +1,44 @@
package org.mifosng.platform.api.data;
/**
* Immutable data object for group loan accounts.
*/
public class GroupAccountSummaryData {
private final Long id;
private final String externalId;
private final Long productId;
private final String productName;
private final Integer accountStatusId;
public GroupAccountSummaryData(Long id, String externalId,
Long productId,
String productName,
Integer accountStatusId) {
this.id = id;
this.externalId = externalId;
this.productId = productId;
this.productName = productName;
this.accountStatusId = accountStatusId;
}
public Long getId() {
return id;
}
public String getExternalId() {
return externalId;
}
public Long getProductId() {
return productId;
}
public String getProductName() {
return productName;
}
public Integer getAccountStatusId() {
return accountStatusId;
}
}
@@ -42,7 +42,15 @@ public class GroupData {
return name;
}
public String getExternalId() {
public Long getOfficeId() {
return officeId;
}
public String getOfficeName() {
return officeName;
}
public String getExternalId() {
return externalId;
}
@@ -17,6 +17,8 @@ public class LoanAccountData {
private final String externalId;
private final Long clientId;
private final String clientName;
private final Long groupId;
private final String groupName;
private final Long loanProductId;
private final String loanProductName;
private final String loanProductDescription;
@@ -134,6 +136,8 @@ public class LoanAccountData {
this.externalId = basicDetails.getExternalId();
this.clientId = basicDetails.getClientId();
this.clientName = basicDetails.getClientName();
this.groupId = basicDetails.getGroupId();
this.groupName = basicDetails.getGroupName();
this.loanProductId = basicDetails.getLoanProductId();
this.loanProductName = basicDetails.getLoanProductName();
this.loanProductDescription = basicDetails.getLoanProductDescription();
@@ -19,6 +19,9 @@ public class LoanBasicDetailsData {
private final Long clientId;
private final Long clientOfficeId;
private final String clientName;
private final Long groupId;
private final Long groupOfficeId;
private final String groupName;
private final Long loanProductId;
private final String loanProductName;
private final String loanProductDescription;
@@ -57,11 +60,17 @@ public class LoanBasicDetailsData {
private final BigDecimal totalDisbursementCharges;
private final Collection<LoanChargeData> charges;
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 populateForNewIndividualClientLoanCreation(final Long clientId, final String clientName, final LocalDate expectedDisbursementDate,
final Long clientOfficeId) {
return new LoanBasicDetailsData(clientId, clientName, clientOfficeId, null, null, null, expectedDisbursementDate);
}
public static LoanBasicDetailsData populateForNewGroupLoanCreation(final Long groupId, final String groupName,
final LocalDate expectedDisbursementDate,
final Long groupOfficeId){
return new LoanBasicDetailsData(null, null, null, groupId, groupName, groupOfficeId, expectedDisbursementDate);
}
public static LoanBasicDetailsData populateForNewLoanCreation(final LoanBasicDetailsData loanAccount, final LoanProductData product) {
final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery();
@@ -75,7 +84,8 @@ public class LoanBasicDetailsData {
return new LoanBasicDetailsData(
loanAccount.clientId, loanAccount.clientName, loanAccount.clientOfficeId,
product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(),
loanAccount.groupId, loanAccount.groupName, loanAccount.groupOfficeId,
product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(),
product.getCurrency(), product.getPrincipal(), product.getInArrearsTolerance(),
termFrequency, termPeriodFrequencyType, product.getNumberOfRepayments(), product.getRepaymentEvery(), product.getRepaymentFrequencyType(),
product.getTransactionProcessingStrategyId(), product.getAmortizationType(),
@@ -85,6 +95,7 @@ public class LoanBasicDetailsData {
private LoanBasicDetailsData(
final Long clientId, final String clientName, final Long clientOfficeId,
final Long groupId, final String groupName, final Long groupOfficeId,
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,
@@ -104,6 +115,9 @@ public class LoanBasicDetailsData {
this.clientId = clientId;
this.clientName = clientName;
this.clientOfficeId = clientOfficeId;
this.groupId = groupId;
this.groupName = groupName;
this.groupOfficeId = groupOfficeId;
this.loanProductId = productId;
this.loanProductName = productName;
this.loanProductDescription= productDescription;
@@ -151,12 +165,17 @@ public class LoanBasicDetailsData {
this.totalDisbursementCharges = BigDecimal.ZERO;
}
private LoanBasicDetailsData(final Long clientId, final String clientName, final LocalDate expectedDisbursementDate, final Long clientOfficeId) {
private LoanBasicDetailsData(final Long clientId, final String clientName, final Long clientOfficeId,
final Long groupId, final String groupName, final Long groupOfficeId,
final LocalDate expectedDisbursementDate) {
this.id = null;
this.externalId = null;
this.clientId = clientId;
this.clientName = clientName;
this.clientOfficeId = clientOfficeId;
this.groupId = groupId;
this.groupName = groupName;
this.groupOfficeId = groupOfficeId;
this.loanProductId = null;
this.loanProductName = null;
this.loanProductDescription= null;
@@ -201,7 +220,11 @@ public class LoanBasicDetailsData {
final Long id,
final String externalId,
final Long clientId, final String clientName,
final Long clientOfficeId, final Long loanProductId,
final Long clientOfficeId,
final Long groupId,
final String groupName,
final Long groupOfficeId,
final Long loanProductId,
final String loanProductName,
final String loanProductDescription,
final Long fundId, String fundName,
@@ -238,6 +261,9 @@ public class LoanBasicDetailsData {
this.clientId = clientId;
this.clientName = clientName;
this.clientOfficeId = clientOfficeId;
this.groupId = groupId;
this.groupName = groupName;
this.groupOfficeId = groupOfficeId;
this.loanProductId = loanProductId;
this.loanProductName = loanProductName;
this.loanProductDescription = loanProductDescription;
@@ -383,6 +409,18 @@ public class LoanBasicDetailsData {
return clientOfficeId;
}
public Long getGroupId() {
return groupId;
}
public Long getGroupOfficeId() {
return groupOfficeId;
}
public String getGroupName() {
return groupName;
}
public Long getFundId() {
return fundId;
}
@@ -444,7 +444,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
Set<String> supportedParams = new HashSet<String>(
Arrays.asList("clientId", "productId", "externalId", "fundId", "transactionProcessingStrategyId",
Arrays.asList("clientId", "groupId", "productId", "externalId", "fundId", "transactionProcessingStrategyId",
"principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments",
"loanTermFrequency", "loanTermFrequencyType", "charges",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
@@ -461,6 +461,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
JsonParserHelper helper = new JsonParserHelper();
final Long clientId = helper.extractLongNamed("clientId", element, modifiedParameters);
final Long groupId = helper.extractLongNamed("groupId", element, modifiedParameters);
final Long productId = helper.extractLongNamed("productId", element, modifiedParameters);
final Long fundId = helper.extractLongNamed("fundId", element, modifiedParameters);
final Long loanOfficerId = helper.extractLongNamed("loanOfficerId", element, modifiedParameters);
@@ -514,7 +515,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
}
return new LoanApplicationCommand(modifiedParameters,
resourceIdentifier, clientId, productId, externalId, fundId, transactionProcessingStrategyId,
resourceIdentifier, clientId, groupId, productId, externalId, fundId, transactionProcessingStrategyId,
submittedOnDate, submittedOnNote,
expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate,
principal, interestRatePerPeriod, interestRateFrequencyType, interestType, interestCalculationPeriodType,
@@ -17,6 +17,7 @@ import org.mifosng.platform.api.data.DepositProductData;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.FundData;
import org.mifosng.platform.api.data.GenericResultsetData;
import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData;
import org.mifosng.platform.api.data.GroupData;
import org.mifosng.platform.api.data.LoanAccountData;
import org.mifosng.platform.api.data.LoanChargeData;
@@ -86,6 +87,8 @@ public interface ApiJsonSerializerService {
String serializeClientAccountSummaryCollectionDataToJson(boolean prettyPrint, Set<String> responseParameters, ClientAccountSummaryCollectionData clientAccount);
String serializeGroupAccountSummaryCollectionDataToJson(boolean prettyPrint, Set<String> responseParameters, GroupAccountSummaryCollectionData groupAccount);
String serializeGroupDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<GroupData> groups);
String serializeGroupDataToJson(boolean prettyPrint, Set<String> responseParameters, GroupData group);
@@ -19,6 +19,7 @@ import org.mifosng.platform.api.data.DepositProductData;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.FundData;
import org.mifosng.platform.api.data.GenericResultsetData;
import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData;
import org.mifosng.platform.api.data.GroupData;
import org.mifosng.platform.api.data.LoanAccountData;
import org.mifosng.platform.api.data.LoanChargeData;
@@ -131,6 +132,12 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
"rejectedDepositAccountsCount","rejectedDepositAccounts",
"preclosedDepositAccountsCount","preclosedDepositAccounts"));
private static final Set<String> GROUP_ACCOUNTS_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("pendingApprovalLoans", "awaitingDisbursalLoans",
"openLoans", "closedLoans", "anyLoanCount",
"pendingApprovalLoanCount", "awaitingDisbursalLoanCount",
"activeLoanCount", "closedLoanCount"));
private static final Set<String> GROUP_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "officeId", "name", "externalId", "clientMembers",
"allowedClients", "allowedOffices"));
@@ -145,7 +152,7 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
Arrays.asList("periods", "cumulativePrincipalDisbursed"));
private static final Set<String> LOAN_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "externalId", "clientId", "clientName", "fundId", "fundName",
Arrays.asList("id", "externalId", "clientId","groupId", "clientName", "groupName", "fundId", "fundName",
"loanProductId", "loanProductName", "loanProductDescription",
"currency", "principal",
"inArrearsTolerance", "numberOfRepayments",
@@ -468,6 +475,17 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
return helper.serializedJsonFrom(gsonDeserializer, clientAccount);
}
@Override
public String serializeGroupAccountSummaryCollectionDataToJson(
final boolean prettyPrint, final Set<String> responseParameters,
final GroupAccountSummaryCollectionData groupAccount) {
final Gson gsonDeserializer = helper
.createGsonBuilderWithParameterExclusionSerializationStrategy(
GROUP_ACCOUNTS_DATA_PARAMETERS, prettyPrint,
responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, groupAccount);
}
@Override
public String serializeGroupDataToJson(final boolean prettyPrint,
final Set<String> responseParameters,
@@ -2,6 +2,7 @@ package org.mifosng.platform.group.service;
import java.util.Collection;
import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData;
import org.mifosng.platform.api.data.ClientLookup;
import org.mifosng.platform.api.data.GroupData;
@@ -14,4 +15,6 @@ public interface GroupReadPlatformService {
GroupData retrieveNewGroupDetails();
Collection<ClientLookup> retrieveClientMembers(Long groupId);
GroupAccountSummaryCollectionData retrieveGroupAccountDetails(Long groupId);
}
@@ -8,10 +8,14 @@ import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.data.ClientLookup;
import org.mifosng.platform.api.data.GroupAccountSummaryCollectionData;
import org.mifosng.platform.api.data.GroupAccountSummaryData;
import org.mifosng.platform.api.data.GroupData;
import org.mifosng.platform.api.data.OfficeLookup;
import org.mifosng.platform.client.service.ClientReadPlatformService;
import org.mifosng.platform.client.service.LoanStatusMapper;
import org.mifosng.platform.exceptions.GroupNotFoundException;
import org.mifosng.platform.infrastructure.JdbcSupport;
import org.mifosng.platform.infrastructure.TenantAwareRoutingDataSource;
import org.mifosng.platform.organisation.service.OfficeReadPlatformService;
import org.mifosng.platform.security.PlatformSecurityContext;
@@ -132,5 +136,75 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
}
}
@Override
public GroupAccountSummaryCollectionData retrieveGroupAccountDetails(Long groupId) {
try {
this.context.authenticatedUser();
// Check if group exists
retrieveGroup(groupId);
List<GroupAccountSummaryData> pendingApprovalLoans = new ArrayList<GroupAccountSummaryData>();
List<GroupAccountSummaryData> awaitingDisbursalLoans = new ArrayList<GroupAccountSummaryData>();
List<GroupAccountSummaryData> openLoans = new ArrayList<GroupAccountSummaryData>();
List<GroupAccountSummaryData> closedLoans = new ArrayList<GroupAccountSummaryData>();
GroupLoanAccountSummaryDataMapper rm = new GroupLoanAccountSummaryDataMapper();
String sql = "select " + rm.loanAccountSummarySchema() + " where l.group_id = ?";
List<GroupAccountSummaryData> results = this.jdbcTemplate.query(sql, rm, new Object[] {groupId});
if (results != null) {
for (GroupAccountSummaryData row : results) {
LoanStatusMapper statusMapper = new LoanStatusMapper(row.getAccountStatusId());
if (statusMapper.isOpen()) {
openLoans.add(row);
} else if (statusMapper.isAwaitingDisbursal()) {
awaitingDisbursalLoans.add(row);
} else if (statusMapper.isPendingApproval()) {
pendingApprovalLoans.add(row);
} else {
closedLoans.add(row);
}
}
}
return new GroupAccountSummaryCollectionData(pendingApprovalLoans,
awaitingDisbursalLoans, openLoans, closedLoans);
} catch (EmptyResultDataAccessException e) {
throw new GroupNotFoundException(groupId);
}
}
private static final class GroupLoanAccountSummaryDataMapper implements
RowMapper<GroupAccountSummaryData> {
public String loanAccountSummarySchema() {
StringBuilder accountsSummary = new StringBuilder("l.id as id, l.external_id as externalId,");
accountsSummary
.append("l.product_id as productId, lp.name as productName,")
.append("l.loan_status_id as statusId ")
.append("from m_loan l ")
.append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id ");
return accountsSummary.toString();
}
@Override
public GroupAccountSummaryData mapRow(final ResultSet rs,
@SuppressWarnings("unused") final int rowNum) throws SQLException {
Long id = JdbcSupport.getLong(rs, "id");
String externalId = rs.getString("externalId");
Long productId = JdbcSupport.getLong(rs, "productId");
String loanProductName = rs.getString("productName");
Integer loanStatusId = JdbcSupport.getInteger(rs, "statusId");
return new GroupAccountSummaryData(id, externalId, productId, loanProductName, loanStatusId);
}
}
}
@@ -36,6 +36,7 @@ import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.InvalidLoanStateTransitionException;
import org.mifosng.platform.exceptions.InvalidLoanTransactionTypeException;
import org.mifosng.platform.fund.domain.Fund;
import org.mifosng.platform.group.domain.Group;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
import org.mifosng.platform.staff.domain.Staff;
import org.mifosng.platform.user.domain.AppUser;
@@ -44,10 +45,14 @@ import org.mifosng.platform.user.domain.AppUser;
@Table(name = "m_loan", uniqueConstraints = @UniqueConstraint(columnNames = { "external_id" }))
public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne
@JoinColumn(name = "client_id", nullable = false)
@ManyToOne(optional = true)
@JoinColumn(name = "client_id")
private Client client;
@ManyToOne(optional = true)
@JoinColumn(name = "group_id")
private Group group;
@ManyToOne
@JoinColumn(name = "product_id")
private LoanProduct loanProduct;
@@ -170,8 +175,15 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return new Loan(client, fund,loanOfficer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null, loanCharges);
}
public static Loan createNew(final Fund fund,final Staff loanOfficer, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProduct loanProduct, final Group group,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges) {
return new Loan(group, fund,loanOfficer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null, loanCharges);
}
protected Loan() {
this.client = null;
this.group = null;
this.loanProduct = null;
this.loanRepaymentScheduleDetail = null;
this.charges = null;
@@ -203,6 +215,32 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.interestRebateOwed = BigDecimal.ZERO;
}
private Loan(
final Group group, Fund fund, Staff loanOfficer,
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProduct loanProduct,
final LoanProductRelatedDetail loanRepaymentScheduleDetail,
final LoanStatus loanStatus, final Set<LoanCharge> loanCharges) {
this.group = group;
this.fund = fund;
this.loanofficer = loanOfficer;
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.loanProduct = loanProduct;
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
if (loanStatus != null) {
this.loanStatus = loanStatus.getValue();
} else {
this.loanStatus = null;
}
if (loanCharges != null && !loanCharges.isEmpty()) {
this.charges = associateChargesWithThisLoan(loanCharges);
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
} else {
this.charges = null;
}
this.interestRebateOwed = BigDecimal.ZERO;
}
private BigDecimal deriveSumTotalOfChargesDueAtDisbursement() {
Money chargesDue = Money.of(getCurrency(), BigDecimal.ZERO);
@@ -227,7 +265,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return this.client;
}
public LoanProduct loanProduct() {
public Group getGroup() {
return group;
}
public LoanProduct loanProduct() {
return this.loanProduct;
}
@@ -26,7 +26,15 @@ public class LoanApplicationCommandValidator {
// "The parameter loanSchedule cannot be empty.", "loanSchedule");
// dataValidationErrors.add(error);
// }
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
if (command.getGroupId() != null){
baseDataValidator.reset().parameter("clientId").value(command.getClientId()).mustBeBlankWhenParameterProvided("groupId", command.getGroupId()).longGreaterThanZero();
}
if (command.getClientId() != null){
baseDataValidator.reset().parameter("groupId").value(command.getGroupId()).mustBeBlankWhenParameterProvided("clientId", command.getClientId()).longGreaterThanZero();
}
if (command.getSubmittedOnDate() == null) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.blank",
"The parameter submittedOnDate cannot be empty.", "submittedOnDate");
@@ -41,7 +49,6 @@ public class LoanApplicationCommandValidator {
}
if (command.getTransactionProcessingStrategyId() == null) {
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(command.getTransactionProcessingStrategyId()).notNull().inMinMaxRange(2, 2);
}
@@ -13,12 +13,15 @@ import org.mifosng.platform.client.domain.ClientRepository;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.exceptions.ClientNotFoundException;
import org.mifosng.platform.exceptions.FundNotFoundException;
import org.mifosng.platform.exceptions.GroupNotFoundException;
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.group.domain.Group;
import org.mifosng.platform.group.domain.GroupRepository;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
@@ -45,6 +48,7 @@ public class LoanAssembler {
private final LoanProductRepository loanProductRepository;
private final ClientRepository clientRepository;
private final GroupRepository groupRepository;
private final AprCalculator aprCalculator = new AprCalculator();
private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
@@ -56,6 +60,7 @@ public class LoanAssembler {
public LoanAssembler(
final LoanProductRepository loanProductRepository,
final ClientRepository clientRepository,
final GroupRepository groupRepository,
final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final StaffRepository staffRepository,
@@ -63,6 +68,7 @@ public class LoanAssembler {
final LoanChargeAssembler loanChargeAssembler) {
this.loanProductRepository = loanProductRepository;
this.clientRepository = clientRepository;
this.groupRepository = groupRepository;
this.fundRepository = fundRepository;
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
this.staffRepository = staffRepository;
@@ -77,11 +83,20 @@ public class LoanAssembler {
throw new LoanProductNotFoundException(command.getProductId());
}
Client client = this.clientRepository.findOne(command.getClientId());
if (client == null || client.isDeleted()) {
throw new ClientNotFoundException(command.getClientId());
Client client = null;
Group group = null;
if (command.getClientId() != null) {
client = this.clientRepository.findOne(command.getClientId());
if (client == null || client.isDeleted()) {
throw new ClientNotFoundException(command.getClientId());
}
} else {
group = this.groupRepository.findOne(command.getGroupId());
if (group == null || group.isDeleted()) {
throw new GroupNotFoundException(command.getGroupId());
}
}
MonetaryCurrency currency = loanProduct.getCurrency();
final Integer loanTermFrequency = command.getLoanTermFrequency();
final PeriodFrequencyType loanTermFrequencyType = PeriodFrequencyType.fromInt(command.getLoanTermFrequencyType());
@@ -97,8 +112,14 @@ public class LoanAssembler {
// optionally, see if charges are associated with loan on creation (through loan product or by being directly added)
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanProduct.getCharges(), command.getPrincipal());
Loan loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges);
Loan loan;
if (client != null) {
loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges);
} else {
loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, group, loanRepaymentScheduleDetail, loanCharges);
}
loan.setExternalId(command.getExternalId());
final LoanScheduleNewData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
@@ -25,6 +25,8 @@ public interface LoanReadPlatformService {
LoanBasicDetailsData retrieveClientAndProductDetails(Long clientId, Long productId);
LoanBasicDetailsData retrieveGroupAndProductDetails(Long groupId, Long productId);
LoanTransactionData retrieveNewLoanRepaymentDetails(Long loanId);
LoanTransactionData retrieveNewLoanWaiverDetails(Long loanId);
@@ -12,6 +12,7 @@ import org.mifosng.platform.api.data.ClientData;
import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.DisbursementData;
import org.mifosng.platform.api.data.EnumOptionData;
import org.mifosng.platform.api.data.GroupData;
import org.mifosng.platform.api.data.LoanBasicDetailsData;
import org.mifosng.platform.api.data.LoanChargeData;
import org.mifosng.platform.api.data.LoanPermissionData;
@@ -27,6 +28,7 @@ import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.CurrencyNotFoundException;
import org.mifosng.platform.exceptions.LoanNotFoundException;
import org.mifosng.platform.exceptions.LoanTransactionNotFoundException;
import org.mifosng.platform.group.service.GroupReadPlatformService;
import org.mifosng.platform.infrastructure.JdbcSupport;
import org.mifosng.platform.infrastructure.TenantAwareRoutingDataSource;
import org.mifosng.platform.loan.domain.Loan;
@@ -52,6 +54,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private final ApplicationCurrencyRepository applicationCurrencyRepository;
private final LoanProductReadPlatformService loanProductReadPlatformService;
private final ClientReadPlatformService clientReadPlatformService;
private final GroupReadPlatformService groupReadPlatformService;
private final LoanTransactionRepository loanTransactionRepository;
@Autowired
@@ -62,6 +65,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final ApplicationCurrencyRepository applicationCurrencyRepository,
final LoanProductReadPlatformService loanProductReadPlatformService,
final ClientReadPlatformService clientReadPlatformService,
final GroupReadPlatformService groupReadPlatformService,
final TenantAwareRoutingDataSource dataSource) {
this.context = context;
this.loanRepository = loanRepository;
@@ -69,6 +73,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.loanProductReadPlatformService = loanProductReadPlatformService;
this.clientReadPlatformService = clientReadPlatformService;
this.groupReadPlatformService = groupReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@@ -210,7 +215,7 @@ 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.populateForNewIndividualClientLoanCreation(clientAccount.getId(), clientAccount.getDisplayName(), expectedDisbursementDate,
clientAccount.getOfficeId());
if (productId != null) {
@@ -221,7 +226,26 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
return loanDetails;
}
@Override
@Override
public LoanBasicDetailsData retrieveGroupAndProductDetails(Long groupId, Long productId) {
context.authenticatedUser();
final LocalDate expectedDisbursementDate = new LocalDate();
final GroupData groupAccount = this.groupReadPlatformService.retrieveGroup(groupId);
LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewGroupLoanCreation(groupAccount.getId(), groupAccount.getName(), expectedDisbursementDate,
groupAccount.getOfficeId());
if (productId != null) {
LoanProductData selectedProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
loanDetails = LoanBasicDetailsData.populateForNewLoanCreation(loanDetails, selectedProduct);
}
return loanDetails;
}
@Override
public LoanTransactionData retrieveNewLoanRepaymentDetails(final Long loanId) {
context.authenticatedUser();
@@ -348,7 +372,8 @@ 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, "
+ " c.office_id as clientOfficeId,l.submittedon_date as submittedOnDate,"
+ " c.office_id as clientOfficeId, g.id as groupId, g.name as groupName, g.office_id as groupOfficeId,"
+ " l.submittedon_date as submittedOnDate,"
+ " l.total_charges_due_at_disbursement_derived as totalDisbursementCharges,"
+ " 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, "
@@ -361,7 +386,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " 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"
+ " left join m_client c on c.id = l.client_id"
+ " left join m_group g on g.id = l.group_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"
@@ -383,9 +409,15 @@ 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");
Long clientOfficeId = JdbcSupport.getLong(rs, "clientOfficeId");;
String clientName = rs.getString("clientName");
Long groupId = JdbcSupport.getLong(rs, "groupId");
Long groupOfficeId = JdbcSupport.getLong(rs, "groupOfficeId");;
String groupName = rs.getString("groupName");
Long fundId = JdbcSupport.getLong(rs, "fundId");
String fundName = rs.getString("fundName");
Long loanOfficerId = JdbcSupport.getLong(rs, "loanOfficerId");
@@ -455,7 +487,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
Collection<LoanChargeData> charges = null;
return new LoanBasicDetailsData(id, externalId, clientId, clientName, clientOfficeId,
return new LoanBasicDetailsData(id, externalId,
clientId, clientName, clientOfficeId,
groupId, groupName, groupOfficeId,
loanProductId, loanProductName, loanProductDescription,
fundId, fundName,
closedOnDate,