You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by GitBox <gi...@apache.org> on 2018/11/16 09:48:01 UTC

[GitHub] ShruthiRajaram closed pull request #471: FINERACT-627 back dated client transfer

ShruthiRajaram closed pull request #471: FINERACT-627 back dated client transfer
URL: https://github.com/apache/fineract/pull/471
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index c182e6588..00751bc2c 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -5761,7 +5761,7 @@ <h4>Update Default Savings Account</h4>
 	<div class="method-section">
 	    <div class="method-description">
 	        <h4>Propose a Client Transfer</h4>
-	        <p>Allows you to propose the transfer of a <i>Client</i> to a different <i>Office</i>.</p>
+	        <p>Allows you to propose the transfer of a <i>Client</i> to a different <i>Office</i> on a specific date, if loan or savings transaction not present from proposed transfer date to current date.</p>
 	    </div>
 		<div class="method-example">
 	        <code class="method-declaration">POST https://Domain Name/api/v1/clients/{clientId}?command=proposeTransfer</code>
@@ -5769,6 +5769,7 @@ <h4>Propose a Client Transfer</h4>
 Content-Type: application/json
 Request Body:
 {
+  "transferDate":"28 August 2018",
   "destinationOfficeId":"2",
   "note":"Client Relocating to Bangalore"
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java
index fc8904907..f4f14b206 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java
@@ -21,6 +21,7 @@
 import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -62,6 +63,7 @@
 import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorReadPlatformService;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
 import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
+import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
@@ -95,10 +97,11 @@ public ClientsApiResource(final PlatformSecurityContext context, final ClientRea
             final AccountDetailsReadPlatformService accountDetailsReadPlatformService,
             final SavingsAccountReadPlatformService savingsAccountReadPlatformService,
             final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService,
-            final BulkImportWorkbookService bulkImportWorkbookService, final GuarantorReadPlatformService guarantorReadPlatformService) {
-        this.context = context;
-        this.clientReadPlatformService = readPlatformService;
-        this.toApiJsonSerializer = toApiJsonSerializer;
+			final BulkImportWorkbookService bulkImportWorkbookService,
+			final GuarantorReadPlatformService guarantorReadPlatformService) {
+		this.context = context;
+		this.clientReadPlatformService = readPlatformService;
+		this.toApiJsonSerializer = toApiJsonSerializer;
         this.clientAccountSummaryToApiJsonSerializer = clientAccountSummaryToApiJsonSerializer;
         this.apiRequestParameterHelper = apiRequestParameterHelper;
         this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
@@ -361,4 +364,16 @@ public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId,
 		return this.toApiJsonSerializer.serialize(ObligeeList);
 	}
 
+	@Path("{clientId}/transferproposaldate")
+	@Consumes({ MediaType.APPLICATION_JSON })
+	@Produces({ MediaType.APPLICATION_JSON })
+	public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+
+		this.context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+		final Date transferDate = this.clientReadPlatformService.retrieveClientTransferProposalDate(clientId);
+		if (transferDate != null) {
+			return this.toApiJsonSerializer.serialize(new LocalDate(transferDate));
+		}
+		return this.toApiJsonSerializer.serialize(transferDate);
+	}
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformService.java
index fc9f72f8c..b9461deaa 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformService.java
@@ -19,6 +19,7 @@
 package org.apache.fineract.portfolio.client.service;
 
 import java.util.Collection;
+import java.util.Date;
 
 import org.apache.fineract.infrastructure.core.service.Page;
 import org.apache.fineract.infrastructure.core.service.SearchParameters;
@@ -45,4 +46,12 @@
     Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long centerId);
 
     ClientData retrieveAllNarrations(String clientNarrations);
+    
+	Date retrieveClientTransferProposalDate(Long clientId);
+
+	Date retrieveClientTransferProposalDateByLoan(Long clientId);
+
+	Date retrieveClientTransferProposalDateBySavings(Long clientId);
+
+	void validateClient(Long clientId);
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java
index 9d1babd8a..43968d8d3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java
@@ -9,6 +9,7 @@
  *
  * http://www.apache.org/licenses/LICENSE-2.0
  *
+ *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -23,6 +24,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Date;
 import java.util.List;
 
 import org.apache.commons.lang.StringUtils;
@@ -59,7 +61,11 @@
 import org.apache.fineract.portfolio.client.domain.LegalForm;
 import org.apache.fineract.portfolio.client.exception.ClientNotFoundException;
 import org.apache.fineract.portfolio.group.data.GroupGeneralData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
