MIFOSX-583 wip

This commit is contained in:
Vishwas Babu A J
2015-08-21 04:39:49 +05:30
parent da514b8856
commit f7c0a833cb
23 changed files with 599 additions and 106 deletions
@@ -2448,4 +2448,13 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder undoClientTransaction(final Long clientId, final Long transactionId) {
this.actionName = ClientApiConstants.CLIENT_TRANSACTION_ACTION_UNDO;
this.entityName = ClientApiConstants.CLIENT_RESOURCE_NAME;
this.entityId = transactionId;
this.clientId = clientId;
this.href = "/clients/" + clientId + "/transactions/" + transactionId + "?command=undo";
return this;
}
}
@@ -5,16 +5,11 @@
*/
package org.mifosplatform.portfolio.client.api;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
public class ClientApiConstants {
@@ -28,10 +23,22 @@ public class ClientApiConstants {
public static final String CLIENT_CHARGE_ACTION_PAY = "PAY";
public static final String CLIENT_CHARGE_ACTION_INACTIVATE = "INACTIVATE";
// Client charge associations and query parameters
public static final String CLIENT_CHARGE_QUERY_PARAM_STATUS = "chargeStatus";
public static final String CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ALL = "all";
public static final String CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ACTIVE = "active";
public static final String CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_INACTIVE = "inactive";
public static final String CLIENT_CHARGE_ASSOCIATIONS_TRANSACTIONS = "transactions";
// Client transaction action names
public static final String CLIENT_TRANSACTION_ACTION_READ = "READTRANSACTION";
public static final String CLIENT_TRANSACTION_ACTION_UNDO = "UNDOTRANSACTION";
// 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_TRANSACTION_COMMAND_UNDO = "undo";
public static final String CLIENT_CLOSURE_REASON = "ClientClosureReason";
public static final String CLIENT_ACTION_REASON = "ClientActionReason";
@@ -119,6 +126,7 @@ public class ClientApiConstants {
public static final String amountParamName = "amount";
public static final String amountPaidParamName = "amountPaid";
public static final String chargeOptionsParamName = "chargeOptions";
public static final String transactionsParamName = "transactions";
// client transactions response parameters
public static final String transactionAmountParamName = "transactionAmount";
@@ -176,7 +184,7 @@ public class ClientApiConstants {
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));
amountOrPercentageParamName, amountParamName, amountPaidParamName, chargeOptionsParamName, transactionsParamName));
public static final Set<String> CLIENT_CHARGES_ADD_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName));
@@ -5,7 +5,9 @@
*/
package org.mifosplatform.portfolio.client.api;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -24,6 +26,7 @@ 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.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException;
@@ -33,9 +36,12 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientChargeData;
import org.mifosplatform.portfolio.client.data.ClientTransactionData;
import org.mifosplatform.portfolio.client.service.ClientChargeReadPlatformService;
import org.mifosplatform.portfolio.client.service.ClientTransactionReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@Path("/clients/{clientId}/charges")
@Component
@@ -44,6 +50,7 @@ public class ClientChargesApiResource {
private final PlatformSecurityContext context;
private final ChargeReadPlatformService chargeReadPlatformService;
private final ClientChargeReadPlatformService clientChargeReadPlatformService;
private final ClientTransactionReadPlatformService clientTransactionReadPlatformService;
private final DefaultToApiJsonSerializer<ClientChargeData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@@ -53,28 +60,36 @@ public class ClientChargesApiResource {
final ClientChargeReadPlatformService clientChargeReadPlatformService,
final DefaultToApiJsonSerializer<ClientChargeData> toApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final ClientTransactionReadPlatformService clientTransactionReadPlatformService) {
this.context = context;
this.chargeReadPlatformService = chargeReadPlatformService;
this.clientChargeReadPlatformService = clientChargeReadPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.clientTransactionReadPlatformService = clientTransactionReadPlatformService;
}
@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) {
@DefaultValue(ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ALL) @QueryParam(ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS) final String chargeStatus,
@QueryParam("paid") final Boolean paid, @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" }); }
if (!(is(chargeStatus, ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ALL)
|| is(chargeStatus, ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ACTIVE)
|| is(chargeStatus,
ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_INACTIVE))) { throw new UnrecognizedQueryParamException(
ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS, chargeStatus,
new Object[] { ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ALL,
ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ACTIVE,
ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_INACTIVE }); }
final Collection<ClientChargeData> clientCharges = this.clientChargeReadPlatformService.retrieveClientCharges(clientId,
chargeStatus);
chargeStatus, paid);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, clientCharges, ClientApiConstants.CLIENT_CHARGES_RESPONSE_DATA_PARAMETERS);
@@ -109,7 +124,24 @@ public class ClientChargesApiResource {
this.context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
final ClientChargeData clientCharge = this.clientChargeReadPlatformService.retrieveClientCharge(clientId, chargeId);
ClientChargeData clientCharge = this.clientChargeReadPlatformService.retrieveClientCharge(clientId, chargeId);
// extract associations
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) {
associationParameters.addAll(Arrays.asList(ClientApiConstants.CLIENT_CHARGE_ASSOCIATIONS_TRANSACTIONS));
}
ApiParameterHelper.excludeAssociationsForResponseIfProvided(uriInfo.getQueryParameters(), associationParameters);
if (associationParameters.contains(ClientApiConstants.CLIENT_CHARGE_ASSOCIATIONS_TRANSACTIONS)) {
Collection<ClientTransactionData> clientTransactionDatas = this.clientTransactionReadPlatformService
.retrieveAllTransactions(clientId, chargeId);
if (CollectionUtils.isEmpty(clientTransactionDatas)) {
clientTransactionDatas = null;
}
clientCharge = ClientChargeData.addAssociations(clientCharge, clientTransactionDatas);
}
}
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, clientCharge, ClientApiConstants.CLIENT_CHARGES_RESPONSE_DATA_PARAMETERS);
@@ -132,7 +164,7 @@ public class ClientChargesApiResource {
@Path("{chargeId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String payOrWaiveSavingsAccountCharge(@PathParam("clientId") final Long clientId, @PathParam("chargeId") final Long chargeId,
public String payOrWaiveClientCharge(@PathParam("clientId") final Long clientId, @PathParam("chargeId") final Long chargeId,
@QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) {
String json = "";
@@ -9,15 +9,22 @@ import java.util.Collection;
import javax.ws.rs.Consumes;
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;
@@ -80,4 +87,30 @@ public class ClientTransactionsApiResource {
ClientApiConstants.CLIENT_TRANSACTION_RESPONSE_DATA_PARAMETERS);
}
@POST
@Path("{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String undoClientTransaction(@PathParam("clientId") final Long clientId, @PathParam("transactionId") final Long transactionId,
@QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) {
String json = "";
if (is(commandParam, ClientApiConstants.CLIENT_TRANSACTION_COMMAND_UNDO)) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().undoClientTransaction(clientId, transactionId)
.withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
json = this.toApiJsonSerializer.serialize(result);
} else {
throw new UnrecognizedQueryParamException("command", commandParam, ClientApiConstants.CLIENT_TRANSACTION_COMMAND_UNDO);
}
return json;
}
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
}
@@ -46,17 +46,31 @@ public class ClientChargeData {
private final Boolean isActive;
private final Boolean isPaid;
private final LocalDate inactivationDate;
private final Collection<ChargeData> chargeOptions;
private final Collection<ClientTransactionData> clientTransactionDatas;
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) {
BigDecimal amountWaived, BigDecimal amountWrittenOff, BigDecimal amountOutstanding, boolean penalty, Boolean isPaid,
Boolean isActive, LocalDate inactivationDate, Collection<ChargeData> chargeOptions) {
Collection<ClientTransactionData> clientTransactionDatas = null;
return new ClientChargeData(id, clientId, chargeId, name, chargeTimeType, dueDate, chargeCalculationType, currency, amount,
amountPaid, amountWaived, amountWrittenOff, amountOutstanding, penalty, isActive, inactivationDate, chargeOptions);
amountPaid, amountWaived, amountWrittenOff, amountOutstanding, penalty, isPaid, isActive, inactivationDate, chargeOptions,
clientTransactionDatas);
}
public static ClientChargeData addAssociations(ClientChargeData clientChargeData,
Collection<ClientTransactionData> clientTransactionDatas) {
return new ClientChargeData(clientChargeData.id, clientChargeData.clientId, clientChargeData.chargeId, clientChargeData.name,
clientChargeData.chargeTimeType, clientChargeData.dueDate, clientChargeData.chargeCalculationType,
clientChargeData.currency, clientChargeData.amount, clientChargeData.amountPaid, clientChargeData.amountWaived,
clientChargeData.amountWrittenOff, clientChargeData.amountOutstanding, clientChargeData.penalty, clientChargeData.isPaid,
clientChargeData.isActive, clientChargeData.inactivationDate, clientChargeData.chargeOptions, clientTransactionDatas);
}
public static ClientChargeData template(final Collection<ChargeData> chargeOptions) {
@@ -74,17 +88,20 @@ public class ClientChargeData {
final BigDecimal amountWrittenOff = null;
final BigDecimal amountOutstanding = null;
final Boolean penalty = false;
final Boolean isPaid = null;
final Boolean isActive = null;
final LocalDate inactivationDate = null;
final Collection<ClientTransactionData> clientTransactionDatas = null;
return new ClientChargeData(id, clientId, chargeId, name, chargeTimeType, dueDate, chargeCalculationType, currency, amount,
amountPaid, amountWaived, amountWrittenOff, amountOutstanding, penalty, isActive, inactivationDate, chargeOptions);
amountPaid, amountWaived, amountWrittenOff, amountOutstanding, penalty, isPaid, isActive, inactivationDate, chargeOptions,
clientTransactionDatas);
}
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) {
BigDecimal amountWrittenOff, BigDecimal amountOutstanding, boolean penalty, Boolean isPaid, Boolean isActive,
LocalDate inactivationDate, Collection<ChargeData> chargeOptions, Collection<ClientTransactionData> clientTransactionDatas) {
super();
this.id = id;
this.clientId = clientId;
@@ -100,11 +117,15 @@ public class ClientChargeData {
this.amountWrittenOff = amountWrittenOff;
this.amountOutstanding = amountOutstanding;
this.penalty = penalty;
this.isPaid = isPaid;
this.isActive = isActive;
this.inactivationDate = inactivationDate;
// template related fields
this.chargeOptions = chargeOptions;
/// associations
this.clientTransactionDatas = clientTransactionDatas;
}
}
@@ -83,7 +83,7 @@ public class ClientCharge extends AbstractPersistable<Long> {
@Transient
private OrganisationCurrency currency;
protected ClientCharge() {
//
}
@@ -117,15 +117,43 @@ public class ClientCharge extends AbstractPersistable<Long> {
this.status = status;
}
public Money pay(final MonetaryCurrency currency, final Money amountPaid) {
Money amountPaidToDate = Money.of(currency, this.amountPaid);
Money amountOutstanding = Money.of(currency, this.amountOutstanding);
public Money pay(final Money amountPaid) {
Money amountPaidToDate = Money.of(this.getCurrency(), this.amountPaid);
Money amountOutstanding = Money.of(this.getCurrency(), this.amountOutstanding);
amountPaidToDate = amountPaidToDate.plus(amountPaid);
amountOutstanding = amountOutstanding.minus(amountPaid);
this.amountPaid = amountPaidToDate.getAmount();
this.amountOutstanding = amountOutstanding.getAmount();
this.paid = determineIfFullyPaid();
return Money.of(currency, this.amountOutstanding);
return Money.of(this.getCurrency(), this.amountOutstanding);
}
public void undoPayment(final Money transactionAmount) {
Money amountPaid = getAmountPaid();
amountPaid = amountPaid.minus(transactionAmount);
this.amountPaid = amountPaid.getAmount();
this.amountOutstanding = calculateOutstanding();
this.paid = false;
this.status = true;
}
public Money waive() {
Money amountWaivedToDate = getAmountWaived();
Money amountOutstanding = getAmountOutstanding();
Money totalAmountWaived = amountWaivedToDate.plus(amountOutstanding);
this.amountWaived = totalAmountWaived.getAmount();
this.amountOutstanding = BigDecimal.ZERO;
this.waived = true;
return totalAmountWaived;
}
public void undoWaiver(final Money transactionAmount) {
Money amountWaived = getAmountWaived();
amountWaived = amountWaived.minus(transactionAmount);
this.amountWaived = amountWaived.getAmount();
this.amountOutstanding = calculateOutstanding();
this.waived = false;
this.status = true;
}
private void populateDerivedFields(final BigDecimal amount) {
@@ -158,7 +186,6 @@ public class ClientCharge extends AbstractPersistable<Long> {
}
private BigDecimal calculateOutstanding() {
BigDecimal amountPaidLocal = BigDecimal.ZERO;
if (this.amountPaid != null) {
amountPaidLocal = this.amountPaid;
@@ -248,28 +275,28 @@ public class ClientCharge extends AbstractPersistable<Long> {
}
public boolean isPaidOrPartiallyPaid(final MonetaryCurrency currency) {
final Money amountWaivedOrWrittenOff = getAmountWaived(currency).plus(getAmountWrittenOff(currency));
final Money amountWaivedOrWrittenOff = getAmountWaived().plus(getAmountWrittenOff());
return Money.of(currency, this.amountPaid).plus(amountWaivedOrWrittenOff).isGreaterThanZero();
}
public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount);
public Money getAmount() {
return Money.of(getCurrency(), this.amount);
}
public Money getAmountPaid(final MonetaryCurrency currency) {
return Money.of(currency, this.amountPaid);
public Money getAmountPaid() {
return Money.of(getCurrency(), this.amountPaid);
}
public Money getAmountWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.amountWaived);
public Money getAmountWaived() {
return Money.of(getCurrency(), this.amountWaived);
}
public Money getAmountWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.amountWrittenOff);
public Money getAmountWrittenOff() {
return Money.of(getCurrency(), this.amountWrittenOff);
}
public Money getAmountOutstanding(final MonetaryCurrency currency) {
return Money.of(currency, this.amountOutstanding);
public Money getAmountOutstanding() {
return Money.of(getCurrency(), this.amountOutstanding);
}
}
@@ -45,4 +45,20 @@ public class ClientChargePaidBy extends AbstractPersistable<Long> {
this.amount = amount;
}
public ClientTransaction getClientTransaction() {
return this.clientTransaction;
}
public void setClientTransaction(ClientTransaction clientTransaction) {
this.clientTransaction = clientTransaction;
}
public ClientCharge getClientCharge() {
return this.clientCharge;
}
public void setClientCharge(ClientCharge clientCharge) {
this.clientCharge = clientCharge;
}
}
@@ -8,7 +8,6 @@ package org.mifosplatform.portfolio.client.domain;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
public class ClientEnumerations {
@@ -19,14 +19,17 @@ import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OrganisationCurrency;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -78,6 +81,11 @@ public class ClientTransaction extends AbstractPersistable<Long> {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "clientTransaction", orphanRemoval = true)
private Set<ClientChargePaidBy> clientChargePaidByCollection = new HashSet<>();
@Transient
private OrganisationCurrency currency;
protected ClientTransaction() {}
public static ClientTransaction payCharge(final Client client, final Office office, PaymentDetail paymentDetail, final LocalDate date,
final Money amount, final String currencyCode, final AppUser appUser) {
final boolean isReversed = false;
@@ -91,7 +99,7 @@ public class ClientTransaction extends AbstractPersistable<Long> {
final boolean isReversed = false;
final String externalId = null;
final PaymentDetail paymentDetail = null;
return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.PAY_CHARGE.getValue(), date, amount, isReversed,
return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.WAIVE_CHARGE.getValue(), date, amount, isReversed,
externalId, DateUtils.getDateOfTenant(), currencyCode, appUser);
}
@@ -111,8 +119,44 @@ public class ClientTransaction extends AbstractPersistable<Long> {
this.appUser = appUser;
}
public void reverse() {
this.reversed = true;
}
public boolean isPayChargeTransaction() {
return ClientTransactionType.PAY_CHARGE.getValue().equals(this.typeOf);
}
public boolean isWaiveChargeTransaction() {
return ClientTransactionType.WAIVE_CHARGE.getValue().equals(this.typeOf);
}
public Set<ClientChargePaidBy> getClientChargePaidByCollection() {
return this.clientChargePaidByCollection;
}
public Long getClientId() {
return client.getId();
}
public Money getAmount() {
return Money.of(getCurrency(), this.amount);
}
public MonetaryCurrency getCurrency() {
return this.currency.toMonetaryCurrency();
}
public void setCurrency(OrganisationCurrency currency) {
this.currency = currency;
}
public String getCurrencyCode() {
return this.currencyCode;
}
public boolean isReversed() {
return this.reversed;
}
}
@@ -0,0 +1,47 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.client.domain;
import org.mifosplatform.organisation.office.domain.OrganisationCurrencyRepositoryWrapper;
import org.mifosplatform.portfolio.client.exception.ClientTransactionNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class ClientTransactionRepositoryWrapper {
private final ClientTransactionRepository repository;
private final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository;
@Autowired
public ClientTransactionRepositoryWrapper(final ClientTransactionRepository repository,
final OrganisationCurrencyRepositoryWrapper currencyRepositoryWrapper) {
this.repository = repository;
this.organisationCurrencyRepository = currencyRepositoryWrapper;
}
public ClientTransaction findOneWithNotFoundDetection(final Long clientId, final Long transactionId) {
final ClientTransaction clientTransaction = this.repository.findOne(transactionId);
if (clientTransaction == null
|| clientTransaction.getClientId() != clientId) { throw new ClientTransactionNotFoundException(clientId, transactionId); }
// enrich Client charge with details of Organizational currency
clientTransaction.setCurrency(organisationCurrencyRepository.findOneWithNotFoundDetection(clientTransaction.getCurrencyCode()));
return clientTransaction;
}
public void save(final ClientTransaction clientTransaction) {
this.repository.save(clientTransaction);
}
public void saveAndFlush(final ClientTransaction clientTransaction) {
this.repository.saveAndFlush(clientTransaction);
}
public void delete(final ClientTransaction clientTransaction) {
this.repository.delete(clientTransaction);
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.client.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when attempting to delete clients
*/
public class ClientTransactionCannotBeUndoneException extends AbstractPlatformDomainRuleException {
public ClientTransactionCannotBeUndoneException(final long clientId, final Long transactionId) {
super("error.msg.clients.transaction.cannot.be.undone", "Client transaction with identifier " + transactionId
+ " for client with identifier " + clientId + " has already been reversed", transactionId);
}
}
@@ -0,0 +1,18 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.client.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class ClientTransactionNotFoundException extends AbstractPlatformDomainRuleException {
public ClientTransactionNotFoundException(final Long clientId, final Long transactionId) {
super("error.msg.client.transaction.not.found.exception",
"The Transaction with id `" + transactionId + "` does not exist for a Client with id `" + clientId, transactionId,
clientId);
}
}
@@ -29,6 +29,6 @@ public class DeleteClientChargeCommandHandler implements NewCommandSourceHandler
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.deleteCharge(command.getClientId(), command.entityId(), command);
return this.writePlatformService.deleteCharge(command.getClientId(), command.entityId());
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
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.ClientTransactionWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = ClientApiConstants.CLIENT_RESOURCE_NAME, action = ClientApiConstants.CLIENT_TRANSACTION_ACTION_UNDO)
public class UndoClientTransactionCommandHandler implements NewCommandSourceHandler {
private final ClientTransactionWritePlatformService writePlatformService;
@Autowired
public UndoClientTransactionCommandHandler(final ClientTransactionWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.undo(command.getClientId(), command.entityId());
}
}
@@ -7,14 +7,11 @@ 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);
Collection<ClientChargeData> retrieveClientCharges(Long clientId, String status, Boolean isPaid);
ClientChargeData retrieveClientCharge(Long clientId, Long clientChargeId);
@@ -17,11 +17,10 @@ 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.api.ClientApiConstants;
import org.mifosplatform.portfolio.client.data.ClientChargeData;
import org.mifosplatform.portfolio.client.exception.ClientChargeNotFoundException;
import org.mifosplatform.portfolio.common.service.DropdownReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -33,17 +32,11 @@ public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlat
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
private final DropdownReadPlatformService dropdownReadPlatformService;
@Autowired
public ClientChargeReadPlatformServiceImpl(final PlatformSecurityContext context,
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource,
final DropdownReadPlatformService dropdownReadPlatformService) {
public ClientChargeReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource) {
this.context = context;
this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.dropdownReadPlatformService = dropdownReadPlatformService;
}
public static final class ClientChargeMapper implements RowMapper<ClientChargeData> {
@@ -76,13 +69,15 @@ public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlat
final int chargeCalculation = rs.getInt("chargeCalculation");
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
final boolean penalty = rs.getBoolean("penalty");
final Boolean isPaid = rs.getBoolean("isPaid");
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);
amountPaid, amountWaived, amountWrittenOff, amountOutstanding, penalty, isPaid, isActive, inactivationDate,
chargeOptions);
}
@@ -91,7 +86,7 @@ public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlat
+ "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.charge_calculation_enum as chargeCalculation, cc.is_paid_derived as isPaid, cc.waived as waived, "
+ "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, "
@@ -102,15 +97,25 @@ public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlat
}
@Override
public Collection<ClientChargeData> retrieveClientCharges(Long clientId, String status) {
public Collection<ClientChargeData> retrieveClientCharges(Long clientId, String status, Boolean isPaid) {
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")) {
// filter for active charges
if (status.equalsIgnoreCase(ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ACTIVE)) {
sqlBuilder.append(" and cc.is_active = 1 ");
} else if (status.equalsIgnoreCase("inactive")) {
} else if (status.equalsIgnoreCase(ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_INACTIVE)) {
sqlBuilder.append(" and cc.is_active = 0 ");
}
// filter for paid charges
if (isPaid != null && isPaid) {
sqlBuilder.append(" and ( cc.is_paid_derived = 1 or cc.waived = 1) ");
} else if (isPaid != null && !isPaid) {
sqlBuilder.append(" and (cc.is_paid_derived = 0 and cc.waived = 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 });
@@ -131,10 +136,4 @@ public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlat
}
}
@Override
public ChargeData retrieveClientChargeTemplate() {
// TODO Auto-generated method stub
return null;
}
}
@@ -18,7 +18,7 @@ public interface ClientChargeWritePlatformService {
CommandProcessingResult updateCharge(Long clientId, JsonCommand command);
@Transactional
CommandProcessingResult deleteCharge(Long clientId, Long clientChargeId, JsonCommand command);
CommandProcessingResult deleteCharge(Long clientId, Long clientChargeId);
@Transactional
CommandProcessingResult waiveCharge(Long clientId, Long clientChargeId);
@@ -120,11 +120,13 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(ClientApiConstants.transactionDateParamName);
final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(ClientApiConstants.amountParamName);
final Money chargePaid = Money.of(clientCharge.getCurrency(), amountPaid);
final Money chargePaid = validatePaymentDateAndAmount(client, clientCharge, fmt, transactionDate, amountPaid);
// Validate business rules for payment
validatePaymentTransaction(client, clientCharge, fmt, transactionDate, amountPaid);
// pay the charge
clientCharge.pay(clientCharge.getCurrency(), chargePaid);
clientCharge.pay(chargePaid);
// create Payment Transaction
final Map<String, Object> changes = new LinkedHashMap<>();
@@ -146,6 +148,52 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
}
@Override
public CommandProcessingResult waiveCharge(Long clientId, Long clientChargeId) {
final Client client = this.clientRepository.getActiveClient(clientId);
final ClientCharge clientCharge = this.clientChargeRepository.findOneWithNotFoundDetection(clientChargeId);
final LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
// Validate business rules for payment
validateWaiverTransaction(client, clientCharge);
// waive the charge
Money waivedAmount = clientCharge.waive();
// create Waiver Transaction
ClientTransaction clientTransaction = ClientTransaction.waiver(client, client.getOffice(), transactionDate, waivedAmount,
clientCharge.getCurrency().getCode(), getAppUserIfPresent());
this.clientTransactionRepository.save(clientTransaction);
// update charge paid by associations
final ClientChargePaidBy chargePaidBy = ClientChargePaidBy.instance(clientTransaction, clientCharge, waivedAmount.getAmount());
clientTransaction.getClientChargePaidByCollection().add(chargePaidBy);
return new CommandProcessingResultBuilder() //
.withEntityId(clientCharge.getId()) //
.withOfficeId(clientCharge.getClient().getOffice().getId()) //
.withClientId(clientCharge.getClient().getId()) //
.build();
}
@Override
public CommandProcessingResult deleteCharge(Long clientId, Long clientChargeId) {
final Client client = this.clientRepository.getActiveClient(clientId);
final ClientCharge clientCharge = this.clientChargeRepository.findOneWithNotFoundDetection(clientChargeId);
// Validate business rules for charge deletion
validateChargeDeletion(client, clientCharge);
// delete the charge
clientChargeRepository.delete(clientCharge);
return new CommandProcessingResultBuilder() //
.withEntityId(clientCharge.getId()) //
.withOfficeId(clientCharge.getClient().getOffice().getId()) //
.withClientId(clientCharge.getClient().getId()) //
.build();
}
/**
* Validates transaction to ensure that <br>
* charge is active <br>
@@ -159,31 +207,39 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
* @param fmt
* @param transactionDate
* @param amountPaid
* @param requiresTransactionDateValidation
* if set to false, transaction date specific validation is
* skipped
* @param requiresTransactionAmountValidation
* if set to false transaction amount validation is skipped
* @return
*/
private Money validatePaymentDateAndAmount(final Client client, final ClientCharge clientCharge, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal amountPaid) {
private void validatePaymentDateAndAmount(final Client client, final ClientCharge clientCharge, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal amountPaid, final boolean requiresTransactionDateValidation,
final boolean requiresTransactionAmountValidation) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
validateTransactionDateOnWorkingDay(transactionDate, clientCharge, fmt);
if (clientCharge.isNotActive()) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("charge.is.not.active");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (client.getActivationLocalDate() != null && transactionDate.isBefore(client.getActivationLocalDate())) {
baseDataValidator.reset().parameter(ClientApiConstants.transactionDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate");
throw new PlatformApiDataValidationException(dataValidationErrors);
}
if (requiresTransactionDateValidation) {
validateTransactionDateOnWorkingDay(transactionDate, clientCharge, fmt);
if (DateUtils.isDateInTheFuture(transactionDate)) {
baseDataValidator.reset().parameter(ClientApiConstants.transactionDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.is.futureDate");
throw new PlatformApiDataValidationException(dataValidationErrors);
if (client.getActivationLocalDate() != null && transactionDate.isBefore(client.getActivationLocalDate())) {
baseDataValidator.reset().parameter(ClientApiConstants.transactionDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate");
throw new PlatformApiDataValidationException(dataValidationErrors);
}
if (DateUtils.isDateInTheFuture(transactionDate)) {
baseDataValidator.reset().parameter(ClientApiConstants.transactionDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.is.futureDate");
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
// validate charge is not already paid or waived
@@ -195,12 +251,41 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
final Money chargePaid = Money.of(clientCharge.getCurrency(), amountPaid);
if (!clientCharge.getAmountOutstanding(clientCharge.getCurrency()).isGreaterThanOrEqualTo(chargePaid)) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.charge.amount.paid.in.access");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
if (requiresTransactionAmountValidation) {
final Money chargePaid = Money.of(clientCharge.getCurrency(), amountPaid);
if (!clientCharge.getAmountOutstanding().isGreaterThanOrEqualTo(chargePaid)) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.charge.amount.paid.in.access");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
return chargePaid;
}
public void validateWaiverTransaction(final Client client, final ClientCharge clientCharge) {
DateTimeFormatter fmt = null;
LocalDate transactionDate = null;
BigDecimal amountPaid = null;
boolean requiresTransactionDateValidation = false;
boolean requiresTransactionAmountValidation = false;
validatePaymentDateAndAmount(client, clientCharge, fmt, transactionDate, amountPaid, requiresTransactionDateValidation,
requiresTransactionAmountValidation);
}
public void validatePaymentTransaction(final Client client, final ClientCharge clientCharge, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal amountPaid) {
boolean requiresTransactionDateValidation = true;
boolean requiresTransactionAmountValidation = true;
validatePaymentDateAndAmount(client, clientCharge, fmt, transactionDate, amountPaid, requiresTransactionDateValidation,
requiresTransactionAmountValidation);
}
public void validateChargeDeletion(final Client client, final ClientCharge clientCharge) {
DateTimeFormatter fmt = null;
LocalDate transactionDate = null;
BigDecimal amountPaid = null;
boolean requiresTransactionDateValidation = false;
boolean requiresTransactionAmountValidation = false;
validatePaymentDateAndAmount(client, clientCharge, fmt, transactionDate, amountPaid, requiresTransactionDateValidation,
requiresTransactionAmountValidation);
}
/**
@@ -210,19 +295,6 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
@Override
public CommandProcessingResult updateCharge(Long clientId, JsonCommand command) {
this.clientChargeDataValidator.validateAdd(command.json());
final Client client = clientRepository.getActiveClient(clientId);
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;
}
@@ -13,7 +13,10 @@ import org.springframework.transaction.annotation.Transactional;
public interface ClientTransactionReadPlatformService {
@Transactional(readOnly = true)
public Collection<ClientTransactionData> retrieveAllTransactions(final Long clientId);
public Collection<ClientTransactionData> retrieveAllTransactions(Long clientId);
@Transactional(readOnly = true)
public Collection<ClientTransactionData> retrieveAllTransactions(final Long clientId, final Long chargeId);
@Transactional(readOnly = true)
public ClientTransactionData retrieveTransaction(Long clientId, Long transactionId);
@@ -18,9 +18,11 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.client.data.ClientTransactionData;
import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
import org.mifosplatform.portfolio.client.domain.ClientTransactionType;
import org.mifosplatform.portfolio.client.exception.ClientTransactionNotFoundException;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
import org.mifosplatform.portfolio.paymenttype.data.PaymentTypeData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@@ -49,7 +51,7 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti
sqlBuilder.append("tr.transaction_date as transactionDate, tr.amount as transactionAmount, ");
sqlBuilder.append("tr.created_date as submittedOnDate, tr.is_reversed as reversed, ");
sqlBuilder.append("tr.external_id as externalId, o.name as officeName, o.id as officeId, ");
sqlBuilder.append("c.id as clientId, c.account_no as accountNo, ");
sqlBuilder.append("c.id as clientId, c.account_no as accountNo, ccpb.client_charge_id as clientChargeId, ");
sqlBuilder.append("pd.payment_type_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, ");
sqlBuilder.append("pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, ");
sqlBuilder.append(
@@ -63,6 +65,7 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti
sqlBuilder.append("left join m_payment_detail pd on tr.payment_detail_id = pd.id ");
sqlBuilder.append("left join m_payment_type pt on pd.payment_type_id = pt.id ");
sqlBuilder.append("left join m_office o on o.id = tr.office_id ");
sqlBuilder.append("left join m_client_charge_paid_by ccpb on ccpb.client_transaction_id = tr.id ");
this.schemaSql = sqlBuilder.toString();
}
@@ -116,15 +119,32 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti
@Override
public Collection<ClientTransactionData> retrieveAllTransactions(Long clientId) {
final String sql = "select " + this.clientTransactionMapper.schema()
+ " where c.id = ? order by tr.transaction_date DESC, tr.created_date DESC, tr.id DESC";
return this.jdbcTemplate.query(sql, this.clientTransactionMapper, new Object[] { clientId });
Long chargeId = null;
return retrieveAllTransactions(clientId, chargeId);
}
@Override
public Collection<ClientTransactionData> retrieveAllTransactions(Long clientId, Long chargeId) {
Object[] parameters = new Object[1];
String sql = "select " + this.clientTransactionMapper.schema() + " where c.id = ? ";
if (chargeId != null) {
parameters = new Object[2];
parameters[1] = chargeId;
sql = sql + " and ccpb.client_charge_id = ?";
}
parameters[0] = clientId;
sql = sql + " order by tr.transaction_date DESC, tr.created_date DESC, tr.id DESC";
return this.jdbcTemplate.query(sql, this.clientTransactionMapper, parameters);
}
@Override
public ClientTransactionData retrieveTransaction(Long clientId, Long transactionId) {
final String sql = "select " + this.clientTransactionMapper.schema() + " where c.id = ? and tr.id= ?";
return this.jdbcTemplate.queryForObject(sql, this.clientTransactionMapper, new Object[] { clientId, transactionId });
try {
final String sql = "select " + this.clientTransactionMapper.schema() + " where c.id = ? and tr.id= ?";
return this.jdbcTemplate.queryForObject(sql, this.clientTransactionMapper, new Object[] { clientId, transactionId });
} catch (final EmptyResultDataAccessException e) {
throw new ClientTransactionNotFoundException(clientId, transactionId);
}
}
}
@@ -0,0 +1,16 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.client.service;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.transaction.annotation.Transactional;
public interface ClientTransactionWritePlatformService {
@Transactional
CommandProcessingResult undo(Long clientId, Long transactionId);
}
@@ -0,0 +1,76 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.client.service;
import java.util.Set;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.organisation.office.domain.OrganisationCurrencyRepositoryWrapper;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientCharge;
import org.mifosplatform.portfolio.client.domain.ClientChargePaidBy;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.client.domain.ClientTransaction;
import org.mifosplatform.portfolio.client.domain.ClientTransactionRepositoryWrapper;
import org.mifosplatform.portfolio.client.exception.ClientTransactionCannotBeUndoneException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ClientTransactionWritePlatformServiceJpaRepositoryImpl implements ClientTransactionWritePlatformService {
private final ClientTransactionRepositoryWrapper clientTransactionRepository;
private final ClientRepositoryWrapper clientRepository;
private final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository;
@Autowired
public ClientTransactionWritePlatformServiceJpaRepositoryImpl(final ClientTransactionRepositoryWrapper clientTransactionRepository,
final ClientRepositoryWrapper clientRepositoryWrapper,
final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepositoryWrapper) {
this.clientTransactionRepository = clientTransactionRepository;
this.clientRepository = clientRepositoryWrapper;
this.organisationCurrencyRepository = organisationCurrencyRepositoryWrapper;
}
@Override
public CommandProcessingResult undo(Long clientId, Long transactionId) {
final Client client = this.clientRepository.getActiveClient(clientId);
final ClientTransaction clientTransaction = this.clientTransactionRepository.findOneWithNotFoundDetection(clientId, transactionId);
// validate that transaction can be undone
if (clientTransaction.isReversed()) { throw new ClientTransactionCannotBeUndoneException(clientId, transactionId); }
// mark transaction as reversed
clientTransaction.reverse();
// revert any charges paid back to their original state
if (clientTransaction.isPayChargeTransaction() || clientTransaction.isWaiveChargeTransaction()) {
// undo charge
final Set<ClientChargePaidBy> chargesPaidBy = clientTransaction.getClientChargePaidByCollection();
for (final ClientChargePaidBy clientChargePaidBy : chargesPaidBy) {
final ClientCharge clientCharge = clientChargePaidBy.getClientCharge();
clientCharge.setCurrency(
organisationCurrencyRepository.findOneWithNotFoundDetection(clientCharge.getCharge().getCurrencyCode()));
if (clientTransaction.isPayChargeTransaction()) {
clientCharge.undoPayment(clientTransaction.getAmount());
} else if (clientTransaction.isWaiveChargeTransaction()) {
clientCharge.undoWaiver(clientTransaction.getAmount());
}
}
}
return new CommandProcessingResultBuilder() //
.withEntityId(transactionId) //
.withOfficeId(client.officeId()) //
.withClientId(clientId) //
.build();
}
}
@@ -0,0 +1,3 @@
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_client', 'READTRANSACTION_CLIENT', 'CLIENT', 'READTRANSACTION', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_client', 'UNDOTRANSACTION_CLIENT', 'CLIENT', 'UNDOTRANSACTION', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_client', 'UNDOTRANSACTION_CLIENT_CHECKER', 'CLIENT', 'UNDOTRANSACTION_CHECKER', 0);