saving, currun, fd and rd account working and tested

This commit is contained in:
keshav898966
2026-06-30 10:06:59 +05:30
parent db41c2b606
commit 1bd41cff43
9 changed files with 353 additions and 37 deletions
@@ -39,6 +39,7 @@ import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.staff.domain.Staff; import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.useradministration.domain.AppUser;
@Entity @Entity
@Table(name = "m_current_account", uniqueConstraints = { @Table(name = "m_current_account", uniqueConstraints = {
@@ -102,6 +103,30 @@ public class CurrentAccount extends AbstractAuditableCustom {
@Column(name = "closedon_date") @Column(name = "closedon_date")
private LocalDate closedOnDate; private LocalDate closedOnDate;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "submittedon_userid")
private AppUser submittedOnByUser;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "approvedon_userid")
private AppUser approvedOnByUser;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "activatedon_userid")
private AppUser activatedOnByUser;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rejectedon_userid")
private AppUser rejectedOnByUser;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "withdrawnon_userid")
private AppUser withdrawnOnByUser;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "closedon_userid")
private AppUser closedOnByUser;
// Currency (copied from product at account creation) // Currency (copied from product at account creation)
@Column(name = "currency_code", nullable = false, length = 3) @Column(name = "currency_code", nullable = false, length = 3)
private String currencyCode; private String currencyCode;
@@ -376,29 +401,34 @@ public class CurrentAccount extends AbstractAuditableCustom {
public void updateAccountNo(String accountNo) { this.accountNo = accountNo; } public void updateAccountNo(String accountNo) { this.accountNo = accountNo; }
public void approve(LocalDate approvedOnDate) { public void approve(LocalDate approvedOnDate, AppUser approvedOnByUser) {
this.statusEnum = 200; this.statusEnum = 200;
this.approvedOnDate = approvedOnDate; this.approvedOnDate = approvedOnDate;
this.approvedOnByUser = approvedOnByUser;
} }
public void activate(LocalDate activatedOnDate) { public void activate(LocalDate activatedOnDate, AppUser activatedOnByUser) {
this.statusEnum = 300; this.statusEnum = 300;
this.activatedOnDate = activatedOnDate; this.activatedOnDate = activatedOnDate;
this.activatedOnByUser = activatedOnByUser;
} }
public void reject(LocalDate rejectedOnDate) { public void reject(LocalDate rejectedOnDate, AppUser rejectedOnByUser) {
this.statusEnum = 500; this.statusEnum = 500;
this.rejectedOnDate = rejectedOnDate; this.rejectedOnDate = rejectedOnDate;
this.rejectedOnByUser = rejectedOnByUser;
} }
public void withdraw(LocalDate withdrawnOnDate) { public void withdraw(LocalDate withdrawnOnDate, AppUser withdrawnOnByUser) {
this.statusEnum = 400; this.statusEnum = 400;
this.withdrawnOnDate = withdrawnOnDate; this.withdrawnOnDate = withdrawnOnDate;
this.withdrawnOnByUser = withdrawnOnByUser;
} }
public void close(LocalDate closedOnDate) { public void close(LocalDate closedOnDate, AppUser closedOnByUser) {
this.statusEnum = 600; this.statusEnum = 600;
this.closedOnDate = closedOnDate; this.closedOnDate = closedOnDate;
this.closedOnByUser = closedOnByUser;
} }
public void setMinRequiredOpeningBalance(java.math.BigDecimal val) { this.minRequiredOpeningBalance = val; } public void setMinRequiredOpeningBalance(java.math.BigDecimal val) { this.minRequiredOpeningBalance = val; }
@@ -37,6 +37,31 @@ public class CurrentAccountNumberGenerator {
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private String getNextSequence() {
final java.util.List<String> accountNos = this.jdbcTemplate.query(
"SELECT account_no FROM m_current_account WHERE account_no IS NOT NULL",
(rs, rowNum) -> rs.getString(1));
long maxVal = 0;
for (String accNo : accountNos) {
if (accNo != null && accNo.length() >= 5) {
String suffix = accNo.substring(accNo.length() - 5);
try {
long val = Long.parseLong(suffix);
if (val > maxVal) {
maxVal = val;
}
} catch (NumberFormatException ignored) {}
}
}
String nextSeq = StringUtils.leftPad(String.valueOf(maxVal + 1), 5, '0');
if (nextSeq.length() > 5) {
nextSeq = nextSeq.substring(nextSeq.length() - 5);
}
return nextSeq;
}
public String generate(CurrentAccount account) { public String generate(CurrentAccount account) {
Long productId = account.getProductId(); Long productId = account.getProductId();
@@ -59,11 +84,10 @@ public class CurrentAccountNumberGenerator {
String officeId = StringUtils.leftPad(account.getOffice().getId().toString(), 2, '0'); String officeId = StringUtils.leftPad(account.getOffice().getId().toString(), 2, '0');
if (officeId.length() > 2) officeId = officeId.substring(officeId.length() - 2); if (officeId.length() > 2) officeId = officeId.substring(officeId.length() - 2);
// Account ID — 5 digits // Next sequence from existing account numbers
String accountId = StringUtils.leftPad(account.getId().toString(), 5, '0'); String nextSeq = getNextSequence();
if (accountId.length() > 5) accountId = accountId.substring(accountId.length() - 5);
return CBS_PREFIX + glCode + officeId + accountId; return CBS_PREFIX + glCode + officeId + nextSeq;
} }
public String preview(Long productId, Long officeId) { public String preview(Long productId, Long officeId) {
@@ -80,13 +104,10 @@ public class CurrentAccountNumberGenerator {
String officeStr = StringUtils.leftPad(officeId != null ? officeId.toString() : "0", 2, '0'); String officeStr = StringUtils.leftPad(officeId != null ? officeId.toString() : "0", 2, '0');
if (officeStr.length() > 2) officeStr = officeStr.substring(officeStr.length() - 2); if (officeStr.length() > 2) officeStr = officeStr.substring(officeStr.length() - 2);
// Next account ID estimate from m_current_account // Next sequence from existing account numbers
Long maxId = jdbcTemplate.query( String nextSeq = getNextSequence();
"SELECT MAX(id) FROM m_current_account",
rs -> rs.next() ? rs.getLong(1) : 0L);
String nextSeq = StringUtils.leftPad(String.valueOf((maxId != null ? maxId : 0L) + 1), 5, '0');
if (nextSeq.length() > 5) nextSeq = nextSeq.substring(nextSeq.length() - 5);
return CBS_PREFIX + glCode + officeStr + nextSeq; return CBS_PREFIX + glCode + officeStr + nextSeq;
} }
} }
@@ -25,6 +25,7 @@ import jakarta.persistence.Embedded;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne; import jakarta.persistence.OneToOne;
import jakarta.persistence.PostLoad;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
@@ -135,7 +136,17 @@ public class DepositAccountRecurringDetail extends AbstractPersistableCustom<Lon
return actualChanges; return actualChanges;
} }
@PostLoad
public void postLoad() {
if (this.recurringDetail == null) {
this.recurringDetail = new DepositRecurringDetail();
}
}
public DepositRecurringDetail recurringDetail() { public DepositRecurringDetail recurringDetail() {
if (this.recurringDetail == null) {
this.recurringDetail = new DepositRecurringDetail();
}
return this.recurringDetail; return this.recurringDetail;
} }
@@ -30,7 +30,7 @@ import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService; import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.savings.SavingsApiConstants; import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator; import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables; import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
@@ -80,7 +80,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
private final SavingsDropdownReadPlatformService dropdownReadPlatformService; private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
private final ChargeReadPlatformService chargeReadPlatformService; private final ChargeReadPlatformService chargeReadPlatformService;
private final EntityDatatableChecksReadService entityDatatableChecksReadService; private final EntityDatatableChecksReadService entityDatatableChecksReadService;
private final AccountNumberGenerator accountNumberGenerator; private final CurrentAccountNumberGenerator currentAccountNumberGenerator;
private final CodeValueReadPlatformService codeValueReadPlatformService; private final CodeValueReadPlatformService codeValueReadPlatformService;
public CurrentAccountTemplateReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate, public CurrentAccountTemplateReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
@@ -91,7 +91,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
final ChargeReadPlatformService chargeReadPlatformService, final ChargeReadPlatformService chargeReadPlatformService,
final EntityDatatableChecksReadService entityDatatableChecksReadService, final EntityDatatableChecksReadService entityDatatableChecksReadService,
final ColumnValidator columnValidator, final ColumnValidator columnValidator,
final AccountNumberGenerator accountNumberGenerator, final CurrentAccountNumberGenerator currentAccountNumberGenerator,
final CodeValueReadPlatformService codeValueReadPlatformService) { final CodeValueReadPlatformService codeValueReadPlatformService) {
this.context = context; this.context = context;
this.jdbcTemplate = jdbcTemplate; this.jdbcTemplate = jdbcTemplate;
@@ -102,7 +102,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
this.dropdownReadPlatformService = dropdownReadPlatformService; this.dropdownReadPlatformService = dropdownReadPlatformService;
this.chargeReadPlatformService = chargeReadPlatformService; this.chargeReadPlatformService = chargeReadPlatformService;
this.entityDatatableChecksReadService = entityDatatableChecksReadService; this.entityDatatableChecksReadService = entityDatatableChecksReadService;
this.accountNumberGenerator = accountNumberGenerator; this.currentAccountNumberGenerator = currentAccountNumberGenerator;
this.codeValueReadPlatformService = codeValueReadPlatformService; this.codeValueReadPlatformService = codeValueReadPlatformService;
} }
@@ -231,7 +231,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
productData.getOdAccountClosedZeroBalanceInDayClosing() != null && productData.getOdAccountClosedZeroBalanceInDayClosing()); productData.getOdAccountClosedZeroBalanceInDayClosing() != null && productData.getOdAccountClosedZeroBalanceInDayClosing());
// Generate preview account number using current product's own GL account ID // Generate preview account number using current product's own GL account ID
try { try {
final String preview = accountNumberGenerator.previewAccountNumberByGlAccountId(productData.getGlAccountId(), officeId); final String preview = currentAccountNumberGenerator.preview(productId, officeId);
template.setPreviewAccountNo(preview); template.setPreviewAccountNo(preview);
} catch (final Exception ex) { } catch (final Exception ex) {
log.warn("Failed to generate preview account number for productId={}: {}", productId, ex.getMessage()); log.warn("Failed to generate preview account number for productId={}: {}", productId, ex.getMessage());
@@ -45,6 +45,7 @@ import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWriteP
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator; import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct; import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository; import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings; import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
import org.apache.fineract.accounting.common.AccountingRuleType; import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.accounting.glaccount.domain.GLAccount;
@@ -127,6 +128,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
boolean balanceSwapOption = json.has("balanceSwapOption") && !json.get("balanceSwapOption").isJsonNull() boolean balanceSwapOption = json.has("balanceSwapOption") && !json.get("balanceSwapOption").isJsonNull()
? json.get("balanceSwapOption").getAsBoolean() : product.isBalanceSwapOption(); ? json.get("balanceSwapOption").getAsBoolean() : product.isBalanceSwapOption();
final AppUser user = context.authenticatedUser();
final CurrentAccount account = CurrentAccount.createNew( final CurrentAccount account = CurrentAccount.createNew(
client, productId, office, null, "TEMP", submittedOnDate, accountTypeEnum, client, productId, office, null, "TEMP", submittedOnDate, accountTypeEnum,
product.currency().getCode(), product.currency().getDigitsAfterDecimal(), product.currency().getCode(), product.currency().getDigitsAfterDecimal(),
@@ -139,6 +141,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
product.getCustomerSegment(), product.isAutoPost(), product.getCustomerSegment(), product.isAutoPost(),
product.getInterestCalculateSelector(), product.getAveragePeriod(), product.getInterestCalculateSelector(), product.getAveragePeriod(),
product.getAvgBalanceCalcPeriod()); product.getAvgBalanceCalcPeriod());
account.setSubmittedOnByUser(user);
boolean shareLinkingEnabled = json.has("shareLinkingEnabled") && !json.get("shareLinkingEnabled").isJsonNull() boolean shareLinkingEnabled = json.has("shareLinkingEnabled") && !json.get("shareLinkingEnabled").isJsonNull()
? json.get("shareLinkingEnabled").getAsBoolean() : product.isShareLinkingEnabled(); ? json.get("shareLinkingEnabled").getAsBoolean() : product.isShareLinkingEnabled();
@@ -258,12 +261,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
@Override @Override
@Transactional @Transactional
public CommandProcessingResult approveApplication(final Long accountId, final JsonCommand command) { public CommandProcessingResult approveApplication(final Long accountId, final JsonCommand command) {
context.authenticatedUser(); final AppUser user = context.authenticatedUser();
final CurrentAccount account = findAccount(accountId); final CurrentAccount account = findAccount(accountId);
final JsonObject json = command.parsedJson().getAsJsonObject(); final JsonObject json = command.parsedJson().getAsJsonObject();
LocalDate approvedOnDate = parseDate(getString(json, "approvedOnDate"), getString(json, "dateFormat"), getString(json, "locale")); LocalDate approvedOnDate = parseDate(getString(json, "approvedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
if (approvedOnDate == null) approvedOnDate = LocalDate.now(); if (approvedOnDate == null) approvedOnDate = LocalDate.now();
account.approve(approvedOnDate); account.approve(approvedOnDate, user);
currentAccountRepository.saveAndFlush(account); currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build(); return new CommandProcessingResultBuilder().withEntityId(accountId).build();
} }
@@ -275,6 +278,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
final CurrentAccount account = findAccount(accountId); final CurrentAccount account = findAccount(accountId);
account.setStatusEnum(100); account.setStatusEnum(100);
account.setApprovedOnDate(null); account.setApprovedOnDate(null);
account.setApprovedOnByUser(null);
currentAccountRepository.saveAndFlush(account); currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build(); return new CommandProcessingResultBuilder().withEntityId(accountId).build();
} }
@@ -282,7 +286,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
@Override @Override
@Transactional @Transactional
public CommandProcessingResult activate(final Long accountId, final JsonCommand command) { public CommandProcessingResult activate(final Long accountId, final JsonCommand command) {
context.authenticatedUser(); final AppUser user = context.authenticatedUser();
final CurrentAccount account = findAccount(accountId); final CurrentAccount account = findAccount(accountId);
final JsonObject json = command.parsedJson().getAsJsonObject(); final JsonObject json = command.parsedJson().getAsJsonObject();
LocalDate activatedOnDate = parseDate(getString(json, "activatedOnDate"), getString(json, "dateFormat"), getString(json, "locale")); LocalDate activatedOnDate = parseDate(getString(json, "activatedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
@@ -303,7 +307,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
openingBalanceValue, minRequired))); openingBalanceValue, minRequired)));
} }
account.activate(activatedOnDate); account.activate(activatedOnDate, user);
applyFacilityChargesOnActivation(account); applyFacilityChargesOnActivation(account);
java.math.BigDecimal currentBalance = java.math.BigDecimal.ZERO; java.math.BigDecimal currentBalance = java.math.BigDecimal.ZERO;
@@ -360,12 +364,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
@Override @Override
@Transactional @Transactional
public CommandProcessingResult rejectApplication(final Long accountId, final JsonCommand command) { public CommandProcessingResult rejectApplication(final Long accountId, final JsonCommand command) {
context.authenticatedUser(); final AppUser user = context.authenticatedUser();
final CurrentAccount account = findAccount(accountId); final CurrentAccount account = findAccount(accountId);
final JsonObject json = command.parsedJson().getAsJsonObject(); final JsonObject json = command.parsedJson().getAsJsonObject();
LocalDate rejectedOnDate = parseDate(getString(json, "rejectedOnDate"), getString(json, "dateFormat"), getString(json, "locale")); LocalDate rejectedOnDate = parseDate(getString(json, "rejectedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
if (rejectedOnDate == null) rejectedOnDate = LocalDate.now(); if (rejectedOnDate == null) rejectedOnDate = LocalDate.now();
account.reject(rejectedOnDate); account.reject(rejectedOnDate, user);
currentAccountRepository.saveAndFlush(account); currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build(); return new CommandProcessingResultBuilder().withEntityId(accountId).build();
} }
@@ -373,12 +377,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
@Override @Override
@Transactional @Transactional
public CommandProcessingResult withdrawApplication(final Long accountId, final JsonCommand command) { public CommandProcessingResult withdrawApplication(final Long accountId, final JsonCommand command) {
context.authenticatedUser(); final AppUser user = context.authenticatedUser();
final CurrentAccount account = findAccount(accountId); final CurrentAccount account = findAccount(accountId);
final JsonObject json = command.parsedJson().getAsJsonObject(); final JsonObject json = command.parsedJson().getAsJsonObject();
LocalDate withdrawnOnDate = parseDate(getString(json, "withdrawnOnDate"), getString(json, "dateFormat"), getString(json, "locale")); LocalDate withdrawnOnDate = parseDate(getString(json, "withdrawnOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
if (withdrawnOnDate == null) withdrawnOnDate = LocalDate.now(); if (withdrawnOnDate == null) withdrawnOnDate = LocalDate.now();
account.withdraw(withdrawnOnDate); account.withdraw(withdrawnOnDate, user);
currentAccountRepository.saveAndFlush(account); currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build(); return new CommandProcessingResultBuilder().withEntityId(accountId).build();
} }
@@ -386,12 +390,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
@Override @Override
@Transactional @Transactional
public CommandProcessingResult closeAccount(final Long accountId, final JsonCommand command) { public CommandProcessingResult closeAccount(final Long accountId, final JsonCommand command) {
context.authenticatedUser(); final AppUser user = context.authenticatedUser();
final CurrentAccount account = findAccount(accountId); final CurrentAccount account = findAccount(accountId);
final JsonObject json = command.parsedJson().getAsJsonObject(); final JsonObject json = command.parsedJson().getAsJsonObject();
LocalDate closedOnDate = parseDate(getString(json, "closedOnDate"), getString(json, "dateFormat"), getString(json, "locale")); LocalDate closedOnDate = parseDate(getString(json, "closedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
if (closedOnDate == null) closedOnDate = LocalDate.now(); if (closedOnDate == null) closedOnDate = LocalDate.now();
account.close(closedOnDate); account.close(closedOnDate, user);
currentAccountRepository.saveAndFlush(account); currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build(); return new CommandProcessingResultBuilder().withEntityId(accountId).build();
} }
@@ -403,6 +407,19 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
final CurrentAccount account = findAccount(accountId); final CurrentAccount account = findAccount(accountId);
final JsonObject json = command.parsedJson().getAsJsonObject(); final JsonObject json = command.parsedJson().getAsJsonObject();
if (account.getStatusEnum() != null) {
if (account.getStatusEnum() == 500) {
account.setStatusEnum(100);
account.setRejectedOnDate(null);
account.setRejectedOnByUser(null);
} else if (account.getStatusEnum() == 400) {
account.setStatusEnum(100);
account.setWithdrawnOnDate(null);
account.setWithdrawnOnByUser(null);
}
}
if (json.has("externalId") && !json.get("externalId").isJsonNull()) if (json.has("externalId") && !json.get("externalId").isJsonNull())
account.setExternalId(json.get("externalId").getAsString()); account.setExternalId(json.get("externalId").getAsString());
if (json.has("accountStatus") && !json.get("accountStatus").isJsonNull()) if (json.has("accountStatus") && !json.get("accountStatus").isJsonNull())
@@ -498,7 +515,10 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
} }
private CurrentAccountFacilityConfig assembleFacilityConfig(CurrentAccount account, JsonObject fcJson) { private CurrentAccountFacilityConfig assembleFacilityConfig(CurrentAccount account, JsonObject fcJson) {
final CurrentAccountFacilityConfig cfg = CurrentAccountFacilityConfig.createFor(account); CurrentAccountFacilityConfig cfg = account.getFacilityConfig();
if (cfg == null) {
cfg = CurrentAccountFacilityConfig.createFor(account);
}
cfg.setAtmFacility(getBool(fcJson, "atmFacility")); cfg.setAtmFacility(getBool(fcJson, "atmFacility"));
cfg.setAtmWithdrawalFacility(getBool(fcJson, "atmWithdrawalFacility")); cfg.setAtmWithdrawalFacility(getBool(fcJson, "atmWithdrawalFacility"));
cfg.setUpiFacility(getBool(fcJson, "upiFacility")); cfg.setUpiFacility(getBool(fcJson, "upiFacility"));
@@ -47,6 +47,7 @@ import org.apache.fineract.portfolio.account.domain.AccountAssociationsRepositor
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService; import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator; import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService; import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
@@ -404,11 +405,11 @@ public class SavingsConfiguration {
GroupReadPlatformService groupReadPlatformService, CurrentProductReadPlatformService currentProductReadPlatformService, GroupReadPlatformService groupReadPlatformService, CurrentProductReadPlatformService currentProductReadPlatformService,
StaffReadPlatformService staffReadPlatformService, SavingsDropdownReadPlatformService dropdownReadPlatformService, StaffReadPlatformService staffReadPlatformService, SavingsDropdownReadPlatformService dropdownReadPlatformService,
ChargeReadPlatformService chargeReadPlatformService, EntityDatatableChecksReadService entityDatatableChecksReadService, ChargeReadPlatformService chargeReadPlatformService, EntityDatatableChecksReadService entityDatatableChecksReadService,
ColumnValidator columnValidator, AccountNumberGenerator accountNumberGenerator, ColumnValidator columnValidator, CurrentAccountNumberGenerator currentAccountNumberGenerator,
CodeValueReadPlatformService codeValueReadPlatformService) { CodeValueReadPlatformService codeValueReadPlatformService) {
return new CurrentAccountTemplateReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService, return new CurrentAccountTemplateReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService,
currentProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService, currentProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService,
entityDatatableChecksReadService, columnValidator, accountNumberGenerator, codeValueReadPlatformService); entityDatatableChecksReadService, columnValidator, currentAccountNumberGenerator, codeValueReadPlatformService);
} }
@Bean @Bean
@@ -33,6 +33,7 @@ import jakarta.persistence.Embedded;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne; import jakarta.persistence.OneToOne;
import jakarta.persistence.PostLoad;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
@@ -181,11 +182,27 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom<L
return actualChanges; return actualChanges;
} }
@PostLoad
public void postLoad() {
if (this.preClosureDetail == null) {
this.preClosureDetail = new DepositPreClosureDetail();
}
if (this.depositTermDetail == null) {
this.depositTermDetail = new DepositTermDetail();
}
}
public DepositPreClosureDetail depositPreClosureDetail() { public DepositPreClosureDetail depositPreClosureDetail() {
if (this.preClosureDetail == null) {
this.preClosureDetail = new DepositPreClosureDetail();
}
return this.preClosureDetail; return this.preClosureDetail;
} }
public DepositTermDetail depositTermDetail() { public DepositTermDetail depositTermDetail() {
if (this.depositTermDetail == null) {
this.depositTermDetail = new DepositTermDetail();
}
return this.depositTermDetail; return this.depositTermDetail;
} }
@@ -1866,6 +1866,218 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
actualChanges.put(shareLinkingEnabledParamName, newValue); actualChanges.put(shareLinkingEnabledParamName, newValue);
this.shareLinkingEnabled = newValue; this.shareLinkingEnabled = newValue;
} }
// CBS extended fields mapping during modify application
final String accountTypeParam = "accountType";
if (command.isChangeInStringParameterNamed(accountTypeParam, this.cbsAccountType)) {
final String newValue = command.stringValueOfParameterNamed(accountTypeParam);
actualChanges.put(accountTypeParam, newValue);
this.cbsAccountType = newValue;
}
final String customerSegmentParam = "customerSegment";
if (command.isChangeInStringParameterNamed(customerSegmentParam, this.customerSegment)) {
final String newValue = command.stringValueOfParameterNamed(customerSegmentParam);
actualChanges.put(customerSegmentParam, newValue);
this.customerSegment = newValue;
}
final String accountStatusParam = "accountStatus";
if (command.isChangeInStringParameterNamed(accountStatusParam, this.accountStatus)) {
final String newValue = command.stringValueOfParameterNamed(accountStatusParam);
actualChanges.put(accountStatusParam, newValue);
this.accountStatus = newValue;
}
final String modeOfOperationParam = "modeOfOperation";
if (command.isChangeInStringParameterNamed(modeOfOperationParam, this.modeOfOperation)) {
final String newValue = command.stringValueOfParameterNamed(modeOfOperationParam);
actualChanges.put(modeOfOperationParam, newValue);
this.modeOfOperation = newValue;
}
final String specialCategoryParam = "specialCategory";
if (command.isChangeInStringParameterNamed(specialCategoryParam, this.specialCategory)) {
final String newValue = command.stringValueOfParameterNamed(specialCategoryParam);
actualChanges.put(specialCategoryParam, newValue);
this.specialCategory = newValue;
}
final String weakerSectionParam = "weakerSection";
if (command.isChangeInStringParameterNamed(weakerSectionParam, this.weakerSection)) {
final String newValue = command.stringValueOfParameterNamed(weakerSectionParam);
actualChanges.put(weakerSectionParam, newValue);
this.weakerSection = newValue;
}
final String nominationFacilityParam = "nominationFacility";
if (command.isChangeInBooleanParameterNamed(nominationFacilityParam, this.nominationFacility)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(nominationFacilityParam);
actualChanges.put(nominationFacilityParam, newValue);
this.nominationFacility = newValue;
}
final String autoDebitFacilityParam = "autoDebitFacility";
if (command.isChangeInBooleanParameterNamed(autoDebitFacilityParam, this.autoDebitFacility)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(autoDebitFacilityParam);
actualChanges.put(autoDebitFacilityParam, newValue);
this.autoDebitFacility = newValue;
}
final String isSeniorCitizenParam = "isSeniorCitizen";
if (command.isChangeInBooleanParameterNamed(isSeniorCitizenParam, this.isSeniorCitizen)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isSeniorCitizenParam);
actualChanges.put(isSeniorCitizenParam, newValue);
this.isSeniorCitizen = newValue;
}
final String seniorCitizenInterestRateParam = "seniorCitizenInterestRate";
if (command.isChangeInBigDecimalParameterNamed(seniorCitizenInterestRateParam, this.seniorCitizenInterestRate)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(seniorCitizenInterestRateParam);
actualChanges.put(seniorCitizenInterestRateParam, newValue);
this.seniorCitizenInterestRate = newValue;
if (newValue != null && newValue.compareTo(BigDecimal.ZERO) > 0) {
updateNominalAnnualInterestRate(newValue);
}
}
final String guardianNameParam = "guardianName";
if (command.isChangeInStringParameterNamed(guardianNameParam, this.guardianName)) {
final String newValue = command.stringValueOfParameterNamed(guardianNameParam);
actualChanges.put(guardianNameParam, newValue);
this.guardianName = newValue;
}
final String guardianCifNumberParam = "guardianCifNumber";
if (command.isChangeInStringParameterNamed(guardianCifNumberParam, this.guardianCifNumber)) {
final String newValue = command.stringValueOfParameterNamed(guardianCifNumberParam);
actualChanges.put(guardianCifNumberParam, newValue);
this.guardianCifNumber = newValue;
}
final String guardianRelationshipParam = "guardianRelationship";
if (command.isChangeInStringParameterNamed(guardianRelationshipParam, this.guardianRelationship)) {
final String newValue = command.stringValueOfParameterNamed(guardianRelationshipParam);
actualChanges.put(guardianRelationshipParam, newValue);
this.guardianRelationship = newValue;
}
final String minorDateOfBirthParam = "minorDateOfBirth";
if (command.isChangeInLocalDateParameterNamed(minorDateOfBirthParam, this.minorDateOfBirth)) {
final LocalDate newValue = command.localDateValueOfParameterNamed(minorDateOfBirthParam);
actualChanges.put(minorDateOfBirthParam, newValue);
this.minorDateOfBirth = newValue;
}
final String guardianClientIdParam = "guardianClientId";
if (command.isChangeInLongParameterNamed(guardianClientIdParam, this.guardianClientId)) {
final Long newValue = command.longValueOfParameterNamed(guardianClientIdParam);
actualChanges.put(guardianClientIdParam, newValue);
this.guardianClientId = newValue;
}
final String introducerIdParam = "introducerId";
if (command.isChangeInLongParameterNamed(introducerIdParam, this.introducerClientId)) {
final Long newValue = command.longValueOfParameterNamed(introducerIdParam);
actualChanges.put(introducerIdParam, newValue);
this.introducerClientId = newValue;
}
final String introducerNameParam = "introducerName";
if (command.isChangeInStringParameterNamed(introducerNameParam, this.introducerName)) {
final String newValue = command.stringValueOfParameterNamed(introducerNameParam);
actualChanges.put(introducerNameParam, newValue);
this.introducerName = newValue;
}
final String introducerCifNumberParam = "introducerCifNumber";
if (command.isChangeInStringParameterNamed(introducerCifNumberParam, this.introducerCifNumber)) {
final String newValue = command.stringValueOfParameterNamed(introducerCifNumberParam);
actualChanges.put(introducerCifNumberParam, newValue);
this.introducerCifNumber = newValue;
}
final String linkedAccountIdParam = "linkedAccountId";
if (command.isChangeInLongParameterNamed(linkedAccountIdParam, this.linkedAccountId)) {
final Long newValue = command.longValueOfParameterNamed(linkedAccountIdParam);
actualChanges.put(linkedAccountIdParam, newValue);
this.linkedAccountId = newValue;
}
final String linkedAccountTypeParam = "linkedAccountType";
if (command.isChangeInStringParameterNamed(linkedAccountTypeParam, this.linkedAccountType)) {
final String newValue = command.stringValueOfParameterNamed(linkedAccountTypeParam);
actualChanges.put(linkedAccountTypeParam, newValue);
this.linkedAccountType = newValue;
}
final String linkedAccountNoParam = "linkedAccountNo";
if (command.isChangeInStringParameterNamed(linkedAccountNoParam, this.linkedAccountNo)) {
final String newValue = command.stringValueOfParameterNamed(linkedAccountNoParam);
actualChanges.put(linkedAccountNoParam, newValue);
this.linkedAccountNo = newValue;
}
final String accountFacilityConfigParam = "accountFacilityConfig";
if (command.hasParameter(accountFacilityConfigParam)) {
final com.google.gson.JsonElement parsedJson = command.parsedJson();
if (parsedJson != null && parsedJson.isJsonObject()) {
final com.google.gson.JsonObject jsonObj = parsedJson.getAsJsonObject();
if (jsonObj.has(accountFacilityConfigParam) && !jsonObj.get(accountFacilityConfigParam).isJsonNull()
&& jsonObj.get(accountFacilityConfigParam).isJsonObject()) {
final com.google.gson.JsonObject configObj = jsonObj.get(accountFacilityConfigParam).getAsJsonObject();
if (this.facilityConfig == null) {
this.facilityConfig = new SavingsAccountFacilityConfig(this);
}
this.facilityConfig.updateFrom(configObj);
actualChanges.put(accountFacilityConfigParam, configObj.toString());
}
}
}
final String jointHoldersParam = "jointHolders";
if (command.hasParameter(jointHoldersParam)) {
final com.google.gson.JsonElement parsedJson = command.parsedJson();
if (parsedJson != null && parsedJson.isJsonObject()) {
final com.google.gson.JsonObject jsonObj = parsedJson.getAsJsonObject();
if (jsonObj.has(jointHoldersParam) && !jsonObj.get(jointHoldersParam).isJsonNull()
&& jsonObj.get(jointHoldersParam).isJsonArray()) {
final com.google.gson.JsonArray holdersArray = jsonObj.get(jointHoldersParam).getAsJsonArray();
final List<SavingsAccountJointHolder> holders = new ArrayList<>();
for (int i = 0; i < holdersArray.size(); i++) {
final com.google.gson.JsonObject h = holdersArray.get(i).getAsJsonObject();
final Long clientId = h.has("clientId") && !h.get("clientId").isJsonNull()
? h.get("clientId").getAsLong() : null;
final String cifNumber = h.has("cifNumber") && !h.get("cifNumber").isJsonNull()
? h.get("cifNumber").getAsString() : null;
final String displayName = h.has("displayName") && !h.get("displayName").isJsonNull()
? h.get("displayName").getAsString() : null;
final BigDecimal sharePercentage = h.has("sharePercentage") && !h.get("sharePercentage").isJsonNull()
? h.get("sharePercentage").getAsBigDecimal() : null;
final boolean isPrimary = h.has("isPrimary") && h.get("isPrimary").getAsBoolean();
String signingAuthority = h.has("signingAuthority") && !h.get("signingAuthority").isJsonNull()
? h.get("signingAuthority").getAsString() : null;
String role = h.has("role") && !h.get("role").isJsonNull()
? h.get("role").getAsString() : null;
if (signingAuthority == null || signingAuthority.trim().isEmpty()) {
signingAuthority = isPrimary ? "Mandatory" : "Optional";
}
if (role == null || role.trim().isEmpty()) {
role = isPrimary ? "Primary Account Holder" : "Co-Holder";
}
final boolean isMandatory = h.has("isMandatory") && h.get("isMandatory").getAsBoolean();
holders.add(new SavingsAccountJointHolder(this, clientId, cifNumber, displayName,
sharePercentage, signingAuthority, role, isPrimary, isMandatory));
}
this.jointHolders.clear();
this.jointHolders.addAll(holders);
actualChanges.put(jointHoldersParam, holdersArray.toString());
}
}
}
validateLockinDetails(baseDataValidator); validateLockinDetails(baseDataValidator);
esnureOverdraftLimitsSetForOverdraftAccounts(); esnureOverdraftLimitsSetForOverdraftAccounts();
} }
@@ -815,16 +815,20 @@ public class SavingsAccountCharge extends AbstractAuditableWithUTCDateTimeCustom
return nextDueLocalDate; return nextDueLocalDate;
} }
public Integer getFeeInterval() {
return this.feeInterval == null ? 1 : this.feeInterval;
}
private LocalDate calculateNextDueDate(final LocalDate date) { private LocalDate calculateNextDueDate(final LocalDate date) {
LocalDate nextDueLocalDate = null; LocalDate nextDueLocalDate = null;
if (isAnnualFee()) { if (isAnnualFee()) {
nextDueLocalDate = date.withMonth(this.feeOnMonth).plusYears(1); nextDueLocalDate = date.withMonth(this.feeOnMonth).plusYears(1);
nextDueLocalDate = setDayOfMonth(nextDueLocalDate); nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isMonthlyFee()) { } else if (isMonthlyFee()) {
nextDueLocalDate = date.plusMonths(this.feeInterval); nextDueLocalDate = date.plusMonths(getFeeInterval());
nextDueLocalDate = setDayOfMonth(nextDueLocalDate); nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isWeeklyFee()) { } else if (isWeeklyFee()) {
nextDueLocalDate = date.plusWeeks(this.feeInterval); nextDueLocalDate = date.plusWeeks(getFeeInterval());
nextDueLocalDate = setDayOfWeek(nextDueLocalDate); nextDueLocalDate = setDayOfWeek(nextDueLocalDate);
} }
return nextDueLocalDate; return nextDueLocalDate;
@@ -857,10 +861,10 @@ public class SavingsAccountCharge extends AbstractAuditableWithUTCDateTimeCustom
nextDueLocalDate = nextDueLocalDate.withMonth(this.feeOnMonth).minusYears(1); nextDueLocalDate = nextDueLocalDate.withMonth(this.feeOnMonth).minusYears(1);
nextDueLocalDate = setDayOfMonth(nextDueLocalDate); nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isMonthlyFee()) { } else if (isMonthlyFee()) {
nextDueLocalDate = nextDueLocalDate.minusMonths(this.feeInterval); nextDueLocalDate = nextDueLocalDate.minusMonths(getFeeInterval());
nextDueLocalDate = setDayOfMonth(nextDueLocalDate); nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
} else if (isWeeklyFee()) { } else if (isWeeklyFee()) {
nextDueLocalDate = nextDueLocalDate.minusDays(7 * this.feeInterval); nextDueLocalDate = nextDueLocalDate.minusDays(7 * getFeeInterval());
nextDueLocalDate = setDayOfWeek(nextDueLocalDate); nextDueLocalDate = setDayOfWeek(nextDueLocalDate);
} }