changes for client fees

This commit is contained in:
Vishwas Babu A J
2015-08-20 01:45:11 +05:30
parent 89aab6b93d
commit d0fd3e4a6c
25 changed files with 1321 additions and 63 deletions
@@ -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;
}
}
@@ -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<Holiday> findUnprocessed() {
return this.repository.findUnprocessed(HolidayStatusType.ACTIVE.getValue());
}
public boolean isHoliday(Long officeId, LocalDate transactionDate) {
final List<Holiday> holidays = findByOfficeIdAndGreaterThanDate(officeId, transactionDate.toDate());
return HolidayUtil.isHoliday(transactionDate, holidays);
}
}
@@ -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();
}
@@ -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<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, transactionDate.toDate());
return HolidayUtil.isHoliday(transactionDate, holidays);
}
@Override
public boolean isTransactionAllowedOnHoliday() {
return this.configurationDomainService.allowTransactionsOnHolidayEnabled();
}
}
@@ -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);
}
}
@@ -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);
}
@@ -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());
@@ -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<String> 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<String> 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<String> 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<String> ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, activationDateParamName));
public static final Set<String> REACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, reactivationDateParamName));
public static final Set<String> ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(localeParamName, dateFormatParamName, activationDateParamName));
public static final Set<String> REACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(localeParamName, dateFormatParamName, reactivationDateParamName));
public static final Set<String> CLIENT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, closureDateParamName, closureReasonIdParamName));
public static final Set<String> CLIENT_REJECT_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,dateFormatParamName,rejectionDateParamName,rejectionReasonIdParamName));
public static final Set<String> CLIENT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(localeParamName, dateFormatParamName, closureDateParamName, closureReasonIdParamName));
public static final Set<String> CLIENT_WITHDRAW_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,dateFormatParamName,withdrawalDateParamName,withdrawalReasonIdParamName));
public static final Set<String> CLIENT_REJECT_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(localeParamName, dateFormatParamName, rejectionDateParamName, rejectionReasonIdParamName));
public static final Set<String> CLIENT_WITHDRAW_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(localeParamName, dateFormatParamName, withdrawalDateParamName, withdrawalReasonIdParamName));
public static final Set<String> 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<String> CLIENT_CHARGES_ADD_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName));
public static final Set<String> CLIENT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(amountParamName, transactionDateParamName, dateFormatParamName, localeParamName));
}
@@ -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<ClientChargeData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
public ClientChargesApiResource(final PlatformSecurityContext context, final ChargeReadPlatformService chargeReadPlatformService,
final ClientChargeReadPlatformService clientChargeReadPlatformService,
final DefaultToApiJsonSerializer<ClientChargeData> 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<ClientChargeData> 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);
}
}
@@ -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 {
}
@@ -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<ChargeData> 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<ChargeData> 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<ChargeData> 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;
}
}
@@ -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<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
ClientApiConstants.CLIENT_CHARGES_ADD_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> 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<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
ClientApiConstants.CLIENT_CHARGES_ADD_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> 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<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
ClientApiConstants.CLIENT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> 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<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -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<Long> {
@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();
}
}
@@ -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<Long> {
@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;
}
}
@@ -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<ClientCharge, Long>, JpaSpecificationExecutor<ClientCharge> {
}
@@ -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<Long> {
@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<LoanChargePaidBy> loanChargesPaid = new HashSet<>();
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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());
}
}
@@ -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<ClientChargeData> retrieveClientCharges(Long clientId, String status);
ClientChargeData retrieveClientCharge(Long clientId, Long clientChargeId);
}
@@ -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<ClientChargeData> {
@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<ChargeData> 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<ClientChargeData> 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;
}
}
@@ -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);
}
@@ -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<ApiParameterError> 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()); }
}
}
}