+import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
 import org.apache.fineract.portfolio.savings.data.SavingsProductData;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
 import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.joda.time.LocalDate;
@@ -100,8 +106,8 @@ public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, fina
             final AddressReadPlatformService addressReadPlatformService,final ClientFamilyMembersReadPlatformService clientFamilyMembersReadPlatformService,
             final ConfigurationReadPlatformService configurationReadPlatformService,
             final EntityDatatableChecksReadService entityDatatableChecksReadService,
-            final ColumnValidator columnValidator) {
-        this.context = context;
+			final ColumnValidator columnValidator) {
+		this.context = context;
         this.officeReadPlatformService = officeReadPlatformService;
         this.jdbcTemplate = new JdbcTemplate(dataSource);
         this.staffReadPlatformService = staffReadPlatformService;
@@ -112,7 +118,7 @@ public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, fina
         this.configurationReadPlatformService=configurationReadPlatformService;
         this.entityDatatableChecksReadService = entityDatatableChecksReadService;
         this.columnValidator = columnValidator;
-    }
+	}
 
     @Override
     public ClientData retrieveTemplate(final Long officeId, final boolean staffInSelectedOfficeOnly) {
@@ -816,5 +822,50 @@ public ClientData retrieveAllNarrations(final String clientNarrations) {
         return ClientData.template(null, null, null, null, narrations, null, null, clientTypeOptions, clientClassificationOptions, 
         		clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions, clientLegalFormOptions,null,null,null, null);
     }
+    
+	@Override
+	public Date retrieveClientTransferProposalDateByLoan(Long clientId) {
+		try {
+			String sql = "SELECT t.transaction_date  FROM m_client c LEFT JOIN m_loan loan ON c.id = loan.client_id AND c.id = ? AND loan.loan_status_id = ? LEFT JOIN m_loan_transaction t ON loan.id = t.loan_id AND t.transaction_type_enum = ? ORDER BY t.id DESC LIMIT 1";
+			return this.jdbcTemplate.queryForObject(sql, Date.class, clientId,
+					LoanStatus.TRANSFER_IN_PROGRESS.getValue(), LoanTransactionType.INITIATE_TRANSFER.getValue());
+		} catch (final EmptyResultDataAccessException e) {
+			return null;
+
+		}
+	}
+
+	@Override
+	public Date retrieveClientTransferProposalDateBySavings(Long clientId) {
+		try {
+			String sql = "SELECT t.transaction_date FROM m_client c LEFT JOIN m_savings_account savings  ON c.id = savings.client_id AND c.id = ? AND savings.status_enum = ? LEFT JOIN m_savings_account_transaction t ON savings.id = t.savings_account_id AND t.transaction_type_enum = ? ORDER BY t.id DESC LIMIT 1";
+			return this.jdbcTemplate.queryForObject(sql, Date.class, clientId,
+					SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue(),
+					SavingsAccountTransactionType.INITIATE_TRANSFER.getValue());
+		} catch (final EmptyResultDataAccessException e) {
+			return null;
+
+		}
+	}
+
+	@Override
+	public Date retrieveClientTransferProposalDate(Long clientId) {
+		validateClient(clientId);
+		Date transferDateForLoan = retrieveClientTransferProposalDateByLoan(clientId);
+		if (transferDateForLoan == null) {
+			transferDateForLoan = retrieveClientTransferProposalDateBySavings(clientId);
+		}
+		return transferDateForLoan;
+	}
+	
+	@Override
+	public void validateClient(Long clientId) {
+		try {
+			final String sql = "SELECT cl.id FROM m_client cl WHERE cl.id =? ";
+			this.jdbcTemplate.queryForObject(sql, Long.class, clientId);
+		} catch (final EmptyResultDataAccessException e) {
+			throw new ClientNotFoundException(clientId);
+		}
+	}
 
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index b6eed1918..411974ee5 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -19,7 +19,17 @@
 package org.apache.fineract.portfolio.loanaccount.service;
 
 import java.math.BigDecimal;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
@@ -57,22 +67,44 @@
 import org.apache.fineract.portfolio.account.PortfolioAccountType;
 import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
 import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
-import org.apache.fineract.portfolio.account.domain.*;
+import org.apache.fineract.portfolio.account.domain.AccountAssociationType;
+import org.apache.fineract.portfolio.account.domain.AccountAssociations;
+import org.apache.fineract.portfolio.account.domain.AccountAssociationsRepository;
+import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
+import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
+import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
+import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
+import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstruction;
+import org.apache.fineract.portfolio.account.domain.AccountTransferTransaction;
+import org.apache.fineract.portfolio.account.domain.AccountTransferType;
+import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority;
+import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
+import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
 import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
 import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
 import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
 import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
-import org.apache.fineract.portfolio.calendar.domain.*;
 import org.apache.fineract.portfolio.calendar.domain.Calendar;
+import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
+import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
+import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
+import org.apache.fineract.portfolio.calendar.domain.CalendarRepository;
+import org.apache.fineract.portfolio.calendar.domain.CalendarType;
 import org.apache.fineract.portfolio.calendar.exception.CalendarParameterUpdateNotSupportedException;
 import org.apache.fineract.portfolio.charge.domain.Charge;
 import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
 import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
-import org.apache.fineract.portfolio.charge.exception.*;
+import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeUpdatedException;
+import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeAddedException;
+import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeDeletedException;
 import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeDeletedException.LOAN_CHARGE_CANNOT_BE_DELETED_REASON;
+import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBePayedException;
 import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBePayedException.LOAN_CHARGE_CANNOT_BE_PAYED_REASON;
+import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeUpdatedException;
 import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeUpdatedException.LOAN_CHARGE_CANNOT_BE_UPDATED_REASON;
+import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeWaivedException;
 import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeWaivedException.LOAN_CHARGE_CANNOT_BE_WAIVED_REASON;
+import org.apache.fineract.portfolio.charge.exception.LoanChargeNotFoundException;
 import org.apache.fineract.portfolio.client.domain.Client;
 import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
 import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
@@ -87,9 +119,44 @@
 import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
 import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
 import org.apache.fineract.portfolio.loanaccount.command.LoanUpdateCommand;
-import org.apache.fineract.portfolio.loanaccount.data.*;
-import org.apache.fineract.portfolio.loanaccount.domain.*;
-import org.apache.fineract.portfolio.loanaccount.exception.*;
+import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
+import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
+import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
+import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
+import org.apache.fineract.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanOverdueInstallmentCharge;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTrancheDisbursementCharge;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
+import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
+import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
+import org.apache.fineract.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanDisbursalException;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanForeclosureException;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanMultiDisbursementException;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
+import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
 import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDomainService;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
@@ -111,6 +178,7 @@
 import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
 import org.apache.fineract.portfolio.savings.exception.InsufficientAccountBalanceException;
+import org.apache.fineract.portfolio.transfer.api.TransferApiConstants;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.joda.time.LocalDate;
 import org.joda.time.format.DateTimeFormat;
@@ -1792,12 +1860,14 @@ public LoanTransaction initiateLoanTransfer(final Loan loan, final LocalDate tra
         AppUser currentUser = getAppUserIfPresent();
         this.loanAssembler.setHelpers(loan);
         checkClientOrGroupActive(loan);
+        validateTransactionsForTransfer(loan, transferDate);
+        
         this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_INITIATE_TRANSFER,
                 constructEntityMap(BUSINESS_ENTITY.LOAN, loan));
 
         final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
         final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
-
+        
         final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate,
                 DateUtils.getLocalDateTimeOfTenant(), currentUser);
         loan.addLoanTransaction(newTransferTransaction);
