feat: current account standing instructions - full backend support
- StandingInstructionData: rename conflicting Lombok getters (getInstructionType,
getRecurrenceType, getRecurrenceFrequency, getFromAccountType, getToAccountType,
getTransferType) to domain enum methods so Jackson serializes EnumOptionData fields
as {id, code, value} objects instead of plain strings
- AccountTransfersWritePlatformServiceImpl: add current account transfer support for
all 4 missing cases (Current->Savings, Savings->Current, Current->Current,
Current->Loan) with performCurrentWithdrawal/performCurrentDeposit helpers
- AccountConfiguration: wire CurrentAccountRepository and CurrentAccountTransactionRepository
into AccountTransfersWritePlatformService bean
- StandingInstructionApiResource: fix getValue() type mismatch after getter rename
- ExecuteStandingInstructionsTasklet: update to use renamed enum accessor methods
- PortfolioAccountType: add CURRENT(3) type support
- DB migration: 0301_add_current_account_to_transfer_details.xml adds
from_current_account_id and to_current_account_id columns to transfer details table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
45585ecc10
commit
2251fd2259
+8
@@ -23,6 +23,7 @@ public enum PortfolioAccountType {
|
||||
INVALID(0, "accountType.invalid"), //
|
||||
LOAN(1, "accountType.loan"), //
|
||||
SAVINGS(2, "accountType.savings"), //
|
||||
CURRENT(3, "accountType.current"), //
|
||||
;
|
||||
|
||||
private final Integer value;
|
||||
@@ -52,6 +53,9 @@ public enum PortfolioAccountType {
|
||||
case 2:
|
||||
enumType = SAVINGS;
|
||||
break;
|
||||
case 3:
|
||||
enumType = CURRENT;
|
||||
break;
|
||||
default:
|
||||
enumType = INVALID;
|
||||
}
|
||||
@@ -68,4 +72,8 @@ public enum PortfolioAccountType {
|
||||
public boolean isLoanAccount() {
|
||||
return this.equals(LOAN);
|
||||
}
|
||||
|
||||
public boolean isCurrentAccount() {
|
||||
return this.equals(CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -207,10 +207,10 @@ public class StandingInstructionApiResource {
|
||||
if (associationParameters.contains("template")) {
|
||||
final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate(
|
||||
standingInstructionData.getFromClient().getOfficeId(), standingInstructionData.getFromClient().getId(),
|
||||
standingInstructionData.getFromAccount().getId(), standingInstructionData.getFromAccountType().getValue(),
|
||||
standingInstructionData.getFromAccount().getId(), standingInstructionData.fromAccountTypeEnum().getValue(),
|
||||
standingInstructionData.getToClient().getOfficeId(), standingInstructionData.getToClient().getId(),
|
||||
standingInstructionData.getToAccount().getId(), standingInstructionData.getToAccountType().getValue(),
|
||||
standingInstructionData.getTransferType().getValue());
|
||||
standingInstructionData.getToAccount().getId(), standingInstructionData.toAccountTypeEnum().getValue(),
|
||||
standingInstructionData.transferTypeEnum().getValue());
|
||||
standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -59,7 +59,7 @@ public class AccountTransfersDetailDataValidator {
|
||||
|
||||
final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType).notNull()
|
||||
.isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2));
|
||||
.isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
|
||||
|
||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||
baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero();
|
||||
@@ -72,7 +72,7 @@ public class AccountTransfersDetailDataValidator {
|
||||
|
||||
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType).notNull().isOneOfTheseValues(Integer.valueOf(1),
|
||||
Integer.valueOf(2));
|
||||
Integer.valueOf(2), Integer.valueOf(3));
|
||||
|
||||
}
|
||||
|
||||
|
||||
+40
-10
@@ -48,20 +48,27 @@ public final class StandingInstructionData {
|
||||
private final Long accountDetailId;
|
||||
@Getter
|
||||
private final String name;
|
||||
@Getter
|
||||
private final OfficeData fromOffice;
|
||||
@Getter
|
||||
private final ClientData fromClient;
|
||||
@Getter
|
||||
private final EnumOptionData fromAccountType;
|
||||
@Getter
|
||||
private final PortfolioAccountData fromAccount;
|
||||
@Getter
|
||||
private final OfficeData toOffice;
|
||||
@Getter
|
||||
private final ClientData toClient;
|
||||
@Getter
|
||||
private final EnumOptionData toAccountType;
|
||||
@Getter
|
||||
private final PortfolioAccountData toAccount;
|
||||
@Getter
|
||||
private final EnumOptionData transferType;
|
||||
@Getter
|
||||
private final EnumOptionData priority;
|
||||
@Getter
|
||||
private final EnumOptionData instructionType;
|
||||
@Getter
|
||||
private final EnumOptionData status;
|
||||
@@ -69,27 +76,46 @@ public final class StandingInstructionData {
|
||||
private final BigDecimal amount;
|
||||
@Getter
|
||||
private final LocalDate validFrom;
|
||||
@Getter
|
||||
private final LocalDate validTill;
|
||||
@Getter
|
||||
private final EnumOptionData recurrenceType;
|
||||
@Getter
|
||||
private final EnumOptionData recurrenceFrequency;
|
||||
@Getter
|
||||
private final Integer recurrenceInterval;
|
||||
@Getter
|
||||
private final MonthDay recurrenceOnMonthDay;
|
||||
@Getter
|
||||
private final Page<AccountTransferData> transactions;
|
||||
|
||||
@Getter
|
||||
private final Collection<OfficeData> fromOfficeOptions;
|
||||
@Getter
|
||||
private final Collection<ClientData> fromClientOptions;
|
||||
@Getter
|
||||
private final Collection<EnumOptionData> fromAccountTypeOptions;
|
||||
@Getter
|
||||
private final Collection<PortfolioAccountData> fromAccountOptions;
|
||||
@Getter
|
||||
private final Collection<OfficeData> toOfficeOptions;
|
||||
@Getter
|
||||
private final Collection<ClientData> toClientOptions;
|
||||
@Getter
|
||||
private final Collection<EnumOptionData> toAccountTypeOptions;
|
||||
@Getter
|
||||
private final Collection<PortfolioAccountData> toAccountOptions;
|
||||
@Getter
|
||||
private final Collection<EnumOptionData> transferTypeOptions;
|
||||
@Getter
|
||||
private final Collection<EnumOptionData> statusOptions;
|
||||
@Getter
|
||||
private final Collection<EnumOptionData> instructionTypeOptions;
|
||||
@Getter
|
||||
private final Collection<EnumOptionData> priorityOptions;
|
||||
@Getter
|
||||
private final Collection<EnumOptionData> recurrenceTypeOptions;
|
||||
@Getter
|
||||
private final Collection<EnumOptionData> recurrenceFrequencyOptions;
|
||||
|
||||
public static StandingInstructionData template(final Collection<OfficeData> fromOfficeOptions,
|
||||
@@ -282,28 +308,32 @@ public final class StandingInstructionData {
|
||||
instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions);
|
||||
}
|
||||
|
||||
public StandingInstructionType getInstructionType() {
|
||||
/**
|
||||
* Returns the domain enum for instruction type — used internally by business logic.
|
||||
* NOTE: renamed from getInstructionType() to avoid overriding Lombok @Getter (which must
|
||||
* return EnumOptionData so Jackson serializes it as an object, not a plain string).
|
||||
*/
|
||||
public StandingInstructionType instructionTypeEnum() {
|
||||
return Optional.ofNullable(this.instructionType).map(e -> StandingInstructionType.fromInt(e.getId().intValue())).orElse(null);
|
||||
|
||||
}
|
||||
|
||||
public AccountTransferRecurrenceType getRecurrenceType() {
|
||||
public AccountTransferRecurrenceType recurrenceTypeEnum() {
|
||||
return Optional.ofNullable(this.recurrenceType).map(e -> AccountTransferRecurrenceType.fromInt(e.getId().intValue())).orElse(null);
|
||||
}
|
||||
|
||||
public PeriodFrequencyType getRecurrenceFrequency() {
|
||||
public PeriodFrequencyType recurrenceFrequencyEnum() {
|
||||
return Optional.ofNullable(this.recurrenceFrequency).map(e -> PeriodFrequencyType.fromInt(e.getId().intValue())).orElse(null);
|
||||
}
|
||||
|
||||
public PortfolioAccountType getFromAccountType() {
|
||||
public PortfolioAccountType fromAccountTypeEnum() {
|
||||
return Optional.ofNullable(this.fromAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null);
|
||||
}
|
||||
|
||||
public PortfolioAccountType getToAccountType() {
|
||||
public PortfolioAccountType toAccountTypeEnum() {
|
||||
return Optional.ofNullable(this.toAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null);
|
||||
}
|
||||
|
||||
public AccountTransferType getTransferType() {
|
||||
public AccountTransferType transferTypeEnum() {
|
||||
return Optional.ofNullable(this.transferType).map(e -> AccountTransferType.fromInt(e.getId().intValue())).orElse(null);
|
||||
}
|
||||
|
||||
@@ -317,10 +347,10 @@ public final class StandingInstructionData {
|
||||
|
||||
public Integer toTransferType() {
|
||||
Integer transferType = null;
|
||||
AccountTransferType accountTransferType = getTransferType();
|
||||
if (accountTransferType.isChargePayment()) {
|
||||
AccountTransferType accountTransferType = transferTypeEnum();
|
||||
if (accountTransferType != null && accountTransferType.isChargePayment()) {
|
||||
transferType = LoanTransactionType.CHARGE_PAYMENT.getValue();
|
||||
} else if (accountTransferType.isLoanRepayment()) {
|
||||
} else if (accountTransferType != null && accountTransferType.isLoanRepayment()) {
|
||||
transferType = LoanTransactionType.REPAYMENT.getValue();
|
||||
}
|
||||
return transferType;
|
||||
|
||||
+4
-2
@@ -171,7 +171,8 @@ public class StandingInstructionDataValidator {
|
||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull();
|
||||
|
||||
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
|
||||
if (toAccountType != null && PortfolioAccountType.fromInt(toAccountType).isSavingsAccount()) {
|
||||
if (toAccountType != null && (PortfolioAccountType.fromInt(toAccountType).isSavingsAccount()
|
||||
|| PortfolioAccountType.fromInt(toAccountType).isCurrentAccount())) {
|
||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
|
||||
.notNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
|
||||
@@ -192,7 +193,8 @@ public class StandingInstructionDataValidator {
|
||||
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) {
|
||||
errorCode = "not.account.transfer";
|
||||
} else if (accountTransferType.isLoanRepayment()
|
||||
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isSavingsAccount())) {
|
||||
&& (fromPortfolioAccountType.isLoanAccount()
|
||||
|| (toPortfolioAccountType.isSavingsAccount() || toPortfolioAccountType.isCurrentAccount()))) {
|
||||
errorCode = "not.loan.repayment";
|
||||
}
|
||||
if (errorCode != null) {
|
||||
|
||||
+101
-1
@@ -34,6 +34,8 @@ import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
@@ -49,16 +51,19 @@ public class AccountTransferDetailAssembler {
|
||||
private final SavingsAccountAssembler savingsAccountAssembler;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final LoanAssembler loanAccountAssembler;
|
||||
private final CurrentAccountRepository currentAccountRepository;
|
||||
|
||||
@Autowired
|
||||
public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository,
|
||||
final OfficeRepositoryWrapper officeRepositoryWrapper, final SavingsAccountAssembler savingsAccountAssembler,
|
||||
final FromJsonHelper fromApiJsonHelper, final LoanAssembler loanAccountAssembler) {
|
||||
final FromJsonHelper fromApiJsonHelper, final LoanAssembler loanAccountAssembler,
|
||||
final CurrentAccountRepository currentAccountRepository) {
|
||||
this.clientRepository = clientRepository;
|
||||
this.officeRepositoryWrapper = officeRepositoryWrapper;
|
||||
this.savingsAccountAssembler = savingsAccountAssembler;
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.loanAccountAssembler = loanAccountAssembler;
|
||||
this.currentAccountRepository = currentAccountRepository;
|
||||
}
|
||||
|
||||
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) {
|
||||
@@ -211,4 +216,99 @@ public class AccountTransferDetailAssembler {
|
||||
return AccountTransferDetails.loanToLoanTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toLoanAccount,
|
||||
transferType);
|
||||
}
|
||||
|
||||
// --- Current account assembly methods ---
|
||||
|
||||
public AccountTransferDetails assembleCurrentToCurrentTransfer(final JsonCommand command) {
|
||||
final Long fromCurrentAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||
final CurrentAccount fromCurrentAccount = currentAccountRepository.findById(fromCurrentAccountId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("From current account not found: " + fromCurrentAccountId));
|
||||
|
||||
final Long toCurrentAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||
final CurrentAccount toCurrentAccount = currentAccountRepository.findById(toCurrentAccountId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("To current account not found: " + toCurrentAccountId));
|
||||
|
||||
final JsonElement element = command.parsedJson();
|
||||
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||
|
||||
return AccountTransferDetails.currentToCurrentTransfer(fromOffice, fromClient, fromCurrentAccount, toOffice, toClient,
|
||||
toCurrentAccount, transfertype);
|
||||
}
|
||||
|
||||
public AccountTransferDetails assembleCurrentToSavingsTransfer(final JsonCommand command) {
|
||||
final Long fromCurrentAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||
final CurrentAccount fromCurrentAccount = currentAccountRepository.findById(fromCurrentAccountId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("From current account not found: " + fromCurrentAccountId));
|
||||
|
||||
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, false);
|
||||
|
||||
final JsonElement element = command.parsedJson();
|
||||
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||
|
||||
return AccountTransferDetails.currentToSavingsTransfer(fromOffice, fromClient, fromCurrentAccount, toOffice, toClient,
|
||||
toSavingsAccount, transfertype);
|
||||
}
|
||||
|
||||
public AccountTransferDetails assembleSavingsToCurrentTransfer(final JsonCommand command) {
|
||||
final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId, false);
|
||||
|
||||
final Long toCurrentAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||
final CurrentAccount toCurrentAccount = currentAccountRepository.findById(toCurrentAccountId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("To current account not found: " + toCurrentAccountId));
|
||||
|
||||
final JsonElement element = command.parsedJson();
|
||||
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||
|
||||
return AccountTransferDetails.savingsToCurrentTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient,
|
||||
toCurrentAccount, transfertype);
|
||||
}
|
||||
|
||||
public AccountTransferDetails assembleCurrentToLoanTransfer(final JsonCommand command) {
|
||||
final Long fromCurrentAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||
final CurrentAccount fromCurrentAccount = currentAccountRepository.findById(fromCurrentAccountId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("From current account not found: " + fromCurrentAccountId));
|
||||
|
||||
final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId);
|
||||
|
||||
final JsonElement element = command.parsedJson();
|
||||
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||
|
||||
return AccountTransferDetails.currentToLoanTransfer(fromOffice, fromClient, fromCurrentAccount, toOffice, toClient,
|
||||
toLoanAccount, transfertype);
|
||||
}
|
||||
}
|
||||
|
||||
+61
-10
@@ -32,6 +32,7 @@ import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
|
||||
@@ -71,6 +72,14 @@ public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
|
||||
@JoinColumn(name = "from_loan_account_id", nullable = true)
|
||||
private Loan fromLoanAccount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "from_current_account_id", nullable = true)
|
||||
private CurrentAccount fromCurrentAccount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "to_current_account_id", nullable = true)
|
||||
private CurrentAccount toCurrentAccount;
|
||||
|
||||
@Column(name = "transfer_type")
|
||||
private Integer transferType;
|
||||
|
||||
@@ -84,21 +93,21 @@ public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
|
||||
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
|
||||
Integer transferType) {
|
||||
|
||||
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, toSavingsAccount, null,
|
||||
transferType, null);
|
||||
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, toSavingsAccount,
|
||||
null, null, transferType, null);
|
||||
}
|
||||
|
||||
public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient,
|
||||
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount,
|
||||
Integer transferType) {
|
||||
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, null, toLoanAccount,
|
||||
transferType, null);
|
||||
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, null, toLoanAccount,
|
||||
null, transferType, null);
|
||||
}
|
||||
|
||||
public static AccountTransferDetails loanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount,
|
||||
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, Integer transferType) {
|
||||
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, toSavingsAccount, null,
|
||||
transferType, null);
|
||||
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, null, toOffice, toClient, toSavingsAccount, null,
|
||||
null, transferType, null);
|
||||
}
|
||||
|
||||
protected AccountTransferDetails() {
|
||||
@@ -106,17 +115,21 @@ public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
|
||||
}
|
||||
|
||||
private AccountTransferDetails(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount,
|
||||
final Loan fromLoanAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
|
||||
final Loan toLoanAccount, final Integer transferType,
|
||||
final Loan fromLoanAccount, final CurrentAccount fromCurrentAccount,
|
||||
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
|
||||
final Loan toLoanAccount, final CurrentAccount toCurrentAccount,
|
||||
final Integer transferType,
|
||||
final AccountTransferStandingInstruction accountTransferStandingInstruction) {
|
||||
this.fromOffice = fromOffice;
|
||||
this.fromClient = fromClient;
|
||||
this.fromSavingsAccount = fromSavingsAccount;
|
||||
this.fromLoanAccount = fromLoanAccount;
|
||||
this.fromCurrentAccount = fromCurrentAccount;
|
||||
this.toOffice = toOffice;
|
||||
this.toClient = toClient;
|
||||
this.toSavingsAccount = toSavingsAccount;
|
||||
this.toLoanAccount = toLoanAccount;
|
||||
this.toCurrentAccount = toCurrentAccount;
|
||||
this.transferType = transferType;
|
||||
this.accountTransferStandingInstruction = accountTransferStandingInstruction;
|
||||
}
|
||||
@@ -145,6 +158,14 @@ public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
|
||||
return this.fromLoanAccount;
|
||||
}
|
||||
|
||||
public CurrentAccount fromCurrentAccount() {
|
||||
return this.fromCurrentAccount;
|
||||
}
|
||||
|
||||
public CurrentAccount toCurrentAccount() {
|
||||
return this.toCurrentAccount;
|
||||
}
|
||||
|
||||
public AccountTransferStandingInstruction accountTransferStandingInstruction() {
|
||||
return this.accountTransferStandingInstruction;
|
||||
}
|
||||
@@ -155,8 +176,38 @@ public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
|
||||
|
||||
public static AccountTransferDetails loanToLoanTransfer(Office fromOffice, Client fromClient, Loan fromLoanAccount, Office toOffice,
|
||||
Client toClient, Loan toLoanAccount, Integer transferType) {
|
||||
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, null, toLoanAccount,
|
||||
transferType, null);
|
||||
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, null, toOffice, toClient, null, toLoanAccount,
|
||||
null, transferType, null);
|
||||
}
|
||||
|
||||
// --- Current account factory methods ---
|
||||
|
||||
public static AccountTransferDetails currentToCurrentTransfer(final Office fromOffice, final Client fromClient,
|
||||
final CurrentAccount fromCurrentAccount, final Office toOffice, final Client toClient,
|
||||
final CurrentAccount toCurrentAccount, Integer transferType) {
|
||||
return new AccountTransferDetails(fromOffice, fromClient, null, null, fromCurrentAccount, toOffice, toClient, null, null,
|
||||
toCurrentAccount, transferType, null);
|
||||
}
|
||||
|
||||
public static AccountTransferDetails currentToSavingsTransfer(final Office fromOffice, final Client fromClient,
|
||||
final CurrentAccount fromCurrentAccount, final Office toOffice, final Client toClient,
|
||||
final SavingsAccount toSavingsAccount, Integer transferType) {
|
||||
return new AccountTransferDetails(fromOffice, fromClient, null, null, fromCurrentAccount, toOffice, toClient, toSavingsAccount,
|
||||
null, null, transferType, null);
|
||||
}
|
||||
|
||||
public static AccountTransferDetails savingsToCurrentTransfer(final Office fromOffice, final Client fromClient,
|
||||
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient,
|
||||
final CurrentAccount toCurrentAccount, Integer transferType) {
|
||||
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, null, null,
|
||||
toCurrentAccount, transferType, null);
|
||||
}
|
||||
|
||||
public static AccountTransferDetails currentToLoanTransfer(final Office fromOffice, final Client fromClient,
|
||||
final CurrentAccount fromCurrentAccount, final Office toOffice, final Client toClient,
|
||||
final Loan toLoanAccount, Integer transferType) {
|
||||
return new AccountTransferDetails(fromOffice, fromClient, null, null, fromCurrentAccount, toOffice, toClient, null, toLoanAccount,
|
||||
null, transferType, null);
|
||||
}
|
||||
|
||||
public List<AccountTransferTransaction> getAccountTransferTransactions() {
|
||||
|
||||
+15
-3
@@ -249,7 +249,9 @@ public class AccountTransferStandingInstruction extends AbstractPersistableCusto
|
||||
}
|
||||
}
|
||||
|
||||
if (this.accountTransferDetails.toSavingsAccount() != null) {
|
||||
// For savings-to-savings or current-to-savings: enforce Fixed instruction + Periodic recurrence
|
||||
if (this.accountTransferDetails.toSavingsAccount() != null
|
||||
|| this.accountTransferDetails.toCurrentAccount() != null) {
|
||||
baseDataValidator.reset().parameter(instructionTypeParamName).value(this.instructionType).notNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter(recurrenceTypeParamName).value(this.recurrenceType).notNull().inMinMaxRange(1, 1);
|
||||
}
|
||||
@@ -258,12 +260,22 @@ public class AccountTransferStandingInstruction extends AbstractPersistableCusto
|
||||
baseDataValidator.reset().parameter(amountParamName).value(this.amount).notNull();
|
||||
}
|
||||
|
||||
// Account Transfer: from must be savings OR current; to must be savings OR current
|
||||
boolean fromAccountValid = this.accountTransferDetails.fromSavingsAccount() != null
|
||||
|| this.accountTransferDetails.fromCurrentAccount() != null;
|
||||
boolean toAccountValid = this.accountTransferDetails.toSavingsAccount() != null
|
||||
|| this.accountTransferDetails.toCurrentAccount() != null;
|
||||
|
||||
// Loan Repayment: from must be savings OR current; to must be loan
|
||||
boolean fromAccountValidForLoan = this.accountTransferDetails.fromSavingsAccount() != null
|
||||
|| this.accountTransferDetails.fromCurrentAccount() != null;
|
||||
|
||||
String errorCode = null;
|
||||
if (this.accountTransferDetails.transferType().isAccountTransfer()
|
||||
&& (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toSavingsAccount() == null)) {
|
||||
&& !(fromAccountValid && toAccountValid)) {
|
||||
errorCode = "not.account.transfer";
|
||||
} else if (this.accountTransferDetails.transferType().isLoanRepayment()
|
||||
&& (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toLoanAccount() == null)) {
|
||||
&& !(fromAccountValidForLoan && this.accountTransferDetails.toLoanAccount() != null)) {
|
||||
errorCode = "not.loan.repayment";
|
||||
}
|
||||
if (errorCode != null) {
|
||||
|
||||
+52
-2
@@ -35,6 +35,9 @@ import java.time.LocalDate;
|
||||
import java.time.MonthDay;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -61,8 +64,13 @@ public class StandingInstructionAssembler {
|
||||
BigDecimal amount = null;
|
||||
final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||
if (transferAmount != null) {
|
||||
final Money monetaryAmount = Money.of(accountTransferDetails.fromSavingsAccount().getCurrency(), transferAmount);
|
||||
amount = monetaryAmount.getAmount();
|
||||
final MonetaryCurrency currency = resolveCurrency(accountTransferDetails);
|
||||
if (currency != null) {
|
||||
final Money monetaryAmount = Money.of(currency, transferAmount);
|
||||
amount = monetaryAmount.getAmount();
|
||||
} else {
|
||||
amount = transferAmount;
|
||||
}
|
||||
}
|
||||
final Integer status = command.integerValueOfParameterNamed(statusParamName);
|
||||
final Integer priority = command.integerValueOfParameterNamed(priorityParamName);
|
||||
@@ -78,6 +86,22 @@ public class StandingInstructionAssembler {
|
||||
accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the currency from the "from" account — savings, loan (null), or current account.
|
||||
*/
|
||||
private MonetaryCurrency resolveCurrency(final AccountTransferDetails details) {
|
||||
final SavingsAccount fromSavings = details.fromSavingsAccount();
|
||||
if (fromSavings != null) {
|
||||
return fromSavings.getCurrency();
|
||||
}
|
||||
final CurrentAccount fromCurrent = details.fromCurrentAccount();
|
||||
if (fromCurrent != null) {
|
||||
return new MonetaryCurrency(fromCurrent.getCurrencyCode(), fromCurrent.getCurrencyDigits(),
|
||||
fromCurrent.getCurrencyMultiplesOf());
|
||||
}
|
||||
return null; // loan accounts don't have a direct currency on AccountTransferDetails
|
||||
}
|
||||
|
||||
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) {
|
||||
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command);
|
||||
assembleStandingInstruction(command, accountTransferDetails);
|
||||
@@ -90,4 +114,30 @@ public class StandingInstructionAssembler {
|
||||
return accountTransferDetails;
|
||||
}
|
||||
|
||||
// --- Current account methods ---
|
||||
|
||||
public AccountTransferDetails assembleCurrentToCurrentTransfer(final JsonCommand command) {
|
||||
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleCurrentToCurrentTransfer(command);
|
||||
assembleStandingInstruction(command, accountTransferDetails);
|
||||
return accountTransferDetails;
|
||||
}
|
||||
|
||||
public AccountTransferDetails assembleCurrentToSavingsTransfer(final JsonCommand command) {
|
||||
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleCurrentToSavingsTransfer(command);
|
||||
assembleStandingInstruction(command, accountTransferDetails);
|
||||
return accountTransferDetails;
|
||||
}
|
||||
|
||||
public AccountTransferDetails assembleSavingsToCurrentTransfer(final JsonCommand command) {
|
||||
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToCurrentTransfer(command);
|
||||
assembleStandingInstruction(command, accountTransferDetails);
|
||||
return accountTransferDetails;
|
||||
}
|
||||
|
||||
public AccountTransferDetails assembleCurrentToLoanTransfer(final JsonCommand command) {
|
||||
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleCurrentToLoanTransfer(command);
|
||||
assembleStandingInstruction(command, accountTransferDetails);
|
||||
return accountTransferDetails;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-7
@@ -66,12 +66,12 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet {
|
||||
List<Throwable> errors = new ArrayList<>();
|
||||
for (StandingInstructionData data : instructionData) {
|
||||
boolean isDueForTransfer = false;
|
||||
AccountTransferRecurrenceType recurrenceType = data.getRecurrenceType();
|
||||
StandingInstructionType instructionType = data.getInstructionType();
|
||||
AccountTransferRecurrenceType recurrenceType = data.recurrenceTypeEnum();
|
||||
StandingInstructionType instructionType = data.instructionTypeEnum();
|
||||
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
||||
if (recurrenceType.isPeriodicRecurrence()) {
|
||||
final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||
PeriodFrequencyType frequencyType = data.getRecurrenceFrequency();
|
||||
PeriodFrequencyType frequencyType = data.recurrenceFrequencyEnum();
|
||||
LocalDate startDate = data.getValidFrom();
|
||||
if (frequencyType.isMonthly()) {
|
||||
startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay());
|
||||
@@ -89,11 +89,11 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet {
|
||||
|
||||
}
|
||||
BigDecimal transactionAmount = data.getAmount();
|
||||
if (data.getToAccountType().isLoanAccount()
|
||||
if (data.toAccountTypeEnum() != null && data.toAccountTypeEnum().isLoanAccount()
|
||||
&& (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) {
|
||||
StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService
|
||||
.retriveLoanDuesData(data.getToAccount().getId());
|
||||
if (data.getInstructionType().isDuesAmoutTransfer()) {
|
||||
if (data.instructionTypeEnum().isDuesAmoutTransfer()) {
|
||||
transactionAmount = standingInstructionDuesData.totalDueAmount();
|
||||
}
|
||||
if (recurrenceType.isDuesRecurrence()) {
|
||||
@@ -106,9 +106,9 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet {
|
||||
final boolean isRegularTransaction = true;
|
||||
final boolean isExceptionForBalanceCheck = false;
|
||||
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount,
|
||||
data.getFromAccountType(), data.getToAccountType(), data.getFromAccount().getId(), data.getToAccount().getId(),
|
||||
data.fromAccountTypeEnum(), data.toAccountTypeEnum(), data.getFromAccount().getId(), data.getToAccount().getId(),
|
||||
data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null,
|
||||
data.getTransferType().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount,
|
||||
data.transferTypeEnum().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount,
|
||||
isRegularTransaction, isExceptionForBalanceCheck);
|
||||
final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId());
|
||||
|
||||
|
||||
+4
@@ -52,6 +52,10 @@ public final class AccountTransferEnumerations {
|
||||
optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(),
|
||||
PortfolioAccountType.SAVINGS.getCode(), "Savings Account");
|
||||
break;
|
||||
case CURRENT:
|
||||
optionData = new EnumOptionData(PortfolioAccountType.CURRENT.getValue().longValue(),
|
||||
PortfolioAccountType.CURRENT.getCode(), "Current Account");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+192
@@ -42,6 +42,8 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator;
|
||||
@@ -52,6 +54,10 @@ import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferTransaction;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
||||
import org.apache.fineract.portfolio.account.exception.DifferentCurrenciesException;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
@@ -88,6 +94,8 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final FineractProperties fineractProperties;
|
||||
private final CurrentAccountRepository currentAccountRepository;
|
||||
private final CurrentAccountTransactionRepository currentAccountTransactionRepository;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -448,6 +456,128 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
gsim.setParentDeposit(newBalance);
|
||||
gsimRepository.save(gsim);
|
||||
}
|
||||
|
||||
} else if (isCurrentToSavingsAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
||||
// --- Current Account → Savings Account ---
|
||||
final CurrentAccount fromCurrentAccount = currentAccountRepository
|
||||
.findById(accountTransferDTO.getFromAccountId())
|
||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getFromAccountId()));
|
||||
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler
|
||||
.assembleFrom(accountTransferDTO.getToAccountId(), backdatedTxnsAllowedTill);
|
||||
|
||||
final CurrentAccountTransaction withdrawal = performCurrentWithdrawal(fromCurrentAccount,
|
||||
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getTransactionDate());
|
||||
|
||||
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount,
|
||||
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(),
|
||||
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
|
||||
isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill);
|
||||
|
||||
final AccountTransferDetails newDetails = AccountTransferDetails.currentToSavingsTransfer(
|
||||
fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount,
|
||||
toSavingsAccount.office(), toSavingsAccount.getClient(), toSavingsAccount,
|
||||
accountTransferDTO.getTransferType());
|
||||
final Money amount = Money.of(toSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount());
|
||||
newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer(
|
||||
newDetails, null, deposit, accountTransferDTO.getTransactionDate(), amount,
|
||||
accountTransferDTO.getDescription()));
|
||||
this.accountTransferDetailRepository.saveAndFlush(newDetails);
|
||||
transferTransactionId = newDetails.getId();
|
||||
|
||||
} else if (isSavingsToCurrentAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
||||
// --- Savings Account → Current Account ---
|
||||
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler
|
||||
.assembleFrom(accountTransferDTO.getFromAccountId(), backdatedTxnsAllowedTill);
|
||||
final CurrentAccount toCurrentAccount = currentAccountRepository
|
||||
.findById(accountTransferDTO.getToAccountId())
|
||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getToAccountId()));
|
||||
|
||||
final SavingsTransactionBooleanValues txnBooleans = new SavingsTransactionBooleanValues(isAccountTransfer,
|
||||
isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), false,
|
||||
accountTransferDTO.isExceptionForBalanceCheck());
|
||||
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount,
|
||||
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(),
|
||||
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
|
||||
txnBooleans, backdatedTxnsAllowedTill);
|
||||
|
||||
performCurrentDeposit(toCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
||||
accountTransferDTO.getTransactionDate());
|
||||
|
||||
final AccountTransferDetails newDetails = AccountTransferDetails.savingsToCurrentTransfer(
|
||||
fromSavingsAccount.office(), fromSavingsAccount.getClient(), fromSavingsAccount,
|
||||
toCurrentAccount.getOffice(), toCurrentAccount.getClient(), toCurrentAccount,
|
||||
accountTransferDTO.getTransferType());
|
||||
final Money amount = Money.of(fromSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount());
|
||||
newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer(
|
||||
newDetails, withdrawal, null, accountTransferDTO.getTransactionDate(), amount,
|
||||
accountTransferDTO.getDescription()));
|
||||
this.accountTransferDetailRepository.saveAndFlush(newDetails);
|
||||
transferTransactionId = newDetails.getId();
|
||||
|
||||
} else if (isCurrentToCurrentAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
||||
// --- Current Account → Current Account ---
|
||||
final CurrentAccount fromCurrentAccount = currentAccountRepository
|
||||
.findById(accountTransferDTO.getFromAccountId())
|
||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getFromAccountId()));
|
||||
final CurrentAccount toCurrentAccount = currentAccountRepository
|
||||
.findById(accountTransferDTO.getToAccountId())
|
||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getToAccountId()));
|
||||
|
||||
performCurrentWithdrawal(fromCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
||||
accountTransferDTO.getTransactionDate());
|
||||
performCurrentDeposit(toCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
||||
accountTransferDTO.getTransactionDate());
|
||||
|
||||
final AccountTransferDetails newDetails = AccountTransferDetails.currentToCurrentTransfer(
|
||||
fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount,
|
||||
toCurrentAccount.getOffice(), toCurrentAccount.getClient(), toCurrentAccount,
|
||||
accountTransferDTO.getTransferType());
|
||||
final MonetaryCurrency currency = new MonetaryCurrency(fromCurrentAccount.getCurrencyCode(),
|
||||
fromCurrentAccount.getCurrencyDigits(), fromCurrentAccount.getCurrencyMultiplesOf());
|
||||
final Money amount = Money.of(currency, accountTransferDTO.getTransactionAmount());
|
||||
newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer(
|
||||
newDetails, null, null, accountTransferDTO.getTransactionDate(), amount,
|
||||
accountTransferDTO.getDescription()));
|
||||
this.accountTransferDetailRepository.saveAndFlush(newDetails);
|
||||
transferTransactionId = newDetails.getId();
|
||||
|
||||
} else if (isCurrentToLoanAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
||||
// --- Current Account → Loan Account ---
|
||||
final CurrentAccount fromCurrentAccount = currentAccountRepository
|
||||
.findById(accountTransferDTO.getFromAccountId())
|
||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getFromAccountId()));
|
||||
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
||||
|
||||
performCurrentWithdrawal(fromCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
||||
accountTransferDTO.getTransactionDate());
|
||||
|
||||
ExternalId externalId = externalIdFactory.create(accountTransferDTO.getTxnExternalId().getValue());
|
||||
final boolean isRecoveryRepayment = false;
|
||||
final Boolean isHolidayValidationDone = false;
|
||||
final String chargeRefundChargeType = null;
|
||||
LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT,
|
||||
toLoanAccount, accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
|
||||
accountTransferDTO.getPaymentDetail(), null, externalId, isRecoveryRepayment,
|
||||
chargeRefundChargeType, isAccountTransfer, null, isHolidayValidationDone);
|
||||
|
||||
final AccountTransferDetails newDetails = AccountTransferDetails.currentToLoanTransfer(
|
||||
fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount,
|
||||
toLoanAccount.getOffice(), toLoanAccount.client(), toLoanAccount,
|
||||
accountTransferDTO.getTransferType());
|
||||
final MonetaryCurrency currency = new MonetaryCurrency(fromCurrentAccount.getCurrencyCode(),
|
||||
fromCurrentAccount.getCurrencyDigits(), fromCurrentAccount.getCurrencyMultiplesOf());
|
||||
final Money amount = Money.of(currency, accountTransferDTO.getTransactionAmount());
|
||||
newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToLoanTransfer(
|
||||
newDetails, null, loanTransaction, accountTransferDTO.getTransactionDate(), amount,
|
||||
accountTransferDTO.getDescription()));
|
||||
this.accountTransferDetailRepository.saveAndFlush(newDetails);
|
||||
transferTransactionId = newDetails.getId();
|
||||
|
||||
} else {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.accounttransfer.loan.to.loan.not.supported",
|
||||
"Account transfer from loan to another loan is not supported");
|
||||
@@ -505,6 +635,68 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount();
|
||||
}
|
||||
|
||||
private boolean isCurrentToSavingsAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||
final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isCurrentAccount() && toAccountType.isSavingsAccount();
|
||||
}
|
||||
|
||||
private boolean isSavingsToCurrentAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||
final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isSavingsAccount() && toAccountType.isCurrentAccount();
|
||||
}
|
||||
|
||||
private boolean isCurrentToCurrentAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||
final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isCurrentAccount() && toAccountType.isCurrentAccount();
|
||||
}
|
||||
|
||||
private boolean isCurrentToLoanAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||
final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isCurrentAccount() && toAccountType.isLoanAccount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Withdraw amount from a current account and save the transaction.
|
||||
* Throws {@link GeneralPlatformDomainRuleException} if balance is insufficient.
|
||||
*/
|
||||
private CurrentAccountTransaction performCurrentWithdrawal(final CurrentAccount account, final BigDecimal amount,
|
||||
final LocalDate txnDate) {
|
||||
final BigDecimal balance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived() : BigDecimal.ZERO;
|
||||
final BigDecimal onHold = account.getOnHoldFundsDerived() != null ? account.getOnHoldFundsDerived() : BigDecimal.ZERO;
|
||||
final BigDecimal available = balance.subtract(onHold);
|
||||
if (!account.isAllowOverdraft() && amount.compareTo(available) > 0) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.current.account.insufficient.balance",
|
||||
"Insufficient balance in current account " + account.getId()
|
||||
+ ". Available: " + available + ", Requested: " + amount, account.getId());
|
||||
}
|
||||
final CurrentAccountTransaction txn = CurrentAccountTransaction.withdrawal(account, account.getOffice(), amount, txnDate, null);
|
||||
final BigDecimal newBalance = balance.subtract(amount);
|
||||
txn.setRunningBalanceDerived(newBalance);
|
||||
currentAccountTransactionRepository.saveAndFlush(txn);
|
||||
account.setAccountBalanceDerived(newBalance);
|
||||
account.setTotalWithdrawalsDerived(
|
||||
(account.getTotalWithdrawalsDerived() != null ? account.getTotalWithdrawalsDerived() : BigDecimal.ZERO).add(amount));
|
||||
currentAccountRepository.save(account);
|
||||
return txn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deposit amount into a current account and save the transaction.
|
||||
*/
|
||||
private CurrentAccountTransaction performCurrentDeposit(final CurrentAccount account, final BigDecimal amount,
|
||||
final LocalDate txnDate) {
|
||||
final BigDecimal balance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived() : BigDecimal.ZERO;
|
||||
final CurrentAccountTransaction txn = CurrentAccountTransaction.deposit(account, account.getOffice(), amount, txnDate, null);
|
||||
final BigDecimal newBalance = balance.add(amount);
|
||||
txn.setRunningBalanceDerived(newBalance);
|
||||
currentAccountTransactionRepository.saveAndFlush(txn);
|
||||
account.setAccountBalanceDerived(newBalance);
|
||||
account.setTotalDepositsDerived(
|
||||
(account.getTotalDepositsDerived() != null ? account.getTotalDepositsDerived() : BigDecimal.ZERO).add(amount));
|
||||
currentAccountRepository.save(account);
|
||||
return txn;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult refundByTransfer(JsonCommand command) {
|
||||
|
||||
+86
@@ -43,12 +43,14 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
|
||||
private final PortfolioSavingsAccountMapper savingsAccountMapper;
|
||||
private final PortfolioLoanAccountMapper loanAccountMapper;
|
||||
private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper;
|
||||
private final PortfolioCurrentAccountMapper currentAccountMapper;
|
||||
|
||||
public PortfolioAccountReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.savingsAccountMapper = new PortfolioSavingsAccountMapper();
|
||||
this.loanAccountMapper = new PortfolioLoanAccountMapper();
|
||||
this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(sqlGenerator);
|
||||
this.currentAccountMapper = new PortfolioCurrentAccountMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,6 +88,15 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
|
||||
|
||||
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
|
||||
break;
|
||||
case CURRENT:
|
||||
sql = "select " + this.currentAccountMapper.schema() + " where ca.id = ?";
|
||||
if (currencyCode != null) {
|
||||
sql += " and ca.currency_code = ?";
|
||||
sqlParams = new Object[] { accountId, currencyCode };
|
||||
}
|
||||
|
||||
accountData = this.jdbcTemplate.queryForObject(sql, this.currentAccountMapper, sqlParams);
|
||||
break;
|
||||
}
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new AccountTransferNotFoundException(accountId, e);
|
||||
@@ -157,6 +168,23 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
|
||||
|
||||
accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams.toArray()); // NOSONAR
|
||||
break;
|
||||
case CURRENT:
|
||||
sql = "select " + this.currentAccountMapper.schema() + " where ";
|
||||
if (portfolioAccountDTO.getClientId() != null) {
|
||||
sql += " ca.client_id = ? and ca.status_enum in (?) ";
|
||||
sqlParams.add(portfolioAccountDTO.getClientId());
|
||||
sqlParams.add(defaultAccountStatus);
|
||||
} else {
|
||||
sql += " ca.status_enum in (?) ";
|
||||
sqlParams.add(defaultAccountStatus);
|
||||
}
|
||||
if (portfolioAccountDTO.getCurrencyCode() != null) {
|
||||
sql += " and ca.currency_code = ?";
|
||||
sqlParams.add(portfolioAccountDTO.getCurrencyCode());
|
||||
}
|
||||
|
||||
accounts = this.jdbcTemplate.query(sql, this.currentAccountMapper, sqlParams.toArray()); // NOSONAR
|
||||
break;
|
||||
}
|
||||
|
||||
return accounts;
|
||||
@@ -393,4 +421,62 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
|
||||
|
||||
return accountData;
|
||||
}
|
||||
|
||||
private static final class PortfolioCurrentAccountMapper implements RowMapper<PortfolioAccountData> {
|
||||
|
||||
private final String schemaSql;
|
||||
|
||||
PortfolioCurrentAccountMapper() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("ca.id as id, ca.account_no as accountNo, ca.external_id as externalId, ");
|
||||
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
|
||||
sqlBuilder.append("null as groupId, null as groupName, ");
|
||||
sqlBuilder.append("cp.id as productId, cp.name as productName, ");
|
||||
sqlBuilder.append("null as fieldOfficerId, null as fieldOfficerName, ");
|
||||
sqlBuilder.append("ca.currency_code as currencyCode, ca.currency_digits as currencyDigits, ");
|
||||
sqlBuilder.append("ca.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("from m_current_account ca ");
|
||||
sqlBuilder.append("join m_current_product cp ON ca.product_id = cp.id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = ca.currency_code ");
|
||||
sqlBuilder.append("left join m_client c ON c.id = ca.client_id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schemaSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final String accountNo = rs.getString("accountNo");
|
||||
final String externalId = rs.getString("externalId");
|
||||
|
||||
final Long groupId = JdbcSupport.getLong(rs, "groupId");
|
||||
final String groupName = rs.getString("groupName");
|
||||
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
||||
final String clientName = rs.getString("clientName");
|
||||
|
||||
final Long productId = rs.getLong("productId");
|
||||
final String productName = rs.getString("productName");
|
||||
|
||||
final Long fieldOfficerId = JdbcSupport.getLong(rs, "fieldOfficerId");
|
||||
final String fieldOfficerName = rs.getString("fieldOfficerName");
|
||||
|
||||
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 new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||
fieldOfficerId, fieldOfficerName, currency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+40
-6
@@ -109,20 +109,21 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
||||
|
||||
final EnumOptionData loanAccountType = accountType(PortfolioAccountType.LOAN);
|
||||
final EnumOptionData savingsAccountType = accountType(PortfolioAccountType.SAVINGS);
|
||||
final EnumOptionData currentAccountType = accountType(PortfolioAccountType.CURRENT);
|
||||
|
||||
final Integer mostRelevantFromAccountType = fromAccountType;
|
||||
Collection<EnumOptionData> fromAccountTypeOptions = null;
|
||||
Collection<EnumOptionData> toAccountTypeOptions = null;
|
||||
|
||||
if (accountTransferType.isAccountTransfer()) {
|
||||
fromAccountTypeOptions = Arrays.asList(savingsAccountType);
|
||||
toAccountTypeOptions = Arrays.asList(savingsAccountType);
|
||||
fromAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType);
|
||||
toAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType);
|
||||
} else if (accountTransferType.isLoanRepayment()) {
|
||||
fromAccountTypeOptions = Arrays.asList(savingsAccountType);
|
||||
fromAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType);
|
||||
toAccountTypeOptions = Arrays.asList(loanAccountType);
|
||||
} else {
|
||||
fromAccountTypeOptions = Arrays.asList(savingsAccountType, loanAccountType);
|
||||
toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType);
|
||||
fromAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType, loanAccountType);
|
||||
toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType, currentAccountType);
|
||||
}
|
||||
final Integer mostRelevantToAccountType = toAccountType;
|
||||
|
||||
@@ -152,6 +153,8 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
||||
Integer accountType;
|
||||
if (mostRelevantFromAccountType == 1) {
|
||||
accountType = PortfolioAccountType.LOAN.getValue();
|
||||
} else if (mostRelevantFromAccountType == 3) {
|
||||
accountType = PortfolioAccountType.CURRENT.getValue();
|
||||
} else {
|
||||
accountType = PortfolioAccountType.SAVINGS.getValue();
|
||||
}
|
||||
@@ -301,6 +304,9 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
||||
} else if (accountType.isLoanAccount()) {
|
||||
sqlBuilder.append(" fromloanacc.id=? ");
|
||||
paramObj.add(standingInstructionDTO.fromAccount());
|
||||
} else if (accountType.isCurrentAccount()) {
|
||||
sqlBuilder.append(" fromcuracc.id=? ");
|
||||
paramObj.add(standingInstructionDTO.fromAccount());
|
||||
}
|
||||
addAndCaluse = true;
|
||||
}
|
||||
@@ -387,7 +393,11 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
||||
sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,");
|
||||
sqlBuilder.append("tosp.id as toProductId, tosp.name as toProductName, ");
|
||||
sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo, ");
|
||||
sqlBuilder.append("tolp.id as toLoanProductId, tolp.name as toLoanProductName ");
|
||||
sqlBuilder.append("tolp.id as toLoanProductId, tolp.name as toLoanProductName, ");
|
||||
sqlBuilder.append("fromcuracc.id as fromCurrentAccountId, fromcuracc.account_no as fromCurrentAccountNo, ");
|
||||
sqlBuilder.append("fromcp.id as fromCurrentProductId, fromcp.name as fromCurrentProductName, ");
|
||||
sqlBuilder.append("tocuracc.id as toCurrentAccountId, tocuracc.account_no as toCurrentAccountNo, ");
|
||||
sqlBuilder.append("tocp.id as toCurrentProductId, tocp.name as toCurrentProductName ");
|
||||
sqlBuilder.append(" FROM m_account_transfer_standing_instructions atsi ");
|
||||
sqlBuilder.append("join m_account_transfer_details atd on atd.id = atsi.account_transfer_details_id ");
|
||||
sqlBuilder.append("join m_office fromoff on fromoff.id = atd.from_office_id ");
|
||||
@@ -402,6 +412,10 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
||||
sqlBuilder.append("left join m_savings_product tosp ON tosavacc.product_id = tosp.id ");
|
||||
sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = atd.to_loan_account_id ");
|
||||
sqlBuilder.append("left join m_product_loan tolp ON toloanacc.product_id = tolp.id ");
|
||||
sqlBuilder.append("left join m_current_account fromcuracc on fromcuracc.id = atd.from_current_account_id ");
|
||||
sqlBuilder.append("left join m_current_product fromcp ON fromcuracc.product_id = fromcp.id ");
|
||||
sqlBuilder.append("left join m_current_account tocuracc on tocuracc.id = atd.to_current_account_id ");
|
||||
sqlBuilder.append("left join m_current_product tocp ON tocuracc.product_id = tocp.id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -489,6 +503,16 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
||||
fromAccount = new PortfolioAccountData(fromLoanAccountId, fromLoanAccountNo, null, null, null, null, null,
|
||||
fromLoanProductId, fromLoanProductName, null, null, null);
|
||||
fromAccountType = accountType(PortfolioAccountType.LOAN);
|
||||
} else {
|
||||
final Long fromCurrentAccountId = JdbcSupport.getLong(rs, "fromCurrentAccountId");
|
||||
if (fromCurrentAccountId != null) {
|
||||
final String fromCurrentAccountNo = rs.getString("fromCurrentAccountNo");
|
||||
final Long fromCurrentProductId = JdbcSupport.getLong(rs, "fromCurrentProductId");
|
||||
final String fromCurrentProductName = rs.getString("fromCurrentProductName");
|
||||
fromAccount = new PortfolioAccountData(fromCurrentAccountId, fromCurrentAccountNo, null, null, null, null, null,
|
||||
fromCurrentProductId, fromCurrentProductName, null, null, null);
|
||||
fromAccountType = accountType(PortfolioAccountType.CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
PortfolioAccountData toAccount = null;
|
||||
@@ -510,6 +534,16 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
||||
toAccount = new PortfolioAccountData(toLoanAccountId, toLoanAccountNo, null, null, null, null, null, toLoanProductId,
|
||||
toLoanProductName, null, null, null);
|
||||
toAccountType = accountType(PortfolioAccountType.LOAN);
|
||||
} else {
|
||||
final Long toCurrentAccountId = JdbcSupport.getLong(rs, "toCurrentAccountId");
|
||||
if (toCurrentAccountId != null) {
|
||||
final String toCurrentAccountNo = rs.getString("toCurrentAccountNo");
|
||||
final Long toCurrentProductId = JdbcSupport.getLong(rs, "toCurrentProductId");
|
||||
final String toCurrentProductName = rs.getString("toCurrentProductName");
|
||||
toAccount = new PortfolioAccountData(toCurrentAccountId, toCurrentAccountNo, null, null, null, null, null,
|
||||
toCurrentProductId, toCurrentProductName, null, null, null);
|
||||
toAccountType = accountType(PortfolioAccountType.CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
return StandingInstructionData.instance(id, accountDetailId, name, fromOffice, toOffice, fromClient, toClient, fromAccountType,
|
||||
|
||||
+36
-2
@@ -82,11 +82,25 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst
|
||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||
} else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) {
|
||||
|
||||
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command);
|
||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||
|
||||
} else if (isCurrentToCurrentAccountTransfer(fromAccountType, toAccountType)) {
|
||||
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToCurrentTransfer(command);
|
||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||
} else if (isCurrentToSavingsAccountTransfer(fromAccountType, toAccountType)) {
|
||||
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToSavingsTransfer(command);
|
||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||
} else if (isSavingsToCurrentAccountTransfer(fromAccountType, toAccountType)) {
|
||||
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToCurrentTransfer(command);
|
||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||
} else if (isCurrentToLoanAccountTransfer(fromAccountType, toAccountType)) {
|
||||
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToLoanTransfer(command);
|
||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||
}
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||
final Throwable throwable = dve.getMostSpecificCause();
|
||||
@@ -122,6 +136,26 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst
|
||||
return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount();
|
||||
}
|
||||
|
||||
private boolean isCurrentToCurrentAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||
final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isCurrentAccount() && toAccountType.isCurrentAccount();
|
||||
}
|
||||
|
||||
private boolean isCurrentToSavingsAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||
final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isCurrentAccount() && toAccountType.isSavingsAccount();
|
||||
}
|
||||
|
||||
private boolean isSavingsToCurrentAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||
final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isSavingsAccount() && toAccountType.isCurrentAccount();
|
||||
}
|
||||
|
||||
private boolean isCurrentToLoanAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||
final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isCurrentAccount() && toAccountType.isLoanAccount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult update(final Long id, final JsonCommand command) {
|
||||
this.standingInstructionDataValidator.validateForUpdate(command);
|
||||
|
||||
+6
-2
@@ -26,6 +26,8 @@ import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecific
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
||||
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator;
|
||||
import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler;
|
||||
@@ -91,11 +93,13 @@ public class AccountConfiguration {
|
||||
LoanAccountDomainService loanAccountDomainService, SavingsAccountWritePlatformService savingsAccountWritePlatformService,
|
||||
AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService,
|
||||
GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory,
|
||||
FineractProperties fineractProperties) {
|
||||
FineractProperties fineractProperties, CurrentAccountRepository currentAccountRepository,
|
||||
CurrentAccountTransactionRepository currentAccountTransactionRepository) {
|
||||
return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler,
|
||||
accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler,
|
||||
loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService,
|
||||
gsimRepository, configurationDomainService, externalIdFactory, fineractProperties);
|
||||
gsimRepository, configurationDomainService, externalIdFactory, fineractProperties,
|
||||
currentAccountRepository, currentAccountTransactionRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -407,7 +407,7 @@ server.tomcat.mbeanregistry.enabled=${FINERACT_SERVER_TOMCAT_MBEANREGISTRY_ENABL
|
||||
spring.datasource.hikari.driverClassName=${FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME:org.postgresql.Driver}
|
||||
spring.datasource.hikari.jdbcUrl=${FINERACT_HIKARI_JDBC_URL:jdbc:postgresql://localhost:5432/fineract_tenants}
|
||||
spring.datasource.hikari.username=${FINERACT_HIKARI_USERNAME:postgres}
|
||||
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:root}
|
||||
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:313863}
|
||||
spring.datasource.hikari.minimumIdle=${FINERACT_HIKARI_MINIMUM_IDLE:3}
|
||||
spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10}
|
||||
spring.datasource.hikari.idleTimeout=${FINERACT_HIKARI_IDLE_TIMEOUT:60000}
|
||||
|
||||
@@ -317,4 +317,5 @@
|
||||
<include file="parts/0298_unique_external_id_on_current_account.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0299_seed_indian_states.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0300_rename_fd_base_interest_rate_tables_to_cbs.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0301_add_current_account_to_transfer_details.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
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
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!-- Add from_current_account_id column to m_account_transfer_details -->
|
||||
<changeSet id="0301-1" author="cbs">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not>
|
||||
<columnExists tableName="m_account_transfer_details" columnName="from_current_account_id"/>
|
||||
</not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_account_transfer_details">
|
||||
<column name="from_current_account_id" type="BIGINT">
|
||||
<constraints nullable="true"
|
||||
foreignKeyName="FK_atd_from_current_account"
|
||||
references="m_current_account(id)"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<!-- Add to_current_account_id column to m_account_transfer_details -->
|
||||
<changeSet id="0301-2" author="cbs">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not>
|
||||
<columnExists tableName="m_account_transfer_details" columnName="to_current_account_id"/>
|
||||
</not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_account_transfer_details">
|
||||
<column name="to_current_account_id" type="BIGINT">
|
||||
<constraints nullable="true"
|
||||
foreignKeyName="FK_atd_to_current_account"
|
||||
references="m_current_account(id)"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user