current account saving account ,rd account code udpate

This commit is contained in:
Nitesh Yadav
2026-04-29 15:30:36 +05:30
42 changed files with 35014 additions and 96 deletions
@@ -459,6 +459,8 @@ public final class DepositsApiConstants {
// CBS product fields — included in template response when productId is provided
recurringDepositResponseData.add("accountType");
recurringDepositResponseData.add("customerSegment");
recurringDepositResponseData.add("accountTypeOptions");
recurringDepositResponseData.add("customerSegmentOptions");
recurringDepositResponseData.add("productCode");
recurringDepositResponseData.add(SavingsApiConstants.glAccountIdParamName);
recurringDepositResponseData.add("glAccountName");
@@ -281,4 +281,31 @@ public class SavingsApiConstants {
public static final String avgBalanceCalcPeriodParamName = "avgBalanceCalcPeriod";
public static final String validForPeriodParamName = "validForPeriod";
public static final String odAccountClosedZeroBalanceInDayClosingParamName = "odAccountClosedZeroBalanceInDayClosing";
// Status of Account fields
public static final String accountStatusParamName = "accountStatus";
public static final String modeOfOperationParamName = "modeOfOperation";
public static final String guardianClientIdParamName = "guardianClientId";
public static final String specialCategoryParamName = "specialCategory";
public static final String weakerSectionParamName = "weakerSection";
public static final String guardianNameParamName = "guardianName";
public static final String guardianCifNumberParamName = "guardianCifNumber";
public static final String guardianRelationshipParamName = "guardianRelationship";
public static final String minorDateOfBirthParamName = "minorDateOfBirth";
// Introducer fields
public static final String introducerIdParamName = "introducerId";
public static final String introducerNameParamName = "introducerName";
public static final String introducerCifNumberParamName = "introducerCifNumber";
// Joint holders
public static final String jointHoldersParamName = "jointHolders";
// Nominee
public static final String nomineeParamName = "nominee";
// Deposit Account Link (DAL)
public static final String linkedAccountIdParamName = "linkedAccountId";
public static final String linkedAccountTypeParamName = "linkedAccountType";
public static final String linkedAccountNoParamName = "linkedAccountNo";
}
@@ -33,6 +33,7 @@ import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.jersey.serializer.legacy.JsonLocalDateArrayFormat;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
@@ -125,9 +126,31 @@ public final class SavingsAccountData implements Serializable {
private List<DatatableData> datatables = null;
// CBS Account fields (saved per account)
private String cbsAccountType;
private String accountStatus;
private String modeOfOperation;
private String specialCategory;
private String weakerSection;
private String guardianName;
private String guardianCifNumber;
private String guardianRelationship;
private LocalDate minorDateOfBirth;
private Long guardianClientId;
private Long introducerClientId;
private String introducerName;
private String introducerCifNumber;
private Map<String, Object> nomineeData;
private Long linkedAccountId;
private String linkedAccountType;
private String linkedAccountNo;
private List<java.util.Map<String, Object>> jointHolders;
// CBS Product fields (populated in template response from selected product)
private String accountType;
private String customerSegment;
private Collection<CodeValueData> accountTypeOptions;
private Collection<CodeValueData> customerSegmentOptions;
private String productCode;
private LocalDate effectiveDate;
private LocalDate effectiveTo;
@@ -728,37 +751,41 @@ public final class SavingsAccountData implements Serializable {
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template,
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges) {
if (template == null) {
final Collection<SavingsProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<SavingsProductData> productOptions = template == null ? null : template.productOptions;
final Collection<StaffData> fieldOfficerOptions = template == null ? null : template.fieldOfficerOptions;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = template == null ? null
: template.interestCompoundingPeriodTypeOptions;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = template == null ? null
: template.interestPostingPeriodTypeOptions;
final Collection<EnumOptionData> interestCalculationTypeOptions = template == null ? null
: template.interestCalculationTypeOptions;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = template == null ? null
: template.interestCalculationDaysInYearTypeOptions;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = template == null ? null
: template.lockinPeriodFrequencyTypeOptions;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = template == null ? null
: template.withdrawalFeeTypeOptions;
final Collection<ChargeData> chargeOptions = template == null ? null : template.chargeOptions;
return withTemplateOptions(account, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions, charges, chargeOptions);
}
return new SavingsAccountData(account.id, account.accountNo, account.depositType, account.externalId, account.groupId,
final SavingsAccountData result = new SavingsAccountData(account.id, account.accountNo, account.depositType, account.externalId,
account.groupId,
account.groupName, account.clientId, account.clientName, account.savingsProductId, account.savingsProductName,
account.fieldOfficerId, account.fieldOfficerName, account.status, account.subStatus, account.reasonForBlock,
account.timeline, account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType,
account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType,
account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType,
account.withdrawalFeeForTransfers, account.summary, transactions, template.productOptions, template.fieldOfficerOptions,
template.interestCompoundingPeriodTypeOptions, template.interestPostingPeriodTypeOptions,
template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions,
template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions,
account.withdrawalFeeForTransfers, account.summary, transactions, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions,
account.allowOverdraft, account.overdraftLimit, account.minRequiredBalance, account.enforceMinRequiredBalance,
account.maxAllowedLienLimit, account.lienAllowed, account.minBalanceForInterestCalculation, account.onHoldFunds,
account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation, account.withHoldTax,
account.taxGroup, account.lastActiveTransactionDate, account.isDormancyTrackingActive, account.daysToInactive,
account.daysToDormancy, account.daysToEscheat, account.savingsAmountOnHold);
copyPresentationFields(result, template);
copyPresentationFields(result, account);
return result;
}
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
@@ -771,7 +798,8 @@ public final class SavingsAccountData implements Serializable {
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges,
final Collection<ChargeData> chargeOptions) {
return new SavingsAccountData(account.id, account.accountNo, account.depositType, account.externalId, account.groupId,
final SavingsAccountData result = new SavingsAccountData(account.id, account.accountNo, account.depositType, account.externalId,
account.groupId,
account.groupName, account.clientId, account.clientName, account.savingsProductId, account.savingsProductName,
account.fieldOfficerId, account.fieldOfficerName, account.status, account.subStatus, account.reasonForBlock,
account.timeline, account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType,
@@ -786,6 +814,223 @@ public final class SavingsAccountData implements Serializable {
account.minOverdraftForInterestCalculation, account.withHoldTax, account.taxGroup, account.lastActiveTransactionDate,
account.isDormancyTrackingActive, account.daysToInactive, account.daysToDormancy, account.daysToEscheat,
account.savingsAmountOnHold);
copyPresentationFields(result, account);
return result;
}
private static void copyPresentationFields(final SavingsAccountData target, final SavingsAccountData source) {
if (source == null) {
return;
}
if (source.previewAccountNo != null) {
target.setPreviewAccountNo(source.previewAccountNo);
}
if (source.cbsAccountType != null) {
target.setCbsAccountType(source.cbsAccountType);
}
if (source.customerSegment != null) {
target.setCustomerSegment(source.customerSegment);
}
if (source.accountStatus != null) {
target.setAccountStatus(source.accountStatus);
}
if (source.modeOfOperation != null) {
target.setModeOfOperation(source.modeOfOperation);
}
if (source.specialCategory != null) {
target.setSpecialCategory(source.specialCategory);
}
if (source.weakerSection != null) {
target.setWeakerSection(source.weakerSection);
}
if (source.guardianName != null) {
target.setGuardianName(source.guardianName);
}
if (source.guardianCifNumber != null) {
target.setGuardianCifNumber(source.guardianCifNumber);
}
if (source.guardianRelationship != null) {
target.setGuardianRelationship(source.guardianRelationship);
}
if (source.minorDateOfBirth != null) {
target.setMinorDateOfBirth(source.minorDateOfBirth);
}
if (source.guardianClientId != null) {
target.setGuardianClientId(source.guardianClientId);
}
if (source.introducerClientId != null) {
target.setIntroducerClientId(source.introducerClientId);
}
if (source.introducerName != null) {
target.setIntroducerName(source.introducerName);
}
if (source.introducerCifNumber != null) {
target.setIntroducerCifNumber(source.introducerCifNumber);
}
if (source.nomineeData != null) {
target.setNomineeData(source.nomineeData);
}
if (source.linkedAccountId != null) {
target.setLinkedAccountId(source.linkedAccountId);
}
if (source.linkedAccountType != null) {
target.setLinkedAccountType(source.linkedAccountType);
}
if (source.linkedAccountNo != null) {
target.setLinkedAccountNo(source.linkedAccountNo);
}
if (source.jointHolders != null) {
target.setJointHolders(new ArrayList<>(source.jointHolders));
}
if (source.accountType != null) {
target.setAccountType(source.accountType);
}
if (source.customerSegmentOptions != null) {
target.setCustomerSegmentOptions(source.customerSegmentOptions);
}
if (source.accountTypeOptions != null) {
target.setAccountTypeOptions(source.accountTypeOptions);
}
if (source.productCode != null) {
target.setProductCode(source.productCode);
}
if (source.effectiveDate != null) {
target.setEffectiveDate(source.effectiveDate);
}
if (source.effectiveTo != null) {
target.setEffectiveTo(source.effectiveTo);
}
if (source.endDate != null) {
target.setEndDate(source.endDate);
}
if (source.introducerRequired != null) {
target.setIntroducerRequired(source.introducerRequired);
}
if (source.shareLinkingEnabled != null) {
target.setShareLinkingEnabled(source.shareLinkingEnabled);
}
if (source.nominationFacility != null) {
target.setNominationFacility(source.nominationFacility);
}
if (source.dormancyAlert != null) {
target.setDormancyAlert(source.dormancyAlert);
}
if (source.isDormancyTrackingActiveFromProduct != null) {
target.setIsDormancyTrackingActiveFromProduct(source.isDormancyTrackingActiveFromProduct);
}
if (source.daysToInactiveFromProduct != null) {
target.setDaysToInactiveFromProduct(source.daysToInactiveFromProduct);
}
if (source.daysToDormancyFromProduct != null) {
target.setDaysToDormancyFromProduct(source.daysToDormancyFromProduct);
}
if (source.daysToEscheatFromProduct != null) {
target.setDaysToEscheatFromProduct(source.daysToEscheatFromProduct);
}
if (source.interestCalculationTypeName != null) {
target.setInterestCalculationTypeName(source.interestCalculationTypeName);
}
if (source.enableLockinPeriod != null) {
target.setEnableLockinPeriod(source.enableLockinPeriod);
}
if (source.balanceFrequency != null) {
target.setBalanceFrequency(source.balanceFrequency);
}
if (source.accountClosedZeroBalanceInDayClosing != null) {
target.setAccountClosedZeroBalanceInDayClosing(source.accountClosedZeroBalanceInDayClosing);
}
if (source.balanceSwapOption != null) {
target.setBalanceSwapOption(source.balanceSwapOption);
}
if (source.msme != null) {
target.setMsme(source.msme);
}
if (source.autoDebitFacility != null) {
target.setAutoDebitFacility(source.autoDebitFacility);
}
if (source.avgBalanceCalcPeriod != null) {
target.setAvgBalanceCalcPeriod(source.avgBalanceCalcPeriod);
}
if (source.validForPeriod != null) {
target.setValidForPeriod(source.validForPeriod);
}
if (source.odAccountClosedZeroBalanceInDayClosing != null) {
target.setOdAccountClosedZeroBalanceInDayClosing(source.odAccountClosedZeroBalanceInDayClosing);
}
if (source.lastInterestDate != null) {
target.setLastInterestDate(source.lastInterestDate);
}
if (source.accountFacilityConfig != null) {
target.setAccountFacilityConfig(source.accountFacilityConfig);
}
if (source.accountFacilityLimits != null) {
target.setAccountFacilityLimits(source.accountFacilityLimits);
}
if (source.depositLimitKyc != null) {
target.setDepositLimitKyc(source.depositLimitKyc);
}
if (source.interestCalculateSelector != null) {
target.setInterestCalculateSelector(source.interestCalculateSelector);
}
if (source.averagePeriod != null) {
target.setAveragePeriod(source.averagePeriod);
}
if (source.autoPost != null) {
target.setAutoPost(source.autoPost);
}
if (source.frequencyFixDay != null) {
target.setFrequencyFixDay(source.frequencyFixDay);
}
if (source.interestCalculationTypeOption != null) {
target.setInterestCalculationTypeOption(source.interestCalculationTypeOption);
}
if (source.periodFrequencyOptions != null) {
target.setPeriodFrequencyOptions(source.periodFrequencyOptions);
}
if (source.productId != null) {
target.setProductId(source.productId);
}
if (source.locale != null) {
target.setLocale(source.locale);
}
if (source.dateFormat != null) {
target.setDateFormat(source.dateFormat);
}
if (source.rowIndex != null) {
target.setRowIndex(source.rowIndex);
}
if (source.submittedOnDate != null) {
target.setSubmittedOnDate(source.submittedOnDate);
}
if (source.startInterestCalculationDate != null) {
target.setStartInterestCalculationDate(source.startInterestCalculationDate);
}
if (source.activatedOnDate != null) {
target.setActivatedOnDate(source.activatedOnDate);
}
if (source.lockedInUntilDate != null) {
target.setLockedInUntilDate(source.lockedInUntilDate);
}
if (source.officeId != null) {
target.setOfficeId(source.officeId);
}
if (source.clientData != null) {
target.setClientData(source.clientData);
}
if (source.groupGeneralData != null) {
target.setGroupGeneralData(source.groupGeneralData);
}
if (source.savingsProductData != null) {
target.setSavingsProduct(source.savingsProductData);
}
if (source.savingsAccountSummaryData != null) {
target.setSavingsAccountSummaryData(source.savingsAccountSummaryData);
}
if (source.lastSavingsAccountTransaction != null) {
target.setLastSavingsAccountTransaction(source.lastSavingsAccountTransaction);
}
}
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -0,0 +1,386 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.domain;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.portfolio.client.domain.Client;
@Entity
@Table(name = "m_current_account")
@Getter
@Setter
public class CurrentAccount extends AbstractAuditableCustom {
@Column(name = "account_no", nullable = false, length = 20, unique = true)
private String accountNo;
@Column(name = "external_id", length = 100)
private String externalId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "client_id")
private Client client;
// Store product_id as plain column to avoid cross-module EclipseLink entity dependency
@Column(name = "product_id", nullable = false)
private Long productId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "staff_id")
private Staff staff;
// Status: 100=Submitted, 200=Approved, 300=Active, 400=Withdrawn, 500=Rejected, 600=Closed
@Column(name = "status_enum", nullable = false)
private Integer statusEnum = 100;
// SubStatus: 0=None, 100=Inactive, 200=Dormant, 300=Block, 400=BlockDebit, 500=BlockCredit
@Column(name = "sub_status_enum", nullable = false)
private Integer subStatusEnum = 0;
// 1=Individual, 2=Group, 3=JLG
@Column(name = "account_type_enum", nullable = false)
private Integer accountTypeEnum = 1;
// Lifecycle dates
@Column(name = "submittedon_date")
private LocalDate submittedOnDate;
@Column(name = "approvedon_date")
private LocalDate approvedOnDate;
@Column(name = "activatedon_date")
private LocalDate activatedOnDate;
@Column(name = "rejectedon_date")
private LocalDate rejectedOnDate;
@Column(name = "withdrawnon_date")
private LocalDate withdrawnOnDate;
@Column(name = "closedon_date")
private LocalDate closedOnDate;
// Currency (copied from product at account creation)
@Column(name = "currency_code", nullable = false, length = 3)
private String currencyCode;
@Column(name = "currency_digits", nullable = false)
private Integer currencyDigits;
@Column(name = "currency_multiplesof")
private Integer currencyMultiplesOf;
// Interest
@Column(name = "nominal_annual_interest_rate", nullable = false, precision = 19, scale = 6)
private BigDecimal nominalAnnualInterestRate = BigDecimal.ZERO;
@Column(name = "interest_compounding_period_enum", nullable = false)
private Integer interestCompoundingPeriodEnum;
@Column(name = "interest_posting_period_enum", nullable = false)
private Integer interestPostingPeriodEnum;
@Column(name = "interest_calculation_type_enum", nullable = false)
private Integer interestCalculationTypeEnum;
@Column(name = "interest_calculation_days_in_year_type_enum", nullable = false)
private Integer interestCalculationDaysInYearTypeEnum;
@Column(name = "min_balance_for_interest_calculation", precision = 19, scale = 6)
private BigDecimal minBalanceForInterestCalculation;
@Column(name = "start_interest_calculation_date")
private LocalDate startInterestCalculationDate;
@Column(name = "accrued_till_date")
private LocalDate accruedTillDate;
@Column(name = "last_interest_calculation_date")
private LocalDate lastInterestCalculationDate;
@Column(name = "last_closed_business_date")
private LocalDate lastClosedBusinessDate;
// Tax
@Column(name = "withhold_tax", nullable = false)
private boolean withholdTax = false;
// Balance rules
@Column(name = "min_required_opening_balance", precision = 19, scale = 6)
private BigDecimal minRequiredOpeningBalance;
@Column(name = "lockin_period_frequency")
private Integer lockinPeriodFrequency;
@Column(name = "lockin_period_frequency_enum")
private Integer lockinPeriodFrequencyEnum;
@Column(name = "lockedin_until_date_derived")
private LocalDate lockedInUntilDate;
@Column(name = "enforce_min_required_balance", nullable = false)
private boolean enforceMinRequiredBalance = false;
@Column(name = "min_required_balance", precision = 19, scale = 6)
private BigDecimal minRequiredBalance;
// Overdraft
@Column(name = "allow_overdraft", nullable = false)
private boolean allowOverdraft = false;
@Column(name = "overdraft_limit", precision = 19, scale = 6)
private BigDecimal overdraftLimit;
@Column(name = "nominal_annual_interest_rate_overdraft", precision = 19, scale = 6)
private BigDecimal nominalAnnualInterestRateOverdraft;
@Column(name = "min_overdraft_for_interest_calculation", precision = 19, scale = 6)
private BigDecimal minOverdraftForInterestCalculation;
// Lien
@Column(name = "is_lien_allowed", nullable = false)
private boolean lienAllowed = false;
@Column(name = "max_allowed_lien_limit", precision = 19, scale = 6)
private BigDecimal maxAllowedLienLimit;
@Column(name = "on_hold_funds_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal onHoldFundsDerived = BigDecimal.ZERO;
// Running balance summary
@Column(name = "account_balance_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal accountBalanceDerived = BigDecimal.ZERO;
@Column(name = "total_deposits_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal totalDepositsDerived = BigDecimal.ZERO;
@Column(name = "total_withdrawals_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal totalWithdrawalsDerived = BigDecimal.ZERO;
@Column(name = "total_interest_earned_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal totalInterestEarnedDerived = BigDecimal.ZERO;
@Column(name = "total_interest_posted_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal totalInterestPostedDerived = BigDecimal.ZERO;
@Column(name = "total_overdraft_interest_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal totalOverdraftInterestDerived = BigDecimal.ZERO;
@Column(name = "total_withhold_tax_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal totalWithholdTaxDerived = BigDecimal.ZERO;
@Column(name = "total_savings_amount_on_hold", nullable = false, precision = 19, scale = 6)
private BigDecimal totalSavingsAmountOnHold = BigDecimal.ZERO;
@Column(name = "withdrawal_fee_for_transfer", nullable = false)
private boolean withdrawalFeeForTransfer = false;
@Column(name = "reason_for_block", length = 500)
private String reasonForBlock;
// Current-account-specific fields
@Column(name = "msme", nullable = false)
private boolean msme = false;
@Column(name = "auto_debit_facility", nullable = false)
private boolean autoDebitFacility = false;
@Column(name = "balance_swap_option", nullable = false)
private boolean balanceSwapOption = false;
@Column(name = "establishment_date")
private LocalDate establishmentDate;
@Column(name = "start_operation_date")
private LocalDate startOperationDate;
@Column(name = "is_non_individual_client", nullable = false)
private boolean nonIndividualClient = false;
@Column(name = "signatory_rights", length = 100)
private String signatoryRights;
// CBS classification
@Column(name = "cbs_account_type", length = 100)
private String cbsAccountType;
@Column(name = "customer_segment", length = 100)
private String customerSegment;
@Column(name = "account_status", length = 50)
private String accountStatus;
@Column(name = "mode_of_operation", length = 50)
private String modeOfOperation;
@Column(name = "special_category", length = 100)
private String specialCategory;
// Introducer
@Column(name = "introducer_client_id")
private Long introducerClientId;
@Column(name = "introducer_name", length = 200)
private String introducerName;
@Column(name = "introducer_cif_number", length = 50)
private String introducerCifNumber;
// Nominee JSON
@Column(name = "nominee_data", columnDefinition = "TEXT")
private String nomineeData;
// Interest calculation settings
@Column(name = "interest_calculate_selector")
private Integer interestCalculateSelector;
@Column(name = "average_period", length = 20)
private String averagePeriod;
@Column(name = "auto_post", nullable = false)
private boolean autoPost = false;
@Column(name = "frequency_fix_day")
private Integer frequencyFixDay;
@Column(name = "enable_lockin_period", nullable = false)
private boolean enableLockinPeriod = false;
@Column(name = "balance_frequency", length = 50)
private String balanceFrequency;
@Column(name = "avg_balance_calc_period", length = 10)
private String avgBalanceCalcPeriod;
// Relationships
@OneToMany(mappedBy = "currentAccount", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<CurrentAccountTransaction> transactions = new ArrayList<>();
@OneToMany(mappedBy = "currentAccount", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<CurrentAccountCharge> charges = new HashSet<>();
@OneToOne(mappedBy = "currentAccount", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private CurrentAccountFacilityConfig facilityConfig;
@OneToMany(mappedBy = "currentAccount", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<CurrentAccountJointHolder> jointHolders = new ArrayList<>();
@OneToMany(mappedBy = "currentAccount", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<CurrentAccountPartner> partners = new ArrayList<>();
@OneToMany(mappedBy = "currentAccount", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<CurrentAccountOfficerHistory> officerHistory = new ArrayList<>();
protected CurrentAccount() {}
public static CurrentAccount createNew(Client client, Long productId, Office office,
Staff staff, String accountNo, LocalDate submittedOnDate, Integer accountTypeEnum,
String currencyCode, Integer currencyDigits, Integer currencyMultiplesOf,
BigDecimal nominalAnnualInterestRate, Integer interestCompoundingPeriodEnum,
Integer interestPostingPeriodEnum, Integer interestCalculationTypeEnum,
Integer interestCalculationDaysInYearTypeEnum, boolean allowOverdraft,
BigDecimal overdraftLimit, boolean msme, boolean autoDebitFacility,
boolean balanceSwapOption, String customerSegment, boolean autoPost,
Integer interestCalculateSelector, String averagePeriod, String avgBalanceCalcPeriod) {
CurrentAccount account = new CurrentAccount();
account.client = client;
account.productId = productId;
account.office = office;
account.staff = staff;
account.accountNo = accountNo;
account.submittedOnDate = submittedOnDate;
account.statusEnum = 100;
account.subStatusEnum = 0;
account.accountTypeEnum = accountTypeEnum != null ? accountTypeEnum : 1;
account.currencyCode = currencyCode;
account.currencyDigits = currencyDigits;
account.currencyMultiplesOf = currencyMultiplesOf;
account.nominalAnnualInterestRate = nominalAnnualInterestRate != null ? nominalAnnualInterestRate : BigDecimal.ZERO;
account.interestCompoundingPeriodEnum = interestCompoundingPeriodEnum;
account.interestPostingPeriodEnum = interestPostingPeriodEnum;
account.interestCalculationTypeEnum = interestCalculationTypeEnum;
account.interestCalculationDaysInYearTypeEnum = interestCalculationDaysInYearTypeEnum;
account.allowOverdraft = allowOverdraft;
account.overdraftLimit = overdraftLimit;
account.msme = msme;
account.autoDebitFacility = autoDebitFacility;
account.balanceSwapOption = balanceSwapOption;
account.customerSegment = customerSegment;
account.autoPost = autoPost;
account.interestCalculateSelector = interestCalculateSelector;
account.averagePeriod = averagePeriod;
account.avgBalanceCalcPeriod = avgBalanceCalcPeriod;
return account;
}
public boolean isActive() { return this.statusEnum != null && this.statusEnum == 300; }
public boolean isSubmittedAndPendingApproval() { return this.statusEnum != null && this.statusEnum == 100; }
public boolean isApproved() { return this.statusEnum != null && this.statusEnum == 200; }
public boolean isClosed() { return this.statusEnum != null && this.statusEnum == 600; }
public void updateAccountNo(String accountNo) { this.accountNo = accountNo; }
public void approve(LocalDate approvedOnDate) {
this.statusEnum = 200;
this.approvedOnDate = approvedOnDate;
}
public void activate(LocalDate activatedOnDate) {
this.statusEnum = 300;
this.activatedOnDate = activatedOnDate;
}
public void reject(LocalDate rejectedOnDate) {
this.statusEnum = 500;
this.rejectedOnDate = rejectedOnDate;
}
public void withdraw(LocalDate withdrawnOnDate) {
this.statusEnum = 400;
this.withdrawnOnDate = withdrawnOnDate;
}
public void close(LocalDate closedOnDate) {
this.statusEnum = 600;
this.closedOnDate = closedOnDate;
}
}
@@ -0,0 +1,109 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.charge.domain.Charge;
@Entity
@Table(name = "m_current_account_charge")
@Getter
@Setter
public class CurrentAccountCharge extends AbstractPersistableCustom<Long> {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_account_id", nullable = false)
private CurrentAccount currentAccount;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "charge_id", nullable = false)
private Charge charge;
@Column(name = "charge_time_enum", nullable = false)
private Integer chargeTimeEnum;
@Column(name = "charge_due_date")
private LocalDate chargeDueDate;
@Column(name = "fee_on_month")
private Integer feeOnMonth;
@Column(name = "fee_on_day")
private Integer feeOnDay;
@Column(name = "fee_interval")
private Integer feeInterval;
@Column(name = "charge_calculation_enum", nullable = false)
private Integer chargeCalculationEnum;
@Column(name = "calculation_percentage", precision = 19, scale = 6)
private BigDecimal calculationPercentage;
@Column(name = "calculation_on_amount", precision = 19, scale = 6)
private BigDecimal calculationOnAmount;
@Column(name = "amount", nullable = false, precision = 19, scale = 6)
private BigDecimal amount;
@Column(name = "amount_paid_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal amountPaidDerived = BigDecimal.ZERO;
@Column(name = "amount_waived_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal amountWaivedDerived = BigDecimal.ZERO;
@Column(name = "amount_writtenoff_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal amountWrittenOffDerived = BigDecimal.ZERO;
@Column(name = "amount_outstanding_derived", nullable = false, precision = 19, scale = 6)
private BigDecimal amountOutstandingDerived;
@Column(name = "is_paid_derived", nullable = false)
private boolean paid = false;
@Column(name = "waived", nullable = false)
private boolean waived = false;
@Column(name = "is_penalty", nullable = false)
private boolean penalty = false;
@Column(name = "is_active", nullable = false)
private boolean active = true;
@Column(name = "inactivated_on_date")
private LocalDate inactivatedOnDate;
@Column(name = "free_withdrawal_count")
private Integer freeWithdrawalCount;
@Column(name = "charge_reset_date")
private LocalDate chargeResetDate;
protected CurrentAccountCharge() {}
}
@@ -0,0 +1,102 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_current_account_facility_config")
@Getter
@Setter
public class CurrentAccountFacilityConfig extends AbstractPersistableCustom<Long> {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_account_id", nullable = false, unique = true)
private CurrentAccount currentAccount;
@Column(name = "atm_facility", nullable = false)
private boolean atmFacility = false;
@Column(name = "atm_withdrawal_facility", nullable = false)
private boolean atmWithdrawalFacility = false;
@Column(name = "upi_facility", nullable = false)
private boolean upiFacility = false;
@Column(name = "pos_transactions_facility", nullable = false)
private boolean posTransactionsFacility = false;
@Column(name = "cheque_book", nullable = false)
private boolean chequeBook = false;
@Column(name = "imps_facility", nullable = false)
private boolean impsFacility = false;
@Column(name = "sms_alerts", nullable = false)
private boolean smsAlerts = false;
@Column(name = "bbps_facility", nullable = false)
private boolean bbpsFacility = false;
@Column(name = "insurance_cover", nullable = false)
private boolean insuranceCover = false;
@Column(name = "bepg_facility", nullable = false)
private boolean bepgFacility = false;
@Column(name = "neft_facility", nullable = false)
private boolean neftFacility = false;
@Column(name = "rtgs_facility", nullable = false)
private boolean rtgsFacility = false;
@Column(name = "inter_bank_total_cap_facility", nullable = false)
private boolean interBankTotalCapFacility = false;
@Column(name = "withdrawal_facility", nullable = false)
private boolean withdrawalFacility = false;
@Column(name = "deposit_facility_kyc", nullable = false)
private boolean depositFacilityKyc = false;
@Column(name = "virtual_debit_card", nullable = false)
private boolean virtualDebitCard = false;
@Column(name = "internet_banking", nullable = false)
private boolean internetBanking = false;
@Column(name = "mobile_banking", nullable = false)
private boolean mobileBanking = false;
protected CurrentAccountFacilityConfig() {}
public static CurrentAccountFacilityConfig createFor(CurrentAccount account) {
CurrentAccountFacilityConfig cfg = new CurrentAccountFacilityConfig();
cfg.currentAccount = account;
return cfg;
}
}
@@ -0,0 +1,64 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_current_account_joint_holder")
@Getter
@Setter
public class CurrentAccountJointHolder extends AbstractPersistableCustom<Long> {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_account_id", nullable = false)
private CurrentAccount currentAccount;
@Column(name = "client_id")
private Long clientId;
@Column(name = "cif_number", length = 50)
private String cifNumber;
@Column(name = "display_name", length = 200)
private String displayName;
@Column(name = "share_percentage", precision = 5, scale = 2)
private BigDecimal sharePercentage;
@Column(name = "signing_authority", length = 100)
private String signingAuthority;
@Column(name = "role", length = 100)
private String role;
@Column(name = "is_primary", nullable = false)
private boolean primary = false;
public CurrentAccountJointHolder() {}
}
@@ -0,0 +1,57 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.time.LocalDate;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.organisation.staff.domain.Staff;
@Entity
@Table(name = "m_current_account_officer_history")
@Getter
@Setter
public class CurrentAccountOfficerHistory extends AbstractPersistableCustom<Long> {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_account_id", nullable = false)
private CurrentAccount currentAccount;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "staff_id")
private Staff staff;
@Column(name = "start_date", nullable = false)
private LocalDate startDate;
@Column(name = "end_date")
private LocalDate endDate;
@Column(name = "created_by_id")
private Long createdById;
protected CurrentAccountOfficerHistory() {}
}
@@ -0,0 +1,58 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_current_account_partner")
@Getter
@Setter
public class CurrentAccountPartner extends AbstractPersistableCustom<Long> {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_account_id", nullable = false)
private CurrentAccount currentAccount;
@Column(name = "client_id")
private Long clientId;
@Column(name = "client_cif", length = 50)
private String clientCif;
@Column(name = "client_name", length = 200)
private String clientName;
@Column(name = "share_percentage", precision = 5, scale = 2)
private BigDecimal sharePercentage;
@Column(name = "is_mandatory", nullable = false)
private boolean mandatory = false;
public CurrentAccountPartner() {}
}
@@ -0,0 +1,40 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.domain;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface CurrentAccountRepository extends JpaRepository<CurrentAccount, Long> {
Optional<CurrentAccount> findByAccountNo(String accountNo);
Optional<CurrentAccount> findByExternalId(String externalId);
List<CurrentAccount> findByClientId(Long clientId);
@Query("SELECT MAX(ca.id) FROM CurrentAccount ca")
Optional<Long> findMaxId();
@Query("SELECT ca FROM CurrentAccount ca WHERE ca.client.id = :clientId AND ca.statusEnum != 600")
List<CurrentAccount> findActiveByClientId(@Param("clientId") Long clientId);
}
@@ -0,0 +1,137 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.organisation.office.domain.Office;
@Entity
@Table(name = "m_current_account_transaction")
@Getter
@Setter
public class CurrentAccountTransaction extends AbstractPersistableCustom<Long> {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "current_account_id", nullable = false)
private CurrentAccount currentAccount;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "office_id", nullable = false)
private Office office;
// 1=Deposit, 2=Withdrawal, 3=InterestPosting, 4=Charge, 5=ChargeWaiver, 6=Transfer, 7=Reversal
@Column(name = "transaction_type_enum", nullable = false)
private Integer transactionTypeEnum;
@Column(name = "transaction_date", nullable = false)
private LocalDate transactionDate;
@Column(name = "amount", nullable = false, precision = 19, scale = 6)
private BigDecimal amount;
@Column(name = "ref_no", length = 100)
private String refNo;
@Column(name = "remarks", length = 500)
private String remarks;
@Column(name = "is_reversed", nullable = false)
private boolean reversed = false;
@Column(name = "is_reversal", nullable = false)
private boolean reversal = false;
@Column(name = "original_transaction_id")
private Long originalTransactionId;
@Column(name = "is_manual", nullable = false)
private boolean manual = false;
@Column(name = "running_balance_derived", precision = 19, scale = 6)
private BigDecimal runningBalanceDerived;
@Column(name = "cumulative_balance_derived", precision = 19, scale = 6)
private BigDecimal cumulativeBalanceDerived;
@Column(name = "balance_end_date_derived")
private LocalDate balanceEndDateDerived;
@Column(name = "balance_number_of_days_derived")
private Integer balanceNumberOfDaysDerived;
@Column(name = "overdraft_amount_derived", precision = 19, scale = 6)
private BigDecimal overdraftAmountDerived;
@Column(name = "is_lien_transaction", nullable = false)
private boolean lienTransaction = false;
@Column(name = "release_id_of_hold_amount_transaction")
private Long releaseIdOfHoldAmountTransaction;
@Column(name = "reason_for_block", length = 200)
private String reasonForBlock;
@Column(name = "submitted_on_date")
private LocalDate submittedOnDate;
@Column(name = "created_date")
private LocalDateTime createdDate;
@Column(name = "created_by")
private Long createdBy;
protected CurrentAccountTransaction() {}
public static CurrentAccountTransaction deposit(CurrentAccount account, Office office,
BigDecimal amount, LocalDate transactionDate, String refNo) {
CurrentAccountTransaction txn = new CurrentAccountTransaction();
txn.currentAccount = account;
txn.office = office;
txn.transactionTypeEnum = 1;
txn.amount = amount;
txn.transactionDate = transactionDate;
txn.refNo = refNo;
txn.submittedOnDate = transactionDate;
return txn;
}
public static CurrentAccountTransaction withdrawal(CurrentAccount account, Office office,
BigDecimal amount, LocalDate transactionDate, String refNo) {
CurrentAccountTransaction txn = new CurrentAccountTransaction();
txn.currentAccount = account;
txn.office = office;
txn.transactionTypeEnum = 2;
txn.amount = amount;
txn.transactionDate = transactionDate;
txn.refNo = refNo;
txn.submittedOnDate = transactionDate;
return txn;
}
}
@@ -0,0 +1,27 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.domain;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CurrentAccountTransactionRepository extends JpaRepository<CurrentAccountTransaction, Long> {
List<CurrentAccountTransaction> findByCurrentAccountIdOrderByTransactionDateDescIdDesc(Long currentAccountId);
}
@@ -0,0 +1,92 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.service;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
/**
* Generates CBS account numbers for current accounts.
* Format: 00 + GL_CODE(6 digits) + OFFICE_ID(2 digits) + ACCOUNT_ID(5 digits) = 15 digits
* GL code is fetched from m_current_product.gl_account_id → m_acc_gl_account.gl_code
*/
@Component
@RequiredArgsConstructor
public class CurrentAccountNumberGenerator {
private static final String CBS_PREFIX = "00";
private final JdbcTemplate jdbcTemplate;
public String generate(CurrentAccount account) {
Long productId = account.getProductId();
// Fetch gl_code directly from current product → gl account
String glCode = jdbcTemplate.query(
"SELECT ga.gl_code FROM m_current_product cp "
+ "JOIN m_acc_gl_account ga ON ga.id = cp.gl_account_id "
+ "WHERE cp.id = ?",
rs -> rs.next() ? rs.getString("gl_code") : null,
productId);
if (glCode == null || glCode.isBlank()) glCode = "000000";
// Pad/trim GL code to 6 digits
glCode = glCode.length() > 6
? glCode.substring(glCode.length() - 6)
: StringUtils.leftPad(glCode, 6, '0');
// Office ID — 2 digits
String officeId = StringUtils.leftPad(account.getOffice().getId().toString(), 2, '0');
if (officeId.length() > 2) officeId = officeId.substring(officeId.length() - 2);
// Account ID — 5 digits
String accountId = StringUtils.leftPad(account.getId().toString(), 5, '0');
if (accountId.length() > 5) accountId = accountId.substring(accountId.length() - 5);
return CBS_PREFIX + glCode + officeId + accountId;
}
public String preview(Long productId, Long officeId) {
String glCode = jdbcTemplate.query(
"SELECT ga.gl_code FROM m_current_product cp "
+ "JOIN m_acc_gl_account ga ON ga.id = cp.gl_account_id "
+ "WHERE cp.id = ?",
rs -> rs.next() ? rs.getString("gl_code") : null,
productId);
if (glCode == null || glCode.isBlank()) glCode = "000000";
glCode = glCode.length() > 6 ? glCode.substring(glCode.length() - 6) : StringUtils.leftPad(glCode, 6, '0');
String officeStr = StringUtils.leftPad(officeId != null ? officeId.toString() : "0", 2, '0');
if (officeStr.length() > 2) officeStr = officeStr.substring(officeStr.length() - 2);
// Next account ID estimate from m_current_account
Long maxId = jdbcTemplate.query(
"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;
}
}
@@ -0,0 +1,43 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.currentaccount.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface CurrentAccountWritePlatformService {
CommandProcessingResult submitApplication(JsonCommand command);
CommandProcessingResult approveApplication(Long accountId, JsonCommand command);
CommandProcessingResult undoApproval(Long accountId, JsonCommand command);
CommandProcessingResult activate(Long accountId, JsonCommand command);
CommandProcessingResult rejectApplication(Long accountId, JsonCommand command);
CommandProcessingResult withdrawApplication(Long accountId, JsonCommand command);
CommandProcessingResult closeAccount(Long accountId, JsonCommand command);
CommandProcessingResult modifyApplication(Long accountId, JsonCommand command);
CommandProcessingResult deleteApplication(Long accountId);
}
@@ -817,6 +817,15 @@ public class CurrentProduct extends AbstractPersistableCustom<Long> {
return actualChanges;
}
public String getName() { return name; }
public String getShortName() { return shortName; }
public boolean isAllowOverdraft() { return allowOverdraft; }
public BigDecimal getOverdraftLimit() { return overdraftLimit; }
public Integer getInterestCompoundingPeriodType() { return interestCompoundingPeriodType; }
public Integer getInterestPostingPeriodType() { return interestPostingPeriodType; }
public Integer getInterestCalculationType() { return interestCalculationType; }
public Integer getInterestCalculationDaysInYearType() { return interestCalculationDaysInYearType; }
// Dormancy/misc getters needed by validator
public Boolean isDormancyTrackingActive() { return this.isDormancyTrackingActive; }
public Long getDaysToInactive() { return this.daysToInactive; }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -36,10 +36,12 @@ import org.apache.fineract.portfolio.client.domain.ClientRepository;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
/**
@@ -69,6 +71,7 @@ public class AccountNumberGenerator {
private final SavingsAccountRepository savingsAccountRepository;
private final GLAccountRepository glAccountRepository;
private final SavingsProductRepository savingsProductRepository;
private final JdbcTemplate jdbcTemplate;
public String generate(Client client, AccountNumberFormat accountNumberFormat) {
Map<String, String> propertyMap = new HashMap<>();
@@ -119,6 +122,35 @@ public class AccountNumberGenerator {
return CBS_FIXED_PREFIX + glCode + officeId + accountId;
}
/**
* Generate CBS account number for a current account.
* GL code is fetched directly from m_current_product using the current account's product_id.
* Format: 00 + GL_CODE(6) + OFFICE_ID(2) + ACCOUNT_ID(5) = 15 digits
*/
public String generateForCurrentAccount(CurrentAccount currentAccount) {
Long productId = currentAccount.getProductId();
Long glAccountId = jdbcTemplate.query(
"SELECT gl_account_id FROM m_current_product WHERE id = ?",
rs -> rs.next() ? rs.getLong("gl_account_id") : null,
productId);
String glCode = "000000";
if (glAccountId != null && glAccountId > 0) {
glCode = glAccountRepository.findById(glAccountId)
.map(gl -> StringUtils.leftPad(gl.getGlCode(), 6, '0'))
.orElse("000000");
}
glCode = glCode.length() > 6 ? glCode.substring(glCode.length() - 6) : StringUtils.leftPad(glCode, 6, '0');
String officeId = StringUtils.leftPad(currentAccount.getOffice().getId().toString(), 2, '0');
if (officeId.length() > 2) officeId = officeId.substring(officeId.length() - 2);
String accountId = StringUtils.leftPad(currentAccount.getId().toString(), 5, '0');
if (accountId.length() > 5) accountId = accountId.substring(accountId.length() - 5);
return CBS_FIXED_PREFIX + glCode + officeId + accountId;
}
public String previewAccountNumber(Long productId, Long officeId) {
String glCode = "000000";
if (productId != null) {
@@ -40,9 +40,6 @@ import java.util.Collection;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
@@ -59,6 +56,7 @@ import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
@@ -75,10 +73,10 @@ public class CurrentAccountsApiResource {
private final CurrentAccountTemplateReadPlatformService currentAccountTemplateReadPlatformService;
private final PlatformSecurityContext context;
private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
private final SqlValidator sqlValidator;
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
@GET
@Path("template")
@@ -128,8 +126,9 @@ public class CurrentAccountsApiResource {
@Operation(summary = "Submit new current account application")
@ApiResponse(responseCode = "200", description = "OK")
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createCurrentAccount().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
org.apache.fineract.infrastructure.core.api.JsonCommand.from(apiRequestBodyAsJson);
final CommandProcessingResult result = currentAccountWritePlatformService.submitApplication(command);
return toApiJsonSerializer.serialize(result);
}
@@ -164,16 +163,9 @@ public class CurrentAccountsApiResource {
@Parameter(hidden = true) final String apiRequestBodyAsJson,
@QueryParam("command") final String commandParam) {
if (is(commandParam, "updateWithHoldTax")) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson)
.updateWithHoldTaxCurrentAccount(accountId).build();
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
return toApiJsonSerializer.serialize(result);
}
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateCurrentAccount(accountId).withJson(apiRequestBodyAsJson)
.build();
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
org.apache.fineract.infrastructure.core.api.JsonCommand.from(apiRequestBodyAsJson);
final CommandProcessingResult result = currentAccountWritePlatformService.modifyApplication(accountId, command);
return toApiJsonSerializer.serialize(result);
}
@@ -192,53 +184,27 @@ public class CurrentAccountsApiResource {
jsonApiRequest = "{}";
}
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest);
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
org.apache.fineract.infrastructure.core.api.JsonCommand.from(jsonApiRequest);
CommandProcessingResult result = null;
if (is(commandParam, "reject")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.rejectCurrentAccountApplication(accountId).build());
result = currentAccountWritePlatformService.rejectApplication(accountId, command);
} else if (is(commandParam, "withdrawnByApplicant")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.withdrawCurrentAccountApplication(accountId).build());
result = currentAccountWritePlatformService.withdrawApplication(accountId, command);
} else if (is(commandParam, "approve")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.approveCurrentAccountApplication(accountId).build());
result = currentAccountWritePlatformService.approveApplication(accountId, command);
} else if (is(commandParam, "undoapproval")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.undoCurrentAccountApplication(accountId).build());
result = currentAccountWritePlatformService.undoApproval(accountId, command);
} else if (is(commandParam, "activate")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.currentAccountActivation(accountId).build());
} else if (is(commandParam, "calculateInterest")) {
result = commandsSourceWritePlatformService
.logCommandSource(builder.withNoJsonBody().currentAccountInterestCalculation(accountId).build());
} else if (is(commandParam, "postInterest")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.currentAccountInterestPosting(accountId).build());
} else if (is(commandParam, "applyAnnualFees")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.currentAccountApplyAnnualFees(accountId).build());
result = currentAccountWritePlatformService.activate(accountId, command);
} else if (is(commandParam, "close")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.closeCurrentAccountApplication(accountId).build());
} else if (is(commandParam, "assignSavingsOfficer")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.assignCurrentAccountOfficer(accountId).build());
} else if (is(commandParam, "unassignSavingsOfficer")) {
result = commandsSourceWritePlatformService.logCommandSource(builder.unassignCurrentAccountOfficer(accountId).build());
} else if (is(commandParam, SavingsApiConstants.COMMAND_BLOCK_DEBIT)) {
result = commandsSourceWritePlatformService.logCommandSource(builder.blockDebitsFromCurrentAccount(accountId).build());
} else if (is(commandParam, SavingsApiConstants.COMMAND_UNBLOCK_DEBIT)) {
result = commandsSourceWritePlatformService.logCommandSource(builder.unblockDebitsFromCurrentAccount(accountId).build());
} else if (is(commandParam, SavingsApiConstants.COMMAND_BLOCK_CREDIT)) {
result = commandsSourceWritePlatformService.logCommandSource(builder.blockCreditsToCurrentAccount(accountId).build());
} else if (is(commandParam, SavingsApiConstants.COMMAND_UNBLOCK_CREDIT)) {
result = commandsSourceWritePlatformService.logCommandSource(builder.unblockCreditsToCurrentAccount(accountId).build());
} else if (is(commandParam, SavingsApiConstants.COMMAND_BLOCK_ACCOUNT)) {
result = commandsSourceWritePlatformService.logCommandSource(builder.blockCurrentAccount(accountId).build());
} else if (is(commandParam, SavingsApiConstants.COMMAND_UNBLOCK_ACCOUNT)) {
result = commandsSourceWritePlatformService.logCommandSource(builder.unblockCurrentAccount(accountId).build());
result = currentAccountWritePlatformService.closeAccount(accountId, command);
}
if (result == null) {
throw new UnrecognizedQueryParamException("command", commandParam,
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest",
"postInterest", "close", "assignSavingsOfficer", "unassignSavingsOfficer",
SavingsApiConstants.COMMAND_BLOCK_DEBIT, SavingsApiConstants.COMMAND_UNBLOCK_DEBIT,
SavingsApiConstants.COMMAND_BLOCK_CREDIT, SavingsApiConstants.COMMAND_UNBLOCK_CREDIT,
SavingsApiConstants.COMMAND_BLOCK_ACCOUNT, SavingsApiConstants.COMMAND_UNBLOCK_ACCOUNT });
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "close" });
}
return toApiJsonSerializer.serialize(result);
@@ -251,8 +217,7 @@ public class CurrentAccountsApiResource {
@Operation(summary = "Delete a current account application")
@ApiResponse(responseCode = "200", description = "OK")
public String delete(@PathParam("accountId") final Long accountId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteCurrentAccount(accountId).build();
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandProcessingResult result = currentAccountWritePlatformService.deleteApplication(accountId);
return toApiJsonSerializer.serialize(result);
}
@@ -19,7 +19,27 @@
package org.apache.fineract.portfolio.savings.domain;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountFacilityConfigParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountStatusParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.guardianClientIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.modeOfOperationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.customerSegmentParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.guardianCifNumberParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.guardianNameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.guardianRelationshipParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerCifNumberParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerNameParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.jointHoldersParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.linkedAccountIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.linkedAccountNoParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.linkedAccountTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minorDateOfBirthParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nomineeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.specialCategoryParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.weakerSectionParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName;
@@ -45,7 +65,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.submitte
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -336,9 +359,114 @@ public class SavingsAccountAssembler {
account.validateAccountValuesWithProduct();
// CBS extended fields
applyCbsFields(command, element, account);
return account;
}
private static final Gson GSON = new Gson();
private void applyCbsFields(final JsonCommand command, final JsonElement element, final SavingsAccount account) {
// Product classification
final String cbsAccountType = this.fromApiJsonHelper.extractStringNamed(accountTypeParamName, element);
if (cbsAccountType != null) account.setCbsAccountType(cbsAccountType);
final String customerSegment = this.fromApiJsonHelper.extractStringNamed(customerSegmentParamName, element);
if (customerSegment != null) account.setCustomerSegment(customerSegment);
// Status of Account
final String accountStatus = this.fromApiJsonHelper.extractStringNamed(accountStatusParamName, element);
if (accountStatus != null) account.setAccountStatus(accountStatus);
final String modeOfOperation = this.fromApiJsonHelper.extractStringNamed(modeOfOperationParamName, element);
if (modeOfOperation != null) account.setModeOfOperation(modeOfOperation);
account.setSpecialCategory(this.fromApiJsonHelper.extractStringNamed(specialCategoryParamName, element));
account.setWeakerSection(this.fromApiJsonHelper.extractStringNamed(weakerSectionParamName, element));
account.setGuardianName(this.fromApiJsonHelper.extractStringNamed(guardianNameParamName, element));
account.setGuardianCifNumber(this.fromApiJsonHelper.extractStringNamed(guardianCifNumberParamName, element));
account.setGuardianRelationship(this.fromApiJsonHelper.extractStringNamed(guardianRelationshipParamName, element));
if (command.parameterExists(minorDateOfBirthParamName)) {
final LocalDate minorDob = this.fromApiJsonHelper.extractLocalDateNamed(minorDateOfBirthParamName, element);
account.setMinorDateOfBirth(minorDob);
}
final Long guardianClientId = this.fromApiJsonHelper.extractLongNamed(guardianClientIdParamName, element);
if (guardianClientId != null) account.setGuardianClientId(guardianClientId);
// Introducer
final Long introducerId = this.fromApiJsonHelper.extractLongNamed(introducerIdParamName, element);
if (introducerId != null) account.setIntroducerClientId(introducerId);
account.setIntroducerName(this.fromApiJsonHelper.extractStringNamed(introducerNameParamName, element));
account.setIntroducerCifNumber(this.fromApiJsonHelper.extractStringNamed(introducerCifNumberParamName, element));
// Nominee (stored as JSON string)
if (element.getAsJsonObject().has(nomineeParamName)) {
final JsonElement nomineeEl = element.getAsJsonObject().get(nomineeParamName);
if (nomineeEl != null && !nomineeEl.isJsonNull()) {
account.setNomineeData(nomineeEl.toString());
}
}
// Deposit Account Link
final Long linkedAccountId = this.fromApiJsonHelper.extractLongNamed(linkedAccountIdParamName, element);
if (linkedAccountId != null) account.setLinkedAccountId(linkedAccountId);
account.setLinkedAccountType(this.fromApiJsonHelper.extractStringNamed(linkedAccountTypeParamName, element));
account.setLinkedAccountNo(this.fromApiJsonHelper.extractStringNamed(linkedAccountNoParamName, element));
// Account Facility Config
if (element.getAsJsonObject().has(accountFacilityConfigParamName)) {
final JsonElement configEl = element.getAsJsonObject().get(accountFacilityConfigParamName);
if (configEl != null && !configEl.isJsonNull() && configEl.isJsonObject()) {
SavingsAccountFacilityConfig facilityConfig = account.getFacilityConfig();
if (facilityConfig == null) {
facilityConfig = new SavingsAccountFacilityConfig(account);
account.setFacilityConfig(facilityConfig);
}
facilityConfig.updateFrom(configEl.getAsJsonObject());
}
}
// Joint holders
if (element.getAsJsonObject().has(jointHoldersParamName)) {
final JsonElement holdersEl = element.getAsJsonObject().get(jointHoldersParamName);
if (holdersEl != null && !holdersEl.isJsonNull() && holdersEl.isJsonArray()) {
final com.google.gson.JsonArray holdersArray = holdersEl.getAsJsonArray();
final List<SavingsAccountJointHolder> holders = new ArrayList<>();
for (int i = 0; i < holdersArray.size(); i++) {
final 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";
}
holders.add(new SavingsAccountJointHolder(account, clientId, cifNumber, displayName,
sharePercentage, signingAuthority, role, isPrimary));
}
account.getJointHolders().clear();
account.getJointHolders().addAll(holders);
}
}
}
public SavingsAccount assembleFrom(final Long savingsId, final boolean backdatedTxnsAllowedTill) {
SavingsAccount account = this.savingsAccountRepository.findSavingsWithNotFoundDetection(savingsId, backdatedTxnsAllowedTill);
return loadTransactionsToSavingsAccount(account, backdatedTxnsAllowedTill);
@@ -26,7 +26,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
@@ -78,6 +81,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
private final ChargeReadPlatformService chargeReadPlatformService;
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
private final AccountNumberGenerator accountNumberGenerator;
private final CodeValueReadPlatformService codeValueReadPlatformService;
public CurrentAccountTemplateReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
@@ -87,7 +91,8 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
final ChargeReadPlatformService chargeReadPlatformService,
final EntityDatatableChecksReadService entityDatatableChecksReadService,
final ColumnValidator columnValidator,
final AccountNumberGenerator accountNumberGenerator) {
final AccountNumberGenerator accountNumberGenerator,
final CodeValueReadPlatformService codeValueReadPlatformService) {
this.context = context;
this.jdbcTemplate = jdbcTemplate;
this.clientReadPlatformService = clientReadPlatformService;
@@ -98,6 +103,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
this.chargeReadPlatformService = chargeReadPlatformService;
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
this.accountNumberGenerator = accountNumberGenerator;
this.codeValueReadPlatformService = codeValueReadPlatformService;
}
@Override
@@ -204,6 +210,12 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
// Classification fields
template.setAccountType(productData.getAccountType());
template.setCustomerSegment(productData.getCustomerSegment());
final Collection<CodeValueData> accountTypeOpts = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(SavingsApiConstants.CURRENT_PRODUCT_ACCOUNT_TYPE);
final Collection<CodeValueData> customerSegmentOpts = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(SavingsApiConstants.CURRENT_PRODUCT_CUSTOMER_SEGMENT);
template.setAccountTypeOptions(accountTypeOpts);
template.setCustomerSegmentOptions(customerSegmentOpts);
template.setProductCode(productData.getProductCode());
template.setEffectiveDate(productData.getEffectiveDate());
template.setEffectiveTo(productData.getEffectiveTo());
@@ -0,0 +1,370 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.savings.service;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountFacilityConfig;
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountJointHolder;
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountPartner;
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct;
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Service
@RequiredArgsConstructor
public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWritePlatformService {
private final PlatformSecurityContext context;
private final CurrentAccountRepository currentAccountRepository;
private final CurrentProductRepository currentProductRepository;
private final ClientRepositoryWrapper clientRepository;
private final OfficeRepositoryWrapper officeRepository;
private final CurrentAccountNumberGenerator accountNumberGenerator;
@Override
@Transactional
public CommandProcessingResult submitApplication(final JsonCommand command) {
context.authenticatedUser();
final Long clientId = command.longValueOfParameterNamed("clientId");
final Long productId = command.longValueOfParameterNamed("productId");
final Long officeId = command.parameterExists("officeId") ? command.longValueOfParameterNamed("officeId") : null;
final Long staffId = command.parameterExists("fieldOfficerId")
&& !isBlankString(command.parsedJson().getAsJsonObject(), "fieldOfficerId")
? command.longValueOfParameterNamed("fieldOfficerId") : null;
final Client client = clientRepository.findOneWithNotFoundDetection(clientId);
final CurrentProduct product = currentProductRepository.findById(productId)
.orElseThrow(() -> new SavingsProductNotFoundException(productId));
final Office office = officeId != null
? officeRepository.findOneWithNotFoundDetection(officeId)
: client.getOffice();
Staff staff = null;
// staff loaded lazily if needed — keep null for now if not in scope
LocalDate submittedOnDate = command.localDateValueOfParameterNamed("submittedOnDate");
if (submittedOnDate == null) submittedOnDate = LocalDate.now();
// accountType may arrive as integer or string — store null if absent/unparseable
Integer accountTypeEnum = null;
if (command.parameterExists("accountType")) {
try {
accountTypeEnum = command.integerValueOfParameterNamed("accountType");
} catch (Exception ignored) {
// string value like "Standard Account" — ignore, store null
}
}
// Override from request if provided, else use product defaults
boolean msme = command.parameterExists("msme")
? command.booleanPrimitiveValueOfParameterNamed("msme") : product.isMsme();
boolean autoDebitFacility = command.parameterExists("autoDebitFacility")
? command.booleanPrimitiveValueOfParameterNamed("autoDebitFacility") : product.isAutoDebitFacility();
boolean balanceSwapOption = command.parameterExists("balanceSwapOption")
? command.booleanPrimitiveValueOfParameterNamed("balanceSwapOption") : product.isBalanceSwapOption();
final CurrentAccount account = CurrentAccount.createNew(
client, productId, office, staff, "TEMP", submittedOnDate, accountTypeEnum,
product.currency().getCode(), product.currency().getDigitsAfterDecimal(),
product.currency().getInMultiplesOf(),
product.nominalAnnualInterestRate(),
product.getInterestCompoundingPeriodType(), product.getInterestPostingPeriodType(),
product.getInterestCalculationType(), product.getInterestCalculationDaysInYearType(),
product.isAllowOverdraft(), product.getOverdraftLimit(),
msme, autoDebitFacility, balanceSwapOption,
product.getCustomerSegment(), product.isAutoPost(),
product.getInterestCalculateSelector(), product.getAveragePeriod(),
product.getAvgBalanceCalcPeriod());
if (command.parameterExists("establishmentDate")) {
account.setEstablishmentDate(command.localDateValueOfParameterNamed("establishmentDate"));
}
if (command.parameterExists("startOperationDate")) {
account.setStartOperationDate(command.localDateValueOfParameterNamed("startOperationDate"));
}
if (command.parameterExists("isNonIndividualClient")) {
account.setNonIndividualClient(command.booleanPrimitiveValueOfParameterNamed("isNonIndividualClient"));
}
if (command.parameterExists("signatoryRights")) {
account.setSignatoryRights(command.stringValueOfParameterNamed("signatoryRights"));
}
if (command.parameterExists("modeOfOperation")) {
account.setModeOfOperation(command.stringValueOfParameterNamed("modeOfOperation"));
}
if (command.parameterExists("introducerName")) {
account.setIntroducerName(command.stringValueOfParameterNamed("introducerName"));
}
if (command.parameterExists("introducerClientId")) {
account.setIntroducerClientId(command.longValueOfParameterNamed("introducerClientId"));
} else if (command.parameterExists("introducerId")) {
account.setIntroducerClientId(command.longValueOfParameterNamed("introducerId"));
}
if (command.parameterExists("externalId")) {
account.setExternalId(command.stringValueOfParameterNamed("externalId"));
}
if (command.parameterExists("accountStatus")) {
account.setAccountStatus(command.stringValueOfParameterNamed("accountStatus"));
}
if (command.parameterExists("customerSegment")) {
account.setCustomerSegment(command.stringValueOfParameterNamed("customerSegment"));
}
// Save first to get ID
currentAccountRepository.saveAndFlush(account);
// Generate account number: 00 + GL_CODE(6) + OFFICE_ID(2) + ACCOUNT_ID(5)
final String accountNo = accountNumberGenerator.generate(account);
account.updateAccountNo(accountNo);
// Facility config — UI sends "accountFacilityConfig", internal uses "facilityConfig"
if (command.parameterExists("accountFacilityConfig") || command.parameterExists("facilityConfig")) {
final CurrentAccountFacilityConfig facilityConfig = assembleFacilityConfig(account, command);
account.setFacilityConfig(facilityConfig);
}
// Joint holders
if (command.parameterExists("jointHolders")) {
assembleJointHolders(account, command);
}
// Partners (non-individual)
if (command.parameterExists("partners")) {
assemblePartners(account, command);
}
currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder()
.withEntityId(account.getId())
.withClientId(clientId)
.withOfficeId(office.getId())
.build();
}
@Override
@Transactional
public CommandProcessingResult approveApplication(final Long accountId, final JsonCommand command) {
context.authenticatedUser();
final CurrentAccount account = findAccount(accountId);
LocalDate approvedOnDate = command.localDateValueOfParameterNamed("approvedOnDate");
if (approvedOnDate == null) approvedOnDate = LocalDate.now();
account.approve(approvedOnDate);
currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
}
@Override
@Transactional
public CommandProcessingResult undoApproval(final Long accountId, final JsonCommand command) {
context.authenticatedUser();
final CurrentAccount account = findAccount(accountId);
account.setStatusEnum(100);
account.setApprovedOnDate(null);
currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
}
@Override
@Transactional
public CommandProcessingResult activate(final Long accountId, final JsonCommand command) {
context.authenticatedUser();
final CurrentAccount account = findAccount(accountId);
LocalDate activatedOnDate = command.localDateValueOfParameterNamed("activatedOnDate");
if (activatedOnDate == null) activatedOnDate = LocalDate.now();
account.activate(activatedOnDate);
currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
}
@Override
@Transactional
public CommandProcessingResult rejectApplication(final Long accountId, final JsonCommand command) {
context.authenticatedUser();
final CurrentAccount account = findAccount(accountId);
LocalDate rejectedOnDate = command.localDateValueOfParameterNamed("rejectedOnDate");
if (rejectedOnDate == null) rejectedOnDate = LocalDate.now();
account.reject(rejectedOnDate);
currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
}
@Override
@Transactional
public CommandProcessingResult withdrawApplication(final Long accountId, final JsonCommand command) {
context.authenticatedUser();
final CurrentAccount account = findAccount(accountId);
LocalDate withdrawnOnDate = command.localDateValueOfParameterNamed("withdrawnOnDate");
if (withdrawnOnDate == null) withdrawnOnDate = LocalDate.now();
account.withdraw(withdrawnOnDate);
currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
}
@Override
@Transactional
public CommandProcessingResult closeAccount(final Long accountId, final JsonCommand command) {
context.authenticatedUser();
final CurrentAccount account = findAccount(accountId);
LocalDate closedOnDate = command.localDateValueOfParameterNamed("closedOnDate");
if (closedOnDate == null) closedOnDate = LocalDate.now();
account.close(closedOnDate);
currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
}
@Override
@Transactional
public CommandProcessingResult modifyApplication(final Long accountId, final JsonCommand command) {
context.authenticatedUser();
final CurrentAccount account = findAccount(accountId);
if (command.parameterExists("msme")) {
account.setMsme(command.booleanPrimitiveValueOfParameterNamed("msme"));
}
if (command.parameterExists("signatoryRights")) {
account.setSignatoryRights(command.stringValueOfParameterNamed("signatoryRights"));
}
if (command.parameterExists("modeOfOperation")) {
account.setModeOfOperation(command.stringValueOfParameterNamed("modeOfOperation"));
}
currentAccountRepository.saveAndFlush(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
}
@Override
@Transactional
public CommandProcessingResult deleteApplication(final Long accountId) {
context.authenticatedUser();
final CurrentAccount account = findAccount(accountId);
currentAccountRepository.delete(account);
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
}
// ─── helpers ──────────────────────────────────────────────────────────────
private CurrentAccount findAccount(Long accountId) {
return currentAccountRepository.findById(accountId)
.orElseThrow(() -> new SavingsAccountNotFoundException(accountId));
}
private CurrentAccountFacilityConfig assembleFacilityConfig(CurrentAccount account, JsonCommand command) {
final CurrentAccountFacilityConfig cfg = CurrentAccountFacilityConfig.createFor(account);
final JsonObject root = command.parsedJson().getAsJsonObject();
JsonObject fcJson = root.getAsJsonObject("accountFacilityConfig");
if (fcJson == null) fcJson = root.getAsJsonObject("facilityConfig");
if (fcJson == null) return cfg;
cfg.setAtmFacility(getBool(fcJson, "atmFacility"));
cfg.setAtmWithdrawalFacility(getBool(fcJson, "atmWithdrawalFacility"));
cfg.setUpiFacility(getBool(fcJson, "upiFacility"));
cfg.setPosTransactionsFacility(getBool(fcJson, "posTransactionsFacility"));
cfg.setChequeBook(getBool(fcJson, "chequeBook"));
cfg.setImpsFacility(getBool(fcJson, "impsFacility"));
cfg.setSmsAlerts(getBool(fcJson, "smsAlerts"));
cfg.setBbpsFacility(getBool(fcJson, "bbpsFacility"));
cfg.setInsuranceCover(getBool(fcJson, "insuranceCover"));
cfg.setBepgFacility(getBool(fcJson, "bepgFacility"));
cfg.setNeftFacility(getBool(fcJson, "neftFacility"));
cfg.setRtgsFacility(getBool(fcJson, "rtgsFacility"));
cfg.setInterBankTotalCapFacility(getBool(fcJson, "interBankTotalCapFacility"));
cfg.setWithdrawalFacility(getBool(fcJson, "withdrawalFacility"));
cfg.setDepositFacilityKyc(getBool(fcJson, "depositFacilityKyc"));
cfg.setVirtualDebitCard(getBool(fcJson, "virtualDebitCard"));
cfg.setInternetBanking(getBool(fcJson, "internetBanking"));
cfg.setMobileBanking(getBool(fcJson, "mobileBanking"));
return cfg;
}
private void assembleJointHolders(CurrentAccount account, JsonCommand command) {
final JsonArray arr = command.arrayOfParameterNamed("jointHolders");
if (arr == null) return;
for (JsonElement el : arr) {
final JsonObject jo = el.getAsJsonObject();
final CurrentAccountJointHolder holder = new CurrentAccountJointHolder();
holder.setCurrentAccount(account);
if (jo.has("clientId") && !jo.get("clientId").isJsonNull()) {
holder.setClientId(jo.get("clientId").getAsLong());
}
if (jo.has("cifNumber") && !jo.get("cifNumber").isJsonNull()) {
holder.setCifNumber(jo.get("cifNumber").getAsString());
}
if (jo.has("displayName") && !jo.get("displayName").isJsonNull()) {
holder.setDisplayName(jo.get("displayName").getAsString());
}
if (jo.has("signingAuthority") && !jo.get("signingAuthority").isJsonNull()) {
holder.setSigningAuthority(jo.get("signingAuthority").getAsString());
}
if (jo.has("role") && !jo.get("role").isJsonNull()) {
holder.setRole(jo.get("role").getAsString());
}
account.getJointHolders().add(holder);
}
}
private void assemblePartners(CurrentAccount account, JsonCommand command) {
final JsonArray arr = command.arrayOfParameterNamed("partners");
if (arr == null) return;
for (JsonElement el : arr) {
final JsonObject jo = el.getAsJsonObject();
final CurrentAccountPartner partner = new CurrentAccountPartner();
partner.setCurrentAccount(account);
if (jo.has("clientId") && !jo.get("clientId").isJsonNull()) {
partner.setClientId(jo.get("clientId").getAsLong());
}
if (jo.has("clientCif") && !jo.get("clientCif").isJsonNull()) {
partner.setClientCif(jo.get("clientCif").getAsString());
}
if (jo.has("clientName") && !jo.get("clientName").isJsonNull()) {
partner.setClientName(jo.get("clientName").getAsString());
}
account.getPartners().add(partner);
}
}
private boolean getBool(JsonObject obj, String key) {
return obj.has(key) && !obj.get(key).isJsonNull() && obj.get(key).getAsBoolean();
}
private boolean isBlankString(JsonObject obj, String key) {
if (!obj.has(key) || obj.get(key).isJsonNull()) return true;
return obj.get(key).getAsString().isBlank();
}
}
@@ -32,6 +32,8 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
import org.apache.fineract.infrastructure.core.data.PaginationParametersDataValidator;
@@ -70,6 +72,7 @@ import org.apache.fineract.portfolio.interestratechart.service.InterestRateChart
import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadService;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
@@ -132,6 +135,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
private final CalendarReadPlatformService calendarReadPlatformService;
private final PaymentTypeReadService paymentTypeReadPlatformService;
private final AccountNumberGenerator accountNumberGenerator;
private final CodeValueReadPlatformService codeValueReadPlatformService;
@Override
public Collection<DepositAccountData> retrieveAll(final DepositAccountType depositAccountType,
@@ -412,6 +416,18 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
// Re-apply CBS product fields lost during factory method object creation
((RecurringDepositAccountData) template).copyCbsFieldsFrom(rdTemplateWithCbs);
// Populate account type / customer segment options from code values
try {
final Collection<CodeValueData> accountTypeOpts = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(SavingsApiConstants.RD_PRODUCT_ACCOUNT_TYPE);
final Collection<CodeValueData> customerSegmentOpts = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(SavingsApiConstants.RD_PRODUCT_CUSTOMER_SEGMENT);
((RecurringDepositAccountData) template).setAccountTypeOptions(accountTypeOpts);
((RecurringDepositAccountData) template).setCustomerSegmentOptions(customerSegmentOpts);
} catch (final Exception ex) {
log.warn("Failed to load RD account type/customer segment options: {}", ex.getMessage());
}
// Generate preview account number
try {
final String preview = accountNumberGenerator.previewAccountNumber(productId, officeId);
@@ -215,7 +215,22 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
try {
final String sql = "select " + this.savingAccountMapper.schema() + " where sa.id = ?";
return this.jdbcTemplate.queryForObject(sql, this.savingAccountMapper, new Object[] { accountId }); // NOSONAR
final SavingsAccountData accountData = this.jdbcTemplate.queryForObject(sql, this.savingAccountMapper,
new Object[] { accountId }); // NOSONAR
// Load joint holders
if (accountData != null) {
final String jointSql = "SELECT jh.id as id, jh.client_id as clientId, jh.cif_number as cifNumber, "
+ "jh.display_name as displayName, jh.share_percentage as sharePercentage, "
+ "jh.signing_authority as signingAuthority, jh.role as role, jh.is_primary as isPrimary "
+ "FROM m_savings_account_joint_holders jh WHERE jh.savings_account_id = ?";
final List<java.util.Map<String, Object>> jointHolders = this.jdbcTemplate.queryForList(jointSql, accountId);
if (!jointHolders.isEmpty()) {
accountData.setJointHolders(jointHolders);
}
}
return accountData;
} catch (final EmptyResultDataAccessException e) {
throw new SavingsAccountNotFoundException(accountId, e);
}
@@ -763,7 +778,43 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("sp.is_dormancy_tracking_active as isDormancyTrackingActive, ");
sqlBuilder.append("sp.days_to_inactive as daysToInactive, ");
sqlBuilder.append("sp.days_to_dormancy as daysToDormancy, ");
sqlBuilder.append("sp.days_to_escheat as daysToEscheat ");
sqlBuilder.append("sp.days_to_escheat as daysToEscheat, ");
// CBS extended fields
sqlBuilder.append("sa.cbs_account_type as cbsAccountType, ");
sqlBuilder.append("sa.customer_segment as customerSegment, ");
sqlBuilder.append("sa.account_status as accountStatus, ");
sqlBuilder.append("sa.mode_of_operation as modeOfOperation, ");
sqlBuilder.append("sa.special_category as specialCategory, ");
sqlBuilder.append("sa.weaker_section as weakerSection, ");
sqlBuilder.append("sa.guardian_name as guardianName, ");
sqlBuilder.append("sa.guardian_cif_number as guardianCifNumber, ");
sqlBuilder.append("sa.guardian_relationship as guardianRelationship, ");
sqlBuilder.append("sa.minor_date_of_birth as minorDateOfBirth, ");
sqlBuilder.append("sa.guardian_client_id as guardianClientId, ");
sqlBuilder.append("sa.introducer_client_id as introducerClientId, ");
sqlBuilder.append("sa.introducer_name as introducerName, ");
sqlBuilder.append("sa.introducer_cif_number as introducerCifNumber, ");
sqlBuilder.append("sa.nominee_data as nomineeData, ");
sqlBuilder.append("sa.linked_account_id as linkedAccountId, ");
sqlBuilder.append("sa.linked_account_type as linkedAccountType, ");
sqlBuilder.append("sa.linked_account_no as linkedAccountNo, ");
// Facility config fields
sqlBuilder.append("safc.atm_facility as atmFacility, ");
sqlBuilder.append("safc.atm_withdrawal_facility as atmWithdrawalFacility, ");
sqlBuilder.append("safc.upi_facility as upiFacility, ");
sqlBuilder.append("safc.pos_transactions_facility as posTransactionsFacility, ");
sqlBuilder.append("safc.cheque_book as chequeBook, ");
sqlBuilder.append("safc.imps_facility as impsFacility, ");
sqlBuilder.append("safc.sms_alerts as smsAlerts, ");
sqlBuilder.append("safc.bbps_facility as bbpsFacility, ");
sqlBuilder.append("safc.insurance_cover as insuranceCover, ");
sqlBuilder.append("safc.bepg_facility as bepgFacility, ");
sqlBuilder.append("safc.neft_facility as neftFacility, ");
sqlBuilder.append("safc.rtgs_facility as rtgsFacility, ");
sqlBuilder.append("safc.inter_bank_total_cap_facility as interBankTotalCapFacility, ");
sqlBuilder.append("safc.withdrawal_facility as withdrawalFacility, ");
sqlBuilder.append("safc.deposit_facility_kyc as depositFacilityKyc, ");
sqlBuilder.append("safc.virtual_debit_card as virtualDebitCard ");
sqlBuilder.append("from m_savings_account sa ");
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
@@ -777,6 +828,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("left join m_appuser avbu on avbu.id = sa.activatedon_userid ");
sqlBuilder.append("left join m_appuser cbu on cbu.id = sa.closedon_userid ");
sqlBuilder.append("left join m_tax_group tg on tg.id = sa.tax_group_id ");
sqlBuilder.append("left join m_savings_account_facility_config safc on safc.savings_account_id = sa.id ");
this.schemaSql = sqlBuilder.toString();
}
@@ -970,14 +1022,70 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
}
return SavingsAccountData.instance(id, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, subStatus, reasonForBlock, timeline, currency,
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount);
final SavingsAccountData accountData = SavingsAccountData.instance(id, accountNo, depositType, externalId, groupId, groupName,
clientId, clientName, productId, productName, fieldOfficerId, fieldOfficerName, status, subStatus, reasonForBlock,
timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance,
enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData,
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount);
// CBS extended fields
accountData.setCbsAccountType(rs.getString("cbsAccountType"));
accountData.setCustomerSegment(rs.getString("customerSegment"));
accountData.setAccountStatus(rs.getString("accountStatus"));
accountData.setModeOfOperation(rs.getString("modeOfOperation"));
accountData.setSpecialCategory(rs.getString("specialCategory"));
accountData.setWeakerSection(rs.getString("weakerSection"));
accountData.setGuardianName(rs.getString("guardianName"));
accountData.setGuardianCifNumber(rs.getString("guardianCifNumber"));
accountData.setGuardianRelationship(rs.getString("guardianRelationship"));
accountData.setMinorDateOfBirth(JdbcSupport.getLocalDate(rs, "minorDateOfBirth"));
accountData.setGuardianClientId(JdbcSupport.getLong(rs, "guardianClientId"));
accountData.setIntroducerClientId(JdbcSupport.getLong(rs, "introducerClientId"));
accountData.setIntroducerName(rs.getString("introducerName"));
accountData.setIntroducerCifNumber(rs.getString("introducerCifNumber"));
accountData.setLinkedAccountId(JdbcSupport.getLong(rs, "linkedAccountId"));
accountData.setLinkedAccountType(rs.getString("linkedAccountType"));
accountData.setLinkedAccountNo(rs.getString("linkedAccountNo"));
// Nominee as parsed map
final String nomineeDataStr = rs.getString("nomineeData");
if (nomineeDataStr != null && !nomineeDataStr.isEmpty()) {
try {
com.google.gson.Gson gson = new com.google.gson.Gson();
@SuppressWarnings("unchecked")
java.util.Map<String, Object> nomineeMap = gson.fromJson(nomineeDataStr,
new com.google.gson.reflect.TypeToken<java.util.Map<String, Object>>() {}.getType());
accountData.setNomineeData(nomineeMap);
} catch (Exception ignored) {}
}
// Facility config
final boolean hasFacilityConfig = rs.getObject("atmFacility") != null;
if (hasFacilityConfig) {
final java.util.Map<String, Object> facilityConfigMap = new java.util.LinkedHashMap<>();
facilityConfigMap.put("atmFacility", rs.getBoolean("atmFacility"));
facilityConfigMap.put("atmWithdrawalFacility", rs.getBoolean("atmWithdrawalFacility"));
facilityConfigMap.put("upiFacility", rs.getBoolean("upiFacility"));
facilityConfigMap.put("posTransactionsFacility", rs.getBoolean("posTransactionsFacility"));
facilityConfigMap.put("chequeBook", rs.getBoolean("chequeBook"));
facilityConfigMap.put("impsFacility", rs.getBoolean("impsFacility"));
facilityConfigMap.put("smsAlerts", rs.getBoolean("smsAlerts"));
facilityConfigMap.put("bbpsFacility", rs.getBoolean("bbpsFacility"));
facilityConfigMap.put("insuranceCover", rs.getBoolean("insuranceCover"));
facilityConfigMap.put("bepgFacility", rs.getBoolean("bepgFacility"));
facilityConfigMap.put("neftFacility", rs.getBoolean("neftFacility"));
facilityConfigMap.put("rtgsFacility", rs.getBoolean("rtgsFacility"));
facilityConfigMap.put("interBankTotalCapFacility", rs.getBoolean("interBankTotalCapFacility"));
facilityConfigMap.put("withdrawalFacility", rs.getBoolean("withdrawalFacility"));
facilityConfigMap.put("depositFacilityKyc", rs.getBoolean("depositFacilityKyc"));
facilityConfigMap.put("virtualDebitCard", rs.getBoolean("virtualDebitCard"));
accountData.setAccountFacilityConfig(facilityConfigMap);
}
return accountData;
}
}
@@ -27,6 +27,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Locale;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
@@ -194,6 +195,12 @@ public class SavingsAccountTemplateReadPlatformServiceImpl implements SavingsAcc
final SavingsProductData fullProduct = this.savingsProductReadPlatformService.retrieveOne(productId);
template.setAccountType(fullProduct.getAccountType());
template.setCustomerSegment(fullProduct.getCustomerSegment());
final Collection<CodeValueData> accountTypeOpts = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(SavingsApiConstants.SAVINGS_PRODUCT_ACCOUNT_TYPE);
final Collection<CodeValueData> customerSegmentOpts = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(SavingsApiConstants.SAVINGS_PRODUCT_CUSTOMER_SEGMENT);
template.setAccountTypeOptions(accountTypeOpts);
template.setCustomerSegmentOptions(customerSegmentOpts);
template.setProductCode(fullProduct.getProductCode());
template.setEffectiveDate(fullProduct.getEffectiveDate());
template.setEffectiveTo(fullProduct.getEffectiveTo());
@@ -228,12 +228,12 @@ public class SavingsConfiguration {
StaffReadPlatformService staffReadPlatformService, DepositsDropdownReadPlatformService depositsDropdownReadPlatformService,
SavingsAccountReadPlatformService savingsAccountReadPlatformService, DropdownReadPlatformService dropdownReadPlatformService,
CalendarReadPlatformService calendarReadPlatformService, PaymentTypeReadService paymentTypeReadPlatformService,
AccountNumberGenerator accountNumberGenerator) {
AccountNumberGenerator accountNumberGenerator, CodeValueReadPlatformService codeValueReadPlatformService) {
return new DepositAccountReadPlatformServiceImpl(context, jdbcTemplate, chartReadPlatformService, productChartReadPlatformService,
paginationParametersDataValidator, sqlGenerator, paginationHelper, clientReadPlatformService, groupReadPlatformService,
depositProductReadPlatformService, savingsDropdownReadPlatformService, chargeReadPlatformService, staffReadPlatformService,
depositsDropdownReadPlatformService, savingsAccountReadPlatformService, dropdownReadPlatformService,
calendarReadPlatformService, paymentTypeReadPlatformService, accountNumberGenerator);
calendarReadPlatformService, paymentTypeReadPlatformService, accountNumberGenerator, codeValueReadPlatformService);
}
@Bean
@@ -385,10 +385,11 @@ public class SavingsConfiguration {
GroupReadPlatformService groupReadPlatformService, CurrentProductReadPlatformService currentProductReadPlatformService,
StaffReadPlatformService staffReadPlatformService, SavingsDropdownReadPlatformService dropdownReadPlatformService,
ChargeReadPlatformService chargeReadPlatformService, EntityDatatableChecksReadService entityDatatableChecksReadService,
ColumnValidator columnValidator, AccountNumberGenerator accountNumberGenerator) {
ColumnValidator columnValidator, AccountNumberGenerator accountNumberGenerator,
CodeValueReadPlatformService codeValueReadPlatformService) {
return new CurrentAccountTemplateReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService,
currentProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService,
entityDatatableChecksReadService, columnValidator, accountNumberGenerator);
entityDatatableChecksReadService, columnValidator, accountNumberGenerator, codeValueReadPlatformService);
}
@Bean
@@ -283,4 +283,5 @@
<include file="parts/0254_fix_ckyc_batch_audit_columns.xml" relativeToChangelogFile="true"/>
<include file="parts/0255_add_ckyc_batch_id_to_client_kyc.xml" relativeToChangelogFile="true"/>
<include file="parts/0273_add_gl_account_id_to_current_product.xml" relativeToChangelogFile="true"/>
<include file="parts/0276_create_current_account_tables.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
@@ -5,6 +5,7 @@
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet id="0272_insert_rd_base_rate_version" author="fineract">
<validCheckSum>9:4cb4d062d4ee9464fe5a4e32023580c2</validCheckSum>
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_rd_base_interest_rate_effective</sqlCheck>
</preConditions>
@@ -0,0 +1,393 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.3.xsd">
<!-- =========================================================
m_current_account — main current account table
(fully separate from m_savings_account)
========================================================= -->
<changeSet id="0276_create_current_account_tables" author="system">
<createTable tableName="m_current_account">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="account_no" type="varchar(20)">
<constraints nullable="false" unique="true" uniqueConstraintName="ca_unq_account_no"/>
</column>
<column name="external_id" type="varchar(100)">
<constraints nullable="true" unique="true" uniqueConstraintName="ca_unq_external_id"/>
</column>
<!-- Ownership -->
<column name="client_id" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_client" referencedTableName="m_client" referencedColumnNames="id"/>
</column>
<column name="group_id" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_group" referencedTableName="m_group" referencedColumnNames="id"/>
</column>
<column name="product_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_ca_product" referencedTableName="m_current_product" referencedColumnNames="id"/>
</column>
<column name="office_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_ca_office" referencedTableName="m_office" referencedColumnNames="id"/>
</column>
<column name="staff_id" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_staff" referencedTableName="m_staff" referencedColumnNames="id"/>
</column>
<!-- Status -->
<column name="status_enum" type="smallint" defaultValue="100">
<constraints nullable="false"/>
</column>
<!-- 100=Submitted, 200=Approved, 300=Active, 400=Withdrawn, 500=Rejected, 600=Closed -->
<column name="sub_status_enum" type="smallint" defaultValue="0">
<constraints nullable="false"/>
</column>
<!-- 0=None, 100=Inactive, 200=Dormant, 300=Escheat, 400=Block, 500=BlockDebit, 600=BlockCredit -->
<column name="account_type_enum" type="smallint" defaultValue="1">
<constraints nullable="false"/>
</column>
<!-- 1=Individual, 2=Group, 3=JLG -->
<!-- Lifecycle dates -->
<column name="submittedon_date" type="date"><constraints nullable="true"/></column>
<column name="submittedon_userid" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_submit_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
</column>
<column name="approvedon_date" type="date"><constraints nullable="true"/></column>
<column name="approvedon_userid" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_approve_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
</column>
<column name="activatedon_date" type="date"><constraints nullable="true"/></column>
<column name="activatedon_userid" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_activate_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
</column>
<column name="rejectedon_date" type="date"><constraints nullable="true"/></column>
<column name="rejectedon_userid" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_reject_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
</column>
<column name="withdrawnon_date" type="date"><constraints nullable="true"/></column>
<column name="withdrawnon_userid" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_withdraw_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
</column>
<column name="closedon_date" type="date"><constraints nullable="true"/></column>
<column name="closedon_userid" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_close_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
</column>
<!-- Currency (copied from product at account creation) -->
<column name="currency_code" type="varchar(3)"><constraints nullable="false"/></column>
<column name="currency_digits" type="smallint"><constraints nullable="false"/></column>
<column name="currency_multiplesof" type="smallint"><constraints nullable="true"/></column>
<!-- Interest settings (copied from product) -->
<column name="nominal_annual_interest_rate" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="interest_compounding_period_enum" type="smallint"><constraints nullable="false"/></column>
<column name="interest_posting_period_enum" type="smallint"><constraints nullable="false"/></column>
<column name="interest_calculation_type_enum" type="smallint"><constraints nullable="false"/></column>
<column name="interest_calculation_days_in_year_type_enum" type="smallint"><constraints nullable="false"/></column>
<column name="min_balance_for_interest_calculation" type="decimal(19,6)"><constraints nullable="true"/></column>
<column name="start_interest_calculation_date" type="date"><constraints nullable="true"/></column>
<column name="accrued_till_date" type="date"><constraints nullable="true"/></column>
<column name="last_interest_calculation_date" type="date"><constraints nullable="true"/></column>
<column name="last_closed_business_date" type="date"><constraints nullable="true"/></column>
<!-- Tax -->
<column name="withhold_tax" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="tax_group_id" type="bigint">
<constraints nullable="true" foreignKeyName="fk_ca_tax_group" referencedTableName="m_tax_group" referencedColumnNames="id"/>
</column>
<!-- Balance rules (copied from product) -->
<column name="min_required_opening_balance" type="decimal(19,6)"><constraints nullable="true"/></column>
<column name="lockin_period_frequency" type="smallint"><constraints nullable="true"/></column>
<column name="lockin_period_frequency_enum" type="smallint"><constraints nullable="true"/></column>
<column name="lockedin_until_date_derived" type="date"><constraints nullable="true"/></column>
<column name="enforce_min_required_balance" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="min_required_balance" type="decimal(19,6)"><constraints nullable="true"/></column>
<!-- Overdraft (copied from product) -->
<column name="allow_overdraft" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="overdraft_limit" type="decimal(19,6)"><constraints nullable="true"/></column>
<column name="nominal_annual_interest_rate_overdraft" type="decimal(19,6)"><constraints nullable="true"/></column>
<column name="min_overdraft_for_interest_calculation" type="decimal(19,6)"><constraints nullable="true"/></column>
<!-- Lien -->
<column name="is_lien_allowed" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="max_allowed_lien_limit" type="decimal(19,6)"><constraints nullable="true"/></column>
<column name="on_hold_funds_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<!-- Running balance summary -->
<column name="account_balance_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="total_deposits_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="total_withdrawals_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="total_interest_earned_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="total_interest_posted_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="total_overdraft_interest_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="total_withhold_tax_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="total_savings_amount_on_hold" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="withdrawal_fee_for_transfer" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<!-- Block reason -->
<column name="reason_for_block" type="varchar(500)"><constraints nullable="true"/></column>
<!-- Current-account-specific fields -->
<column name="msme" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="auto_debit_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="balance_swap_option" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="establishment_date" type="date"><constraints nullable="true"/></column>
<column name="start_operation_date" type="date"><constraints nullable="true"/></column>
<column name="is_non_individual_client" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="signatory_rights" type="varchar(100)"><constraints nullable="true"/></column>
<!-- CBS account type / segment -->
<column name="cbs_account_type" type="varchar(100)"><constraints nullable="true"/></column>
<column name="customer_segment" type="varchar(100)"><constraints nullable="true"/></column>
<column name="account_status" type="varchar(50)"><constraints nullable="true"/></column>
<column name="mode_of_operation" type="varchar(50)"><constraints nullable="true"/></column>
<column name="special_category" type="varchar(100)"><constraints nullable="true"/></column>
<!-- Introducer -->
<column name="introducer_client_id" type="bigint"><constraints nullable="true"/></column>
<column name="introducer_name" type="varchar(200)"><constraints nullable="true"/></column>
<column name="introducer_cif_number" type="varchar(50)"><constraints nullable="true"/></column>
<!-- Nominee (JSON) -->
<column name="nominee_data" type="text"><constraints nullable="true"/></column>
<!-- Interest calculation settings from product -->
<column name="interest_calculate_selector" type="smallint"><constraints nullable="true"/></column>
<column name="average_period" type="varchar(20)"><constraints nullable="true"/></column>
<column name="auto_post" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="frequency_fix_day" type="smallint"><constraints nullable="true"/></column>
<column name="enable_lockin_period" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="balance_frequency" type="varchar(50)"><constraints nullable="true"/></column>
<column name="avg_balance_calc_period" type="varchar(10)"><constraints nullable="true"/></column>
<!-- Audit -->
<column name="created_by" type="bigint"><constraints nullable="true"/></column>
<column name="created_date" type="datetime"><constraints nullable="true"/></column>
<column name="last_modified_by" type="bigint"><constraints nullable="true"/></column>
<column name="last_modified_date" type="datetime"><constraints nullable="true"/></column>
</createTable>
<!-- =========================================================
m_current_account_transaction
========================================================= -->
<createTable tableName="m_current_account_transaction">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_account_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_cat_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
</column>
<column name="office_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_cat_office" referencedTableName="m_office" referencedColumnNames="id"/>
</column>
<column name="payment_detail_id" type="bigint">
<constraints nullable="true" foreignKeyName="fk_cat_payment" referencedTableName="m_payment_detail" referencedColumnNames="id"/>
</column>
<!-- Type: 1=Deposit, 2=Withdrawal, 3=Interest, 4=Charge, 5=ChargeWaiver, 6=Transfer, 7=Reversal -->
<column name="transaction_type_enum" type="smallint"><constraints nullable="false"/></column>
<column name="transaction_date" type="date"><constraints nullable="false"/></column>
<column name="amount" type="decimal(19,6)"><constraints nullable="false"/></column>
<column name="ref_no" type="varchar(100)"><constraints nullable="true"/></column>
<column name="remarks" type="varchar(500)"><constraints nullable="true"/></column>
<!-- State -->
<column name="is_reversed" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="is_reversal" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="original_transaction_id" type="bigint"><constraints nullable="true"/></column>
<column name="is_manual" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<!-- Balance -->
<column name="running_balance_derived" type="decimal(19,6)"><constraints nullable="true"/></column>
<column name="cumulative_balance_derived" type="decimal(19,6)"><constraints nullable="true"/></column>
<column name="balance_end_date_derived" type="date"><constraints nullable="true"/></column>
<column name="balance_number_of_days_derived" type="integer"><constraints nullable="true"/></column>
<column name="overdraft_amount_derived" type="decimal(19,6)"><constraints nullable="true"/></column>
<!-- Lien / Hold -->
<column name="is_lien_transaction" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="release_id_of_hold_amount_transaction" type="bigint"><constraints nullable="true"/></column>
<column name="reason_for_block" type="varchar(200)"><constraints nullable="true"/></column>
<column name="submitted_on_date" type="date"><constraints nullable="true"/></column>
<column name="created_date" type="datetime"><constraints nullable="true"/></column>
<column name="created_by" type="bigint"><constraints nullable="true"/></column>
</createTable>
<!-- =========================================================
m_current_account_charge
========================================================= -->
<createTable tableName="m_current_account_charge">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_account_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_cac_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
</column>
<column name="charge_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_cac_charge" referencedTableName="m_charge" referencedColumnNames="id"/>
</column>
<column name="charge_time_enum" type="smallint"><constraints nullable="false"/></column>
<column name="charge_due_date" type="date"><constraints nullable="true"/></column>
<column name="fee_on_month" type="smallint"><constraints nullable="true"/></column>
<column name="fee_on_day" type="smallint"><constraints nullable="true"/></column>
<column name="fee_interval" type="smallint"><constraints nullable="true"/></column>
<column name="charge_calculation_enum" type="smallint"><constraints nullable="false"/></column>
<column name="calculation_percentage" type="decimal(19,6)"><constraints nullable="true"/></column>
<column name="calculation_on_amount" type="decimal(19,6)"><constraints nullable="true"/></column>
<column name="amount" type="decimal(19,6)"><constraints nullable="false"/></column>
<column name="amount_paid_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="amount_waived_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="amount_writtenoff_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
<column name="amount_outstanding_derived" type="decimal(19,6)"><constraints nullable="false"/></column>
<column name="is_paid_derived" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="waived" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="is_penalty" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="is_active" type="boolean" defaultValueBoolean="true"><constraints nullable="false"/></column>
<column name="inactivated_on_date" type="date"><constraints nullable="true"/></column>
<column name="free_withdrawal_count" type="integer"><constraints nullable="true"/></column>
<column name="charge_reset_date" type="date"><constraints nullable="true"/></column>
</createTable>
<!-- =========================================================
m_current_account_facility_config
ATM / NEFT / RTGS / Cheque Book etc.
========================================================= -->
<createTable tableName="m_current_account_facility_config">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_account_id" type="bigint">
<constraints nullable="false" unique="true" uniqueConstraintName="ca_fac_unq_account"
foreignKeyName="fk_cafac_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
</column>
<column name="atm_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="atm_withdrawal_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="upi_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="pos_transactions_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="cheque_book" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="imps_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="sms_alerts" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="bbps_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="insurance_cover" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="bepg_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="neft_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="rtgs_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="inter_bank_total_cap_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="withdrawal_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="deposit_facility_kyc" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="virtual_debit_card" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="internet_banking" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
<column name="mobile_banking" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
</createTable>
<!-- =========================================================
m_current_account_joint_holder
Joint account holders
========================================================= -->
<createTable tableName="m_current_account_joint_holder">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_account_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_cajh_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
</column>
<column name="client_id" type="bigint">
<constraints nullable="true" foreignKeyName="fk_cajh_client" referencedTableName="m_client" referencedColumnNames="id"/>
</column>
<column name="cif_number" type="varchar(50)"><constraints nullable="true"/></column>
<column name="display_name" type="varchar(200)"><constraints nullable="true"/></column>
<column name="share_percentage" type="decimal(5,2)"><constraints nullable="true"/></column>
<column name="signing_authority" type="varchar(100)"><constraints nullable="true"/></column>
<column name="role" type="varchar(100)"><constraints nullable="true"/></column>
<column name="is_primary" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
</createTable>
<!-- =========================================================
m_current_account_partner
Partners for non-individual (business) current accounts
========================================================= -->
<createTable tableName="m_current_account_partner">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_account_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_cap_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
</column>
<column name="client_id" type="bigint">
<constraints nullable="true" foreignKeyName="fk_cap_client" referencedTableName="m_client" referencedColumnNames="id"/>
</column>
<column name="client_cif" type="varchar(50)"><constraints nullable="true"/></column>
<column name="client_name" type="varchar(200)"><constraints nullable="true"/></column>
<column name="share_percentage" type="decimal(5,2)"><constraints nullable="true"/></column>
<column name="is_mandatory" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
</createTable>
<!-- =========================================================
m_current_account_officer_history
Officer assignment history
========================================================= -->
<createTable tableName="m_current_account_officer_history">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_account_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_caoh_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
</column>
<column name="staff_id" type="bigint">
<constraints nullable="true" foreignKeyName="fk_caoh_staff" referencedTableName="m_staff" referencedColumnNames="id"/>
</column>
<column name="start_date" type="date"><constraints nullable="false"/></column>
<column name="end_date" type="date"><constraints nullable="true"/></column>
<column name="created_by_id" type="bigint"><constraints nullable="true"/></column>
</createTable>
<!-- =========================================================
m_current_account_on_hold_transaction
On-hold / lien funds
========================================================= -->
<createTable tableName="m_current_account_on_hold_transaction">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="current_account_id" type="bigint">
<constraints nullable="false" foreignKeyName="fk_caoht_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
</column>
<column name="amount" type="decimal(19,6)"><constraints nullable="false"/></column>
<!-- 1=Hold, 2=Release -->
<column name="transaction_type_enum" type="smallint"><constraints nullable="false"/></column>
<column name="transaction_date" type="date"><constraints nullable="false"/></column>
<column name="is_reversed" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
</createTable>
<!-- Indexes for fast lookup -->
<createIndex indexName="idx_ca_client_id" tableName="m_current_account">
<column name="client_id"/>
</createIndex>
<createIndex indexName="idx_ca_product_id" tableName="m_current_account">
<column name="product_id"/>
</createIndex>
<createIndex indexName="idx_ca_status" tableName="m_current_account">
<column name="status_enum"/>
</createIndex>
<createIndex indexName="idx_cat_account_id" tableName="m_current_account_transaction">
<column name="current_account_id"/>
</createIndex>
<createIndex indexName="idx_cat_date" tableName="m_current_account_transaction">
<column name="transaction_date"/>
</createIndex>
</changeSet>
</databaseChangeLog>
@@ -151,6 +151,15 @@
<class>org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMapping</class>
<class>org.apache.fineract.portfolio.self.registration.domain.SelfServiceRegistration</class>
<!-- Current Account Entities -->
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountFacilityConfig</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountJointHolder</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountPartner</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountOfficerHistory</class>
<!-- Charge Module Entities -->
<class>org.apache.fineract.portfolio.charge.domain.Charge</class>
@@ -263,6 +272,8 @@
<class>org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKyc</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfig</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsAccountJointHolder</class>
<!-- Progressive Loan Module Entities -->
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance</class>
@@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationC
import org.apache.fineract.infrastructure.configuration.data.GlobalConfigurationPropertyData;
import org.apache.fineract.infrastructure.configuration.service.ConfigurationReadPlatformService;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientRepository;
@@ -39,6 +40,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct;
import org.junit.jupiter.api.BeforeEach;
@@ -50,6 +52,8 @@ public class AccountNumberGeneratorTest {
private ClientRepository clientRepo;
private LoanRepository loanRepo;
private SavingsAccountRepository savingsRepo;
private GLAccountRepository glAccountRepo;
private SavingsProductRepository savingsProductRepo;
private AccountNumberGenerator generator;
@@ -59,8 +63,10 @@ public class AccountNumberGeneratorTest {
clientRepo = mock(ClientRepository.class);
loanRepo = mock(LoanRepository.class);
savingsRepo = mock(SavingsAccountRepository.class);
glAccountRepo = mock(GLAccountRepository.class);
savingsProductRepo = mock(SavingsProductRepository.class);
generator = new AccountNumberGenerator(configService, clientRepo, loanRepo, savingsRepo);
generator = new AccountNumberGenerator(configService, clientRepo, loanRepo, savingsRepo, glAccountRepo, savingsProductRepo);
GlobalConfigurationPropertyData accountLengthConfig = mock(GlobalConfigurationPropertyData.class);
when(accountLengthConfig.getValue()).thenReturn(Long.valueOf("9"));
@@ -82,13 +82,19 @@ public class SavingsApiSetConstants extends SavingsApiConstants {
"minBalanceForInterestCalculation", "minOverdraftForInterestCalculation",
"periodFrequencyOptions",
// CBS Product fields
"accountType", "customerSegment", "productCode", "glAccountId", "glAccountName", "effectiveTo", "endDate", "introducerRequired", "shareLinkingEnabled",
"accountType", "customerSegment", "accountTypeOptions", "customerSegmentOptions", "productCode", "glAccountId", "glAccountName", "effectiveTo", "endDate", "introducerRequired", "shareLinkingEnabled",
"nominationFacility", "dormancyAlert", "enableLockinPeriod", "balanceFrequency",
"accountClosedZeroBalanceInDayClosing", "accountFacilityConfig", "accountFacilityLimits", "depositLimitKyc",
"interestCalculateSelector", "averagePeriod", "autoPost", "frequencyFixDay", "lastInterestDate",
"balanceSwapOption", "msme", "autoDebitFacility", "interestCalculationTypeOption",
"effectiveDate", "isDormancyTrackingActive", "daysToInactive", "daysToDormancy", "daysToEscheat",
"avgBalanceCalcPeriod", "validForPeriod", "odAccountClosedZeroBalanceInDayClosing"));
"avgBalanceCalcPeriod", "validForPeriod", "odAccountClosedZeroBalanceInDayClosing",
// CBS Account fields
"cbsAccountType", "accountStatus", "modeOfOperation", "specialCategory", "weakerSection",
"guardianName", "guardianCifNumber", "guardianRelationship", "guardianClientId", "minorDateOfBirth",
"introducerClientId", "introducerName", "introducerCifNumber",
"nomineeData", "linkedAccountId", "linkedAccountType", "linkedAccountNo",
"jointHolders"));
protected static final Set<String> SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(idParamName, "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName,
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.savings.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import lombok.Getter;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
@@ -86,6 +87,8 @@ public final class RecurringDepositAccountData extends DepositAccountData {
// CBS product fields (populated from product template)
private String accountType;
private String customerSegment;
private Collection<CodeValueData> accountTypeOptions;
private Collection<CodeValueData> customerSegmentOptions;
private String productCode;
private LocalDate effectiveDate;
private LocalDate effectiveTo;
@@ -122,10 +125,20 @@ public final class RecurringDepositAccountData extends DepositAccountData {
private Boolean deceasedSettlement;
private Boolean allowDebitTxnAfterDueDate;
public void setAccountTypeOptions(Collection<CodeValueData> accountTypeOptions) {
this.accountTypeOptions = accountTypeOptions;
}
public void setCustomerSegmentOptions(Collection<CodeValueData> customerSegmentOptions) {
this.customerSegmentOptions = customerSegmentOptions;
}
public void copyCbsFieldsFrom(final RecurringDepositAccountData src) {
if (src == null) return;
this.accountType = src.accountType;
this.customerSegment = src.customerSegment;
this.accountTypeOptions = src.accountTypeOptions;
this.customerSegmentOptions = src.customerSegmentOptions;
this.productCode = src.productCode;
this.effectiveDate = src.effectiveDate;
this.effectiveTo = src.effectiveTo;
@@ -39,7 +39,15 @@ public class SavingsAccountConstant extends SavingsApiConstants {
withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, chargesParamName, allowOverdraftParamName,
overdraftLimitParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName, lienAllowedParamName,
maxAllowedLienLimitParamName, nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
withHoldTaxParamName, datatables, gsimApplicationId, gsimLastApplication));
withHoldTaxParamName, datatables, gsimApplicationId, gsimLastApplication,
// CBS Account fields
"accountType", "customerSegment", accountStatusParamName, modeOfOperationParamName,
specialCategoryParamName, weakerSectionParamName,
guardianNameParamName, guardianCifNumberParamName, guardianRelationshipParamName,
guardianClientIdParamName, minorDateOfBirthParamName,
introducerIdParamName, introducerNameParamName, introducerCifNumberParamName,
nomineeParamName, linkedAccountIdParamName, linkedAccountTypeParamName, linkedAccountNoParamName,
accountFacilityConfigParamName, jointHoldersParamName));
/**
* These parameters will match the class level parameters of {@link SavingsAccountData}. Where possible, we try to
@@ -21,7 +21,7 @@ package org.apache.fineract.portfolio.savings.domain;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
@Entity
@Entity(name = "SavingsCurrentAccount")
@DiscriminatorValue("400")
public class CurrentAccount extends SavingsAccount {
@@ -3900,4 +3900,126 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
public DepositAccountType depositAccountType() {
return DepositAccountType.fromInt(100);
}
// ─── CBS extended fields ───────────────────────────────────────────────────
@Column(name = "cbs_account_type", length = 100)
private String cbsAccountType;
@Column(name = "customer_segment", length = 100)
private String customerSegment;
@Column(name = "account_status", length = 50)
private String accountStatus;
@Column(name = "mode_of_operation", length = 50)
private String modeOfOperation;
@Column(name = "special_category", length = 100)
private String specialCategory;
@Column(name = "weaker_section", length = 100)
private String weakerSection;
@Column(name = "guardian_name", length = 200)
private String guardianName;
@Column(name = "guardian_cif_number", length = 50)
private String guardianCifNumber;
@Column(name = "guardian_relationship", length = 100)
private String guardianRelationship;
@Column(name = "minor_date_of_birth")
private LocalDate minorDateOfBirth;
@Column(name = "guardian_client_id")
private Long guardianClientId;
@Column(name = "introducer_client_id")
private Long introducerClientId;
@Column(name = "introducer_name", length = 200)
private String introducerName;
@Column(name = "introducer_cif_number", length = 50)
private String introducerCifNumber;
@Column(name = "nominee_data", columnDefinition = "TEXT")
private String nomineeData;
@Column(name = "linked_account_id")
private Long linkedAccountId;
@Column(name = "linked_account_type", length = 20)
private String linkedAccountType;
@Column(name = "linked_account_no", length = 50)
private String linkedAccountNo;
@jakarta.persistence.OneToOne(cascade = jakarta.persistence.CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true, fetch = FetchType.LAZY)
private SavingsAccountFacilityConfig facilityConfig;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true, fetch = FetchType.LAZY)
private List<SavingsAccountJointHolder> jointHolders = new ArrayList<>();
public String getCbsAccountType() { return cbsAccountType; }
public void setCbsAccountType(String v) { this.cbsAccountType = v; }
public String getCustomerSegment() { return customerSegment; }
public void setCustomerSegment(String v) { this.customerSegment = v; }
public String getAccountStatus() { return accountStatus; }
public void setAccountStatus(String v) { this.accountStatus = v; }
public String getModeOfOperation() { return modeOfOperation; }
public void setModeOfOperation(String v) { this.modeOfOperation = v; }
public String getSpecialCategory() { return specialCategory; }
public void setSpecialCategory(String v) { this.specialCategory = v; }
public String getWeakerSection() { return weakerSection; }
public void setWeakerSection(String v) { this.weakerSection = v; }
public String getGuardianName() { return guardianName; }
public void setGuardianName(String v) { this.guardianName = v; }
public String getGuardianCifNumber() { return guardianCifNumber; }
public void setGuardianCifNumber(String v) { this.guardianCifNumber = v; }
public String getGuardianRelationship() { return guardianRelationship; }
public void setGuardianRelationship(String v) { this.guardianRelationship = v; }
public LocalDate getMinorDateOfBirth() { return minorDateOfBirth; }
public void setMinorDateOfBirth(LocalDate v) { this.minorDateOfBirth = v; }
public Long getGuardianClientId() { return guardianClientId; }
public void setGuardianClientId(Long v) { this.guardianClientId = v; }
public Long getIntroducerClientId() { return introducerClientId; }
public void setIntroducerClientId(Long v) { this.introducerClientId = v; }
public String getIntroducerName() { return introducerName; }
public void setIntroducerName(String v) { this.introducerName = v; }
public String getIntroducerCifNumber() { return introducerCifNumber; }
public void setIntroducerCifNumber(String v) { this.introducerCifNumber = v; }
public String getNomineeData() { return nomineeData; }
public void setNomineeData(String v) { this.nomineeData = v; }
public Long getLinkedAccountId() { return linkedAccountId; }
public void setLinkedAccountId(Long v) { this.linkedAccountId = v; }
public String getLinkedAccountType() { return linkedAccountType; }
public void setLinkedAccountType(String v) { this.linkedAccountType = v; }
public String getLinkedAccountNo() { return linkedAccountNo; }
public void setLinkedAccountNo(String v) { this.linkedAccountNo = v; }
public SavingsAccountFacilityConfig getFacilityConfig() { return facilityConfig; }
public void setFacilityConfig(SavingsAccountFacilityConfig v) { this.facilityConfig = v; }
public List<SavingsAccountJointHolder> getJointHolders() { return jointHolders; }
public void setJointHolders(List<SavingsAccountJointHolder> v) { this.jointHolders = v; }
}
@@ -0,0 +1,168 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.savings.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_savings_account_facility_config")
public class SavingsAccountFacilityConfig extends AbstractPersistableCustom<Long> {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "savings_account_id", nullable = false)
private SavingsAccount savingsAccount;
@Column(name = "atm_facility")
private Boolean atmFacility = false;
@Column(name = "atm_withdrawal_facility")
private Boolean atmWithdrawalFacility = false;
@Column(name = "upi_facility")
private Boolean upiFacility = false;
@Column(name = "pos_transactions_facility")
private Boolean posTransactionsFacility = false;
@Column(name = "cheque_book")
private Boolean chequeBook = false;
@Column(name = "imps_facility")
private Boolean impsFacility = false;
@Column(name = "sms_alerts")
private Boolean smsAlerts = false;
@Column(name = "bbps_facility")
private Boolean bbpsFacility = false;
@Column(name = "insurance_cover")
private Boolean insuranceCover = false;
@Column(name = "bepg_facility")
private Boolean bepgFacility = false;
@Column(name = "neft_facility")
private Boolean neftFacility = false;
@Column(name = "rtgs_facility")
private Boolean rtgsFacility = false;
@Column(name = "inter_bank_total_cap_facility")
private Boolean interBankTotalCapFacility = false;
@Column(name = "withdrawal_facility")
private Boolean withdrawalFacility = false;
@Column(name = "deposit_facility_kyc")
private Boolean depositFacilityKyc = false;
@Column(name = "virtual_debit_card")
private Boolean virtualDebitCard = false;
@Column(name = "created_date", nullable = false)
private LocalDateTime createdDate;
protected SavingsAccountFacilityConfig() {
this.createdDate = LocalDateTime.now();
}
public SavingsAccountFacilityConfig(SavingsAccount savingsAccount) {
this();
this.savingsAccount = savingsAccount;
}
public SavingsAccount getSavingsAccount() { return savingsAccount; }
public void setSavingsAccount(SavingsAccount savingsAccount) { this.savingsAccount = savingsAccount; }
public Boolean getAtmFacility() { return atmFacility; }
public void setAtmFacility(Boolean v) { this.atmFacility = v != null ? v : false; }
public Boolean getAtmWithdrawalFacility() { return atmWithdrawalFacility; }
public void setAtmWithdrawalFacility(Boolean v) { this.atmWithdrawalFacility = v != null ? v : false; }
public Boolean getUpiFacility() { return upiFacility; }
public void setUpiFacility(Boolean v) { this.upiFacility = v != null ? v : false; }
public Boolean getPosTransactionsFacility() { return posTransactionsFacility; }
public void setPosTransactionsFacility(Boolean v) { this.posTransactionsFacility = v != null ? v : false; }
public Boolean getChequeBook() { return chequeBook; }
public void setChequeBook(Boolean v) { this.chequeBook = v != null ? v : false; }
public Boolean getImpsFacility() { return impsFacility; }
public void setImpsFacility(Boolean v) { this.impsFacility = v != null ? v : false; }
public Boolean getSmsAlerts() { return smsAlerts; }
public void setSmsAlerts(Boolean v) { this.smsAlerts = v != null ? v : false; }
public Boolean getBbpsFacility() { return bbpsFacility; }
public void setBbpsFacility(Boolean v) { this.bbpsFacility = v != null ? v : false; }
public Boolean getInsuranceCover() { return insuranceCover; }
public void setInsuranceCover(Boolean v) { this.insuranceCover = v != null ? v : false; }
public Boolean getBepgFacility() { return bepgFacility; }
public void setBepgFacility(Boolean v) { this.bepgFacility = v != null ? v : false; }
public Boolean getNeftFacility() { return neftFacility; }
public void setNeftFacility(Boolean v) { this.neftFacility = v != null ? v : false; }
public Boolean getRtgsFacility() { return rtgsFacility; }
public void setRtgsFacility(Boolean v) { this.rtgsFacility = v != null ? v : false; }
public Boolean getInterBankTotalCapFacility() { return interBankTotalCapFacility; }
public void setInterBankTotalCapFacility(Boolean v) { this.interBankTotalCapFacility = v != null ? v : false; }
public Boolean getWithdrawalFacility() { return withdrawalFacility; }
public void setWithdrawalFacility(Boolean v) { this.withdrawalFacility = v != null ? v : false; }
public Boolean getDepositFacilityKyc() { return depositFacilityKyc; }
public void setDepositFacilityKyc(Boolean v) { this.depositFacilityKyc = v != null ? v : false; }
public Boolean getVirtualDebitCard() { return virtualDebitCard; }
public void setVirtualDebitCard(Boolean v) { this.virtualDebitCard = v != null ? v : false; }
public void updateFrom(com.google.gson.JsonObject config) {
if (config == null) return;
if (config.has("atmFacility")) setAtmFacility(config.get("atmFacility").getAsBoolean());
if (config.has("atmWithdrawalFacility")) setAtmWithdrawalFacility(config.get("atmWithdrawalFacility").getAsBoolean());
if (config.has("upiFacility")) setUpiFacility(config.get("upiFacility").getAsBoolean());
if (config.has("posTransactionsFacility")) setPosTransactionsFacility(config.get("posTransactionsFacility").getAsBoolean());
if (config.has("chequeBook")) setChequeBook(config.get("chequeBook").getAsBoolean());
if (config.has("impsFacility")) setImpsFacility(config.get("impsFacility").getAsBoolean());
if (config.has("smsAlerts")) setSmsAlerts(config.get("smsAlerts").getAsBoolean());
if (config.has("bbpsFacility")) setBbpsFacility(config.get("bbpsFacility").getAsBoolean());
if (config.has("insuranceCover")) setInsuranceCover(config.get("insuranceCover").getAsBoolean());
if (config.has("bepgFacility")) setBepgFacility(config.get("bepgFacility").getAsBoolean());
if (config.has("neftFacility")) setNeftFacility(config.get("neftFacility").getAsBoolean());
if (config.has("rtgsFacility")) setRtgsFacility(config.get("rtgsFacility").getAsBoolean());
if (config.has("interBankTotalCapFacility")) setInterBankTotalCapFacility(config.get("interBankTotalCapFacility").getAsBoolean());
if (config.has("withdrawalFacility")) setWithdrawalFacility(config.get("withdrawalFacility").getAsBoolean());
if (config.has("depositFacilityKyc")) setDepositFacilityKyc(config.get("depositFacilityKyc").getAsBoolean());
if (config.has("virtualDebitCard")) setVirtualDebitCard(config.get("virtualDebitCard").getAsBoolean());
}
}
@@ -0,0 +1,30 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.savings.domain;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface SavingsAccountFacilityConfigRepository extends JpaRepository<SavingsAccountFacilityConfig, Long> {
@Query("SELECT fc FROM SavingsAccountFacilityConfig fc WHERE fc.savingsAccount.id = :accountId")
Optional<SavingsAccountFacilityConfig> findBySavingsAccountId(@Param("accountId") Long accountId);
}
@@ -0,0 +1,81 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.savings.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "m_savings_account_joint_holders")
public class SavingsAccountJointHolder extends AbstractPersistableCustom<Long> {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "savings_account_id", nullable = false)
private SavingsAccount savingsAccount;
@Column(name = "client_id")
private Long clientId;
@Column(name = "cif_number", length = 50)
private String cifNumber;
@Column(name = "display_name", length = 200)
private String displayName;
@Column(name = "share_percentage", precision = 5, scale = 2)
private BigDecimal sharePercentage;
@Column(name = "signing_authority", length = 100)
private String signingAuthority;
@Column(name = "role", length = 100)
private String role;
@Column(name = "is_primary")
private boolean primary = false;
protected SavingsAccountJointHolder() {}
public SavingsAccountJointHolder(SavingsAccount account, Long clientId, String cifNumber,
String displayName, BigDecimal sharePercentage, String signingAuthority, String role, boolean primary) {
this.savingsAccount = account;
this.clientId = clientId;
this.cifNumber = cifNumber;
this.displayName = displayName;
this.sharePercentage = sharePercentage;
this.signingAuthority = signingAuthority;
this.role = role;
this.primary = primary;
}
public SavingsAccount getSavingsAccount() { return savingsAccount; }
public Long getClientId() { return clientId; }
public String getCifNumber() { return cifNumber; }
public String getDisplayName() { return displayName; }
public BigDecimal getSharePercentage() { return sharePercentage; }
public String getSigningAuthority() { return signingAuthority; }
public String getRole() { return role; }
public boolean isPrimary() { return primary; }
}
@@ -32,4 +32,5 @@
<include file="parts/2007_add_interest_calculation_type_option.xml" relativeToChangelogFile="true" />
<include file="parts/2008_add_other_gl_mappings_to_savings_product.xml" relativeToChangelogFile="true" />
<include file="parts/2009_add_gl_account_id_to_savings_product.xml" relativeToChangelogFile="true" />
<include file="parts/2010_add_savings_account_cbs_fields.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
@@ -0,0 +1,174 @@
<?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. This file is licensed under the Apache License, Version 2.0.
-->
<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.3.xsd">
<!-- CBS extended fields on m_savings_account -->
<changeSet author="fineract" id="2010-add-savings-account-cbs-fields">
<addColumn tableName="m_savings_account">
<column name="cbs_account_type" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="customer_segment" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="account_status" type="VARCHAR(50)">
<constraints nullable="true"/>
</column>
<column name="mode_of_operation" type="VARCHAR(50)">
<constraints nullable="true"/>
</column>
<column name="special_category" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="weaker_section" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="guardian_name" type="VARCHAR(200)">
<constraints nullable="true"/>
</column>
<column name="guardian_cif_number" type="VARCHAR(50)">
<constraints nullable="true"/>
</column>
<column name="guardian_relationship" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="minor_date_of_birth" type="DATE">
<constraints nullable="true"/>
</column>
<column name="guardian_client_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="introducer_client_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="introducer_name" type="VARCHAR(200)">
<constraints nullable="true"/>
</column>
<column name="introducer_cif_number" type="VARCHAR(50)">
<constraints nullable="true"/>
</column>
<column name="nominee_data" type="TEXT">
<constraints nullable="true"/>
</column>
<column name="linked_account_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="linked_account_type" type="VARCHAR(20)">
<constraints nullable="true"/>
</column>
<column name="linked_account_no" type="VARCHAR(50)">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
<!-- Facility config table for savings accounts -->
<changeSet author="fineract" id="2010-create-savings-account-facility-config">
<createTable tableName="m_savings_account_facility_config">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="savings_account_id" type="BIGINT">
<constraints nullable="false" unique="true"
foreignKeyName="fk_sa_facility_config_account"
references="m_savings_account(id)"/>
</column>
<column name="atm_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="atm_withdrawal_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="upi_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="pos_transactions_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="cheque_book" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="imps_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="sms_alerts" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="bbps_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="insurance_cover" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="bepg_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="neft_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="rtgs_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="inter_bank_total_cap_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="withdrawal_facility" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="deposit_facility_kyc" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="virtual_debit_card" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="created_date" type="DATETIME">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- Joint holders table for savings accounts -->
<changeSet author="fineract" id="2010-create-savings-account-joint-holders">
<createTable tableName="m_savings_account_joint_holders">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="savings_account_id" type="BIGINT">
<constraints nullable="false"
foreignKeyName="fk_sa_joint_holder_account"
references="m_savings_account(id)"/>
</column>
<column name="client_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="cif_number" type="VARCHAR(50)">
<constraints nullable="true"/>
</column>
<column name="display_name" type="VARCHAR(200)">
<constraints nullable="true"/>
</column>
<column name="share_percentage" type="DECIMAL(5,2)">
<constraints nullable="true"/>
</column>
<column name="signing_authority" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="role" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="is_primary" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
</databaseChangeLog>
@@ -112,6 +112,17 @@
<class>org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKyc</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfig</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsAccountJointHolder</class>
<!-- Current Account Entities -->
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountFacilityConfig</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountJointHolder</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountPartner</class>
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountOfficerHistory</class>
<!-- Charge Module Entities -->
<class>org.apache.fineract.portfolio.charge.domain.Charge</class>