wip:changes for client fees

This commit is contained in:
Vishwas Babu A J
2015-08-20 01:45:15 +05:30
parent d0fd3e4a6c
commit b6626b0b83
23 changed files with 877 additions and 196 deletions
@@ -9,6 +9,7 @@ import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.springframework.data.jpa.domain.AbstractPersistable;
/**
@@ -54,8 +55,12 @@ public class OrganisationCurrency extends AbstractPersistable<Long> {
this.nameCode = nameCode;
this.displaySymbol = displaySymbol;
}
public final String getCode() {
return code;
return code;
}
public final MonetaryCurrency toMonetaryCurrency() {
return new MonetaryCurrency(this.code, this.decimalPlaces, this.inMultiplesOf);
}
}
@@ -121,7 +121,6 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
}
if (!changes.isEmpty()) {
this.chargeRepository.save(chargeForUpdate);
}
@@ -5,11 +5,16 @@
*/
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 {
@@ -74,6 +79,14 @@ public class ClientApiConstants {
public static final String clientTypeParamName = "clientType";
public static final String clientClassificationIdParamName = "clientClassificationId";
public static final String clientClassificationParamName = "clientClassification";
// request parameters for payment details
public static final String paymentTypeIdParamName = "paymentTypeId";
public static final String transactionAccountNumberParamName = "accountNumber";
public static final String checkNumberParamName = "checkNumber";
public static final String routingCodeParamName = "routingCode";
public static final String receiptNumberParamName = "receiptNumber";
public static final String bankNumberParamName = "bankNumber";
// response parameters
public static final String statusParamName = "status";
public static final String hierarchyParamName = "hierarchy";
@@ -107,6 +120,14 @@ public class ClientApiConstants {
public static final String amountPaidParamName = "amountPaid";
public static final String chargeOptionsParamName = "chargeOptions";
// client transactions response parameters
public static final String transactionAmountParamName = "transactionAmount";
public static final String paymentDetailDataParamName = "paymentDetailData";
public static final String reversedParamName = "reversed";
public static final String dateParamName = "date";
private static final String transactionTypeParamName = "type";
private static final String transactionCurrencyParamName = "currency";
// associations related part of response
public static final String groupsParamName = "groups";
@@ -160,7 +181,12 @@ public class ClientApiConstants {
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));
public static final Set<String> CLIENT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(amountParamName,
transactionDateParamName, dateFormatParamName, localeParamName, paymentTypeIdParamName, transactionAccountNumberParamName,
checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName));
public static final Set<String> CLIENT_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName,
transactionAmountParamName, paymentDetailDataParamName, reversedParamName, dateParamName, officeIdParamName,
officeNameParamName, transactionTypeParamName, transactionCurrencyParamName, externalIdParamName, submittedOnDateParamName));
}
@@ -28,6 +28,7 @@ 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Path("/clients/{clientId}/charges")
@@ -41,6 +42,7 @@ public class ClientChargesApiResource {
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public ClientChargesApiResource(final PlatformSecurityContext context, final ChargeReadPlatformService chargeReadPlatformService,
final ClientChargeReadPlatformService clientChargeReadPlatformService,
final DefaultToApiJsonSerializer<ClientChargeData> toApiJsonSerializer,
@@ -1,17 +0,0 @@
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,78 @@
package org.mifosplatform.portfolio.client.api;
import java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
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.client.data.ClientTransactionData;
import org.mifosplatform.portfolio.client.service.ClientTransactionReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Path("/clients/{clientId}/transactions")
@Component
public class ClientTransactionsApiResource {
private final PlatformSecurityContext context;
private final ClientTransactionReadPlatformService clientTransactionReadPlatformService;
private final DefaultToApiJsonSerializer<ClientTransactionData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public ClientTransactionsApiResource(final PlatformSecurityContext context,
final ClientTransactionReadPlatformService clientTransactionReadPlatformService,
final DefaultToApiJsonSerializer<ClientTransactionData> toApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.context = context;
this.clientTransactionReadPlatformService = clientTransactionReadPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllClientTransactions(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
final Collection<ClientTransactionData> clientTransactions = this.clientTransactionReadPlatformService
.retrieveAllTransactions(clientId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, clientTransactions,
ClientApiConstants.CLIENT_TRANSACTION_RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveClientTransaction(@PathParam("clientId") final Long clientId,
@PathParam("transactionId") final Long transactionId, @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
final ClientTransactionData clientTransaction = this.clientTransactionReadPlatformService.retrieveTransaction(clientId,
transactionId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, clientTransaction,
ClientApiConstants.CLIENT_TRANSACTION_RESPONSE_DATA_PARAMETERS);
}
}
@@ -0,0 +1,56 @@
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.paymentdetail.data.PaymentDetailData;
import org.mifosplatform.portfolio.paymenttype.data.PaymentTypeData;
@SuppressWarnings("unused")
public class ClientTransactionData {
private final Long id;
private final Long officeId;
private final String officeName;
private final EnumOptionData type;
private final LocalDate date;
private final CurrencyData currency;
private final PaymentDetailData paymentDetailData;
private final BigDecimal amount;
private final String externalId;
private final LocalDate submittedOnDate;
private final boolean reversed;
// templates
final Collection<PaymentTypeData> paymentTypeOptions;
public static ClientTransactionData create(Long id, Long officeId, String officeName, EnumOptionData type, LocalDate date,
CurrencyData currency, PaymentDetailData paymentDetailData, BigDecimal amount, String externalId, LocalDate submittedOnDate,
boolean reversed) {
final Collection<PaymentTypeData> paymentTypeOptions = null;
return new ClientTransactionData(id, officeId, officeName, type, date, currency, paymentDetailData, amount, externalId,
submittedOnDate, reversed, paymentTypeOptions);
}
private ClientTransactionData(Long id, Long officeId, String officeName, EnumOptionData type, LocalDate date, CurrencyData currency,
PaymentDetailData paymentDetailData, BigDecimal amount, String externalId, LocalDate submittedOnDate, boolean reversed,
Collection<PaymentTypeData> paymentTypeOptions) {
super();
this.id = id;
this.officeId = officeId;
this.officeName = officeName;
this.type = type;
this.date = date;
this.currency = currency;
this.paymentDetailData = paymentDetailData;
this.amount = amount;
this.externalId = externalId;
this.submittedOnDate = submittedOnDate;
this.reversed = reversed;
this.paymentTypeOptions = paymentTypeOptions;
}
}
@@ -1,7 +1,12 @@
package org.mifosplatform.portfolio.client.domain;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -12,11 +17,21 @@ import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
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.OrganisationCurrency;
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.mifosplatform.portfolio.savings.domain.SavingsAccountCharge;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@@ -72,6 +87,8 @@ public class ClientCharge extends AbstractPersistable<Long> {
@Column(name = "inactivated_on_date")
private Date inactivationDate;
private OrganisationCurrency currency;
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);
@@ -101,6 +118,17 @@ 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);
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);
}
private void populateDerivedFields(final BigDecimal amount) {
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
case INVALID:
@@ -152,6 +180,14 @@ public class ClientCharge extends AbstractPersistable<Long> {
return this.amount.subtract(totalAccountedFor);
}
public LocalDate getDueLocalDate() {
LocalDate dueDate = null;
if (this.dueDate != null) {
dueDate = new LocalDate(this.dueDate);
}
return dueDate;
}
public Client getClient() {
return this.client;
}
@@ -172,26 +208,6 @@ public class ClientCharge extends AbstractPersistable<Long> {
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;
}
@@ -204,10 +220,14 @@ public class ClientCharge extends AbstractPersistable<Long> {
return this.waived;
}
public boolean isStatus() {
public boolean isActive() {
return this.status;
}
public boolean isNotActive() {
return !this.status;
}
public Date getInactivationDate() {
return this.inactivationDate;
}
@@ -220,4 +240,37 @@ public class ClientCharge extends AbstractPersistable<Long> {
return this.client.getOffice().getId();
}
public void setCurrency(OrganisationCurrency currency) {
this.currency = currency;
}
public MonetaryCurrency getCurrency() {
return this.currency.toMonetaryCurrency();
}
public boolean isPaidOrPartiallyPaid(final MonetaryCurrency currency) {
final Money amountWaivedOrWrittenOff = getAmountWaived(currency).plus(getAmountWrittenOff(currency));
return Money.of(currency, this.amountPaid).plus(amountWaivedOrWrittenOff).isGreaterThanZero();
}
public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount);
}
public Money getAmountPaid(final MonetaryCurrency currency) {
return Money.of(currency, this.amountPaid);
}
public Money getAmountWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.amountWaived);
}
public Money getAmountWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.amountWrittenOff);
}
public Money getAmountOutstanding(final MonetaryCurrency currency) {
return Money.of(currency, this.amountOutstanding);
}
}
@@ -29,6 +29,11 @@ public class ClientChargePaidBy extends AbstractPersistable<Long> {
}
public static ClientChargePaidBy instance(final ClientTransaction clientTransaction, final ClientCharge clientCharge,
final BigDecimal amount) {
return new ClientChargePaidBy(clientTransaction, clientCharge, amount);
}
public ClientChargePaidBy(final ClientTransaction clientTransaction, final ClientCharge clientCharge, final BigDecimal amount) {
this.clientTransaction = clientTransaction;
this.clientCharge = clientCharge;
@@ -0,0 +1,39 @@
package org.mifosplatform.portfolio.client.domain;
import org.mifosplatform.organisation.office.domain.OrganisationCurrencyRepositoryWrapper;
import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
public class ClientChargeRepositoryWrapper {
private final ClientChargeRepository repository;
private final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository;
@Autowired
public ClientChargeRepositoryWrapper(final ClientChargeRepository repository,
final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepositoryWrapper) {
this.repository = repository;
this.organisationCurrencyRepository = organisationCurrencyRepositoryWrapper;
}
public ClientCharge findOneWithNotFoundDetection(final Long id) {
final ClientCharge clientCharge = this.repository.findOne(id);
if (clientCharge == null) { throw new ChargeNotFoundException(id); }
// enrich Client charge with details of Organizational currency
clientCharge.setCurrency(organisationCurrencyRepository.findOneWithNotFoundDetection(clientCharge.getCharge().getCurrencyCode()));
return clientCharge;
}
public void save(final ClientCharge clientCharge) {
this.repository.save(clientCharge);
}
public void saveAndFlush(final ClientCharge clientCharge) {
this.repository.saveAndFlush(clientCharge);
}
public void delete(final ClientCharge clientCharge) {
this.repository.delete(clientCharge);
}
}
@@ -5,6 +5,10 @@
*/
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 {
@@ -33,7 +37,8 @@ public class ClientEnumerations {
optionData = new EnumOptionData(ClientStatus.REJECTED.getValue().longValue(), ClientStatus.REJECTED.getCode(), "Rejected");
break;
case WITHDRAWN:
optionData = new EnumOptionData(ClientStatus.WITHDRAWN.getValue().longValue(), ClientStatus.WITHDRAWN.getCode(), "Withdrawn");
optionData = new EnumOptionData(ClientStatus.WITHDRAWN.getValue().longValue(), ClientStatus.WITHDRAWN.getCode(),
"Withdrawn");
break;
case TRANSFER_IN_PROGRESS:
optionData = new EnumOptionData(ClientStatus.TRANSFER_IN_PROGRESS.getValue().longValue(),
@@ -49,4 +54,23 @@ public class ClientEnumerations {
return optionData;
}
public static EnumOptionData clientTransactionType(final int id) {
return clientTransactionType(ClientTransactionType.fromInt(id));
}
public static EnumOptionData clientTransactionType(final ClientTransactionType clientTransactionType) {
final EnumOptionData optionData = new EnumOptionData(clientTransactionType.getValue().longValue(), clientTransactionType.getCode(),
clientTransactionType.toString());
return optionData;
}
public static List<EnumOptionData> clientTransactionType(final ClientTransactionType[] clientTransactionTypes) {
final List<EnumOptionData> optionDatas = new ArrayList<>();
for (final ClientTransactionType clientTransaction : clientTransactionTypes) {
optionDatas.add(clientTransactionType(clientTransaction));
}
return optionDatas;
}
}
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.client.domain;
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -42,4 +43,10 @@ public class ClientRepositoryWrapper {
public void delete(final Client client) {
this.repository.delete(client);
}
public Client getActiveClient(Long clientId) {
final Client client = this.findOneWithNotFoundDetection(clientId);
if (client.isNotActive()) { throw new ClientNotActiveException(client.getId()); }
return client;
}
}
@@ -18,9 +18,10 @@ 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.Money;
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;
@@ -41,16 +42,15 @@ public class ClientTransaction extends AbstractPersistable<Long> {
@JoinColumn(name = "payment_detail_id", nullable = true)
private PaymentDetail paymentDetail;
@Column(name = "currency_code", length = 3)
private String currencyCode;
@Column(name = "transaction_type_enum", nullable = false)
private final Integer typeOf;
private 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;
private Date dateOf;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@@ -63,13 +63,51 @@ public class ClientTransaction extends AbstractPersistable<Long> {
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_date", nullable = false)
private final Date createdDate;
private Date createdDate;
@ManyToOne
@JoinColumn(name = "appuser_id", nullable = true)
private final AppUser appUser;
private AppUser appUser;
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction", orphanRemoval = true)
private Set<LoanChargePaidBy> loanChargesPaid = new HashSet<>();
@OneToMany(cascade = CascadeType.ALL, mappedBy = "clientTransaction", orphanRemoval = true)
private Set<ClientChargePaidBy> clientChargePaidByCollection = new HashSet<>();
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;
final String externalId = null;
return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.PAY_CHARGE.getValue(), date, amount, isReversed,
externalId, DateUtils.getDateOfTenant(), currencyCode, appUser);
}
public static ClientTransaction waiver(final Client client, final Office office, final LocalDate date, final Money amount,
final String currencyCode, final AppUser appUser) {
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,
externalId, DateUtils.getDateOfTenant(), currencyCode, appUser);
}
public ClientTransaction(Client client, Office office, PaymentDetail paymentDetail, Integer typeOf, LocalDate transactionLocalDate,
Money amount, boolean reversed, String externalId, Date createdDate, String currencyCode, AppUser appUser) {
super();
this.client = client;
this.office = office;
this.paymentDetail = paymentDetail;
this.typeOf = typeOf;
this.dateOf = transactionLocalDate.toDate();
this.amount = amount.getAmount();
this.reversed = reversed;
this.externalId = externalId;
this.createdDate = createdDate;
this.currencyCode = currencyCode;
this.appUser = appUser;
}
public Set<ClientChargePaidBy> getClientChargePaidByCollection() {
return this.clientChargePaidByCollection;
}
}
@@ -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 ClientTransactionRepository extends JpaRepository<ClientTransaction, Long>, JpaSpecificationExecutor<ClientTransaction> {
}
@@ -0,0 +1,66 @@
package org.mifosplatform.portfolio.client.domain;
import java.util.HashMap;
import java.util.Map;
public enum ClientTransactionType {
PAY_CHARGE(1, "clientTransactionType.payCharge"), //
WAIVE_CHARGE(2, "clientTransactionType.waiveCharge");
private final Integer value;
private final String code;
private ClientTransactionType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
private static final Map<Integer, ClientTransactionType> intToEnumMap = new HashMap<>();
private static int minValue;
private static int maxValue;
static {
int i = 0;
for (final ClientTransactionType type : ClientTransactionType.values()) {
if (i == 0) {
minValue = type.value;
}
intToEnumMap.put(type.value, type);
if (minValue >= type.value) {
minValue = type.value;
}
if (maxValue < type.value) {
maxValue = type.value;
}
i = i + 1;
}
}
public static ClientTransactionType fromInt(final int i) {
final ClientTransactionType type = intToEnumMap.get(Integer.valueOf(i));
return type;
}
public static int getMinValue() {
return minValue;
}
public static int getMaxValue() {
return maxValue;
}
@Override
public String toString() {
return name().toString();
}
}
@@ -17,6 +17,7 @@ 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.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@@ -30,6 +31,7 @@ public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlat
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
private final DropdownReadPlatformService dropdownReadPlatformService;
@Autowired
public ClientChargeReadPlatformServiceImpl(final PlatformSecurityContext context,
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource,
final DropdownReadPlatformService dropdownReadPlatformService) {
@@ -1,9 +1,13 @@
package org.mifosplatform.portfolio.client.service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
@@ -13,9 +17,10 @@ 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.core.service.DateUtils;
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.monetary.domain.Money;
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.mifosplatform.portfolio.charge.domain.Charge;
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
@@ -23,13 +28,14 @@ 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.ClientChargePaidBy;
import org.mifosplatform.portfolio.client.domain.ClientChargeRepositoryWrapper;
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.mifosplatform.portfolio.client.domain.ClientTransaction;
import org.mifosplatform.portfolio.client.domain.ClientTransactionRepository;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -43,14 +49,17 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
private final ConfigurationDomainService configurationDomainService;
private final HolidayRepositoryWrapper holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final ClientChargeRepository clientChargeRepository;
private final ClientChargeRepositoryWrapper clientChargeRepository;
private final ClientTransactionRepository clientTransactionRepository;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
@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) {
final ConfigurationDomainService configurationDomainService, final ClientChargeRepositoryWrapper clientChargeRepository,
final WorkingDaysRepositoryWrapper workingDaysRepository, final ClientTransactionRepository clientTransactionRepository,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService) {
this.context = context;
this.chargeRepository = chargeRepository;
this.clientChargeDataValidator = clientChargeDataValidator;
@@ -59,6 +68,8 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
this.configurationDomainService = configurationDomainService;
this.clientChargeRepository = clientChargeRepository;
this.workingDaysRepository = workingDaysRepository;
this.clientTransactionRepository = clientTransactionRepository;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
}
@Override
@@ -66,17 +77,14 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
this.clientChargeDataValidator.validateAdd(command.json());
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
checkClientActive(client);
final Client client = clientRepository.getActiveClient(clientId);
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);
@@ -88,37 +96,109 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
.build();
}
@Override
public CommandProcessingResult payCharge(Long clientId, Long clientChargeId, JsonCommand command) {
this.clientChargeDataValidator.validateAdd(command.json());
final Client client = this.clientRepository.getActiveClient(clientId);
final ClientCharge clientCharge = this.clientChargeRepository.findOneWithNotFoundDetection(clientChargeId);
final Locale locale = command.extractLocale();
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 = validatePaymentDateAndAmount(client, clientCharge, fmt, transactionDate, amountPaid);
// pay the charge
clientCharge.pay(clientCharge.getCurrency(), chargePaid);
// create Payment Transaction
final Map<String, Object> changes = new LinkedHashMap<>();
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
ClientTransaction clientTransaction = ClientTransaction.payCharge(client, client.getOffice(), paymentDetail, transactionDate,
chargePaid, clientCharge.getCurrency().getCode(), getAppUserIfPresent());
this.clientTransactionRepository.save(clientTransaction);
// update charge paid by associations
final ClientChargePaidBy chargePaidBy = ClientChargePaidBy.instance(clientTransaction, clientCharge, amountPaid);
clientTransaction.getClientChargePaidByCollection().add(chargePaidBy);
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
* Validates transaction to ensure that <br>
* charge is active <br>
* transaction date is valid (between client activation and todays date)
* <br>
* charge is not already paid or waived <br>
* amount is not more than total due
*
* @param savingsAccountCharge
* @param client
* @param clientCharge
* @param fmt
* @param transactionDate
* @param amountPaid
* @return
*/
private void validateDueDateOnWorkingDay(final SavingsAccountCharge savingsAccountCharge, final DateTimeFormatter fmt) {
private Money validatePaymentDateAndAmount(final Client client, final ClientCharge clientCharge, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal amountPaid) {
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); }
}
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 (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
if (clientCharge.isWaived()) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.already.waived");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
} else if (clientCharge.isPaid()) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.paid");
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); }
}
return chargePaid;
}
/**
* @param clientId
* @return
*/
@Override
public CommandProcessingResult updateCharge(Long clientId, JsonCommand command) {
// TODO Auto-generated method stub
this.clientChargeDataValidator.validateAdd(command.json());
final Client client = clientRepository.getActiveClient(clientId);
return null;
}
@@ -134,22 +214,73 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
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()); }
/**
* Ensures that the charge due date is not on a holiday or a non working day
*
* @param clientCharge
* @param fmt
*/
private void validateDueDateOnWorkingDay(final ClientCharge clientCharge, final DateTimeFormatter fmt) {
validateActivityDateFallOnAWorkingDay(clientCharge.getDueLocalDate(), clientCharge.getOfficeId(),
ClientApiConstants.dueAsOfDateParamName, "charge.due.date.is.on.holiday", "charge.due.date.is.a.non.workingday", fmt);
}
/**
* Ensures that the charge transaction date (for payments) is not on a
* holiday or a non working day
*
* @param savingsAccountCharge
* @param fmt
*/
private void validateTransactionDateOnWorkingDay(final LocalDate transactionDate, final ClientCharge clientCharge,
final DateTimeFormatter fmt) {
validateActivityDateFallOnAWorkingDay(transactionDate, clientCharge.getOfficeId(), ClientApiConstants.transactionDateParamName,
"transaction.not.allowed.transaction.date.is.on.holiday", "transaction.not.allowed.transaction.date.is.a.non.workingday",
fmt);
}
/**
* @param date
* @param officeId
* @param jsonPropertyName
* @param errorMessageFragment
* @param fmt
*/
private void validateActivityDateFallOnAWorkingDay(final LocalDate date, final Long officeId, final String jsonPropertyName,
final String errorMessageFragmentForActivityOnHoliday, final String errorMessageFragmentForActivityOnNonWorkingDay,
final DateTimeFormatter fmt) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
if (date != null) {
// transaction date should not be on a holiday or non working day
if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() && this.holidayRepository.isHoliday(officeId, date)) {
baseDataValidator.reset().parameter(jsonPropertyName).value(date.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode(errorMessageFragmentForActivityOnHoliday);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled()
&& !this.workingDaysRepository.isWorkingDay(date)) {
baseDataValidator.reset().parameter(jsonPropertyName).value(date.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode(errorMessageFragmentForActivityOnNonWorkingDay);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
}
private AppUser getAppUserIfPresent() {
AppUser user = null;
if (this.context != null) {
user = this.context.getAuthenticatedUserIfPresent();
}
return user;
}
}
@@ -0,0 +1,16 @@
package org.mifosplatform.portfolio.client.service;
import java.util.Collection;
import org.mifosplatform.portfolio.client.data.ClientTransactionData;
import org.springframework.transaction.annotation.Transactional;
public interface ClientTransactionReadPlatformService {
@Transactional(readOnly = true)
public Collection<ClientTransactionData> retrieveAllTransactions(final Long clientId);
@Transactional(readOnly = true)
public ClientTransactionData retrieveTransaction(Long clientId, Long transactionId);
}
@@ -0,0 +1,123 @@
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.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.paymentdetail.data.PaymentDetailData;
import org.mifosplatform.portfolio.paymenttype.data.PaymentTypeData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
public class ClientTransactionReadPlatformServiceImpl implements ClientTransactionReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final ClientTransactionMapper clientTransactionMapper;
@Autowired
public ClientTransactionReadPlatformServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.clientTransactionMapper = new ClientTransactionMapper();
}
private static final class ClientTransactionMapper implements RowMapper<ClientTransactionData> {
private final String schemaSql;
public ClientTransactionMapper() {
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("tr.id as transactionId, tr.transaction_type_enum as transactionType, ");
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("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(
"tr.currency_code as currencyCode, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
sqlBuilder.append("pt.value as paymentTypeName ");
sqlBuilder.append("from m_client c ");
sqlBuilder.append("join m_client_transaction tr on tr.client_id = c.id ");
sqlBuilder.append("join m_currency curr on curr.code = tr.currency_code ");
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 ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public ClientTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("transactionId");
final Long officeId = rs.getLong("officeId");
final String officeName = rs.getString("officeName");
final String externalId = rs.getString("externalId");
final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType");
final EnumOptionData transactionType = ClientEnumerations.clientTransactionType(transactionTypeInt);
final LocalDate date = JdbcSupport.getLocalDate(rs, "transactionDate");
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "transactionAmount");
final boolean reversed = rs.getBoolean("reversed");
PaymentDetailData paymentDetailData = null;
if (ClientTransactionType.fromInt(transactionType.getId().intValue()).equals(ClientTransactionType.PAY_CHARGE)) {
final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentType");
if (paymentTypeId != null) {
final String typeName = rs.getString("paymentTypeName");
final PaymentTypeData paymentType = PaymentTypeData.instance(paymentTypeId, typeName);
final String accountNumber = rs.getString("accountNumber");
final String checkNumber = rs.getString("checkNumber");
final String routingCode = rs.getString("routingCode");
final String receiptNumber = rs.getString("receiptNumber");
final String bankNumber = rs.getString("bankNumber");
paymentDetailData = new PaymentDetailData(id, paymentType, accountNumber, checkNumber, routingCode, receiptNumber,
bankNumber);
}
}
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 currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol,
currencyNameCode);
return ClientTransactionData.create(id, officeId, officeName, transactionType, date, currency, paymentDetailData, amount,
externalId, submittedOnDate, reversed);
}
}
@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 });
}
@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 });
}
}
@@ -2208,7 +2208,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
if (DateUtils.isDateInTheFuture(transactionDate)) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getSubmittedOnLocalDate().toString(formatter))
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(formatter))
.failWithCodeNoParameterAddedToErrorCode("transaction.is.futureDate");
throw new PlatformApiDataValidationException(dataValidationErrors);
}
@@ -41,6 +41,7 @@ import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
import org.mifosplatform.infrastructure.jobs.service.JobName;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.holiday.domain.HolidayRepositoryWrapper;
import org.mifosplatform.organisation.holiday.service.HolidayWritePlatformService;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
@@ -48,6 +49,7 @@ 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.staff.domain.Staff;
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.mifosplatform.organisation.workingdays.service.WorkingDaysWritePlatformService;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.data.AccountTransferDTO;
@@ -119,13 +121,13 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
private final ChargeRepositoryWrapper chargeRepository;
private final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository;
private final HolidayWritePlatformService holidayWritePlatformService;
private final WorkingDaysWritePlatformService workingDaysWritePlatformService;
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
private final DepositAccountReadPlatformService depositAccountReadPlatformService;
private final CalendarInstanceRepository calendarInstanceRepository;
private final ConfigurationDomainService configurationDomainService;
private final HolidayRepositoryWrapper holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
@Autowired
public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -139,9 +141,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final JournalEntryWritePlatformService journalEntryWritePlatformService,
final DepositAccountDomainService depositAccountDomainService, final NoteRepository noteRepository,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService, final ChargeRepositoryWrapper chargeRepository,
final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository,
final HolidayWritePlatformService holidayWritePlatformService,
final WorkingDaysWritePlatformService workingDaysWritePlatformService,
final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository, final HolidayRepositoryWrapper holidayRepository,
final WorkingDaysRepositoryWrapper workingDaysRepository,
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final DepositAccountReadPlatformService depositAccountReadPlatformService,
@@ -161,8 +162,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
this.chargeRepository = chargeRepository;
this.savingsAccountChargeRepository = savingsAccountChargeRepository;
this.holidayWritePlatformService = holidayWritePlatformService;
this.workingDaysWritePlatformService = workingDaysWritePlatformService;
this.holidayRepository = holidayRepository;
this.workingDaysRepository = workingDaysRepository;
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
this.depositAccountReadPlatformService = depositAccountReadPlatformService;
@@ -318,7 +319,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final boolean isInterestTransfer = false;
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
@@ -364,8 +366,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate depositAmountUpdateEffectiveFromDate = command
.localDateValueOfParameterNamed(DepositsApiConstants.effectiveDateParamName);
final RecurringDepositAccount recurringDepositAccount = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(
savingsId, DepositAccountType.RECURRING_DEPOSIT);
final RecurringDepositAccount recurringDepositAccount = (RecurringDepositAccount) this.depositAccountAssembler
.assembleFrom(savingsId, DepositAccountType.RECURRING_DEPOSIT);
DepositAccountRecurringDetail recurringDetail = recurringDepositAccount.getRecurringDetail();
Map<String, Object> changes = recurringDetail.updateMandatoryRecommendedDepositAmount(mandatoryRecommendedDepositAmount,
depositAmountUpdateEffectiveFromDate, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
@@ -419,7 +421,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Transactional
@Override
public CommandProcessingResult withdrawal(final Long savingsId, final JsonCommand command, final DepositAccountType depositAccountType) {
public CommandProcessingResult withdrawal(final Long savingsId, final JsonCommand command,
final DepositAccountType depositAccountType) {
boolean isRegularTransaction = true;
@@ -560,10 +563,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.findOneByIdAndSavingsAccountId(transactionId, savingsId);
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
if (!allowAccountTransferModification
&& this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.recurring.deposit.account.transfer.transaction.update.not.allowed", "Recurring deposit account transaction:"
+ transactionId + " update not allowed as it involves in account transfer", transactionId); }
if (!allowAccountTransferModification && this.accountTransfersReadPlatformService.isAccountTransfer(transactionId,
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.recurring.deposit.account.transfer.transaction.update.not.allowed",
"Recurring deposit account transaction:" + transactionId + " update not allowed as it involves in account transfer",
transactionId); }
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = MathContext.DECIMAL64;
@@ -584,7 +588,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction);
// account.activateAccountBasedOnBalance();
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant();
@@ -627,12 +632,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.findOneByIdAndSavingsAccountId(transactionId, savingsId);
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(
savingsId, transactionId); }
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal())
|| savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(savingsId, transactionId); }
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Deposit account transaction:" + transactionId
+ " update not allowed as it involves in account transfer", transactionId); }
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId,
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transfer.transaction.update.not.allowed",
"Deposit account transaction:" + transactionId + " update not allowed as it involves in account transfer",
transactionId); }
final LocalDate today = DateUtils.getLocalDateOfTenant();
@@ -995,15 +1002,15 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
if (savingsAccountCharge.getDueLocalDate() != null) {
// transaction date should not be on a holiday or non working day
if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday()
&& this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) {
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.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay()
&& !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) {
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); }
@@ -1050,15 +1057,15 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
// transaction date should not be on a holiday or non working day
if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday()
&& this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) {
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.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay()
&& !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) {
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); }
@@ -1088,8 +1095,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
// Get Savings account from savings charge
final SavingsAccount account = savingsAccountCharge.savingsAccount();
@@ -1134,8 +1141,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsAccountId, depositAccountType);
checkClientOrGroupActive(savingsAccount);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
savingsAccount.removeCharge(savingsAccountCharge);
this.savingAccountRepository.saveAndFlush(savingsAccount);
@@ -1161,23 +1168,23 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(amountParamName);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
// transaction date should not be on a holiday or non working day
if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday()
&& this.holidayWritePlatformService.isHoliday(savingsAccountCharge.savingsAccount().officeId(), transactionDate)) {
if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled()
&& this.holidayRepository.isHoliday(savingsAccountCharge.savingsAccount().officeId(), transactionDate)) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.on.holiday");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay()
&& !this.workingDaysWritePlatformService.isWorkingDay(transactionDate)) {
if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled()
&& !this.workingDaysRepository.isWorkingDay(transactionDate)) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.a.nonworking.day");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
@@ -1200,8 +1207,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@SuppressWarnings("unused") final DepositAccountType depositAccountType) {
// always use current date as transaction date for batch job
final LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, accountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId);
final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
@@ -1289,8 +1296,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override
public SavingsAccountTransaction mandatorySavingsAccountDeposit(final SavingsAccountTransactionDTO accountTransactionDTO) {
boolean isRegularTransaction = false;
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(
accountTransactionDTO.getSavingsAccountId(), DepositAccountType.RECURRING_DEPOSIT);
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler
.assembleFrom(accountTransactionDTO.getSavingsAccountId(), DepositAccountType.RECURRING_DEPOSIT);
final PaymentDetail paymentDetail = accountTransactionDTO.getPaymentDetail();
if (paymentDetail != null && paymentDetail.getId() == null) {
this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail);
@@ -40,7 +40,7 @@ import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
import org.mifosplatform.infrastructure.jobs.service.JobName;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.holiday.service.HolidayWritePlatformService;
import org.mifosplatform.organisation.holiday.domain.HolidayRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
@@ -48,7 +48,7 @@ import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper;
import org.mifosplatform.organisation.workingdays.service.WorkingDaysWritePlatformService;
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.service.AccountAssociationsReadPlatformService;
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
@@ -111,8 +111,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
private final ChargeRepositoryWrapper chargeRepository;
private final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository;
private final HolidayWritePlatformService holidayWritePlatformService;
private final WorkingDaysWritePlatformService workingDaysWritePlatformService;
private final HolidayRepositoryWrapper holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final ConfigurationDomainService configurationDomainService;
@Autowired
@@ -126,11 +126,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
final JournalEntryWritePlatformService journalEntryWritePlatformService,
final SavingsAccountDomainService savingsAccountDomainService, final NoteRepository noteRepository,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService, final HolidayRepositoryWrapper holidayRepository,
final WorkingDaysRepositoryWrapper workingDaysRepository,
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final ChargeRepositoryWrapper chargeRepository, final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository,
final HolidayWritePlatformService holidayWritePlatformService,
final WorkingDaysWritePlatformService workingDaysWritePlatformService,
final SavingsAccountDataValidator fromApiJsonDeserializer, final SavingsAccountRepositoryWrapper savingsRepository,
final StaffRepositoryWrapper staffRepository, final ConfigurationDomainService configurationDomainService) {
this.context = context;
@@ -148,8 +147,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
this.chargeRepository = chargeRepository;
this.savingsAccountChargeRepository = savingsAccountChargeRepository;
this.holidayWritePlatformService = holidayWritePlatformService;
this.workingDaysWritePlatformService = workingDaysWritePlatformService;
this.holidayRepository = holidayRepository;
this.workingDaysRepository = workingDaysRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.savingsRepository = savingsRepository;
this.staffRepository = staffRepository;
@@ -296,8 +295,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
AppUser user = getAppUserIfPresent();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, accountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId);
final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
@@ -389,8 +388,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@CronTarget(jobName = JobName.POST_INTEREST_FOR_SAVINGS)
@Override
public void postInterestForAccounts() {
final List<SavingsAccount> savingsAccounts = this.savingAccountRepository.findSavingAccountByStatus(SavingsAccountStatusType.ACTIVE
.getValue());
final List<SavingsAccount> savingsAccounts = this.savingAccountRepository
.findSavingAccountByStatus(SavingsAccountStatusType.ACTIVE.getValue());
for (final SavingsAccount savingsAccount : savingsAccounts) {
this.savingAccountAssembler.assignSavingAccountHelpers(savingsAccount);
postInterest(savingsAccount);
@@ -415,13 +414,16 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
if (!allowAccountTransferModification
&& this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings account transaction:" + transactionId
+ " update not allowed as it involves in account transfer", transactionId); }
&& this.accountTransfersReadPlatformService
.isAccountTransfer(transactionId,
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings account transaction:"
+ transactionId + " update not allowed as it involves in account transfer",
transactionId); }
if (!account.allowModify()) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transaction.update.not.allowed", "Savings account transaction:" + transactionId
+ " update not allowed for this savings type", transactionId); }
if (!account
.allowModify()) { throw new PlatformServiceUnavailableException("error.msg.saving.account.transaction.update.not.allowed",
"Savings account transaction:" + transactionId + " update not allowed for this savings type", transactionId); }
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN);
@@ -475,12 +477,14 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.findOneByIdAndSavingsAccountId(transactionId, savingsId);
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(
savingsId, transactionId); }
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal())
|| savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(savingsId, transactionId); }
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings account transaction:" + transactionId
+ " update not allowed as it involves in account transfer", transactionId); }
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId,
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transfer.transaction.update.not.allowed",
"Savings account transaction:" + transactionId + " update not allowed as it involves in account transfer",
transactionId); }
this.savingsAccountTransactionDataValidator.validate(command);
@@ -490,9 +494,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (account.isNotActive()) {
throwValidationForActiveStatus(SavingsApiConstants.adjustTransactionAction);
}
if (!account.allowModify()) { throw new PlatformServiceUnavailableException(
"error.msg.saving.account.transaction.update.not.allowed", "Savings account transaction:" + transactionId
+ " update not allowed for this savings type", transactionId); }
if (!account
.allowModify()) { throw new PlatformServiceUnavailableException("error.msg.saving.account.transaction.update.not.allowed",
"Savings account transaction:" + transactionId + " update not allowed for this savings type", transactionId); }
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
@@ -762,15 +766,15 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (savingsAccountCharge.getDueLocalDate() != null) {
// transaction date should not be on a holiday or non working day
if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday()
&& this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) {
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.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay()
&& !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) {
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); }
@@ -817,15 +821,15 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
// transaction date should not be on a holiday or non working day
if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday()
&& this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) {
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.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay()
&& !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) {
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); }
@@ -854,8 +858,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
// Get Savings account from savings charge
final SavingsAccount account = savingsAccountCharge.savingsAccount();
@@ -900,8 +904,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId);
checkClientOrGroupActive(savingsAccount);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
savingsAccount.removeCharge(savingsAccountCharge);
this.savingAccountRepository.saveAndFlush(savingsAccount);
@@ -926,23 +930,23 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(amountParamName);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
// transaction date should not be on a holiday or non working day
if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday()
&& this.holidayWritePlatformService.isHoliday(savingsAccountCharge.savingsAccount().officeId(), transactionDate)) {
if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled()
&& this.holidayRepository.isHoliday(savingsAccountCharge.savingsAccount().officeId(), transactionDate)) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.on.holiday");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay()
&& !this.workingDaysWritePlatformService.isWorkingDay(transactionDate)) {
if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled()
&& !this.workingDaysRepository.isWorkingDay(transactionDate)) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.a.nonworking.day");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
@@ -966,8 +970,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
AppUser user = null;
final LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, accountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId);
final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
fmt.withZone(DateUtils.getDateTimeZoneOfTenant());
@@ -1032,8 +1036,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.context.authenticatedUser();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
savingsAccountChargeId, savingsAccountId);
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
final SavingsAccount account = savingsAccountCharge.savingsAccount();
this.savingAccountAssembler.assignSavingAccountHelpers(account);
@@ -1111,16 +1115,16 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final LocalDate dateOfSavingsOfficerAssignment = command.localDateValueOfParameterNamed("assignmentDate");
if (fromSavingsOfficerId != null) {
fromSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(fromSavingsOfficerId, savingsForUpdate
.office().getHierarchy());
fromSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(fromSavingsOfficerId,
savingsForUpdate.office().getHierarchy());
}
if (toSavingsOfficerId != null) {
toSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(toSavingsOfficerId, savingsForUpdate
.office().getHierarchy());
toSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(toSavingsOfficerId,
savingsForUpdate.office().getHierarchy());
actualChanges.put("toSavingsOfficerId", toSavingsOfficer.getId());
}
if (!savingsForUpdate.hasSavingsOfficer(fromSavingsOfficer)) { throw new SavingsOfficerAssignmentException(savingsAccountId,
fromSavingsOfficerId); }
if (!savingsForUpdate.hasSavingsOfficer(
fromSavingsOfficer)) { throw new SavingsOfficerAssignmentException(savingsAccountId, fromSavingsOfficerId); }
savingsForUpdate.reassignSavingsOfficer(toSavingsOfficer, dateOfSavingsOfficerAssignment);
@@ -0,0 +1,9 @@
-- add permissions for Client Fees
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'CREATE_CLIENTCHARGE', 'CLIENTCHARGE', 'CREATE', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'DELETE_CLIENTCHARGE', 'CLIENTCHARGE', 'DELETE', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'WAIVE_CLIENTCHARGE', 'CLIENTCHARGE', 'WAIVE', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'PAY_CLIENTCHARGE', 'CLIENTCHARGE', 'PAY', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'INACTIVATE_CLIENTCHARGE', 'CLIENTCHARGE', 'INACTIVATE', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'UPDATE_CLIENTCHARGE', 'CLIENTCHARGE', 'UPDATE', 0);