@@ -2942,4 +3012,19 @@ private void syncExpectedDateWithActualDisbursementDate(final Loan loan, LocalDa
 	   	}
 	   	
 	   }
+    
+	private void validateTransactionsForTransfer(final Loan loan, final LocalDate transferDate) {
+
+		for (LoanTransaction transaction : loan.getLoanTransactions()) {
+			if ((transaction.getTransactionDate().isEqual(transferDate)
+					&& transaction.getCreatedDateTime().isEqual(transferDate.toDateTimeAtStartOfDay().toLocalDateTime()))
+					|| transaction.getTransactionDate().isAfter(transferDate)) {
+				throw new GeneralPlatformDomainRuleException(TransferApiConstants.transferClientLoanException,
+						TransferApiConstants.transferClientLoanExceptionMessage, transaction.getCreatedDateTime().toLocalDate(),
+						transferDate);
+			}
+
+		}
+
+	}
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
index cf5ae0354..2f25e602a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
@@ -111,7 +111,7 @@
     @Column(name = "created_date", nullable = false)
     private Date createdDate;
 
-    @ManyToOne
+	@ManyToOne
     @JoinColumn(name = "appuser_id", nullable = true)
     private AppUser appUser;
     
@@ -790,5 +790,9 @@ public Long getReleaseIdOfHoldAmountTransaction() {
     
 	public boolean isAmountOnHoldNotReleased() {
 		return (isAmountOnHold() && getReleaseIdOfHoldAmountTransaction() == null);
-	}	    
+	}
+	
+	public Date getCreatedDate() {
+		return createdDate;
+	}
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
index 1e7d782ba..b421330b9 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
@@ -18,11 +18,25 @@
  */
 package org.apache.fineract.portfolio.savings.service;
 
