diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index c0e24ec2b..a4f62e5e5 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -7,6 +7,7 @@ package org.mifosplatform.commands.service; import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.infrastructure.accountnumberformat.service.AccountNumberFormatConstants; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.paymenttype.api.PaymentTypeApiResourceConstants; import org.mifosplatform.portfolio.savings.DepositsApiConstants; import org.mifosplatform.useradministration.api.PasswordPreferencesApiConstants; @@ -2399,9 +2400,52 @@ public class CommandWrapperBuilder { this.actionName = "UPDATE"; this.entityName = "EXTERNALSERVICES"; this.transactionId = externalServiceName; - this.href = "/externalservices/" + externalServiceName; return this; } + public CommandWrapperBuilder createClientCharge(final Long clientId) { + this.actionName = ClientApiConstants.CLIENT_CHARGE_ACTION_CREATE; + this.entityName = ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME; + this.clientId = clientId; + this.href = "/clients/" + clientId + "/charges"; + return this; + } + + public CommandWrapperBuilder deleteClientCharge(final Long clientId, final Long chargeId) { + this.actionName = ClientApiConstants.CLIENT_CHARGE_ACTION_DELETE; + this.entityName = ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME; + this.clientId = clientId; + this.entityId = chargeId; + this.href = "/clients/" + clientId + "/charges/" + chargeId; + return this; + } + + public CommandWrapperBuilder waiveClientCharge(final Long clientId, final Long chargeId) { + this.actionName = ClientApiConstants.CLIENT_CHARGE_ACTION_WAIVE; + this.entityName = ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME; + this.entityId = chargeId; + this.clientId = clientId; + this.href = "/clients/" + clientId + "/charges/" + chargeId + "?command=waive"; + return this; + } + + public CommandWrapperBuilder payClientCharge(final Long clientId, final Long chargeId) { + this.actionName = ClientApiConstants.CLIENT_CHARGE_ACTION_PAY; + this.entityName = ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME; + this.entityId = chargeId; + this.clientId = clientId; + this.href = "/clients/" + clientId + "/charges/" + chargeId + "?command=paycharge"; + return this; + } + + public CommandWrapperBuilder inactivateClientCharge(final Long clientId, final Long chargeId) { + this.actionName = "INACTIVATE"; + this.entityName = ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME; + this.entityId = chargeId; + this.clientId = clientId; + this.href = "/clients/" + clientId + "/charges/" + chargeId + "?command=inactivate"; + return this; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/HolidayRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/HolidayRepositoryWrapper.java index ecb65e77f..2039054bf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/HolidayRepositoryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/domain/HolidayRepositoryWrapper.java @@ -8,7 +8,9 @@ package org.mifosplatform.organisation.holiday.domain; import java.util.Date; import java.util.List; +import org.joda.time.LocalDate; import org.mifosplatform.organisation.holiday.exception.HolidayNotFoundException; +import org.mifosplatform.organisation.holiday.service.HolidayUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -57,4 +59,9 @@ public class HolidayRepositoryWrapper { public List findUnprocessed() { return this.repository.findUnprocessed(HolidayStatusType.ACTIVE.getValue()); } + + public boolean isHoliday(Long officeId, LocalDate transactionDate) { + final List holidays = findByOfficeIdAndGreaterThanDate(officeId, transactionDate.toDate()); + return HolidayUtil.isHoliday(transactionDate, holidays); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/service/HolidayWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/service/HolidayWritePlatformService.java index 8878ff4bc..8c0283f03 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/service/HolidayWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/service/HolidayWritePlatformService.java @@ -5,7 +5,6 @@ */ package org.mifosplatform.organisation.holiday.service; -import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; @@ -19,7 +18,4 @@ public interface HolidayWritePlatformService { CommandProcessingResult deleteHoliday(final Long holidayId); - boolean isHoliday(Long officeId, LocalDate transactionDate); - - boolean isTransactionAllowedOnHoliday(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/service/HolidayWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/service/HolidayWritePlatformServiceJpaRepositoryImpl.java index eb7293fa7..16acee1f0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/service/HolidayWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/holiday/service/HolidayWritePlatformServiceJpaRepositoryImpl.java @@ -188,7 +188,8 @@ public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWrit if (toDate.isBefore(fromDate)) { defaultUserMessage = "To Date date cannot be before the From Date."; - throw new HolidayDateException("to.date.cannot.be.before.from.date", defaultUserMessage, fromDate.toString(), toDate.toString()); + throw new HolidayDateException("to.date.cannot.be.before.from.date", defaultUserMessage, fromDate.toString(), + toDate.toString()); } if (repaymentsRescheduledTo.isEqual(fromDate) || repaymentsRescheduledTo.isEqual(toDate) @@ -223,15 +224,4 @@ public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWrit } } - @Override - public boolean isHoliday(Long officeId, LocalDate transactionDate) { - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, transactionDate.toDate()); - return HolidayUtil.isHoliday(transactionDate, holidays); - } - - @Override - public boolean isTransactionAllowedOnHoliday() { - return this.configurationDomainService.allowTransactionsOnHolidayEnabled(); - } - } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java index dc81b8224..ebc8d8eee 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java @@ -7,7 +7,9 @@ package org.mifosplatform.organisation.workingdays.domain; import java.util.List; +import org.joda.time.LocalDate; import org.mifosplatform.organisation.workingdays.exception.WorkingDaysNotFoundException; +import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -45,4 +47,9 @@ public class WorkingDaysRepositoryWrapper { public void delete(final WorkingDays workingDays) { this.repository.delete(workingDays); } + + public boolean isWorkingDay(LocalDate transactionDate) { + final WorkingDays workingDays = findOne(); + return WorkingDaysUtil.isWorkingDay(workingDays, transactionDate); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformService.java index b5a8e40ae..ee3faa35d 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformService.java @@ -5,15 +5,10 @@ */ package org.mifosplatform.organisation.workingdays.service; -import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; public interface WorkingDaysWritePlatformService { - boolean isWorkingDay(LocalDate transactionDate); - - boolean isTransactionAllowedOnNonWorkingDay(); - CommandProcessingResult updateWorkingDays(JsonCommand command); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java index 4c4448d5f..bede960b5 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysWritePlatformServiceJpaRepositoryImpl.java @@ -5,9 +5,9 @@ */ package org.mifosplatform.organisation.workingdays.service; -import net.fortuna.ical4j.model.ValidationException; -import net.fortuna.ical4j.model.property.RRule; -import org.joda.time.LocalDate; +import java.text.ParseException; +import java.util.Map; + import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; @@ -21,8 +21,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.text.ParseException; -import java.util.Map; +import net.fortuna.ical4j.model.ValidationException; +import net.fortuna.ical4j.model.property.RRule; @Service public class WorkingDaysWritePlatformServiceJpaRepositoryImpl implements WorkingDaysWritePlatformService { @@ -39,17 +39,6 @@ public class WorkingDaysWritePlatformServiceJpaRepositoryImpl implements Working this.fromApiJsonDeserializer = fromApiJsonDeserializer; } - @Override - public boolean isWorkingDay(LocalDate transactionDate) { - final WorkingDays workingDays = this.daysRepositoryWrapper.findOne(); - return WorkingDaysUtil.isWorkingDay(workingDays, transactionDate); - } - - @Override - public boolean isTransactionAllowedOnNonWorkingDay() { - return this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled(); - } - @Transactional @Override public CommandProcessingResult updateWorkingDays(JsonCommand command) { @@ -68,8 +57,8 @@ public class WorkingDaysWritePlatformServiceJpaRepositoryImpl implements Working return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(workingDays.getId()).with(changes) .build(); } catch (final ValidationException e) { - throw new PlatformDataIntegrityException("error.msg.invalid.recurring.rule", "The Recurring Rule value: " + recurrence - + " is not valid.", "recurrence", recurrence); + throw new PlatformDataIntegrityException("error.msg.invalid.recurring.rule", + "The Recurring Rule value: " + recurrence + " is not valid.", "recurrence", recurrence); } catch (final IllegalArgumentException | ParseException e) { throw new PlatformDataIntegrityException("error.msg.recurring.rule.parsing.error", "Error in passing the Recurring Rule value: " + recurrence, "recurrence", e.getMessage()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java index 7249534ef..676ea6972 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java @@ -14,12 +14,25 @@ import org.mifosplatform.portfolio.client.data.ClientData; public class ClientApiConstants { public static final String CLIENT_RESOURCE_NAME = "client"; + public static final String CLIENT_CHARGES_RESOURCE_NAME = "CLIENTCHARGE"; + + // Client Charge Action Names + public static final String CLIENT_CHARGE_ACTION_CREATE = "CREATE"; + public static final String CLIENT_CHARGE_ACTION_DELETE = "DELETE"; + public static final String CLIENT_CHARGE_ACTION_WAIVE = "WAIVE"; + public static final String CLIENT_CHARGE_ACTION_PAY = "PAY"; + public static final String CLIENT_CHARGE_ACTION_INACTIVATE = "INACTIVATE"; + + // Commands + public static final String CLIENT_CHARGE_COMMAND_WAIVE_CHARGE = "waive"; + public static final String CLIENT_CHARGE_COMMAND_PAY_CHARGE = "paycharge"; + public static final String CLIENT_CHARGE_COMMAND_INACTIVATE_CHARGE = "inactivate"; + public static final String CLIENT_CLOSURE_REASON = "ClientClosureReason"; public static final String CLIENT_ACTION_REASON = "ClientActionReason"; public static final String CLIENT_REJECT_REASON = "ClientRejectReason"; public static final String CLIENT_WITHDRAW_REASON = "ClientWithdrawReason"; - public static final String GENDER = "Gender"; public static final String CLIENT_TYPE = "ClientType"; public static final String CLIENT_CLASSIFICATION = "ClientClassification"; @@ -45,12 +58,12 @@ public class ClientApiConstants { public static final String staffIdParamName = "staffId"; public static final String closureDateParamName = "closureDate"; public static final String closureReasonIdParamName = "closureReasonId"; - + public static final String rejectionDateParamName = "rejectionDate"; - public static final String rejectionReasonIdParamName ="rejectionReasonId"; + public static final String rejectionReasonIdParamName = "rejectionReasonId"; public static final String withdrawalDateParamName = "withdrawalDate"; - public static final String withdrawalReasonIdParamName ="withdrawalReasonId"; - + public static final String withdrawalReasonIdParamName = "withdrawalReasonId"; + public static final String submittedOnDateParamName = "submittedOnDate"; public static final String savingsProductIdParamName = "savingsProductId"; public static final String savingsAccountIdParamName = "savingsAccountId"; @@ -75,6 +88,25 @@ public class ClientApiConstants { public static final String imagePresentParamName = "imagePresent"; public static final String timelineParamName = "timeline"; + // client charges response parameters + public static final String chargeIdParamName = "chargeId"; + public static final String clientIdParamName = "clientId"; + public static final String chargesParamName = "charges"; + public static final String chargeNameParamName = "name"; + public static final String penaltyParamName = "penalty"; + public static final String chargeTimeTypeParamName = "chargeTimeType"; + public static final String dueAsOfDateParamName = "dueDate"; + public static final String transactionDateParamName = "transactionDate"; + public static final String chargeCalculationTypeParamName = "chargeCalculationType"; + public static final String currencyParamName = "currency"; + public static final String amountWaivedParamName = "amountWaived"; + public static final String amountWrittenOffParamName = "amountWrittenOff"; + public static final String amountOutstandingParamName = "amountOutstanding"; + public static final String amountOrPercentageParamName = "amountOrPercentage"; + public static final String amountParamName = "amount"; + public static final String amountPaidParamName = "amountPaid"; + public static final String chargeOptionsParamName = "chargeOptions"; + // associations related part of response public static final String groupsParamName = "groups"; @@ -82,18 +114,17 @@ public class ClientApiConstants { public static final String officeOptionsParamName = "officeOptions"; public static final String staffOptionsParamName = "staffOptions"; - public static final Set CLIENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, - dateFormatParamName, groupIdParamName, accountNoParamName, externalIdParamName, mobileNoParamName, firstnameParamName, - middlenameParamName, lastnameParamName, fullnameParamName, officeIdParamName, activeParamName, activationDateParamName, - staffIdParamName, submittedOnDateParamName, savingsProductIdParamName, dateOfBirthParamName, genderIdParamName, - clientTypeIdParamName, clientClassificationIdParamName)); + public static final Set CLIENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>( + Arrays.asList(localeParamName, dateFormatParamName, groupIdParamName, accountNoParamName, externalIdParamName, + mobileNoParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, officeIdParamName, + activeParamName, activationDateParamName, staffIdParamName, submittedOnDateParamName, savingsProductIdParamName, + dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName)); public static final Set CLIENT_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, dateFormatParamName, accountNoParamName, externalIdParamName, mobileNoParamName, firstnameParamName, middlenameParamName, - lastnameParamName, fullnameParamName, activeParamName, activationDateParamName, staffIdParamName, savingsProductIdParamName, - dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName,submittedOnDateParamName)); - + lastnameParamName, fullnameParamName, activeParamName, activationDateParamName, staffIdParamName, savingsProductIdParamName, + dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName, submittedOnDateParamName)); /** * These parameters will match the class level parameters of @@ -105,18 +136,31 @@ public class ClientApiConstants { lastnameParamName, fullnameParamName, displayNameParamName, mobileNoParamName, officeIdParamName, officeNameParamName, transferToOfficeIdParamName, transferToOfficeNameParamName, hierarchyParamName, imageIdParamName, imagePresentParamName, staffIdParamName, staffNameParamName, timelineParamName, groupsParamName, officeOptionsParamName, staffOptionsParamName, - dateOfBirthParamName, genderParamName,clientTypeParamName,clientClassificationParamName)); + dateOfBirthParamName, genderParamName, clientTypeParamName, clientClassificationParamName)); - public static final Set ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, - dateFormatParamName, activationDateParamName)); - public static final Set REACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, - dateFormatParamName, reactivationDateParamName)); + public static final Set ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>( + Arrays.asList(localeParamName, dateFormatParamName, activationDateParamName)); + public static final Set REACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>( + Arrays.asList(localeParamName, dateFormatParamName, reactivationDateParamName)); - public static final Set CLIENT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, - dateFormatParamName, closureDateParamName, closureReasonIdParamName)); - - public static final Set CLIENT_REJECT_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,dateFormatParamName,rejectionDateParamName,rejectionReasonIdParamName)); + public static final Set CLIENT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<>( + Arrays.asList(localeParamName, dateFormatParamName, closureDateParamName, closureReasonIdParamName)); - public static final Set CLIENT_WITHDRAW_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,dateFormatParamName,withdrawalDateParamName,withdrawalReasonIdParamName)); + public static final Set CLIENT_REJECT_DATA_PARAMETERS = new HashSet<>( + Arrays.asList(localeParamName, dateFormatParamName, rejectionDateParamName, rejectionReasonIdParamName)); + + public static final Set CLIENT_WITHDRAW_DATA_PARAMETERS = new HashSet<>( + Arrays.asList(localeParamName, dateFormatParamName, withdrawalDateParamName, withdrawalReasonIdParamName)); + + public static final Set CLIENT_CHARGES_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(chargeIdParamName, + clientIdParamName, chargeNameParamName, penaltyParamName, chargeTimeTypeParamName, dueAsOfDateParamName, + chargeCalculationTypeParamName, currencyParamName, amountWaivedParamName, amountWrittenOffParamName, amountOutstandingParamName, + amountOrPercentageParamName, amountParamName, amountPaidParamName, chargeOptionsParamName)); + + public static final Set CLIENT_CHARGES_ADD_REQUEST_DATA_PARAMETERS = new HashSet<>( + Arrays.asList(chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName)); + + public static final Set CLIENT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet<>( + Arrays.asList(amountParamName, transactionDateParamName, dateFormatParamName, localeParamName)); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientChargesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientChargesApiResource.java new file mode 100644 index 000000000..4330f5f4d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientChargesApiResource.java @@ -0,0 +1,168 @@ +package org.mifosplatform.portfolio.client.api; + +import java.util.Collection; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService; +import org.mifosplatform.portfolio.client.data.ClientChargeData; +import org.mifosplatform.portfolio.client.service.ClientChargeReadPlatformService; +import org.springframework.stereotype.Component; + +@Path("/clients/{clientId}/charges") +@Component +public class ClientChargesApiResource { + + private final PlatformSecurityContext context; + private final ChargeReadPlatformService chargeReadPlatformService; + private final ClientChargeReadPlatformService clientChargeReadPlatformService; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + + public ClientChargesApiResource(final PlatformSecurityContext context, final ChargeReadPlatformService chargeReadPlatformService, + final ClientChargeReadPlatformService clientChargeReadPlatformService, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final ApiRequestParameterHelper apiRequestParameterHelper, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { + this.context = context; + this.chargeReadPlatformService = chargeReadPlatformService; + this.clientChargeReadPlatformService = clientChargeReadPlatformService; + this.toApiJsonSerializer = toApiJsonSerializer; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAllClientCharges(@PathParam("clientId") final Long clientId, + @DefaultValue("all") @QueryParam("chargeStatus") final String chargeStatus, @Context final UriInfo uriInfo) { + this.context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); + + if (!(is(chargeStatus, "all") || is(chargeStatus, "active") + || is(chargeStatus, "inactive"))) { throw new UnrecognizedQueryParamException("status", chargeStatus, + new Object[] { "all", "active", "inactive" }); } + + final Collection clientCharges = this.clientChargeReadPlatformService.retrieveClientCharges(clientId, + chargeStatus); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, clientCharges, ClientApiConstants.CLIENT_CHARGES_RESPONSE_DATA_PARAMETERS); + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveTemplate(@PathParam("savingsAccountId") final Long savingsAccountId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); + + return null; + + } + + @GET + @Path("{chargeId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveClientCharge(@PathParam("clientId") final Long clientId, @PathParam("chargeId") final Long chargeId, + @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); + + final ClientChargeData clientCharge = this.clientChargeReadPlatformService.retrieveClientCharge(clientId, chargeId); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, clientCharge, ClientApiConstants.CLIENT_CHARGES_RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String applyClientCharge(@PathParam("clientId") final Long clientId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createClientCharge(clientId).withJson(apiRequestBodyAsJson) + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + + @POST + @Path("{chargeId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String payOrWaiveSavingsAccountCharge(@PathParam("clientId") final Long clientId, @PathParam("chargeId") final Long chargeId, + @QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) { + + String json = ""; + if (is(commandParam, ClientApiConstants.CLIENT_CHARGE_COMMAND_WAIVE_CHARGE)) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().waiveClientCharge(clientId, chargeId) + .withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + json = this.toApiJsonSerializer.serialize(result); + } else if (is(commandParam, ClientApiConstants.CLIENT_CHARGE_COMMAND_PAY_CHARGE)) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().payClientCharge(clientId, chargeId) + .withJson(apiRequestBodyAsJson).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + json = this.toApiJsonSerializer.serialize(result); + } else if (is(commandParam, ClientApiConstants.CLIENT_CHARGE_COMMAND_INACTIVATE_CHARGE)) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().inactivateClientCharge(clientId, chargeId).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + json = this.toApiJsonSerializer.serialize(result); + } else { + throw new UnrecognizedQueryParamException("command", commandParam, ClientApiConstants.CLIENT_CHARGE_COMMAND_WAIVE_CHARGE, + ClientApiConstants.CLIENT_CHARGE_COMMAND_PAY_CHARGE, ClientApiConstants.CLIENT_CHARGE_COMMAND_INACTIVATE_CHARGE); + } + + return json; + } + + @DELETE + @Path("{chargeId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String deleteClientCharge(@PathParam("clientId") final Long clientId, @PathParam("chargeId") final Long chargeId) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteClientCharge(clientId, chargeId).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientPaymentsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientPaymentsApiResource.java new file mode 100644 index 000000000..e7dbfa6ac --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientPaymentsApiResource.java @@ -0,0 +1,17 @@ +package org.mifosplatform.portfolio.client.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +public class ClientPaymentsApiResource { + + + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientChargeData.java new file mode 100644 index 000000000..5c72333b3 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientChargeData.java @@ -0,0 +1,83 @@ +package org.mifosplatform.portfolio.client.data; + +import java.math.BigDecimal; +import java.util.Collection; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.charge.data.ChargeData; + +@SuppressWarnings("unused") +public class ClientChargeData { + + private final Long id; + + private final Long clientId; + + private final Long chargeId; + + private final String name; + + private final EnumOptionData chargeTimeType; + + private final LocalDate dueDate; + + private final EnumOptionData chargeCalculationType; + + private final CurrencyData currency; + + private final BigDecimal amount; + + private final BigDecimal amountPaid; + + private final BigDecimal amountWaived; + + private final BigDecimal amountWrittenOff; + + private final BigDecimal amountOutstanding; + + private final boolean penalty; + + private final Boolean isActive; + + private final LocalDate inactivationDate; + + private final Collection chargeOptions; + + public static ClientChargeData instance(Long id, Long clientId, Long chargeId, String name, EnumOptionData chargeTimeType, + LocalDate dueDate, EnumOptionData chargeCalculationType, CurrencyData currency, BigDecimal amount, BigDecimal amountPaid, + BigDecimal amountWaived, BigDecimal amountWrittenOff, BigDecimal amountOutstanding, boolean penalty, Boolean isActive, + LocalDate inactivationDate, Collection chargeOptions) { + + return new ClientChargeData(id, clientId, chargeId, name, chargeTimeType, dueDate, chargeCalculationType, currency, amount, + amountPaid, amountWaived, amountWrittenOff, amountOutstanding, penalty, isActive, inactivationDate, chargeOptions); + } + + private ClientChargeData(Long id, Long clientId, Long chargeId, String name, EnumOptionData chargeTimeType, LocalDate dueDate, + EnumOptionData chargeCalculationType, CurrencyData currency, BigDecimal amount, BigDecimal amountPaid, BigDecimal amountWaived, + BigDecimal amountWrittenOff, BigDecimal amountOutstanding, boolean penalty, Boolean isActive, LocalDate inactivationDate, + Collection chargeOptions) { + super(); + this.id = id; + this.clientId = clientId; + this.chargeId = chargeId; + this.name = name; + this.chargeTimeType = chargeTimeType; + this.dueDate = dueDate; + this.chargeCalculationType = chargeCalculationType; + this.currency = currency; + this.amount = amount; + this.amountPaid = amountPaid; + this.amountWaived = amountWaived; + this.amountWrittenOff = amountWrittenOff; + this.amountOutstanding = amountOutstanding; + this.penalty = penalty; + this.isActive = isActive; + this.inactivationDate = inactivationDate; + + // template related fields + this.chargeOptions = chargeOptions; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientChargeDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientChargeDataValidator.java new file mode 100644 index 000000000..55c8daea8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientChargeDataValidator.java @@ -0,0 +1,109 @@ +package org.mifosplatform.portfolio.client.data; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +@Component +public class ClientChargeDataValidator { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public ClientChargeDataValidator(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + public void validateAdd(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + ClientApiConstants.CLIENT_CHARGES_ADD_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + final Long chargeId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.chargeIdParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.chargeIdParamName).value(chargeId).notNull().integerGreaterThanZero(); + + final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(ClientApiConstants.amountParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.amountParamName).value(amount).notNull().positiveAmount(); + + final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.dueAsOfDateParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.dueAsOfDateParamName).value(dueDate).notNull(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validateUpdate(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + ClientApiConstants.CLIENT_CHARGES_ADD_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.amountParamName, element)) { + final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(ClientApiConstants.amountParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.amountParamName).value(amount).notNull().positiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.dueAsOfDateParamName, element)) { + final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.dueAsOfDateParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.dueAsOfDateParamName).value(dueDate).notNull(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + public void validatePayCharge(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + ClientApiConstants.CLIENT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(ClientApiConstants.amountParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.amountParamName).value(amount).notNull().positiveAmount(); + + final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.transactionDateParamName, + element); + baseDataValidator.reset().parameter(ClientApiConstants.transactionDateParamName).value(transactionDate).notNull(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientCharge.java new file mode 100644 index 000000000..4e4e942a2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientCharge.java @@ -0,0 +1,223 @@ +package org.mifosplatform.portfolio.client.domain; + +import java.math.BigDecimal; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.portfolio.charge.domain.Charge; +import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType; +import org.mifosplatform.portfolio.charge.domain.ChargeTimeType; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_client_charge") +public class ClientCharge extends AbstractPersistable { + + @ManyToOne(optional = false) + @JoinColumn(name = "client_id", referencedColumnName = "id", nullable = false) + private Client client; + + @ManyToOne(optional = false) + @JoinColumn(name = "charge_id", referencedColumnName = "id", nullable = false) + private Charge charge; + + @Column(name = "charge_time_enum", nullable = false) + private Integer chargeTime; + + @Temporal(TemporalType.DATE) + @Column(name = "charge_due_date") + private Date dueDate; + + @Column(name = "charge_calculation_enum") + private Integer chargeCalculation; + + @Column(name = "amount", scale = 6, precision = 19, nullable = false) + private BigDecimal amount; + + @Column(name = "amount_paid_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal amountPaid; + + @Column(name = "amount_waived_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal amountWaived; + + @Column(name = "amount_writtenoff_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal amountWrittenOff; + + @Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false) + private BigDecimal amountOutstanding; + + @Column(name = "is_penalty", nullable = false) + private boolean penaltyCharge = false; + + @Column(name = "is_paid_derived", nullable = false) + private boolean paid = false; + + @Column(name = "waived", nullable = false) + private boolean waived = false; + + @Column(name = "is_active", nullable = false) + private boolean status = true; + + @Temporal(TemporalType.DATE) + @Column(name = "inactivated_on_date") + private Date inactivationDate; + + public static ClientCharge createNew(final Client client, final Charge charge, final JsonCommand command) { + BigDecimal amount = command.bigDecimalValueOfParameterNamed(ClientApiConstants.amountParamName); + final LocalDate dueDate = command.localDateValueOfParameterNamed(ClientApiConstants.dueAsOfDateParamName); + final boolean status = true; + // Derive from charge definition if not passed in as a parameter + amount = (amount == null) ? charge.getAmount() : amount; + return new ClientCharge(client, charge, amount, dueDate, status); + } + + private ClientCharge(final Client client, final Charge charge, final BigDecimal amount, final LocalDate dueDate, final boolean status) { + + this.client = client; + this.charge = charge; + this.penaltyCharge = charge.isPenalty(); + this.chargeTime = charge.getChargeTime(); + this.dueDate = (dueDate == null) ? null : dueDate.toDate(); + this.chargeCalculation = charge.getChargeCalculation(); + + BigDecimal chargeAmount = charge.getAmount(); + if (amount != null) { + chargeAmount = amount; + } + + populateDerivedFields(chargeAmount); + + this.paid = determineIfFullyPaid(); + this.status = status; + } + + private void populateDerivedFields(final BigDecimal amount) { + switch (ChargeCalculationType.fromInt(this.chargeCalculation)) { + case INVALID: + this.amount = null; + this.amountPaid = null; + this.amountOutstanding = BigDecimal.ZERO; + this.amountWaived = null; + this.amountWrittenOff = null; + break; + case FLAT: + this.amount = amount; + this.amountPaid = null; + this.amountOutstanding = amount; + this.amountWaived = null; + this.amountWrittenOff = null; + break; + default: + break; + } + } + + public boolean isOnSpecifiedDueDate() { + return ChargeTimeType.fromInt(this.chargeTime).isOnSpecifiedDueDate(); + } + + private boolean determineIfFullyPaid() { + return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0; + } + + private BigDecimal calculateOutstanding() { + + BigDecimal amountPaidLocal = BigDecimal.ZERO; + if (this.amountPaid != null) { + amountPaidLocal = this.amountPaid; + } + + BigDecimal amountWaivedLocal = BigDecimal.ZERO; + if (this.amountWaived != null) { + amountWaivedLocal = this.amountWaived; + } + + BigDecimal amountWrittenOffLocal = BigDecimal.ZERO; + if (this.amountWrittenOff != null) { + amountWrittenOffLocal = this.amountWrittenOff; + } + + final BigDecimal totalAccountedFor = amountPaidLocal.add(amountWaivedLocal).add(amountWrittenOffLocal); + + return this.amount.subtract(totalAccountedFor); + } + + public Client getClient() { + return this.client; + } + + public Charge getCharge() { + return this.charge; + } + + public Integer getChargeTime() { + return this.chargeTime; + } + + public Date getDueDate() { + return this.dueDate; + } + + public Integer getChargeCalculation() { + return this.chargeCalculation; + } + + public BigDecimal getAmount() { + return this.amount; + } + + public BigDecimal getAmountPaid() { + return this.amountPaid; + } + + public BigDecimal getAmountWaived() { + return this.amountWaived; + } + + public BigDecimal getAmountWrittenOff() { + return this.amountWrittenOff; + } + + public BigDecimal getAmountOutstanding() { + return this.amountOutstanding; + } + + public boolean isPenaltyCharge() { + return this.penaltyCharge; + } + + public boolean isPaid() { + return this.paid; + } + + public boolean isWaived() { + return this.waived; + } + + public boolean isStatus() { + return this.status; + } + + public Date getInactivationDate() { + return this.inactivationDate; + } + + public Long getClientId() { + return client.getId(); + } + + public Long getOfficeId() { + return this.client.getOffice().getId(); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientChargePaidBy.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientChargePaidBy.java new file mode 100644 index 000000000..367371dd1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientChargePaidBy.java @@ -0,0 +1,38 @@ +package org.mifosplatform.portfolio.client.domain; + +import java.math.BigDecimal; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_client_charge_paid_by") +public class ClientChargePaidBy extends AbstractPersistable { + + @ManyToOne + @JoinColumn(name = "client_transaction_id", nullable = false) + private ClientTransaction clientTransaction; + + @ManyToOne + @JoinColumn(name = "client_charge_id", nullable = false) + private ClientCharge clientCharge; + + @Column(name = "amount", scale = 6, precision = 19, nullable = false) + private BigDecimal amount; + + protected ClientChargePaidBy() { + + } + + public ClientChargePaidBy(final ClientTransaction clientTransaction, final ClientCharge clientCharge, final BigDecimal amount) { + this.clientTransaction = clientTransaction; + this.clientCharge = clientCharge; + this.amount = amount; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientChargeRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientChargeRepository.java new file mode 100644 index 000000000..f02779346 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientChargeRepository.java @@ -0,0 +1,8 @@ +package org.mifosplatform.portfolio.client.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface ClientChargeRepository extends JpaRepository, JpaSpecificationExecutor { + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientTransaction.java new file mode 100644 index 000000000..251291e33 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientTransaction.java @@ -0,0 +1,75 @@ +package org.mifosplatform.portfolio.client.domain; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.UniqueConstraint; + +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanChargePaidBy; +import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; +import org.mifosplatform.useradministration.domain.AppUser; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_client_transaction", uniqueConstraints = { @UniqueConstraint(columnNames = { "external_id" }, name = "external_id") }) +public class ClientTransaction extends AbstractPersistable { + + @ManyToOne(optional = false) + @JoinColumn(name = "client_id", nullable = false) + private Client client; + + @ManyToOne + @JoinColumn(name = "office_id", nullable = false) + private Office office; + + @ManyToOne(optional = true) + @JoinColumn(name = "payment_detail_id", nullable = true) + private PaymentDetail paymentDetail; + + @Column(name = "transaction_type_enum", nullable = false) + private final Integer typeOf; + + @Temporal(TemporalType.DATE) + @Column(name = "transaction_date", nullable = false) + private final Date dateOf; + + @Temporal(TemporalType.DATE) + @Column(name = "submitted_on_date", nullable = false) + private final Date submittedOnDate; + + @Column(name = "amount", scale = 6, precision = 19, nullable = false) + private BigDecimal amount; + + @Column(name = "is_reversed", nullable = false) + private boolean reversed; + + @Column(name = "external_id", length = 100, nullable = true, unique = true) + private String externalId; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_date", nullable = false) + private final Date createdDate; + + @ManyToOne + @JoinColumn(name = "appuser_id", nullable = true) + private final AppUser appUser; + + @LazyCollection(LazyCollectionOption.FALSE) + @OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction", orphanRemoval = true) + private Set loanChargesPaid = new HashSet<>(); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientChargeNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientChargeNotFoundException.java new file mode 100644 index 000000000..885b287a5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientChargeNotFoundException.java @@ -0,0 +1,16 @@ +package org.mifosplatform.portfolio.client.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class ClientChargeNotFoundException extends AbstractPlatformResourceNotFoundException { + + public ClientChargeNotFoundException(final Long id) { + super("error.msg.client.charge.id.invalid", "Client charge with identifier " + id + " does not exist", id); + } + + public ClientChargeNotFoundException(final Long id, final Long clientId) { + super("error.msg.client.charge.id.invalid.for.given.client", + "Client charge with identifier " + id + " does not exist for client with id " + clientId, id, clientId); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CreateClientChargeCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CreateClientChargeCommandHandler.java new file mode 100644 index 000000000..5a65b04a3 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CreateClientChargeCommandHandler.java @@ -0,0 +1,29 @@ +package org.mifosplatform.portfolio.client.handler; + +import org.mifosplatform.commands.annotation.CommandType; +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; +import org.mifosplatform.portfolio.client.service.ClientChargeWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@CommandType(entity = ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME, action = ClientApiConstants.CLIENT_CHARGE_ACTION_CREATE) +public class CreateClientChargeCommandHandler implements NewCommandSourceHandler { + + private final ClientChargeWritePlatformService writePlatformService; + + @Autowired + public CreateClientChargeCommandHandler(final ClientChargeWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.addCharge(command.getClientId(), command); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/DeleteClientChargeCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/DeleteClientChargeCommandHandler.java new file mode 100644 index 000000000..12ae25a42 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/DeleteClientChargeCommandHandler.java @@ -0,0 +1,29 @@ +package org.mifosplatform.portfolio.client.handler; + +import org.mifosplatform.commands.annotation.CommandType; +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; +import org.mifosplatform.portfolio.client.service.ClientChargeWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@CommandType(entity = ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME, action = ClientApiConstants.CLIENT_CHARGE_ACTION_DELETE) +public class DeleteClientChargeCommandHandler implements NewCommandSourceHandler { + + private final ClientChargeWritePlatformService writePlatformService; + + @Autowired + public DeleteClientChargeCommandHandler(final ClientChargeWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.deleteCharge(command.getClientId(), command.entityId(), command); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/PayClientChargeCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/PayClientChargeCommandHandler.java new file mode 100644 index 000000000..bdd77fda4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/PayClientChargeCommandHandler.java @@ -0,0 +1,30 @@ +package org.mifosplatform.portfolio.client.handler; + +import org.mifosplatform.commands.annotation.CommandType; +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; +import org.mifosplatform.portfolio.client.service.ClientChargeWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@CommandType(entity = ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME, action = ClientApiConstants.CLIENT_CHARGE_ACTION_PAY) +public class PayClientChargeCommandHandler implements NewCommandSourceHandler { + + private final ClientChargeWritePlatformService writePlatformService; + + @Autowired + public PayClientChargeCommandHandler(final ClientChargeWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.payCharge(command.getClientId(), command.entityId(), command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/WaiveClientChargeCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/WaiveClientChargeCommandHandler.java new file mode 100644 index 000000000..6c230ffa8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/WaiveClientChargeCommandHandler.java @@ -0,0 +1,30 @@ +package org.mifosplatform.portfolio.client.handler; + +import org.mifosplatform.commands.annotation.CommandType; +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; +import org.mifosplatform.portfolio.client.service.ClientChargeWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@CommandType(entity = ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME, action = ClientApiConstants.CLIENT_CHARGE_ACTION_WAIVE) +public class WaiveClientChargeCommandHandler implements NewCommandSourceHandler { + + private final ClientChargeWritePlatformService writePlatformService; + + @Autowired + public WaiveClientChargeCommandHandler(final ClientChargeWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.waiveCharge(command.getClientId(), command.entityId()); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeReadPlatformService.java new file mode 100644 index 000000000..3d1a519b2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeReadPlatformService.java @@ -0,0 +1,16 @@ +package org.mifosplatform.portfolio.client.service; + +import java.util.Collection; + +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.client.data.ClientChargeData; + +public interface ClientChargeReadPlatformService { + + ChargeData retrieveClientChargeTemplate(); + + Collection retrieveClientCharges(Long clientId, String status); + + ClientChargeData retrieveClientCharge(Long clientId, Long clientChargeId); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeReadPlatformServiceImpl.java new file mode 100644 index 000000000..fa03aaf39 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeReadPlatformServiceImpl.java @@ -0,0 +1,133 @@ +package org.mifosplatform.portfolio.client.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.charge.service.ChargeDropdownReadPlatformService; +import org.mifosplatform.portfolio.charge.service.ChargeEnumerations; +import org.mifosplatform.portfolio.client.data.ClientChargeData; +import org.mifosplatform.portfolio.client.exception.ClientChargeNotFoundException; +import org.mifosplatform.portfolio.common.service.DropdownReadPlatformService; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final PlatformSecurityContext context; + private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService; + private final DropdownReadPlatformService dropdownReadPlatformService; + + public ClientChargeReadPlatformServiceImpl(final PlatformSecurityContext context, + final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource, + final DropdownReadPlatformService dropdownReadPlatformService) { + this.context = context; + this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService; + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.dropdownReadPlatformService = dropdownReadPlatformService; + } + + public static final class ClientChargeMapper implements RowMapper { + + @Override + public ClientChargeData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final Long chargeId = rs.getLong("chargeId"); + final Long clientId = rs.getLong("clientId"); + final String name = rs.getString("name"); + final BigDecimal amount = rs.getBigDecimal("amountDue"); + final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPaid"); + final BigDecimal amountWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWaived"); + final BigDecimal amountWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWrittenOff"); + final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + final int chargeTime = rs.getInt("chargeTime"); + final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime); + + final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate"); + final int chargeCalculation = rs.getInt("chargeCalculation"); + final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation); + final boolean penalty = rs.getBoolean("penalty"); + final Boolean isActive = rs.getBoolean("isActive"); + final LocalDate inactivationDate = JdbcSupport.getLocalDate(rs, "inactivationDate"); + + final Collection chargeOptions = null; + + return ClientChargeData.instance(id, clientId, chargeId, name, chargeTimeType, dueDate, chargeCalculationType, currency, amount, + amountPaid, amountWaived, amountWrittenOff, amountOutstanding, penalty, isActive, inactivationDate, chargeOptions); + + } + + public String schema() { + return " cc.id as id, c.id as chargeId, cc.client_id as clientId, c.name as name, cc.amount as amountDue, " + + "cc.amount_paid_derived as amountPaid, cc.amount_waived_derived as amountWaived, " + + "cc.amount_writtenoff_derived as amountWrittenOff, cc.amount_outstanding_derived as amountOutstanding, " + + "cc.charge_time_enum as chargeTime, cc.is_penalty as penalty, cc.charge_due_date as dueAsOfDate, " + + "cc.charge_calculation_enum as chargeCalculation, " + + "cc.is_active as isActive, cc.inactivated_on_date as inactivationDate, " + + "c.currency_code as currencyCode, oc.name as currencyName, " + + "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, " + + "oc.internationalized_name_code as currencyNameCode from m_charge c " + + "join m_organisation_currency oc on c.currency_code = oc.code join m_client_charge cc on cc.charge_id = c.id "; + } + + } + + @Override + public Collection retrieveClientCharges(Long clientId, String status) { + final ClientChargeMapper rm = new ClientChargeMapper(); + final StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("select ").append(rm.schema()).append(" where cc.client_id=? "); + if (status.equalsIgnoreCase("active")) { + sqlBuilder.append(" and cc.is_active = 1 "); + } else if (status.equalsIgnoreCase("inactive")) { + sqlBuilder.append(" and cc.is_active = 0 "); + } + sqlBuilder.append(" order by cc.charge_time_enum ASC, cc.charge_due_date ASC, cc.is_penalty ASC"); + + return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { clientId }); + } + + @Override + public ClientChargeData retrieveClientCharge(Long clientId, Long clientChargeId) { + try { + this.context.authenticatedUser(); + + final ClientChargeMapper rm = new ClientChargeMapper(); + + final String sql = "select " + rm.schema() + " where sc.client_id=? and sc.id=? "; + + return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { clientId, clientChargeId }); + } catch (final EmptyResultDataAccessException e) { + throw new ClientChargeNotFoundException(clientChargeId, clientId); + } + } + + @Override + public ChargeData retrieveClientChargeTemplate() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeWritePlatformService.java new file mode 100644 index 000000000..d8f4c1f14 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeWritePlatformService.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.client.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.springframework.transaction.annotation.Transactional; + +public interface ClientChargeWritePlatformService { + + @Transactional + CommandProcessingResult addCharge(Long clientId, JsonCommand command); + + @Transactional + CommandProcessingResult updateCharge(Long clientId, JsonCommand command); + + @Transactional + CommandProcessingResult deleteCharge(Long clientId, Long clientChargeId, JsonCommand command); + + @Transactional + CommandProcessingResult waiveCharge(Long clientId, Long clientChargeId); + + @Transactional + CommandProcessingResult payCharge(Long clientId, Long clientChargeId, JsonCommand command); + + @Transactional + CommandProcessingResult inactivateCharge(Long clientId, Long clientChargeId); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..b7572c3f0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientChargeWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,155 @@ +package org.mifosplatform.portfolio.client.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.holiday.domain.HolidayRepositoryWrapper; +import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepository; +import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; +import org.mifosplatform.portfolio.charge.domain.Charge; +import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; +import org.mifosplatform.portfolio.client.data.ClientChargeDataValidator; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientCharge; +import org.mifosplatform.portfolio.client.domain.ClientChargeRepository; +import org.mifosplatform.portfolio.client.domain.ClientRepository; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; +import org.mifosplatform.portfolio.savings.data.SavingsAccountDataValidator; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountCharge; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements ClientChargeWritePlatformService { + + private final PlatformSecurityContext context; + private final ChargeRepositoryWrapper chargeRepository; + private final ClientRepositoryWrapper clientRepository; + private final ClientChargeDataValidator clientChargeDataValidator; + private final ConfigurationDomainService configurationDomainService; + private final HolidayRepositoryWrapper holidayRepository; + private final WorkingDaysRepositoryWrapper workingDaysRepository; + private final ClientChargeRepository clientChargeRepository; + + @Autowired + public ClientChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, + final ChargeRepositoryWrapper chargeRepository, final ClientChargeDataValidator clientChargeDataValidator, + final ClientRepositoryWrapper clientRepository, final HolidayRepositoryWrapper holidayRepositoryWrapper, + final ConfigurationDomainService configurationDomainService, final ClientChargeRepository clientChargeRepository, + final WorkingDaysRepositoryWrapper workingDaysRepository) { + this.context = context; + this.chargeRepository = chargeRepository; + this.clientChargeDataValidator = clientChargeDataValidator; + this.clientRepository = clientRepository; + this.holidayRepository = holidayRepositoryWrapper; + this.configurationDomainService = configurationDomainService; + this.clientChargeRepository = clientChargeRepository; + this.workingDaysRepository = workingDaysRepository; + } + + @Override + public CommandProcessingResult addCharge(Long clientId, JsonCommand command) { + + this.clientChargeDataValidator.validateAdd(command.json()); + + final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); + checkClientActive(client); + + final Long chargeDefinitionId = command.longValueOfParameterNamed(ClientApiConstants.chargeIdParamName); + final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId); + + final ClientCharge clientCharge = ClientCharge.createNew(client, charge, command); + + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()); + + validateDueDateOnWorkingDay(clientCharge, fmt); + + this.clientChargeRepository.save(clientCharge); + + return new CommandProcessingResultBuilder() // + .withEntityId(clientCharge.getId()) // + .withOfficeId(clientCharge.getClient().getOffice().getId()) // + .withClientId(clientCharge.getClient().getId()) // + .build(); + } + + /** + * Ensures that the charge due date is not on a holiday or a non working day + * + * @param savingsAccountCharge + * @param fmt + */ + private void validateDueDateOnWorkingDay(final SavingsAccountCharge savingsAccountCharge, final DateTimeFormatter fmt) { + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); + if (savingsAccountCharge.getDueLocalDate() != null) { + // transaction date should not be on a holiday or non working day + if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() + && this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) { + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled() + && !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + } + } + + @Override + public CommandProcessingResult updateCharge(Long clientId, JsonCommand command) { + // TODO Auto-generated method stub + return null; + } + + @Override + public CommandProcessingResult deleteCharge(Long clientId, Long clientChargeId, JsonCommand command) { + // TODO Auto-generated method stub + return null; + } + + @Override + public CommandProcessingResult waiveCharge(Long clientId, Long clientChargeId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public CommandProcessingResult payCharge(Long clientId, Long clientChargeId, JsonCommand command) { + // TODO Auto-generated method stub + return null; + } + + @Override + public CommandProcessingResult inactivateCharge(Long clientId, Long clientChargeId) { + // TODO Auto-generated method stub + return null; + } + + private void checkClientActive(Client client) { + if (client != null) { + if (client.isNotActive()) { throw new ClientNotActiveException(client.getId()); } + } + } + +}