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"), //
|
INVALID(0, "accountType.invalid"), //
|
||||||
LOAN(1, "accountType.loan"), //
|
LOAN(1, "accountType.loan"), //
|
||||||
SAVINGS(2, "accountType.savings"), //
|
SAVINGS(2, "accountType.savings"), //
|
||||||
|
CURRENT(3, "accountType.current"), //
|
||||||
;
|
;
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
@@ -52,6 +53,9 @@ public enum PortfolioAccountType {
|
|||||||
case 2:
|
case 2:
|
||||||
enumType = SAVINGS;
|
enumType = SAVINGS;
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
enumType = CURRENT;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
enumType = INVALID;
|
enumType = INVALID;
|
||||||
}
|
}
|
||||||
@@ -68,4 +72,8 @@ public enum PortfolioAccountType {
|
|||||||
public boolean isLoanAccount() {
|
public boolean isLoanAccount() {
|
||||||
return this.equals(LOAN);
|
return this.equals(LOAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCurrentAccount() {
|
||||||
|
return this.equals(CURRENT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -207,10 +207,10 @@ public class StandingInstructionApiResource {
|
|||||||
if (associationParameters.contains("template")) {
|
if (associationParameters.contains("template")) {
|
||||||
final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate(
|
final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate(
|
||||||
standingInstructionData.getFromClient().getOfficeId(), standingInstructionData.getFromClient().getId(),
|
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.getToClient().getOfficeId(), standingInstructionData.getToClient().getId(),
|
||||||
standingInstructionData.getToAccount().getId(), standingInstructionData.getToAccountType().getValue(),
|
standingInstructionData.getToAccount().getId(), standingInstructionData.toAccountTypeEnum().getValue(),
|
||||||
standingInstructionData.getTransferType().getValue());
|
standingInstructionData.transferTypeEnum().getValue());
|
||||||
standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData);
|
standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -59,7 +59,7 @@ public class AccountTransfersDetailDataValidator {
|
|||||||
|
|
||||||
final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element);
|
final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element);
|
||||||
baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType).notNull()
|
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);
|
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||||
baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero();
|
||||||
@@ -72,7 +72,7 @@ public class AccountTransfersDetailDataValidator {
|
|||||||
|
|
||||||
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
|
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
|
||||||
baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType).notNull().isOneOfTheseValues(Integer.valueOf(1),
|
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;
|
private final Long accountDetailId;
|
||||||
@Getter
|
@Getter
|
||||||
private final String name;
|
private final String name;
|
||||||
|
@Getter
|
||||||
private final OfficeData fromOffice;
|
private final OfficeData fromOffice;
|
||||||
@Getter
|
@Getter
|
||||||
private final ClientData fromClient;
|
private final ClientData fromClient;
|
||||||
|
@Getter
|
||||||
private final EnumOptionData fromAccountType;
|
private final EnumOptionData fromAccountType;
|
||||||
@Getter
|
@Getter
|
||||||
private final PortfolioAccountData fromAccount;
|
private final PortfolioAccountData fromAccount;
|
||||||
|
@Getter
|
||||||
private final OfficeData toOffice;
|
private final OfficeData toOffice;
|
||||||
@Getter
|
@Getter
|
||||||
private final ClientData toClient;
|
private final ClientData toClient;
|
||||||
|
@Getter
|
||||||
private final EnumOptionData toAccountType;
|
private final EnumOptionData toAccountType;
|
||||||
@Getter
|
@Getter
|
||||||
private final PortfolioAccountData toAccount;
|
private final PortfolioAccountData toAccount;
|
||||||
|
@Getter
|
||||||
private final EnumOptionData transferType;
|
private final EnumOptionData transferType;
|
||||||
|
@Getter
|
||||||
private final EnumOptionData priority;
|
private final EnumOptionData priority;
|
||||||
|
@Getter
|
||||||
private final EnumOptionData instructionType;
|
private final EnumOptionData instructionType;
|
||||||
@Getter
|
@Getter
|
||||||
private final EnumOptionData status;
|
private final EnumOptionData status;
|
||||||
@@ -69,27 +76,46 @@ public final class StandingInstructionData {
|
|||||||
private final BigDecimal amount;
|
private final BigDecimal amount;
|
||||||
@Getter
|
@Getter
|
||||||
private final LocalDate validFrom;
|
private final LocalDate validFrom;
|
||||||
|
@Getter
|
||||||
private final LocalDate validTill;
|
private final LocalDate validTill;
|
||||||
|
@Getter
|
||||||
private final EnumOptionData recurrenceType;
|
private final EnumOptionData recurrenceType;
|
||||||
|
@Getter
|
||||||
private final EnumOptionData recurrenceFrequency;
|
private final EnumOptionData recurrenceFrequency;
|
||||||
@Getter
|
@Getter
|
||||||
private final Integer recurrenceInterval;
|
private final Integer recurrenceInterval;
|
||||||
|
@Getter
|
||||||
private final MonthDay recurrenceOnMonthDay;
|
private final MonthDay recurrenceOnMonthDay;
|
||||||
|
@Getter
|
||||||
private final Page<AccountTransferData> transactions;
|
private final Page<AccountTransferData> transactions;
|
||||||
|
|
||||||
|
@Getter
|
||||||
private final Collection<OfficeData> fromOfficeOptions;
|
private final Collection<OfficeData> fromOfficeOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<ClientData> fromClientOptions;
|
private final Collection<ClientData> fromClientOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<EnumOptionData> fromAccountTypeOptions;
|
private final Collection<EnumOptionData> fromAccountTypeOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<PortfolioAccountData> fromAccountOptions;
|
private final Collection<PortfolioAccountData> fromAccountOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<OfficeData> toOfficeOptions;
|
private final Collection<OfficeData> toOfficeOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<ClientData> toClientOptions;
|
private final Collection<ClientData> toClientOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<EnumOptionData> toAccountTypeOptions;
|
private final Collection<EnumOptionData> toAccountTypeOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<PortfolioAccountData> toAccountOptions;
|
private final Collection<PortfolioAccountData> toAccountOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<EnumOptionData> transferTypeOptions;
|
private final Collection<EnumOptionData> transferTypeOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<EnumOptionData> statusOptions;
|
private final Collection<EnumOptionData> statusOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<EnumOptionData> instructionTypeOptions;
|
private final Collection<EnumOptionData> instructionTypeOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<EnumOptionData> priorityOptions;
|
private final Collection<EnumOptionData> priorityOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<EnumOptionData> recurrenceTypeOptions;
|
private final Collection<EnumOptionData> recurrenceTypeOptions;
|
||||||
|
@Getter
|
||||||
private final Collection<EnumOptionData> recurrenceFrequencyOptions;
|
private final Collection<EnumOptionData> recurrenceFrequencyOptions;
|
||||||
|
|
||||||
public static StandingInstructionData template(final Collection<OfficeData> fromOfficeOptions,
|
public static StandingInstructionData template(final Collection<OfficeData> fromOfficeOptions,
|
||||||
@@ -282,28 +308,32 @@ public final class StandingInstructionData {
|
|||||||
instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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() {
|
public Integer toTransferType() {
|
||||||
Integer transferType = null;
|
Integer transferType = null;
|
||||||
AccountTransferType accountTransferType = getTransferType();
|
AccountTransferType accountTransferType = transferTypeEnum();
|
||||||
if (accountTransferType.isChargePayment()) {
|
if (accountTransferType != null && accountTransferType.isChargePayment()) {
|
||||||
transferType = LoanTransactionType.CHARGE_PAYMENT.getValue();
|
transferType = LoanTransactionType.CHARGE_PAYMENT.getValue();
|
||||||
} else if (accountTransferType.isLoanRepayment()) {
|
} else if (accountTransferType != null && accountTransferType.isLoanRepayment()) {
|
||||||
transferType = LoanTransactionType.REPAYMENT.getValue();
|
transferType = LoanTransactionType.REPAYMENT.getValue();
|
||||||
}
|
}
|
||||||
return transferType;
|
return transferType;
|
||||||
|
|||||||
+4
-2
@@ -171,7 +171,8 @@ public class StandingInstructionDataValidator {
|
|||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull();
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull();
|
||||||
|
|
||||||
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
|
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)
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
|
||||||
.notNull().inMinMaxRange(1, 1);
|
.notNull().inMinMaxRange(1, 1);
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
|
||||||
@@ -192,7 +193,8 @@ public class StandingInstructionDataValidator {
|
|||||||
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) {
|
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) {
|
||||||
errorCode = "not.account.transfer";
|
errorCode = "not.account.transfer";
|
||||||
} else if (accountTransferType.isLoanRepayment()
|
} else if (accountTransferType.isLoanRepayment()
|
||||||
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isSavingsAccount())) {
|
&& (fromPortfolioAccountType.isLoanAccount()
|
||||||
|
|| (toPortfolioAccountType.isSavingsAccount() || toPortfolioAccountType.isCurrentAccount()))) {
|
||||||
errorCode = "not.loan.repayment";
|
errorCode = "not.loan.repayment";
|
||||||
}
|
}
|
||||||
if (errorCode != null) {
|
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.organisation.office.domain.OfficeRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
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.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
@@ -49,16 +51,19 @@ public class AccountTransferDetailAssembler {
|
|||||||
private final SavingsAccountAssembler savingsAccountAssembler;
|
private final SavingsAccountAssembler savingsAccountAssembler;
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
private final LoanAssembler loanAccountAssembler;
|
private final LoanAssembler loanAccountAssembler;
|
||||||
|
private final CurrentAccountRepository currentAccountRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository,
|
public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository,
|
||||||
final OfficeRepositoryWrapper officeRepositoryWrapper, final SavingsAccountAssembler savingsAccountAssembler,
|
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.clientRepository = clientRepository;
|
||||||
this.officeRepositoryWrapper = officeRepositoryWrapper;
|
this.officeRepositoryWrapper = officeRepositoryWrapper;
|
||||||
this.savingsAccountAssembler = savingsAccountAssembler;
|
this.savingsAccountAssembler = savingsAccountAssembler;
|
||||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
this.loanAccountAssembler = loanAccountAssembler;
|
this.loanAccountAssembler = loanAccountAssembler;
|
||||||
|
this.currentAccountRepository = currentAccountRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) {
|
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) {
|
||||||
@@ -211,4 +216,99 @@ public class AccountTransferDetailAssembler {
|
|||||||
return AccountTransferDetails.loanToLoanTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toLoanAccount,
|
return AccountTransferDetails.loanToLoanTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toLoanAccount,
|
||||||
transferType);
|
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.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
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.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
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)
|
@JoinColumn(name = "from_loan_account_id", nullable = true)
|
||||||
private Loan fromLoanAccount;
|
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")
|
@Column(name = "transfer_type")
|
||||||
private Integer transferType;
|
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,
|
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
|
||||||
Integer transferType) {
|
Integer transferType) {
|
||||||
|
|
||||||
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, toSavingsAccount, null,
|
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, toSavingsAccount,
|
||||||
transferType, null);
|
null, null, transferType, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient,
|
public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient,
|
||||||
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount,
|
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount,
|
||||||
Integer transferType) {
|
Integer transferType) {
|
||||||
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, null, toLoanAccount,
|
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, null, toLoanAccount,
|
||||||
transferType, null);
|
null, transferType, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AccountTransferDetails loanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount,
|
public static AccountTransferDetails loanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount,
|
||||||
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, Integer transferType) {
|
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, Integer transferType) {
|
||||||
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, toSavingsAccount, null,
|
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, null, toOffice, toClient, toSavingsAccount, null,
|
||||||
transferType, null);
|
null, transferType, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AccountTransferDetails() {
|
protected AccountTransferDetails() {
|
||||||
@@ -106,17 +115,21 @@ public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private AccountTransferDetails(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount,
|
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 fromLoanAccount, final CurrentAccount fromCurrentAccount,
|
||||||
final Loan toLoanAccount, final Integer transferType,
|
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
|
||||||
|
final Loan toLoanAccount, final CurrentAccount toCurrentAccount,
|
||||||
|
final Integer transferType,
|
||||||
final AccountTransferStandingInstruction accountTransferStandingInstruction) {
|
final AccountTransferStandingInstruction accountTransferStandingInstruction) {
|
||||||
this.fromOffice = fromOffice;
|
this.fromOffice = fromOffice;
|
||||||
this.fromClient = fromClient;
|
this.fromClient = fromClient;
|
||||||
this.fromSavingsAccount = fromSavingsAccount;
|
this.fromSavingsAccount = fromSavingsAccount;
|
||||||
this.fromLoanAccount = fromLoanAccount;
|
this.fromLoanAccount = fromLoanAccount;
|
||||||
|
this.fromCurrentAccount = fromCurrentAccount;
|
||||||
this.toOffice = toOffice;
|
this.toOffice = toOffice;
|
||||||
this.toClient = toClient;
|
this.toClient = toClient;
|
||||||
this.toSavingsAccount = toSavingsAccount;
|
this.toSavingsAccount = toSavingsAccount;
|
||||||
this.toLoanAccount = toLoanAccount;
|
this.toLoanAccount = toLoanAccount;
|
||||||
|
this.toCurrentAccount = toCurrentAccount;
|
||||||
this.transferType = transferType;
|
this.transferType = transferType;
|
||||||
this.accountTransferStandingInstruction = accountTransferStandingInstruction;
|
this.accountTransferStandingInstruction = accountTransferStandingInstruction;
|
||||||
}
|
}
|
||||||
@@ -145,6 +158,14 @@ public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
|
|||||||
return this.fromLoanAccount;
|
return this.fromLoanAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CurrentAccount fromCurrentAccount() {
|
||||||
|
return this.fromCurrentAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CurrentAccount toCurrentAccount() {
|
||||||
|
return this.toCurrentAccount;
|
||||||
|
}
|
||||||
|
|
||||||
public AccountTransferStandingInstruction accountTransferStandingInstruction() {
|
public AccountTransferStandingInstruction accountTransferStandingInstruction() {
|
||||||
return this.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,
|
public static AccountTransferDetails loanToLoanTransfer(Office fromOffice, Client fromClient, Loan fromLoanAccount, Office toOffice,
|
||||||
Client toClient, Loan toLoanAccount, Integer transferType) {
|
Client toClient, Loan toLoanAccount, Integer transferType) {
|
||||||
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, null, toLoanAccount,
|
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, null, toOffice, toClient, null, toLoanAccount,
|
||||||
transferType, null);
|
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() {
|
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(instructionTypeParamName).value(this.instructionType).notNull().inMinMaxRange(1, 1);
|
||||||
baseDataValidator.reset().parameter(recurrenceTypeParamName).value(this.recurrenceType).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();
|
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;
|
String errorCode = null;
|
||||||
if (this.accountTransferDetails.transferType().isAccountTransfer()
|
if (this.accountTransferDetails.transferType().isAccountTransfer()
|
||||||
&& (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toSavingsAccount() == null)) {
|
&& !(fromAccountValid && toAccountValid)) {
|
||||||
errorCode = "not.account.transfer";
|
errorCode = "not.account.transfer";
|
||||||
} else if (this.accountTransferDetails.transferType().isLoanRepayment()
|
} else if (this.accountTransferDetails.transferType().isLoanRepayment()
|
||||||
&& (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toLoanAccount() == null)) {
|
&& !(fromAccountValidForLoan && this.accountTransferDetails.toLoanAccount() != null)) {
|
||||||
errorCode = "not.loan.repayment";
|
errorCode = "not.loan.repayment";
|
||||||
}
|
}
|
||||||
if (errorCode != null) {
|
if (errorCode != null) {
|
||||||
|
|||||||
+52
-2
@@ -35,6 +35,9 @@ import java.time.LocalDate;
|
|||||||
import java.time.MonthDay;
|
import java.time.MonthDay;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -61,8 +64,13 @@ public class StandingInstructionAssembler {
|
|||||||
BigDecimal amount = null;
|
BigDecimal amount = null;
|
||||||
final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName);
|
final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||||
if (transferAmount != null) {
|
if (transferAmount != null) {
|
||||||
final Money monetaryAmount = Money.of(accountTransferDetails.fromSavingsAccount().getCurrency(), transferAmount);
|
final MonetaryCurrency currency = resolveCurrency(accountTransferDetails);
|
||||||
amount = monetaryAmount.getAmount();
|
if (currency != null) {
|
||||||
|
final Money monetaryAmount = Money.of(currency, transferAmount);
|
||||||
|
amount = monetaryAmount.getAmount();
|
||||||
|
} else {
|
||||||
|
amount = transferAmount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final Integer status = command.integerValueOfParameterNamed(statusParamName);
|
final Integer status = command.integerValueOfParameterNamed(statusParamName);
|
||||||
final Integer priority = command.integerValueOfParameterNamed(priorityParamName);
|
final Integer priority = command.integerValueOfParameterNamed(priorityParamName);
|
||||||
@@ -78,6 +86,22 @@ public class StandingInstructionAssembler {
|
|||||||
accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction);
|
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) {
|
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) {
|
||||||
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command);
|
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command);
|
||||||
assembleStandingInstruction(command, accountTransferDetails);
|
assembleStandingInstruction(command, accountTransferDetails);
|
||||||
@@ -90,4 +114,30 @@ public class StandingInstructionAssembler {
|
|||||||
return accountTransferDetails;
|
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<>();
|
List<Throwable> errors = new ArrayList<>();
|
||||||
for (StandingInstructionData data : instructionData) {
|
for (StandingInstructionData data : instructionData) {
|
||||||
boolean isDueForTransfer = false;
|
boolean isDueForTransfer = false;
|
||||||
AccountTransferRecurrenceType recurrenceType = data.getRecurrenceType();
|
AccountTransferRecurrenceType recurrenceType = data.recurrenceTypeEnum();
|
||||||
StandingInstructionType instructionType = data.getInstructionType();
|
StandingInstructionType instructionType = data.instructionTypeEnum();
|
||||||
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
||||||
if (recurrenceType.isPeriodicRecurrence()) {
|
if (recurrenceType.isPeriodicRecurrence()) {
|
||||||
final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||||
PeriodFrequencyType frequencyType = data.getRecurrenceFrequency();
|
PeriodFrequencyType frequencyType = data.recurrenceFrequencyEnum();
|
||||||
LocalDate startDate = data.getValidFrom();
|
LocalDate startDate = data.getValidFrom();
|
||||||
if (frequencyType.isMonthly()) {
|
if (frequencyType.isMonthly()) {
|
||||||
startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay());
|
startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay());
|
||||||
@@ -89,11 +89,11 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
BigDecimal transactionAmount = data.getAmount();
|
BigDecimal transactionAmount = data.getAmount();
|
||||||
if (data.getToAccountType().isLoanAccount()
|
if (data.toAccountTypeEnum() != null && data.toAccountTypeEnum().isLoanAccount()
|
||||||
&& (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) {
|
&& (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) {
|
||||||
StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService
|
StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService
|
||||||
.retriveLoanDuesData(data.getToAccount().getId());
|
.retriveLoanDuesData(data.getToAccount().getId());
|
||||||
if (data.getInstructionType().isDuesAmoutTransfer()) {
|
if (data.instructionTypeEnum().isDuesAmoutTransfer()) {
|
||||||
transactionAmount = standingInstructionDuesData.totalDueAmount();
|
transactionAmount = standingInstructionDuesData.totalDueAmount();
|
||||||
}
|
}
|
||||||
if (recurrenceType.isDuesRecurrence()) {
|
if (recurrenceType.isDuesRecurrence()) {
|
||||||
@@ -106,9 +106,9 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet {
|
|||||||
final boolean isRegularTransaction = true;
|
final boolean isRegularTransaction = true;
|
||||||
final boolean isExceptionForBalanceCheck = false;
|
final boolean isExceptionForBalanceCheck = false;
|
||||||
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount,
|
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.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);
|
isRegularTransaction, isExceptionForBalanceCheck);
|
||||||
final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId());
|
final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId());
|
||||||
|
|
||||||
|
|||||||
+4
@@ -52,6 +52,10 @@ public final class AccountTransferEnumerations {
|
|||||||
optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(),
|
optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(),
|
||||||
PortfolioAccountType.SAVINGS.getCode(), "Savings Account");
|
PortfolioAccountType.SAVINGS.getCode(), "Savings Account");
|
||||||
break;
|
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.domain.ExternalId;
|
||||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
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.PortfolioAccountType;
|
||||||
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
||||||
import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator;
|
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.AccountTransferTransaction;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
||||||
import org.apache.fineract.portfolio.account.exception.DifferentCurrenciesException;
|
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.data.HolidayDetailDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||||
@@ -88,6 +94,8 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
private final ConfigurationDomainService configurationDomainService;
|
private final ConfigurationDomainService configurationDomainService;
|
||||||
private final ExternalIdFactory externalIdFactory;
|
private final ExternalIdFactory externalIdFactory;
|
||||||
private final FineractProperties fineractProperties;
|
private final FineractProperties fineractProperties;
|
||||||
|
private final CurrentAccountRepository currentAccountRepository;
|
||||||
|
private final CurrentAccountTransactionRepository currentAccountTransactionRepository;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
@@ -448,6 +456,128 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
gsim.setParentDeposit(newBalance);
|
gsim.setParentDeposit(newBalance);
|
||||||
gsimRepository.save(gsim);
|
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 {
|
} else {
|
||||||
throw new GeneralPlatformDomainRuleException("error.msg.accounttransfer.loan.to.loan.not.supported",
|
throw new GeneralPlatformDomainRuleException("error.msg.accounttransfer.loan.to.loan.not.supported",
|
||||||
"Account transfer from loan to another loan is 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();
|
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
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public CommandProcessingResult refundByTransfer(JsonCommand command) {
|
public CommandProcessingResult refundByTransfer(JsonCommand command) {
|
||||||
|
|||||||
+86
@@ -43,12 +43,14 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
|
|||||||
private final PortfolioSavingsAccountMapper savingsAccountMapper;
|
private final PortfolioSavingsAccountMapper savingsAccountMapper;
|
||||||
private final PortfolioLoanAccountMapper loanAccountMapper;
|
private final PortfolioLoanAccountMapper loanAccountMapper;
|
||||||
private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper;
|
private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper;
|
||||||
|
private final PortfolioCurrentAccountMapper currentAccountMapper;
|
||||||
|
|
||||||
public PortfolioAccountReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseSpecificSQLGenerator sqlGenerator) {
|
public PortfolioAccountReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||||
this.jdbcTemplate = jdbcTemplate;
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
this.savingsAccountMapper = new PortfolioSavingsAccountMapper();
|
this.savingsAccountMapper = new PortfolioSavingsAccountMapper();
|
||||||
this.loanAccountMapper = new PortfolioLoanAccountMapper();
|
this.loanAccountMapper = new PortfolioLoanAccountMapper();
|
||||||
this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(sqlGenerator);
|
this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(sqlGenerator);
|
||||||
|
this.currentAccountMapper = new PortfolioCurrentAccountMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -86,6 +88,15 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
|
|||||||
|
|
||||||
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
|
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
|
||||||
break;
|
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) {
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
throw new AccountTransferNotFoundException(accountId, 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
|
accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams.toArray()); // NOSONAR
|
||||||
break;
|
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;
|
return accounts;
|
||||||
@@ -393,4 +421,62 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
|
|||||||
|
|
||||||
return accountData;
|
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 loanAccountType = accountType(PortfolioAccountType.LOAN);
|
||||||
final EnumOptionData savingsAccountType = accountType(PortfolioAccountType.SAVINGS);
|
final EnumOptionData savingsAccountType = accountType(PortfolioAccountType.SAVINGS);
|
||||||
|
final EnumOptionData currentAccountType = accountType(PortfolioAccountType.CURRENT);
|
||||||
|
|
||||||
final Integer mostRelevantFromAccountType = fromAccountType;
|
final Integer mostRelevantFromAccountType = fromAccountType;
|
||||||
Collection<EnumOptionData> fromAccountTypeOptions = null;
|
Collection<EnumOptionData> fromAccountTypeOptions = null;
|
||||||
Collection<EnumOptionData> toAccountTypeOptions = null;
|
Collection<EnumOptionData> toAccountTypeOptions = null;
|
||||||
|
|
||||||
if (accountTransferType.isAccountTransfer()) {
|
if (accountTransferType.isAccountTransfer()) {
|
||||||
fromAccountTypeOptions = Arrays.asList(savingsAccountType);
|
fromAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType);
|
||||||
toAccountTypeOptions = Arrays.asList(savingsAccountType);
|
toAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType);
|
||||||
} else if (accountTransferType.isLoanRepayment()) {
|
} else if (accountTransferType.isLoanRepayment()) {
|
||||||
fromAccountTypeOptions = Arrays.asList(savingsAccountType);
|
fromAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType);
|
||||||
toAccountTypeOptions = Arrays.asList(loanAccountType);
|
toAccountTypeOptions = Arrays.asList(loanAccountType);
|
||||||
} else {
|
} else {
|
||||||
fromAccountTypeOptions = Arrays.asList(savingsAccountType, loanAccountType);
|
fromAccountTypeOptions = Arrays.asList(savingsAccountType, currentAccountType, loanAccountType);
|
||||||
toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType);
|
toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType, currentAccountType);
|
||||||
}
|
}
|
||||||
final Integer mostRelevantToAccountType = toAccountType;
|
final Integer mostRelevantToAccountType = toAccountType;
|
||||||
|
|
||||||
@@ -152,6 +153,8 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
|||||||
Integer accountType;
|
Integer accountType;
|
||||||
if (mostRelevantFromAccountType == 1) {
|
if (mostRelevantFromAccountType == 1) {
|
||||||
accountType = PortfolioAccountType.LOAN.getValue();
|
accountType = PortfolioAccountType.LOAN.getValue();
|
||||||
|
} else if (mostRelevantFromAccountType == 3) {
|
||||||
|
accountType = PortfolioAccountType.CURRENT.getValue();
|
||||||
} else {
|
} else {
|
||||||
accountType = PortfolioAccountType.SAVINGS.getValue();
|
accountType = PortfolioAccountType.SAVINGS.getValue();
|
||||||
}
|
}
|
||||||
@@ -301,6 +304,9 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
|||||||
} else if (accountType.isLoanAccount()) {
|
} else if (accountType.isLoanAccount()) {
|
||||||
sqlBuilder.append(" fromloanacc.id=? ");
|
sqlBuilder.append(" fromloanacc.id=? ");
|
||||||
paramObj.add(standingInstructionDTO.fromAccount());
|
paramObj.add(standingInstructionDTO.fromAccount());
|
||||||
|
} else if (accountType.isCurrentAccount()) {
|
||||||
|
sqlBuilder.append(" fromcuracc.id=? ");
|
||||||
|
paramObj.add(standingInstructionDTO.fromAccount());
|
||||||
}
|
}
|
||||||
addAndCaluse = true;
|
addAndCaluse = true;
|
||||||
}
|
}
|
||||||
@@ -387,7 +393,11 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
|||||||
sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,");
|
sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,");
|
||||||
sqlBuilder.append("tosp.id as toProductId, tosp.name as toProductName, ");
|
sqlBuilder.append("tosp.id as toProductId, tosp.name as toProductName, ");
|
||||||
sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo, ");
|
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(" 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_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 ");
|
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_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_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_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();
|
this.schemaSql = sqlBuilder.toString();
|
||||||
}
|
}
|
||||||
@@ -489,6 +503,16 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
|||||||
fromAccount = new PortfolioAccountData(fromLoanAccountId, fromLoanAccountNo, null, null, null, null, null,
|
fromAccount = new PortfolioAccountData(fromLoanAccountId, fromLoanAccountNo, null, null, null, null, null,
|
||||||
fromLoanProductId, fromLoanProductName, null, null, null);
|
fromLoanProductId, fromLoanProductName, null, null, null);
|
||||||
fromAccountType = accountType(PortfolioAccountType.LOAN);
|
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;
|
PortfolioAccountData toAccount = null;
|
||||||
@@ -510,6 +534,16 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
|||||||
toAccount = new PortfolioAccountData(toLoanAccountId, toLoanAccountNo, null, null, null, null, null, toLoanProductId,
|
toAccount = new PortfolioAccountData(toLoanAccountId, toLoanAccountNo, null, null, null, null, null, toLoanProductId,
|
||||||
toLoanProductName, null, null, null);
|
toLoanProductName, null, null, null);
|
||||||
toAccountType = accountType(PortfolioAccountType.LOAN);
|
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,
|
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);
|
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||||
} else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) {
|
} else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) {
|
||||||
|
|
||||||
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command);
|
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command);
|
||||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
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) {
|
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||||
final Throwable throwable = dve.getMostSpecificCause();
|
final Throwable throwable = dve.getMostSpecificCause();
|
||||||
@@ -122,6 +136,26 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst
|
|||||||
return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount();
|
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
|
@Override
|
||||||
public CommandProcessingResult update(final Long id, final JsonCommand command) {
|
public CommandProcessingResult update(final Long id, final JsonCommand command) {
|
||||||
this.standingInstructionDataValidator.validateForUpdate(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.service.SqlValidator;
|
||||||
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
||||||
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
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.AccountTransfersDataValidator;
|
||||||
import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator;
|
import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler;
|
||||||
@@ -91,11 +93,13 @@ public class AccountConfiguration {
|
|||||||
LoanAccountDomainService loanAccountDomainService, SavingsAccountWritePlatformService savingsAccountWritePlatformService,
|
LoanAccountDomainService loanAccountDomainService, SavingsAccountWritePlatformService savingsAccountWritePlatformService,
|
||||||
AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService,
|
AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService,
|
||||||
GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory,
|
GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory,
|
||||||
FineractProperties fineractProperties) {
|
FineractProperties fineractProperties, CurrentAccountRepository currentAccountRepository,
|
||||||
|
CurrentAccountTransactionRepository currentAccountTransactionRepository) {
|
||||||
return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler,
|
return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler,
|
||||||
accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler,
|
accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler,
|
||||||
loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService,
|
loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService,
|
||||||
gsimRepository, configurationDomainService, externalIdFactory, fineractProperties);
|
gsimRepository, configurationDomainService, externalIdFactory, fineractProperties,
|
||||||
|
currentAccountRepository, currentAccountTransactionRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@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.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.jdbcUrl=${FINERACT_HIKARI_JDBC_URL:jdbc:postgresql://localhost:5432/fineract_tenants}
|
||||||
spring.datasource.hikari.username=${FINERACT_HIKARI_USERNAME:postgres}
|
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.minimumIdle=${FINERACT_HIKARI_MINIMUM_IDLE:3}
|
||||||
spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10}
|
spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10}
|
||||||
spring.datasource.hikari.idleTimeout=${FINERACT_HIKARI_IDLE_TIMEOUT:60000}
|
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/0298_unique_external_id_on_current_account.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="parts/0299_seed_indian_states.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/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>
|
</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