-import static org.apache.fineract.portfolio.savings.SavingsApiConstants.*;
+import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME;
+import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
+import static org.apache.fineract.portfolio.savings.SavingsApiConstants.amountParamName;
+import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName;
+import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
+import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
+import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawBalanceParamName;
 
 import java.math.BigDecimal;
 import java.math.MathContext;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
@@ -32,6 +46,7 @@
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
 import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
 import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
@@ -40,7 +55,11 @@
 import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper;
-import org.apache.fineract.organisation.monetary.domain.*;
+import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
+import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
 import org.apache.fineract.organisation.office.domain.Office;
 import org.apache.fineract.organisation.staff.domain.Staff;
 import org.apache.fineract.organisation.staff.domain.StaffRepositoryWrapper;
@@ -72,12 +91,30 @@
 import org.apache.fineract.portfolio.savings.data.SavingsAccountDataValidator;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDTO;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDataValidator;
-import org.apache.fineract.portfolio.savings.domain.*;
-import org.apache.fineract.portfolio.savings.exception.*;
+import org.apache.fineract.portfolio.savings.domain.DepositAccountOnHoldTransaction;
+import org.apache.fineract.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountCharge;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountChargeRepositoryWrapper;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountDomainService;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository;
+import org.apache.fineract.portfolio.savings.exception.PostInterestAsOnDateException;
 import org.apache.fineract.portfolio.savings.exception.PostInterestAsOnDateException.PostInterestAsOnException_TYPE;
