diff --git a/mifosng-db/patches/patch-0013-MIFOSX-109-Guarantors.sql b/mifosng-db/patches/patch-0013-MIFOSX-109-Guarantors.sql new file mode 100644 index 000000000..96ae3f2bf --- /dev/null +++ b/mifosng-db/patches/patch-0013-MIFOSX-109-Guarantors.sql @@ -0,0 +1,25 @@ +CREATE TABLE `m_guarantor_external` ( + `loan_id` BIGINT(20) NOT NULL, + `firstname` VARCHAR(50) NOT NULL, + `lastname` VARCHAR(50) NOT NULL, + `dob` DATE NULL, + `address_line_1` VARCHAR(500) NULL, + `address_line_2` VARCHAR(500) NULL, + `city` VARCHAR(50) NULL, + `state` VARCHAR(50) NULL, + `country` VARCHAR(50) NULL, + `zip` VARCHAR(20) NULL, + `house_phone_number` VARCHAR(20) NULL, + `mobile_number` VARCHAR(20) NULL, + `comment` VARCHAR(20) NULL, + PRIMARY KEY (`loan_id`), + CONSTRAINT `FK_m_guarantor_m_loan` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`) +) +ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +ALTER TABLE `m_loan` + ADD COLUMN `guarantor_id` BIGINT(20) NULL DEFAULT NULL AFTER `loan_officer_id`, + ADD CONSTRAINT `FK_m_loan_guarantor` FOREIGN KEY (`guarantor_id`) REFERENCES `m_client` (`id`); + +insert into x_registered_table values ('m_guarantor_external','m_loan'); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/GuarantorsApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/GuarantorsApiResource.java new file mode 100644 index 000000000..1e8a533a5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/GuarantorsApiResource.java @@ -0,0 +1,118 @@ +package org.mifosng.platform.api; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +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.mifosng.platform.api.commands.GuarantorCommand; +import org.mifosng.platform.api.data.EntityIdentifier; +import org.mifosng.platform.api.data.GuarantorData; +import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService; +import org.mifosng.platform.api.infrastructure.PortfolioApiJsonSerializerService; +import org.mifosng.platform.guarantor.GuarantorReadPlatformService; +import org.mifosng.platform.guarantor.GuarantorWritePlatformService; +import org.mifosng.platform.infrastructure.api.ApiParameterHelper; +import org.mifosng.platform.security.PlatformSecurityContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/loans/{loanId}/guarantor") +@Component +@Scope("singleton") +public class GuarantorsApiResource { + + @Autowired + private GuarantorWritePlatformService guarantorWritePlatformService; + + @Autowired + private GuarantorReadPlatformService guarantorReadPlatformService; + + @Autowired + private PortfolioApiDataConversionService apiDataConversionService; + + @Autowired + private PortfolioApiJsonSerializerService apiJsonSerializerService; + + private static final Set typicalResponseParameters = new HashSet( + Arrays.asList("externalGuarantor", "existingClientId", "firstname", + "lastname", "addressLine1", "addressLine2", "city", + "state", "zip", "country", "mobileNumber", + "housePhoneNumber", "comment", "dob")); + + private final String SystemEntityType = "m_guarantor_external"; + + @Autowired + private PlatformSecurityContext context; + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveGuarantorDetails(@Context final UriInfo uriInfo, + @PathParam("loanId") final Long loanId) { + context.authenticatedUser().validateHasReadPermission(SystemEntityType); + Set responseParameters = ApiParameterHelper + .extractFieldsForResponseIfProvided(uriInfo + .getQueryParameters()); + if (responseParameters.isEmpty()) { + responseParameters.addAll(typicalResponseParameters); + } + boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo + .getQueryParameters()); + + GuarantorData guarantorData = guarantorReadPlatformService + .retrieveGuarantor(loanId); + + return this.apiJsonSerializerService.serializeGuarantorDataToJson( + prettyPrint, responseParameters, guarantorData); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public Response createGuarantor(@PathParam("loanId") final Long loanId, + final String jsonRequestBody) { + + final GuarantorCommand command = this.apiDataConversionService + .convertJsonToGuarantorCommand(null, loanId, jsonRequestBody); + + this.guarantorWritePlatformService.createGuarantor(loanId, command); + + // returns the loan Identifier for which the guarantor was associated + return Response.ok().entity(new EntityIdentifier(loanId)).build(); + } + + @PUT + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public Response updateGuarantor(@PathParam("loanId") final Long loanId, + final String jsonRequestBody) { + final GuarantorCommand command = this.apiDataConversionService + .convertJsonToGuarantorCommand(null, loanId, jsonRequestBody); + + this.guarantorWritePlatformService.updateGuarantor(loanId, command); + + return Response.ok().entity(new EntityIdentifier(loanId)).build(); + } + + @DELETE + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public Response deleteGuarantor(@PathParam("loanId") final Long loanId) { + this.guarantorWritePlatformService.removeGuarantor(loanId); + return Response.ok(new EntityIdentifier(loanId)).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java index 2af098970..528b06914 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java @@ -33,6 +33,7 @@ import org.mifosng.platform.api.data.DisbursementData; import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.EnumOptionData; import org.mifosng.platform.api.data.FundData; +import org.mifosng.platform.api.data.GuarantorData; import org.mifosng.platform.api.data.LoanAccountData; import org.mifosng.platform.api.data.LoanBasicDetailsData; import org.mifosng.platform.api.data.LoanChargeData; @@ -50,6 +51,7 @@ import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.exceptions.UnrecognizedQueryParamException; import org.mifosng.platform.fund.service.FundReadPlatformService; +import org.mifosng.platform.guarantor.GuarantorReadPlatformService; import org.mifosng.platform.infrastructure.api.ApiParameterHelper; import org.mifosng.platform.loan.service.CalculationPlatformService; import org.mifosng.platform.loan.service.LoanReadPlatformService; @@ -100,7 +102,10 @@ public class LoansApiResource { @Autowired private PlatformSecurityContext context; - + + @Autowired + private GuarantorReadPlatformService guarantorReadPlatformService; + @GET @Path("template") @Consumes({ MediaType.APPLICATION_JSON }) @@ -152,7 +157,7 @@ public class LoansApiResource { final LoanAccountData newLoanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, null, null, null, charges, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, - amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers); + amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers,null); return this.apiJsonSerializerService.serializeLoanAccountDataToJson(prettyPrint, responseParameters, newLoanAccount); } @@ -177,13 +182,22 @@ public class LoansApiResource { LoanScheduleData repaymentSchedule = null; LoanPermissionData permissions = null; Collection charges = null; + GuarantorData guarantorData = null; boolean convenienceDataRequired = false; final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); if (!associationParameters.isEmpty()) { if (associationParameters.contains("all")) { - associationParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData", "charges")); + associationParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData", "charges", "guarantor")); + } + + boolean existsGuarantor = false; + if (associationParameters.contains("guarantor")) { + if(guarantorReadPlatformService.existsGuarantor(loanId)){ + guarantorData = this.guarantorReadPlatformService.retrieveGuarantor(loanId); + existsGuarantor = true; + } } if (associationParameters.contains("transactions")) { @@ -208,7 +222,7 @@ public class LoansApiResource { if (associationParameters.contains("permissions")) { loanRepaymentsCount = retrieveNonDisbursementTransactions(loanRepayments); - permissions = this.loanReadPlatformService.retrieveLoanPermissions(loanBasicDetails, isWaiveAllowed, loanRepaymentsCount); + permissions = this.loanReadPlatformService.retrieveLoanPermissions(loanBasicDetails, isWaiveAllowed, loanRepaymentsCount, existsGuarantor); } convenienceDataRequired = true; } @@ -256,7 +270,7 @@ public class LoansApiResource { productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, - fundOptions, chargeOptions, chargeTemplate, null); + fundOptions, chargeOptions, chargeTemplate, null, guarantorData); return this.apiJsonSerializerService.serializeLoanAccountDataToJson(prettyPrint, responseParameters, loanAccount); } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/GuarantorCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/GuarantorCommand.java new file mode 100644 index 000000000..b5fe103b4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/GuarantorCommand.java @@ -0,0 +1,141 @@ +package org.mifosng.platform.api.commands; + +import java.util.Set; + +/** + * Immutable command for creating or updating details of a Guarantor. + */ +public class GuarantorCommand { + + private final boolean externalGuarantor; + + /*** Fields for current customers serving as guarantors **/ + private final Long existingClientId; + + /*** Fields for external persons serving as guarantors ***/ + private final String firstname; + private final String lastname; + private final String addressLine1; + private final String addressLine2; + private final String city; + private final String state; + private final String zip; + private final String country; + private final String mobileNumber; + private final String housePhoneNumber; + private final String comment; + private final String dob; + + /**Feilds for passing Locale info to generic data service api**/ + private String dateFormat; + private String locale; + + private final Set modifiedParameters; + + public GuarantorCommand(final Set modifiedParameters, + final Long existingClientId, final String firstname, + final String lastname, final Boolean externalGuarantor, + final String addressLine1, final String addressLine2, + final String city, final String state, final String zip, + final String country, final String mobileNumber, + final String housePhoneNumber, final String comment, + final String dob) { + this.modifiedParameters = modifiedParameters; + + this.externalGuarantor = externalGuarantor; + + /*** Fields for current customers serving as guarantors **/ + this.existingClientId = existingClientId; + + /*** Fields for external persons serving as guarantors ***/ + this.firstname = firstname; + this.lastname = lastname; + this.addressLine1 = addressLine1; + this.addressLine2 = addressLine2; + this.city = city; + this.state = state; + this.zip = zip; + this.country = country; + this.mobileNumber = mobileNumber; + this.housePhoneNumber = housePhoneNumber; + this.comment = comment; + this.dob = dob; + } + + public String getFirstname() { + return firstname; + } + + public String getLastname() { + return lastname; + } + + public Boolean isExternalGuarantor() { + return externalGuarantor; + } + + public Long getExistingClientId() { + return existingClientId; + } + + public String getAddressLine1() { + return addressLine1; + } + + public String getAddressLine2() { + return addressLine2; + } + + public String getCity() { + return city; + } + + public String getState() { + return state; + } + + public String getZip() { + return zip; + } + + public String getCountry() { + return country; + } + + public String getMobileNumber() { + return mobileNumber; + } + + public String getHousePhoneNumber() { + return housePhoneNumber; + } + + public String getComment() { + return comment; + } + + public Set getModifiedParameters() { + return modifiedParameters; + } + + public String getDob() { + return dob; + } + + public String getDateFormat() { + return dateFormat; + } + + public void setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + } + + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GuarantorData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GuarantorData.java new file mode 100644 index 000000000..7a36337fa --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/GuarantorData.java @@ -0,0 +1,196 @@ +package org.mifosng.platform.api.data; + +import org.joda.time.LocalDate; + +public class GuarantorData { + private String firstname; + private String lastname; + private boolean externalGuarantor; + + /*** Fields for current customers serving as guarantors **/ + private Long existingClientId; + private String externalId; + private String officeName; + private LocalDate joinedDate; + + /*** Fields for external persons serving as guarantors ***/ + private String addressLine1; + private String addressLine2; + private String city; + private String state; + private String zip; + private String country; + private String mobileNumber; + private String housePhoneNumber; + private String comment; + private LocalDate dob; + + public GuarantorData(final Long existingClientId, final String firstname, + final String lastname, final String externalId, + final String officeName, final LocalDate joinedDate) { + this.existingClientId = existingClientId; + this.firstname = firstname; + this.lastname = lastname; + this.externalId = externalId; + this.officeName = officeName; + this.joinedDate = joinedDate; + this.externalGuarantor = false; + } + + public GuarantorData(final String firstname, final String lastname, + final LocalDate dob, final String addressLine1, + final String addressLine2, final String city, final String state, + final String zip, final String country, final String mobileNumber, + final String housePhoneNumber, final String comment) { + this.firstname = firstname; + this.lastname = lastname; + this.dob = dob; + this.addressLine1 = addressLine1; + this.addressLine2 = addressLine2; + this.city = city; + this.state = state; + this.zip = zip; + this.country = country; + this.mobileNumber = mobileNumber; + this.housePhoneNumber = housePhoneNumber; + this.comment = comment; + this.externalGuarantor = true; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public Long getExistingClientId() { + return existingClientId; + } + + public void setExistingClientId(Long existingClientId) { + this.existingClientId = existingClientId; + } + + public String getExternalId() { + return externalId; + } + + public void setExternalId(String externalId) { + this.externalId = externalId; + } + + public String getOfficeName() { + return officeName; + } + + public void setOfficeName(String officeName) { + this.officeName = officeName; + } + + public LocalDate getJoinedDate() { + return joinedDate; + } + + public void setJoinedDate(LocalDate joinedDate) { + this.joinedDate = joinedDate; + } + + public String getAddressLine1() { + return addressLine1; + } + + public void setAddressLine1(String addressLine1) { + this.addressLine1 = addressLine1; + } + + public String getAddressLine2() { + return addressLine2; + } + + public void setAddressLine2(String addressLine2) { + this.addressLine2 = addressLine2; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getMobileNumber() { + return mobileNumber; + } + + public void setMobileNumber(String mobileNumber) { + this.mobileNumber = mobileNumber; + } + + public String getHousePhoneNumber() { + return housePhoneNumber; + } + + public void setHousePhoneNumber(String housePhoneNumber) { + this.housePhoneNumber = housePhoneNumber; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public boolean getExternalGuarantor() { + return externalGuarantor; + } + + public void setExternalGuarantor(boolean externalGuarantor) { + this.externalGuarantor = externalGuarantor; + } + + public LocalDate getDob() { + return dob; + } + + public void setDob(LocalDate dob) { + this.dob = dob; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java index 8c12c507b..1b2683c85 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java @@ -59,6 +59,7 @@ public class LoanAccountData { private final Collection charges; private final LoanPermissionData permissions; private final LoanConvenienceData convenienceData; + private final GuarantorData guarantorData; // template private final Collection loanOfficerOptions; @@ -92,7 +93,8 @@ public class LoanAccountData { final Collection fundOptions, final Collection chargeOptions, final ChargeData chargeTemplate, - final Collection loanOfficerOptions) { + final Collection loanOfficerOptions, + final GuarantorData guarantorData) { this.repaymentSchedule = repaymentSchedule; this.transactions = transactions; this.permissions = permissions; @@ -109,6 +111,7 @@ public class LoanAccountData { this.chargeOptions = chargeOptions; this.chargeTemplate = chargeTemplate; this.loanOfficerOptions = loanOfficerOptions; + this.guarantorData = guarantorData; if (convenienceDataRequired) { int maxSubmittedOnOffsetFromToday = basicDetails diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanPermissionData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanPermissionData.java index bdc342b80..ee3fdc17f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanPermissionData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanPermissionData.java @@ -19,6 +19,10 @@ public class LoanPermissionData { private final boolean undoDisbursalAllowed; private final boolean anyActionOnLoanAllowed; + //permissions for setting/removing guarantors for Loans + private final boolean setGuarantorAllowed; + private final boolean editGuarantorAllowed; + // status of loan private final boolean pendingApproval; private final boolean waitingForDisbursal; @@ -35,6 +39,8 @@ public class LoanPermissionData { final boolean undoApprovalAllowed, final boolean undoDisbursalAllowed, final boolean disbursalAllowed, + final boolean setGuarantorAllowed, + final boolean editGuarantorAllowed, final boolean pendingApproval, final boolean waitingForDisbursal, final boolean closedObligationsMet) { @@ -49,6 +55,8 @@ public class LoanPermissionData { this.undoDisbursalAllowed = undoDisbursalAllowed; this.disbursalAllowed = disbursalAllowed; this.pendingApproval = pendingApproval; + this.setGuarantorAllowed = setGuarantorAllowed; + this.editGuarantorAllowed = editGuarantorAllowed; this.waitingForDisbursal = waitingForDisbursal; this.closedObligationsMet = closedObligationsMet; this.anyActionOnLoanAllowed = closeLoanAllowed || closeLoanAsRescheduledAllowed || rejectAllowed || withdrawnByApplicantAllowed || pendingApproval || undoDisbursalAllowed || makeRepaymentAllowed; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonPortfolioApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonPortfolioApiJsonSerializerService.java index b0dfbc2d4..b9fd5933f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonPortfolioApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonPortfolioApiJsonSerializerService.java @@ -25,6 +25,7 @@ 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.GuarantorData; import org.mifosng.platform.api.data.LoanAccountData; import org.mifosng.platform.api.data.LoanChargeData; import org.mifosng.platform.api.data.LoanProductData; @@ -165,6 +166,13 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi "projectedInterestAccuredOnMaturity", "actualInterestAccured", "projectedMaturityAmount", "actualMaturityAmount", "preClosureAllowed", "preClosureInterestRate", "withdrawnonDate", "rejectedonDate", "closedonDate", "isLockinPeriodAllowed", "lockinPeriod", "lockinPeriodType")); + + //guarantors + private static final Set GUARANTOR_DATA_PARAMETERS = new HashSet( + Arrays.asList("externalGuarantor", "existingClientId", + "firstname", "lastname", "addressLine1", + "addressLine2", "city", "state", "zip", "country", + "mobileNumber", "housePhoneNumber", "comment", "dob")); // accounting private static final Set CHART_OF_ACCOUNTS_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "name")); @@ -584,4 +592,13 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi CHART_OF_ACCOUNTS_DATA_PARAMETERS, prettyPrint, responseParameters); return helper.serializedJsonFrom(gsonDeserializer, chartOfAccounts); } + + @Override + public String serializeGuarantorDataToJson(boolean prettyPrint, + Set responseParameters, GuarantorData guarantorData) { + final Gson gsonDeserializer = helper + .createGsonBuilderWithParameterExclusionSerializationStrategy( + GUARANTOR_DATA_PARAMETERS, prettyPrint, responseParameters); + return helper.serializedJsonFrom(gsonDeserializer, guarantorData); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionService.java index cf47e3770..a449cce1c 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionService.java @@ -15,6 +15,7 @@ import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand; import org.mifosng.platform.api.commands.DepositStateTransitionCommand; import org.mifosng.platform.api.commands.FundCommand; import org.mifosng.platform.api.commands.GroupCommand; +import org.mifosng.platform.api.commands.GuarantorCommand; import org.mifosng.platform.api.commands.LoanApplicationCommand; import org.mifosng.platform.api.commands.LoanChargeCommand; import org.mifosng.platform.api.commands.LoanProductCommand; @@ -94,5 +95,7 @@ public interface PortfolioApiDataConversionService { CodeCommand convertJsonToCodeCommand(Long resourceIdentifier, String json); - SavingAccountCommand convertJsonToSavingAccountCommand(Long resourceIdentifier, String json); + SavingAccountCommand convertJsonToSavingAccountCommand(Long resourceIdentifier,String json); + + GuarantorCommand convertJsonToGuarantorCommand(Long resourceIdentifier, Long loanId, String json); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionServiceImpl.java index b7158dfeb..a253b00fc 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionServiceImpl.java @@ -31,6 +31,7 @@ import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand; import org.mifosng.platform.api.commands.DepositStateTransitionCommand; import org.mifosng.platform.api.commands.FundCommand; import org.mifosng.platform.api.commands.GroupCommand; +import org.mifosng.platform.api.commands.GuarantorCommand; import org.mifosng.platform.api.commands.LoanApplicationCommand; import org.mifosng.platform.api.commands.LoanChargeCommand; import org.mifosng.platform.api.commands.LoanProductCommand; @@ -1628,4 +1629,68 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo savingProductType, tenureType, frequency, interestType, minimumBalanceForWithdrawal, interestCalculationMethod, isLockinPeriodAllowed, isPartialDepositAllowed, lockinPeriod, lockinPeriodType); } + + @Override + public GuarantorCommand convertJsonToGuarantorCommand( + Long resourceIdentifier, Long loanId, String json) { + if (StringUtils.isBlank(json)) { + throw new InvalidJsonException(); + } + + Type typeOfMap = new TypeToken>() { + }.getType(); + Map requestMap = gsonConverter + .fromJson(json, typeOfMap); + + Set supportedParams = new HashSet(Arrays.asList( + "externalGuarantor", "existingClientId", "firstname", + "lastname", "addressLine1", "addressLine2", "city", "state", + "zip", "country", "mobileNumber", "housePhoneNumber", + "comment", "dob", "locale", "dateFormat")); + + checkForUnsupportedParameters(requestMap, supportedParams); + + Set modifiedParameters = new HashSet(); + + boolean externalGuarantor = extractBooleanParameter( + "externalGuarantor", requestMap, modifiedParameters); + + Long existingClientId = extractLongParameter("existingClientId", + requestMap, modifiedParameters); + String firstname = extractStringParameter("firstname", requestMap, + modifiedParameters); + String lastname = extractStringParameter("lastname", requestMap, + modifiedParameters); + String addressLine1 = extractStringParameter("addressLine1", + requestMap, modifiedParameters); + String addressLine2 = extractStringParameter("addressLine2", + requestMap, modifiedParameters); + String city = extractStringParameter("city", requestMap, + modifiedParameters); + String state = extractStringParameter("state", requestMap, + modifiedParameters); + String zip = extractStringParameter("zip", requestMap, + modifiedParameters); + String country = extractStringParameter("country", requestMap, + modifiedParameters); + String mobileNumber = extractStringParameter("mobileNumber", + requestMap, modifiedParameters); + String housePhoneNumber = extractStringParameter("housePhoneNumber", + requestMap, modifiedParameters); + String comment = extractStringParameter("comment", requestMap, + modifiedParameters); + String dob = extractStringParameter("dob", requestMap, + modifiedParameters); + // workaround for passing locale info to data table api + final String dateFormat = (String) requestMap.get("dateFormat"); + final String locale = (String) requestMap.get("locale"); + + GuarantorCommand command = new GuarantorCommand(modifiedParameters, + existingClientId, firstname, lastname, externalGuarantor, + addressLine1, addressLine2, city, state, zip, country, + mobileNumber, housePhoneNumber, comment, dob); + command.setDateFormat(dateFormat); + command.setLocale(locale); + return command; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiJsonSerializerService.java index b3bcdd7f2..3bc4d7494 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiJsonSerializerService.java @@ -23,6 +23,7 @@ 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.GuarantorData; import org.mifosng.platform.api.data.LoanAccountData; import org.mifosng.platform.api.data.LoanChargeData; import org.mifosng.platform.api.data.LoanProductData; @@ -156,7 +157,9 @@ public interface PortfolioApiJsonSerializerService { String serializeSavingAccountsDataToJson(boolean prettyPrint, Set responseParameters, Collection accounts); - // TODO - KW - might devide serialize up into portfolio and account to - // reduce interface - String serializeChartOfAccountDataToJson(boolean prettyPrint, Set responseParameters, ChartOfAccountsData chartOfAccounts); + // TODO - KW - might devide serialize up into portfolio and account to reduce interface + String serializeChartOfAccountDataToJson(boolean prettyPrint, Set responseParameters, ChartOfAccountsData chartOfAccounts); + + String serializeGuarantorDataToJson(boolean prettyPrint,Set responseParameters, GuarantorData guarantorData); + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/documentmanagement/service/DocumentWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/documentmanagement/service/DocumentWritePlatformService.java index dcd3c9d97..385853061 100755 --- a/mifosng-provider/src/main/java/org/mifosng/platform/documentmanagement/service/DocumentWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/documentmanagement/service/DocumentWritePlatformService.java @@ -7,14 +7,15 @@ import org.mifosng.platform.api.data.EntityIdentifier; import org.springframework.security.access.prepost.PreAuthorize; public interface DocumentWritePlatformService { - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CREATE_DOCUMENT')") + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CREATE_m_guarantor_external')") Long createDocument(DocumentCommand documentCommand, InputStream inputStream); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'UPDATE_DOCUMENT')") + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'UPDATE_m_guarantor_external')") EntityIdentifier updateDocument(DocumentCommand documentCommand, InputStream inputStream); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'DELETE_DOCUMENT')") + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'DELETE_m_guarantor_external')") EntityIdentifier deleteDocument(DocumentCommand documentCommand); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/GuarantorNotFoundException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/GuarantorNotFoundException.java new file mode 100644 index 000000000..2ab922b8c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/GuarantorNotFoundException.java @@ -0,0 +1,13 @@ +package org.mifosng.platform.exceptions; + +/** + * A {@link RuntimeException} thrown when guarantor resources are not found. + */ +public class GuarantorNotFoundException extends + AbstractPlatformResourceNotFoundException { + + public GuarantorNotFoundException(Long id) { + super("error.msg.loan.guarantor.", "Loan with identifier " + id + + " does not have a guarantor associated with it", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InvalidGuarantorException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InvalidGuarantorException.java new file mode 100644 index 000000000..8ef48f959 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InvalidGuarantorException.java @@ -0,0 +1,17 @@ +package org.mifosng.platform.exceptions; + +/** + * {@link AbstractPlatformDomainRuleException} thrown when a Client is set as a + * guarantor for his/her own loans + */ +public class InvalidGuarantorException extends + AbstractPlatformDomainRuleException { + + public InvalidGuarantorException(Long clientId, Long loanId) { + super("error.msg.invalid.guarantor", "Tried to set Client with id " + + clientId + + " as a guarantor to his/her own loan with loan identifier =" + + loanId, clientId, loanId); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorCommandValidator.java new file mode 100644 index 000000000..343888a3b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorCommandValidator.java @@ -0,0 +1,119 @@ +package org.mifosng.platform.guarantor; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosng.platform.DataValidatorBuilder; +import org.mifosng.platform.api.commands.GuarantorCommand; +import org.mifosng.platform.api.data.ApiParameterError; +import org.mifosng.platform.exceptions.PlatformApiDataValidationException; + +public class GuarantorCommandValidator { + + private final GuarantorCommand command; + + public GuarantorCommandValidator(GuarantorCommand command) { + this.command = command; + } + + public void validateForCreate() { + + List dataValidationErrors = new ArrayList(); + + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder( + dataValidationErrors).resource("Guarantor"); + + // validate for existing Client + if (command.isExternalGuarantor() == null + || command.isExternalGuarantor() == false) { + baseDataValidator.reset().parameter("existingClientId") + .value(command.getExistingClientId()).notNull() + .integerGreaterThanZero(); + } else { + // validate for an external guarantor + baseDataValidator.reset().parameter("firstname") + .value(command.getFirstname()).notBlank() + .notExceedingLengthOf(50); + baseDataValidator.reset().parameter("lastname") + .value(command.getLastname()).notBlank() + .notExceedingLengthOf(50); + + validateNonMandatoryFieldsForMaxLength(baseDataValidator); + } + + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException( + "validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); + } + } + + public void validateForUpdate() { + List dataValidationErrors = new ArrayList(); + + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder( + dataValidationErrors).resource("Guarantor"); + + // validate for existing Client + if (command.isExternalGuarantor() == null + || command.isExternalGuarantor() == false) { + baseDataValidator.reset().parameter("existingClientId") + .value(command.getExistingClientId()).notNull() + .integerGreaterThanZero(); + } else { + // validate for an external guarantor + baseDataValidator.reset().parameter("firstname") + .value(command.getFirstname()).notBlank() + .notExceedingLengthOf(50); + baseDataValidator.reset().parameter("lastname") + .value(command.getLastname()).notBlank() + .notExceedingLengthOf(50); + + validateNonMandatoryFieldsForMaxLength(baseDataValidator); + } + baseDataValidator.reset().anyOfNotNull(command.getExistingClientId(), + command.getAddressLine1(), command.getAddressLine2(), + command.getCity(), command.getComment(), command.getCountry(), + command.getFirstname(), command.getHousePhoneNumber(), + command.getLastname(), command.getMobileNumber(), + command.getState(), command.getZip()); + + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException( + "validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); + } + } + + /** + * @param baseDataValidator + */ + private void validateNonMandatoryFieldsForMaxLength( + DataValidatorBuilder baseDataValidator) { + // validate non mandatory fields for length + baseDataValidator.reset().parameter("addressLine1") + .value(command.getAddressLine1()).ignoreIfNull() + .notExceedingLengthOf(50); + baseDataValidator.reset().parameter("addressLine2") + .value(command.getAddressLine2()).ignoreIfNull() + .notExceedingLengthOf(50); + baseDataValidator.reset().parameter("city").value(command.getCity()) + .ignoreIfNull().notExceedingLengthOf(50); + baseDataValidator.reset().parameter("state").value(command.getState()) + .ignoreIfNull().notExceedingLengthOf(50); + baseDataValidator.reset().parameter("zip").value(command.getZip()) + .ignoreIfNull().notExceedingLengthOf(50); + baseDataValidator.reset().parameter("country") + .value(command.getCountry()).ignoreIfNull() + .notExceedingLengthOf(50); + baseDataValidator.reset().parameter("mobileNumber") + .value(command.getMobileNumber()).ignoreIfNull() + .notExceedingLengthOf(20); + baseDataValidator.reset().parameter("housePhoneNumber") + .value(command.getHousePhoneNumber()).ignoreIfNull() + .notExceedingLengthOf(20); + baseDataValidator.reset().parameter("comment") + .value(command.getComment()).ignoreIfNull() + .notExceedingLengthOf(500); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorReadPlatformService.java new file mode 100644 index 000000000..90bd5f7eb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorReadPlatformService.java @@ -0,0 +1,13 @@ +package org.mifosng.platform.guarantor; + +import org.mifosng.platform.api.data.GuarantorData; + +public interface GuarantorReadPlatformService { + + GuarantorData retrieveGuarantor(Long loanId); + + boolean existsGuarantor(Long loanId); + + GuarantorData getExternalGuarantor(Long loanId); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorReadPlatformServiceImpl.java new file mode 100644 index 000000000..3deff67d1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorReadPlatformServiceImpl.java @@ -0,0 +1,122 @@ +package org.mifosng.platform.guarantor; + +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosng.platform.api.data.GenericResultsetData; +import org.mifosng.platform.api.data.GuarantorData; +import org.mifosng.platform.client.domain.Client; +import org.mifosng.platform.exceptions.GuarantorNotFoundException; +import org.mifosng.platform.exceptions.LoanNotFoundException; +import org.mifosng.platform.loan.domain.Loan; +import org.mifosng.platform.loan.domain.LoanRepository; +import org.mifosng.platform.noncore.ReadWriteNonCoreDataService; +import org.mifosng.platform.organisation.domain.OfficeRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class GuarantorReadPlatformServiceImpl implements + GuarantorReadPlatformService { + + private final LoanRepository loanRepository; + private final ReadWriteNonCoreDataService readWriteNonCoreDataService; + // Table for storing external Guarantor Details + public static final String EXTERNAL_GUARANTOR_TABLE_NAME = "m_guarantor_external"; + + @Autowired + public GuarantorReadPlatformServiceImpl( + final LoanRepository loanRepository, + final ReadWriteNonCoreDataService readWriteNonCoreDataService, + final OfficeRepository officeRepository) { + this.loanRepository = loanRepository; + this.readWriteNonCoreDataService = readWriteNonCoreDataService; + } + + @Override + public boolean existsGuarantor(Long loanId) { + Loan loan = validateLoanExists(loanId); + // return if internal guarantor exists + if (loan.getGuarantor() != null) { + return true; + } + // return if an external guarantor exists + if (null != getExternalGuarantor(loanId)) { + return true; + } + // else no guarantor exists + return false; + } + + @Override + public GuarantorData retrieveGuarantor(Long loanId) { + GuarantorData guarantorData = null; + Loan loan = validateLoanExists(loanId); + // does an internal guarantor exist + if (loan.getGuarantor() != null) { + Client guarantor = loan.getGuarantor(); + LocalDate localDate = new LocalDate(guarantor.getJoiningDate()); + guarantorData = new GuarantorData(guarantor.getId(), + guarantor.getFirstName(), guarantor.getLastName(), + guarantor.getExternalId(), guarantor.getOffice().getName(), + localDate); + } else { + guarantorData = getExternalGuarantor(loanId); + } + // throw error if guarantor does not exist + if (guarantorData == null) { + throw new GuarantorNotFoundException(loanId); + } + return guarantorData; + } + + /** + * @param loanId + * @return + */ + private Loan validateLoanExists(Long loanId) { + Loan loan = loanRepository.findOne(loanId); + if (loan == null) { + throw new LoanNotFoundException(loanId); + } + return loan; + } + + /** + * @param loanId + * @return + */ + public GuarantorData getExternalGuarantor(Long loanId) { + GenericResultsetData genericResultDataSet = readWriteNonCoreDataService + .retrieveDataTableGenericResultSet( + EXTERNAL_GUARANTOR_TABLE_NAME, loanId, null, null); + if (genericResultDataSet.getData().size() == 1) { + List guarantorRow = genericResultDataSet.getData().get(0) + .getRow(); + String firstname = guarantorRow.get(1); + String lastname = guarantorRow.get(2); + + LocalDate dateOfBirth = null; + if (!StringUtils.isBlank(guarantorRow.get(3))) { + dateOfBirth = new LocalDate(guarantorRow.get(3)); + } + String addressLine1 = guarantorRow.get(4); + String addressLine2 = guarantorRow.get(5); + String city = guarantorRow.get(6); + String state = guarantorRow.get(7); + String country = guarantorRow.get(8); + String zip = guarantorRow.get(9); + String housePhoneNumber = guarantorRow.get(10); + String mobileNumber = guarantorRow.get(11); + String comment = guarantorRow.get(12); + GuarantorData guarantorData = new GuarantorData(firstname, + lastname, dateOfBirth, addressLine1, addressLine2, city, + state, zip, country, mobileNumber, housePhoneNumber, + comment); + return guarantorData; + } + return null; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorWritePlatformService.java new file mode 100644 index 000000000..af8b61506 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorWritePlatformService.java @@ -0,0 +1,17 @@ +package org.mifosng.platform.guarantor; + +import org.mifosng.platform.api.commands.GuarantorCommand; +import org.springframework.security.access.prepost.PreAuthorize; + +public interface GuarantorWritePlatformService { + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS','PORTFOLIO_MANAGEMENT_SUPER_USER')") + void createGuarantor(final Long loanId, final GuarantorCommand command); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS','PORTFOLIO_MANAGEMENT_SUPER_USER')") + void updateGuarantor(final Long loanId, final GuarantorCommand command); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS','PORTFOLIO_MANAGEMENT_SUPER_USER')") + void removeGuarantor(final Long loanId); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorWritePlatformServiceImpl.java new file mode 100644 index 000000000..b8ce51614 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/guarantor/GuarantorWritePlatformServiceImpl.java @@ -0,0 +1,182 @@ +package org.mifosng.platform.guarantor; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.mifosng.platform.api.commands.GuarantorCommand; +import org.mifosng.platform.api.data.ApiParameterError; +import org.mifosng.platform.client.domain.Client; +import org.mifosng.platform.client.domain.ClientRepository; +import org.mifosng.platform.exceptions.ClientNotFoundException; +import org.mifosng.platform.exceptions.InvalidGuarantorException; +import org.mifosng.platform.exceptions.LoanNotFoundException; +import org.mifosng.platform.exceptions.PlatformApiDataValidationException; +import org.mifosng.platform.loan.domain.Loan; +import org.mifosng.platform.loan.domain.LoanRepository; +import org.mifosng.platform.noncore.ReadWriteNonCoreDataService; +import org.mifosng.platform.organisation.domain.OfficeRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class GuarantorWritePlatformServiceImpl implements + GuarantorWritePlatformService { + + private final LoanRepository loanRepository; + private final ClientRepository clientRepository; + private final ReadWriteNonCoreDataService readWriteNonCoreDataService; + private final GuarantorReadPlatformService guarantorReadPlatformService; + // Table for storing external Guarantor Details + public static final String EXTERNAL_GUARANTOR_TABLE_NAME = "m_guarantor_external"; + + @Autowired + public GuarantorWritePlatformServiceImpl( + final LoanRepository loanRepository, + final ReadWriteNonCoreDataService readWriteNonCoreDataService, + final OfficeRepository officeRepository, + final ClientRepository clientRepository, + final GuarantorReadPlatformService guarantorReadPlatformService) { + this.loanRepository = loanRepository; + this.readWriteNonCoreDataService = readWriteNonCoreDataService; + this.clientRepository = clientRepository; + this.guarantorReadPlatformService = guarantorReadPlatformService; + } + + @Override + @Transactional + public void createGuarantor(Long loanId, GuarantorCommand command) { + GuarantorCommandValidator validator = new GuarantorCommandValidator( + command); + validator.validateForCreate(); + saveOrUpdateGuarantor(loanId, command); + } + + @Override + @Transactional + public void updateGuarantor(Long loanId, GuarantorCommand command) { + GuarantorCommandValidator validator = new GuarantorCommandValidator( + command); + validator.validateForUpdate(); + saveOrUpdateGuarantor(loanId, command); + } + + @Override + @Transactional + public void removeGuarantor(Long loanId) { + Loan loan = validateLoanExists(loanId); + // remove internal guarantor, if any + if (loan.getGuarantor() != null) { + loan.setGuarantor(null); + loanRepository.saveAndFlush(loan); + } + // remove external guarantor, if any + readWriteNonCoreDataService.deleteDatatableEntries( + EXTERNAL_GUARANTOR_TABLE_NAME, loanId); + } + + /** + * @param loanId + * @param command + */ + private void saveOrUpdateGuarantor(Long loanId, GuarantorCommand command) { + Loan loan = validateLoanExists(loanId); + // mark an existing client as a guarantor + if (command.isExternalGuarantor() == null + || !command.isExternalGuarantor()) { + // can't set client as a guarantor to himself + if (loan.getClient().getId().equals(command.getExistingClientId())) { + throw new InvalidGuarantorException( + command.getExistingClientId(), loanId); + } + Client guarantor = clientRepository.findOne(command + .getExistingClientId()); + if (guarantor == null) { + throw new ClientNotFoundException(command.getExistingClientId()); + } + loan.setGuarantor(guarantor); + this.loanRepository.saveAndFlush(loan); + // also delete any existing External Guarantors + readWriteNonCoreDataService.deleteDatatableEntries( + EXTERNAL_GUARANTOR_TABLE_NAME, loanId); + }// or create an external guarantor + else { + Set modifiedParameters = command.getModifiedParameters(); + Map modifiedParametersMap = new HashMap(); + /*** + * TODO Vishwas: Check with JW/KW if using reflection here is a good + * idea + **/ + Class guarantorCommandClass = command + .getClass(); + for (String modifiedParameter : modifiedParameters) { + Method method; + try { + if (modifiedParameter.equalsIgnoreCase("externalGuarantor") + || modifiedParameter + .equalsIgnoreCase("existingClientId")) { + continue; + } else { + method = guarantorCommandClass.getMethod("get" + + StringUtils.capitalize(modifiedParameter)); + } + String columnName = StringUtils.join(StringUtils + .splitByCharacterTypeCamelCase(modifiedParameter), + '_'); + modifiedParametersMap.put(columnName.toLowerCase(), method + .invoke(command).toString()); + } catch (Exception e) { + // TODO: This block would ideally never be reached, could + // use an + // empty catch block instead? + List dataValidationErrors = new ArrayList(); + ApiParameterError apiParameterError = ApiParameterError + .parameterError( + "validation.msg.validation.errors.exist", + "", modifiedParameter); + dataValidationErrors.add(apiParameterError); + throw new PlatformApiDataValidationException( + "validation.msg.validation.errors.exist", + "Invalid parameter passed for Creating updating Guarantor", + dataValidationErrors); + } + + } + + /*** update or create a new external Guarantor entry ***/ + modifiedParametersMap.put("locale", command.getLocale()); + modifiedParametersMap.put("dateFormat", command.getDateFormat()); + if (guarantorReadPlatformService.getExternalGuarantor(loanId) != null) { + readWriteNonCoreDataService.updateDatatableEntryOnetoOne( + EXTERNAL_GUARANTOR_TABLE_NAME, loanId, + modifiedParametersMap); + } else { + readWriteNonCoreDataService.newDatatableEntry( + EXTERNAL_GUARANTOR_TABLE_NAME, loanId, + modifiedParametersMap); + } + // finally unset any existing internal guarantors + if (loan.getGuarantor() != null) { + loan.setGuarantor(null); + this.loanRepository.saveAndFlush(loan); + } + } + } + + /** + * @param loanId + * @return + */ + private Loan validateLoanExists(Long loanId) { + Loan loan = loanRepository.findOne(loanId); + if (loan == null) { + throw new LoanNotFoundException(loanId); + } + return loan; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java index df62da954..beadfc2f3 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java @@ -50,6 +50,10 @@ public class Loan extends AbstractAuditableCustom { @ManyToOne(optional = true) @JoinColumn(name = "client_id") private Client client; + + @ManyToOne(optional = true) + @JoinColumn(name = "guarantor_id") + private Client guarantor; @SuppressWarnings("unused") @ManyToOne(optional = true) @@ -1629,4 +1633,17 @@ public class Loan extends AbstractAuditableCustom { } return latestRecordWithNoEndDate; } + + public Client getGuarantor() { + return guarantor; + } + + public void setGuarantor(Client guarantor) { + this.guarantor = guarantor; + } + + public Client getClient() { + return client; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java index e77da685b..6481bf057 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java @@ -20,7 +20,7 @@ public interface LoanReadPlatformService { LoanPermissionData retrieveLoanPermissions( LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed, - int repaymentAndWaiveCount); + int repaymentAndWaiveCount, boolean existsGuarantor); LoanBasicDetailsData retrieveClientAndProductDetails(Long clientId, Long productId); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java index cfeba07ba..c4f07b6fe 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java @@ -197,7 +197,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { public LoanPermissionData retrieveLoanPermissions( final LoanBasicDetailsData loanBasicDetails, final boolean isWaiverAllowed, - final int repaymentAndWaiveCount) { + final int repaymentAndWaiveCount, + final boolean existsGuarantor) { final boolean pendingApproval = (loanBasicDetails.getStatus().getId().equals(100L)); final boolean waitingForDisbursal = (loanBasicDetails.getStatus().getId().equals(200L)); @@ -210,8 +211,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final boolean isOverpaid = (loanBasicDetails.getStatus().getId().equals(700L)); boolean addLoanChargeAllowed = true; + boolean setGuarantorAllowed = true; + boolean editGuarantorAllowed = true; if (closed || isOverpaid) { addLoanChargeAllowed = false; + setGuarantorAllowed = false; + editGuarantorAllowed = false; + } + + if(existsGuarantor){ + setGuarantorAllowed = false; + }else{ + editGuarantorAllowed = false; } final boolean waiveAllowed = isWaiverAllowed && isActive; @@ -234,6 +245,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { addLoanChargeAllowed, waiveAllowed, makeRepaymentAllowed, rejectAllowed, withdrawnByApplicantAllowed, undoApprovalAllowed, undoDisbursalAllowed, disbursalAllowed, + setGuarantorAllowed,editGuarantorAllowed, pendingApproval, waitingForDisbursal, closedObligationsMet); }