existing account saving and current add or remove nominee and digital & account features and also FE & RD nominee add or remove
This commit is contained in:
+3
@@ -932,6 +932,9 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
||||
}
|
||||
|
||||
private BigDecimal getEffectiveOverdraftInterestRateAsFraction(MathContext mc) {
|
||||
if (this.nominalAnnualInterestRateOverdraft == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return this.nominalAnnualInterestRateOverdraft.divide(BigDecimal.valueOf(100L), mc);
|
||||
}
|
||||
|
||||
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* 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.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.useradministration.domain.AppUser;
|
||||
|
||||
/**
|
||||
* Holds a pending edit request for an ACTIVE savings account's Nominee + Digital & Account Features.
|
||||
* Presence of a row here with {@code status = PENDING} (enforced via the unique
|
||||
* {@link #pendingLockKey}) is itself the lock - nothing is applied to the real
|
||||
* {@code m_savings_account_facility_config} / {@code m_client_nominee} rows until a checker approves
|
||||
* (see SavingsAccountEditWritePlatformServiceImpl). Only one nominee change (add/update/delete) can be
|
||||
* proposed per request, so its fields are kept as flat typed columns on this same table.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_savings_account_pending_edit")
|
||||
@Getter
|
||||
@Setter
|
||||
public class SavingsAccountPendingEdit extends AbstractPersistableCustom<Long> {
|
||||
|
||||
public static final String STATUS_PENDING = "PENDING";
|
||||
public static final String STATUS_APPROVED = "APPROVED";
|
||||
public static final String STATUS_REJECTED = "REJECTED";
|
||||
|
||||
public static final String NOMINEE_OP_ADD = "ADD";
|
||||
public static final String NOMINEE_OP_UPDATE = "UPDATE";
|
||||
public static final String NOMINEE_OP_DELETE = "DELETE";
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "savings_account_id", nullable = false)
|
||||
private SavingsAccount savingsAccount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "createdby_id", nullable = false)
|
||||
private AppUser maker;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "lastmodifiedby_id")
|
||||
private AppUser checker;
|
||||
|
||||
@Column(name = "created_date", nullable = false)
|
||||
private LocalDateTime createdOn;
|
||||
|
||||
@Column(name = "lastmodified_date")
|
||||
private LocalDateTime checkedOn;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status;
|
||||
|
||||
@Column(name = "reject_reason", length = 500)
|
||||
private String rejectReason;
|
||||
|
||||
/** "SAVINGS_ACCOUNT_EDIT:<savingsAccountId>" while PENDING, NULL otherwise - the real DB-level lock. */
|
||||
@Column(name = "pending_lock_key", length = 40)
|
||||
private String pendingLockKey;
|
||||
|
||||
/** Links to the audit-only m_portfolio_command_source row created for this edit's submit/approve/reject. */
|
||||
@Column(name = "command_source_id")
|
||||
private Long commandSourceId;
|
||||
|
||||
@Column(name = "hold_transaction_id")
|
||||
private Long holdTransactionId;
|
||||
|
||||
@Column(name = "hold_amount", precision = 19, scale = 6)
|
||||
private BigDecimal holdAmount;
|
||||
|
||||
@Column(name = "facility_config_submitted", nullable = false)
|
||||
private boolean facilityConfigSubmitted = false;
|
||||
|
||||
@Column(name = "atm_facility")
|
||||
private Boolean atmFacility;
|
||||
|
||||
@Column(name = "atm_withdrawal_facility")
|
||||
private Boolean atmWithdrawalFacility;
|
||||
|
||||
@Column(name = "upi_facility")
|
||||
private Boolean upiFacility;
|
||||
|
||||
@Column(name = "pos_transactions_facility")
|
||||
private Boolean posTransactionsFacility;
|
||||
|
||||
@Column(name = "cheque_book")
|
||||
private Boolean chequeBook;
|
||||
|
||||
@Column(name = "imps_facility")
|
||||
private Boolean impsFacility;
|
||||
|
||||
@Column(name = "sms_alerts")
|
||||
private Boolean smsAlerts;
|
||||
|
||||
@Column(name = "bbps_facility")
|
||||
private Boolean bbpsFacility;
|
||||
|
||||
@Column(name = "insurance_cover")
|
||||
private Boolean insuranceCover;
|
||||
|
||||
@Column(name = "bepg_facility")
|
||||
private Boolean bepgFacility;
|
||||
|
||||
@Column(name = "neft_facility")
|
||||
private Boolean neftFacility;
|
||||
|
||||
@Column(name = "rtgs_facility")
|
||||
private Boolean rtgsFacility;
|
||||
|
||||
@Column(name = "inter_bank_total_cap_facility")
|
||||
private Boolean interBankTotalCapFacility;
|
||||
|
||||
@Column(name = "withdrawal_facility")
|
||||
private Boolean withdrawalFacility;
|
||||
|
||||
@Column(name = "deposit_facility_kyc")
|
||||
private Boolean depositFacilityKyc;
|
||||
|
||||
@Column(name = "virtual_debit_card")
|
||||
private Boolean virtualDebitCard;
|
||||
|
||||
@Column(name = "nominee_submitted", nullable = false)
|
||||
private boolean nomineeSubmitted = false;
|
||||
|
||||
@Column(name = "nominee_operation", length = 10)
|
||||
private String nomineeOperation;
|
||||
|
||||
@Column(name = "nominee_id")
|
||||
private Long nomineeId;
|
||||
|
||||
@Column(name = "nominee_client_id")
|
||||
private Long nomineeClientId;
|
||||
|
||||
@Column(name = "relationship_cv_id")
|
||||
private Long relationshipCvId;
|
||||
|
||||
@Column(name = "percentage_share", precision = 5, scale = 2)
|
||||
private BigDecimal percentageShare;
|
||||
|
||||
@Column(name = "nominee_is_active")
|
||||
private Boolean nomineeIsActive;
|
||||
|
||||
@Column(name = "nominee_is_minor")
|
||||
private Boolean nomineeIsMinor;
|
||||
|
||||
@Column(name = "guardian_client_id")
|
||||
private Long guardianClientId;
|
||||
|
||||
@Column(name = "guardian_relationship_cv_id")
|
||||
private Long guardianRelationshipCvId;
|
||||
|
||||
@Column(name = "effective_date")
|
||||
private LocalDate effectiveDate;
|
||||
|
||||
@Column(name = "nominee_remarks", length = 500)
|
||||
private String nomineeRemarks;
|
||||
|
||||
protected SavingsAccountPendingEdit() {}
|
||||
|
||||
public SavingsAccountPendingEdit(final SavingsAccount savingsAccount, final AppUser maker) {
|
||||
this.savingsAccount = savingsAccount;
|
||||
this.maker = maker;
|
||||
this.createdOn = LocalDateTime.now();
|
||||
this.status = STATUS_PENDING;
|
||||
this.pendingLockKey = lockKeyFor(savingsAccount.getId());
|
||||
}
|
||||
|
||||
public static String lockKeyFor(final Long savingsAccountId) {
|
||||
return "SAVINGS_ACCOUNT_EDIT:" + savingsAccountId;
|
||||
}
|
||||
|
||||
public boolean isPending() {
|
||||
return STATUS_PENDING.equals(this.status);
|
||||
}
|
||||
|
||||
public void markApproved(final AppUser checker) {
|
||||
this.status = STATUS_APPROVED;
|
||||
this.checker = checker;
|
||||
this.checkedOn = LocalDateTime.now();
|
||||
this.pendingLockKey = null;
|
||||
}
|
||||
|
||||
public void markRejected(final AppUser checker, final String reason) {
|
||||
this.status = STATUS_REJECTED;
|
||||
this.checker = checker;
|
||||
this.checkedOn = LocalDateTime.now();
|
||||
this.rejectReason = reason;
|
||||
this.pendingLockKey = null;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface SavingsAccountPendingEditRepository extends JpaRepository<SavingsAccountPendingEdit, Long> {
|
||||
|
||||
Optional<SavingsAccountPendingEdit> findBySavingsAccountIdAndStatus(Long savingsAccountId, String status);
|
||||
|
||||
List<SavingsAccountPendingEdit> findByStatus(String status);
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.exception;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
/**
|
||||
* Thrown at edit-submission time when the account's available balance cannot cover the charge for
|
||||
* the digital/account feature(s) the maker is requesting to enable.
|
||||
*/
|
||||
public class SavingsAccountEditInsufficientBalanceException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public SavingsAccountEditInsufficientBalanceException(final Long savingsId, final BigDecimal availableBalance,
|
||||
final BigDecimal requiredAmount) {
|
||||
super("error.msg.savingsaccount.edit.insufficient.balance",
|
||||
"Account balance is insufficient for the requested feature(s). Available balance: " + availableBalance
|
||||
+ ", required: " + requiredAmount,
|
||||
savingsId, availableBalance, requiredAmount);
|
||||
}
|
||||
}
|
||||
+30
@@ -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.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class SavingsAccountEditPendingApprovalException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public SavingsAccountEditPendingApprovalException(final Long savingsId) {
|
||||
super("error.msg.savingsaccount.edit.pending.approval",
|
||||
"Savings account with identifier " + savingsId + " already has a Nominee/Digital-Feature edit pending approval",
|
||||
savingsId);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class SavingsAccountEditSelfApprovalNotAllowedException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public SavingsAccountEditSelfApprovalNotAllowedException(final Long savingsId) {
|
||||
super("error.msg.savingsaccount.edit.self.approval.not.allowed",
|
||||
"The maker of the pending Nominee/Digital-Feature edit for savings account " + savingsId
|
||||
+ " cannot also approve or reject it",
|
||||
savingsId);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class SavingsAccountNoPendingEditException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public SavingsAccountNoPendingEditException(final Long savingsId) {
|
||||
super("error.msg.savingsaccount.no.pending.edit",
|
||||
"Savings account with identifier " + savingsId + " has no Nominee/Digital-Feature edit awaiting approval", savingsId);
|
||||
}
|
||||
}
|
||||
+75
@@ -88,6 +88,81 @@ public class FacilityChargeAutoApplyService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum of the mapped charge amounts for the given facility types - used to size the hold placed at
|
||||
* edit-submission time (see SavingsAccountEditWritePlatformServiceImpl) before any charge is actually applied.
|
||||
*/
|
||||
public BigDecimal totalChargeAmountFor(final java.util.Set<String> facilityTypes) {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (final String facilityType : facilityTypes) {
|
||||
final java.util.Optional<FacilityChargeMapping> mapping = mappingRepository.findByFacilityType(facilityType);
|
||||
if (mapping.isPresent() && mapping.get().getCharge() != null) {
|
||||
total = total.add(mapping.get().getCharge().getAmount());
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a fresh {@link SavingsAccountCharge} for each of the given (newly re-enabled) facility types, so approving
|
||||
* a Nominee/Digital-Feature edit always charges again for the transition - unlike
|
||||
* {@link #applyFacilityChargesOnActivation(SavingsAccount)}'s {@code applyIfEnabled}, this deliberately has NO
|
||||
* "already applied" dedup-by-name guard: a facility that was enabled at activation, disabled, then re-enabled via
|
||||
* an approved edit must be charged again, even though a (now-paid, now-irrelevant) charge object with the same
|
||||
* name already sits in {@code account.charges()} from the earlier cycle.
|
||||
*/
|
||||
public List<SavingsAccountCharge> applyChargesForFacilities(final SavingsAccount account, final java.util.Set<String> facilityTypes) {
|
||||
final List<SavingsAccountCharge> chargesToApply = new ArrayList<>();
|
||||
for (final String facilityType : facilityTypes) {
|
||||
mappingRepository.findByFacilityType(facilityType).ifPresent(mapping -> {
|
||||
final Charge charge = mapping.getCharge();
|
||||
if (charge == null) {
|
||||
return;
|
||||
}
|
||||
final MonthDay feeOnMonthDay = resolveFeeOnMonthDay(charge);
|
||||
final SavingsAccountCharge accountCharge = SavingsAccountCharge.createNewWithoutSavingsAccount(charge, charge.getAmount(),
|
||||
null, null, resolveDueDate(charge), true, feeOnMonthDay, charge.getFeeInterval());
|
||||
accountCharge.update(account);
|
||||
account.charges().add(accountCharge);
|
||||
chargesToApply.add(accountCharge);
|
||||
});
|
||||
}
|
||||
if (!chargesToApply.isEmpty()) {
|
||||
savingsAccountChargeRepository.saveAll(chargesToApply);
|
||||
}
|
||||
return chargesToApply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pays exactly the given charges (typically the just-added result of {@link #applyChargesForFacilities}) as of
|
||||
* {@code transactionDate}. Unlike {@link #collectActivationCharges(SavingsAccount)} - which posts at the
|
||||
* account's activation date and sweeps every unpaid activation-type charge - this is scoped to a specific set of
|
||||
* charges and a caller-supplied date, for posting a Nominee/Digital-Feature edit's charges on the day it's
|
||||
* approved rather than backdating them to activation.
|
||||
*/
|
||||
public List<SavingsAccountTransaction> collectCharges(final SavingsAccount account, final List<SavingsAccountCharge> charges,
|
||||
final LocalDate transactionDate) {
|
||||
final List<SavingsAccountTransaction> transactions = new ArrayList<>();
|
||||
for (final SavingsAccountCharge charge : charges) {
|
||||
if (charge.isPaid() || charge.isWaived()) continue;
|
||||
final BigDecimal amount = charge.getAmountOutstanding(account.getCurrency()).getAmount();
|
||||
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) continue;
|
||||
|
||||
final Money amountMoney = Money.of(account.getCurrency(), amount);
|
||||
final SavingsAccountTransaction txn = account.payCharge(charge, amountMoney, transactionDate, false, null);
|
||||
if (txn != null) {
|
||||
transactions.add(txn);
|
||||
log.info("Collected facility charge '{}' ₹{} from savings account id={}", charge.getCharge().getName(), amount,
|
||||
account.getId());
|
||||
}
|
||||
}
|
||||
if (!transactions.isEmpty()) {
|
||||
savingsAccountTransactionRepository.saveAll(transactions);
|
||||
account.recalculateBalancesAfterFacilityCharges(transactionDate);
|
||||
}
|
||||
return transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all unpaid SavingsActivation charges on the account.
|
||||
* Called after facility charges are saved and flushed, so amounts are properly initialized.
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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 org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountPendingEditData;
|
||||
|
||||
public interface SavingsAccountEditWritePlatformService {
|
||||
|
||||
CommandProcessingResult submitEditForApproval(Long savingsId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult approveEdit(Long savingsId);
|
||||
|
||||
CommandProcessingResult rejectEdit(Long savingsId, String rejectReason);
|
||||
|
||||
SavingsAccountPendingEditData retrievePendingEdit(Long savingsId);
|
||||
|
||||
java.util.Collection<SavingsAccountPendingEditData> retrieveAllPendingEdits();
|
||||
}
|
||||
+2
@@ -51,4 +51,6 @@
|
||||
<include file="parts/2027_add_pending_edit_satellite_tables.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/2028_update_saving_product_temp_table.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/2029_remove_proposed_payload_json.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/2030_add_savings_account_pending_edit_table.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/2031_add_savings_account_edit_approval_permissions.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!--
|
||||
Holds a pending edit request for an ACTIVE savings account's Nominee + Digital & Account
|
||||
Features, mirroring the m_savings_product_temp maker-checker pattern: presence of a row here
|
||||
with status=PENDING (enforced via the unique pending_lock_key) is itself the lock, and nothing
|
||||
is applied to the real m_savings_account_facility_config / m_client_nominee rows until a
|
||||
checker approves (see SavingsAccountEditWritePlatformServiceImpl). One row per request; only
|
||||
one nominee change (add/update/delete) can be proposed per request, so its fields are kept as
|
||||
flat typed columns on this same table rather than a satellite table.
|
||||
-->
|
||||
<changeSet author="fineract" id="2030-1">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not>
|
||||
<tableExists tableName="m_savings_account_pending_edit" />
|
||||
</not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_savings_account_pending_edit">
|
||||
<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_saoe_savings_account_id" references="m_savings_account(id)" />
|
||||
</column>
|
||||
<column name="createdby_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_saoe_maker_id" references="m_appuser(id)" />
|
||||
</column>
|
||||
<column name="lastmodifiedby_id" type="BIGINT">
|
||||
<constraints nullable="true" foreignKeyName="fk_saoe_checker_id" references="m_appuser(id)" />
|
||||
</column>
|
||||
<column name="created_date" type="DATETIME">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="lastmodified_date" type="DATETIME">
|
||||
<constraints nullable="true" />
|
||||
</column>
|
||||
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="reject_reason" type="VARCHAR(500)" />
|
||||
<column name="pending_lock_key" type="VARCHAR(40)">
|
||||
<constraints nullable="true" unique="true" uniqueConstraintName="saoe_unq_pending_lock_key" />
|
||||
</column>
|
||||
<column name="command_source_id" type="BIGINT">
|
||||
<constraints nullable="true" foreignKeyName="fk_saoe_command_source_id" references="m_portfolio_command_source(id)" />
|
||||
</column>
|
||||
|
||||
<!-- Hold placed at submission time for the total charge of newly-enabled facilities; released
|
||||
(no charge) on reject, released-then-converted-to-a-posted-charge on approve. -->
|
||||
<column name="hold_transaction_id" type="BIGINT">
|
||||
<constraints nullable="true" foreignKeyName="fk_saoe_hold_txn_id" references="m_savings_account_transaction(id)" />
|
||||
</column>
|
||||
<column name="hold_amount" type="DECIMAL(19,6)" />
|
||||
|
||||
<!-- Digital & Account Features - same 16 columns/names as m_savings_account_facility_config.
|
||||
NULL = maker did not touch this toggle; facility_config_submitted tells approve()/UI
|
||||
whether this block was submitted at all. -->
|
||||
<column name="facility_config_submitted" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="atm_facility" type="BOOLEAN" />
|
||||
<column name="atm_withdrawal_facility" type="BOOLEAN" />
|
||||
<column name="upi_facility" type="BOOLEAN" />
|
||||
<column name="pos_transactions_facility" type="BOOLEAN" />
|
||||
<column name="cheque_book" type="BOOLEAN" />
|
||||
<column name="imps_facility" type="BOOLEAN" />
|
||||
<column name="sms_alerts" type="BOOLEAN" />
|
||||
<column name="bbps_facility" type="BOOLEAN" />
|
||||
<column name="insurance_cover" type="BOOLEAN" />
|
||||
<column name="bepg_facility" type="BOOLEAN" />
|
||||
<column name="neft_facility" type="BOOLEAN" />
|
||||
<column name="rtgs_facility" type="BOOLEAN" />
|
||||
<column name="inter_bank_total_cap_facility" type="BOOLEAN" />
|
||||
<column name="withdrawal_facility" type="BOOLEAN" />
|
||||
<column name="deposit_facility_kyc" type="BOOLEAN" />
|
||||
<column name="virtual_debit_card" type="BOOLEAN" />
|
||||
|
||||
<!-- Nominee change - one per request (add / update / delete), same fields as m_client_nominee. -->
|
||||
<column name="nominee_submitted" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="nominee_operation" type="VARCHAR(10)" />
|
||||
<column name="nominee_id" type="BIGINT">
|
||||
<constraints nullable="true" foreignKeyName="fk_saoe_nominee_id" references="m_client_nominee(id)" />
|
||||
</column>
|
||||
<column name="nominee_client_id" type="BIGINT">
|
||||
<constraints nullable="true" foreignKeyName="fk_saoe_nominee_client_id" references="m_client(id)" />
|
||||
</column>
|
||||
<column name="relationship_cv_id" type="BIGINT" />
|
||||
<column name="percentage_share" type="DECIMAL(5,2)" />
|
||||
<column name="nominee_is_active" type="BOOLEAN" />
|
||||
<column name="nominee_is_minor" type="BOOLEAN" />
|
||||
<column name="guardian_client_id" type="BIGINT">
|
||||
<constraints nullable="true" foreignKeyName="fk_saoe_guardian_client_id" references="m_client(id)" />
|
||||
</column>
|
||||
<column name="guardian_relationship_cv_id" type="BIGINT" />
|
||||
<column name="effective_date" type="DATE" />
|
||||
<column name="nominee_remarks" type="VARCHAR(500)" />
|
||||
</createTable>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!--
|
||||
Permissions gating the Nominee + Digital & Account Features maker-checker edit flow for
|
||||
savings accounts. SUBMITEDIT lets a maker propose the edit (this is the "Edit" action shown
|
||||
on the account view); APPROVEEDIT/REJECTEDIT let a checker resolve it. The _CHECKER permission
|
||||
follows the "<ACTION>_<ENTITY>_CHECKER" convention (see 2022_add_product_checker_permissions.xml)
|
||||
so the pending request also surfaces in the generic Checker Inbox for limited checkers.
|
||||
-->
|
||||
<changeSet author="fineract" id="2031-1">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'SUBMITEDIT_SAVINGSACCOUNT'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="transaction_savings" />
|
||||
<column name="code" value="SUBMITEDIT_SAVINGSACCOUNT" />
|
||||
<column name="entity_name" value="SAVINGSACCOUNT" />
|
||||
<column name="action_name" value="SUBMITEDIT" />
|
||||
<column name="can_maker_checker" valueBoolean="false" />
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="2031-2">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_SAVINGSACCOUNT'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="transaction_savings" />
|
||||
<column name="code" value="APPROVEEDIT_SAVINGSACCOUNT" />
|
||||
<column name="entity_name" value="SAVINGSACCOUNT" />
|
||||
<column name="action_name" value="APPROVEEDIT" />
|
||||
<column name="can_maker_checker" valueBoolean="false" />
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="2031-3">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECTEDIT_SAVINGSACCOUNT'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="transaction_savings" />
|
||||
<column name="code" value="REJECTEDIT_SAVINGSACCOUNT" />
|
||||
<column name="entity_name" value="SAVINGSACCOUNT" />
|
||||
<column name="action_name" value="REJECTEDIT" />
|
||||
<column name="can_maker_checker" valueBoolean="false" />
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="2031-4">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_SAVINGSACCOUNT_CHECKER'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="transaction_savings" />
|
||||
<column name="code" value="APPROVEEDIT_SAVINGSACCOUNT_CHECKER" />
|
||||
<column name="entity_name" value="SAVINGSACCOUNT" />
|
||||
<column name="action_name" value="APPROVEEDIT_CHECKER" />
|
||||
<column name="can_maker_checker" valueBoolean="false" />
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user