+import org.apache.fineract.portfolio.savings.exception.PostInterestClosingDateException;
+import org.apache.fineract.portfolio.savings.exception.SavingsAccountClosingNotAllowedException;
+import org.apache.fineract.portfolio.savings.exception.SavingsAccountTransactionNotFoundException;
+import org.apache.fineract.portfolio.savings.exception.SavingsOfficerAssignmentException;
+import org.apache.fineract.portfolio.savings.exception.SavingsOfficerUnassignmentException;
+import org.apache.fineract.portfolio.savings.exception.TransactionUpdateNotAllowedException;
+import org.apache.fineract.portfolio.transfer.api.TransferApiConstants;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
 import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
 import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -720,6 +757,9 @@ public SavingsAccountTransaction initiateSavingsTransfer(final SavingsAccount sa
 
         final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
                 .isSavingsInterestPostingAtCurrentPeriodEnd();
+        
+        validateTransactionsForTransfer(savingsAccount, transferDate);
+        
         final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
         this.savingAccountAssembler.setHelpers(savingsAccount);
         final Set<Long> existingTransactionIds = new HashSet<>();
@@ -1497,4 +1537,19 @@ public CommandProcessingResult unblockDebits(final Long savingsId) {
         return new CommandProcessingResultBuilder().withEntityId(savingsId).withOfficeId(account.officeId())
                 .withClientId(account.clientId()).withGroupId(account.groupId()).withSavingsId(savingsId).with(changes).build();
     }
+    
+	private void validateTransactionsForTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) {
+
+		for (SavingsAccountTransaction transaction : savingsAccount.getTransactions()) {
+			if ((transaction.getTransactionLocalDate().isEqual(transferDate)
+					&& transaction.getTransactionLocalDate().isAfter(new LocalDate(transferDate)))
+					|| transaction.getTransactionLocalDate().isAfter(transferDate)) {
+				throw new GeneralPlatformDomainRuleException(TransferApiConstants.transferClientSavingsException,
+						TransferApiConstants.transferClientSavingsException, new LocalDate(transaction.getCreatedDate()),
+						transferDate);
+			}
+
+		}
+
+	}
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/api/TransferApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/api/TransferApiConstants.java
index 0135f0055..37519e92e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/api/TransferApiConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/api/TransferApiConstants.java
@@ -18,24 +18,26 @@
  */
 package org.apache.fineract.portfolio.transfer.api;
 
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
 public class TransferApiConstants {
 
-    // general
-    public static final String localeParamName = "locale";
-    public static final String dateFormatParamName = "dateFormat";
-
-    // request parameters
-    public static final String idParamName = "id";
-    public static final String destinationGroupIdParamName = "destinationGroupId";
-    public static final String clients = "clients";
-    public static final String inheritDestinationGroupLoanOfficer = "inheritDestinationGroupLoanOfficer";
-    public static final String newStaffIdParamName = "staffId";
-    public static final String transferActiveLoans = "transferActiveLoans";
-    public static final String destinationOfficeIdParamName = "destinationOfficeId";
-    public static final String note = "note";
+	// general
+	public static final String localeParamName = "locale";
+	public static final String dateFormatParamName = "dateFormat";
 
+	// request parameters
+	public static final String idParamName = "id";
+	public static final String destinationGroupIdParamName = "destinationGroupId";
+	public static final String clients = "clients";
+	public static final String inheritDestinationGroupLoanOfficer = "inheritDestinationGroupLoanOfficer";
+	public static final String newStaffIdParamName = "staffId";
+	public static final String transferActiveLoans = "transferActiveLoans";
+	public static final String destinationOfficeIdParamName = "destinationOfficeId";
+	public static final String note = "note";
+	public static final String transferDate = "transferDate";
+	public static final String transferClientLoanException = "error.msg.caanot.transfer.client.as.loan.transaction.present.on.or.after.transfer.date";
+	public static final String transferClientLoanExceptionMessage = "error msg caanot transfer client as loan transaction present on or after transfer date";
+	public static final String transferClientSavingsException = "error.msg.caanot.transfer.client.as.savings.transaction.present.on.or.after.transfer.date";
+	public static final String transferClientSavingsExceptionMessage = "error msg caanot transfer client as savings transaction present on or after transfer date";
+	public static final String transferClientToSameOfficeException = "error.msg.cannot.transfer.clinet.as.selected.office.and.current.office.are.same";
+	public static final String transferClientToSameOfficeExceptionMessage = "error.msg.cannot.transfer.clinet.as.selected.office.and.current.office.are.same";
 }
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/data/TransfersDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/data/TransfersDataValidator.java
index 273e87a08..b769212a3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/data/TransfersDataValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/data/TransfersDataValidator.java
@@ -35,6 +35,7 @@
 import org.apache.fineract.portfolio.client.api.ClientApiConstants;
 import org.apache.fineract.portfolio.group.api.GroupingTypesApiConstants;
 import org.apache.fineract.portfolio.transfer.api.TransferApiConstants;
+import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -54,7 +55,7 @@
 	private static final Set<String> PROPOSE_CLIENT_TRANSFER_DATA_PARAMETERS = new HashSet<>(
 			Arrays.asList(TransferApiConstants.localeParamName, TransferApiConstants.dateFormatParamName,
 					TransferApiConstants.destinationOfficeIdParamName, TransferApiConstants.transferActiveLoans,
-					TransferApiConstants.note));
+					TransferApiConstants.note, TransferApiConstants.transferDate));
 
 	private static final Set<String> ACCEPT_CLIENT_TRANSFER_DATA_PARAMETERS = new HashSet<>(
 			Arrays.asList(TransferApiConstants.newStaffIdParamName, TransferApiConstants.destinationGroupIdParamName,
@@ -132,7 +133,7 @@ public void validateForProposeClientTransfer(final String json) {
                 .extractLongNamed(TransferApiConstants.destinationOfficeIdParamName, element);
         baseDataValidator.reset().parameter(TransferApiConstants.destinationOfficeIdParamName).value(destinationOfficeId).notNull()
                 .integerGreaterThanZero();
-
+        
         validateNote(baseDataValidator, element);
 
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java
index d037c1612..b464dba9b 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java
@@ -20,13 +20,12 @@
 
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.organisation.office.domain.Office;
 import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
@@ -42,7 +41,6 @@
 import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
 import org.apache.fineract.portfolio.client.domain.ClientStatus;
 import org.apache.fineract.portfolio.client.exception.ClientHasBeenClosedException;
-import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY;
 import org.apache.fineract.portfolio.group.domain.Group;
 import org.apache.fineract.portfolio.group.domain.GroupRepositoryWrapper;
 import org.apache.fineract.portfolio.group.exception.ClientNotInGroupException;
@@ -60,6 +58,7 @@
 import org.apache.fineract.portfolio.transfer.exception.ClientNotAwaitingTransferApprovalOrOnHoldException;
 import org.apache.fineract.portfolio.transfer.exception.TransferNotSupportedException;
 import org.apache.fineract.portfolio.transfer.exception.TransferNotSupportedException.TRANSFER_NOT_SUPPORTED_REASON;
+import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -295,8 +294,13 @@ public CommandProcessingResult proposeClientTransfer(final Long clientId, final
         final Long destinationOfficeId = jsonCommand.longValueOfParameterNamed(TransferApiConstants.destinationOfficeIdParamName);
         final Office office = this.officeRepository.findOneWithNotFoundDetection(destinationOfficeId);
         final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
-        handleClientTransferLifecycleEvent(client, office, TransferEventType.PROPOSAL, jsonCommand);
-        this.clientRepositoryWrapper.save(client);
+		if (client.getOffice().getId().equals(destinationOfficeId)) {
+			throw new GeneralPlatformDomainRuleException(TransferApiConstants.transferClientToSameOfficeException,
+					TransferApiConstants.transferClientToSameOfficeExceptionMessage, office.getName());
+
+		}
+		handleClientTransferLifecycleEvent(client, office, TransferEventType.PROPOSAL, jsonCommand);
+		this.clientRepositoryWrapper.save(client);
         return new CommandProcessingResultBuilder() //
                 .withClientId(clientId) //
                 .withEntityId(clientId) //
@@ -369,6 +373,7 @@ private void handleClientTransferLifecycleEvent(final Client client, final Offic
         Group destinationGroup = null;
         final Long staffId = jsonCommand.longValueOfParameterNamed(TransferApiConstants.newStaffIdParamName);
         final Long destinationGroupId = jsonCommand.longValueOfParameterNamed(TransferApiConstants.destinationGroupIdParamName);
+        final LocalDate transferDate = jsonCommand.localDateValueOfParameterNamed(TransferApiConstants.transferDate);
         if (staffId != null) {
             staff = this.staffRepositoryWrapper.findByOfficeHierarchyWithNotFoundDetection(staffId, destinationOffice.getHierarchy());
         }
@@ -387,17 +392,17 @@ private void handleClientTransferLifecycleEvent(final Client client, final Offic
                 if (loan.isDisbursed() && !loan.isClosed()) {
                     switch (transferEventType) {
                         case ACCEPTANCE:
-                            this.loanWritePlatformService.acceptLoanTransfer(loan, DateUtils.getLocalDateOfTenant(),
+                            this.loanWritePlatformService.acceptLoanTransfer(loan, loan.getLastUserTransactionDate(),
                                     destinationOffice, staff);
                         break;
                         case PROPOSAL:
-                            this.loanWritePlatformService.initiateLoanTransfer(loan, DateUtils.getLocalDateOfTenant());
+                            this.loanWritePlatformService.initiateLoanTransfer(loan, transferDate);
                         break;
                         case REJECTION:
                             this.loanWritePlatformService.rejectLoanTransfer(loan);
                         break;
                         case WITHDRAWAL:
-                            this.loanWritePlatformService.withdrawLoanTransfer(loan, DateUtils.getLocalDateOfTenant());
+                            this.loanWritePlatformService.withdrawLoanTransfer(loan, loan.getLastUserTransactionDate());
                     }
                 }
             }
@@ -411,18 +416,18 @@ private void handleClientTransferLifecycleEvent(final Client client, final Offic
                     switch (transferEventType) {
                         case ACCEPTANCE:
                             this.savingsAccountWritePlatformService.acceptSavingsTransfer(savingsAccount,
-                                    DateUtils.getLocalDateOfTenant(), destinationOffice, staff);
+                            		savingsAccount.retrieveLastTransactionDate(), destinationOffice, staff);
                         break;
                         case PROPOSAL:
                             this.savingsAccountWritePlatformService.initiateSavingsTransfer(savingsAccount,
-                                    DateUtils.getLocalDateOfTenant());
+                            		transferDate);
                         break;
                         case REJECTION:
                             this.savingsAccountWritePlatformService.rejectSavingsTransfer(savingsAccount);
                         break;
                         case WITHDRAWAL:
                             this.savingsAccountWritePlatformService.withdrawSavingsTransfer(savingsAccount,
-                                    DateUtils.getLocalDateOfTenant());
+                            		savingsAccount.retrieveLastTransactionDate());
                     }
                 }
             }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services