Merge remote-tracking branch 'nexora/uate' into merge-uate-into-dev
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
# Conflicts: # .gitignore # fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml # fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/FacilityChargeAutoApplyService.java
This commit is contained in:
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-side view of a single pending current account Nominee/Digital-Feature edit
|
||||||
|
* (m_current_account_pending_edit), for the Approvals -> Pending Current Account Edits screen.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class CurrentAccountPendingEditData implements Serializable {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long currentAccountId;
|
||||||
|
private String accountNo;
|
||||||
|
private Long makerId;
|
||||||
|
private String makerUsername;
|
||||||
|
private Long checkerId;
|
||||||
|
private String checkerUsername;
|
||||||
|
private LocalDateTime createdOn;
|
||||||
|
private LocalDateTime checkedOn;
|
||||||
|
private String status;
|
||||||
|
private String rejectReason;
|
||||||
|
private BigDecimal holdAmount;
|
||||||
|
private boolean facilityConfigSubmitted;
|
||||||
|
private boolean nomineeSubmitted;
|
||||||
|
private Map<String, Object> proposedFacilityConfig;
|
||||||
|
private Map<String, Object> proposedNominee;
|
||||||
|
|
||||||
|
public CurrentAccountPendingEditData() {}
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-side view of a single pending savings account Nominee/Digital-Feature edit
|
||||||
|
* (m_savings_account_pending_edit), for the Approvals -> Pending Savings Account Edits screen.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class SavingsAccountPendingEditData implements Serializable {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long savingsAccountId;
|
||||||
|
private String accountNo;
|
||||||
|
private Long makerId;
|
||||||
|
private String makerUsername;
|
||||||
|
private Long checkerId;
|
||||||
|
private String checkerUsername;
|
||||||
|
private LocalDateTime createdOn;
|
||||||
|
private LocalDateTime checkedOn;
|
||||||
|
private String status;
|
||||||
|
private String rejectReason;
|
||||||
|
private BigDecimal holdAmount;
|
||||||
|
private boolean facilityConfigSubmitted;
|
||||||
|
private boolean nomineeSubmitted;
|
||||||
|
private Map<String, Object> proposedFacilityConfig;
|
||||||
|
private Map<String, Object> proposedNominee;
|
||||||
|
private String depositAccountType;
|
||||||
|
|
||||||
|
public SavingsAccountPendingEditData() {}
|
||||||
|
}
|
||||||
+27
@@ -99,4 +99,31 @@ public class CurrentAccountFacilityConfig extends AbstractPersistableCustom<Long
|
|||||||
cfg.currentAccount = account;
|
cfg.currentAccount = account;
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partial update: only overwrites fields present (non-null) in the given JSON, leaving all others untouched.
|
||||||
|
* Mirrors {@code SavingsAccountFacilityConfig.updateFrom(JsonObject)}. Used by the Nominee/Digital-Feature
|
||||||
|
* edit-approval flow, which only ever submits the toggles the maker actually touched.
|
||||||
|
*/
|
||||||
|
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());
|
||||||
|
if (config.has("internetBanking")) setInternetBanking(config.get("internetBanking").getAsBoolean());
|
||||||
|
if (config.has("mobileBanking")) setMobileBanking(config.get("mobileBanking").getAsBoolean());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This repository did not previously exist - {@link CurrentAccountFacilityConfig} rows were only ever persisted via
|
||||||
|
* cascade from {@link CurrentAccountRepository} (see {@code CurrentAccount#facilityConfig}, {@code cascade = ALL}).
|
||||||
|
* Added so the Nominee/Digital-Feature edit-approval flow can save the config row directly, mirroring
|
||||||
|
* {@code SavingsAccountFacilityConfigRepository}.
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface CurrentAccountFacilityConfigRepository extends JpaRepository<CurrentAccountFacilityConfig, Long> {
|
||||||
|
|
||||||
|
Optional<CurrentAccountFacilityConfig> findByCurrentAccountId(Long currentAccountId);
|
||||||
|
}
|
||||||
+596
@@ -0,0 +1,596 @@
|
|||||||
|
/**
|
||||||
|
* 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.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 current 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_current_account_facility_config} / {@code m_client_nominee}
|
||||||
|
* rows until a checker approves (see CurrentAccountEditWritePlatformServiceJpaRepositoryImpl). Mirrors
|
||||||
|
* {@code SavingsAccountPendingEdit} but additionally carries a full point-in-time "wide snapshot" of every business
|
||||||
|
* column on {@link CurrentAccount} at submission time, for audit/UI display purposes only (not used to drive
|
||||||
|
* approve/reject logic, which reads the live account).
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_current_account_pending_edit")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class CurrentAccountPendingEdit 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 = "current_account_id", nullable = false)
|
||||||
|
private CurrentAccount currentAccount;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
|
||||||
|
/** "CURRENT_ACCOUNT_EDIT:<currentAccountId>" 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;
|
||||||
|
|
||||||
|
// ── Digital & Account Features (same names as m_current_account_facility_config) ──
|
||||||
|
@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 = "internet_banking")
|
||||||
|
private Boolean internetBanking;
|
||||||
|
|
||||||
|
@Column(name = "mobile_banking")
|
||||||
|
private Boolean mobileBanking;
|
||||||
|
|
||||||
|
// ── Nominee change - one per request (add / update / delete) ──
|
||||||
|
@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;
|
||||||
|
|
||||||
|
// ── Wide point-in-time snapshot of every business column on CurrentAccount at submission time ──
|
||||||
|
// (audit/UI display only - approve()/reject() logic never reads these, it reads the live account)
|
||||||
|
@Column(name = "account_no", length = 20)
|
||||||
|
private String snapshotAccountNo;
|
||||||
|
|
||||||
|
@Column(name = "external_id", length = 100)
|
||||||
|
private String snapshotExternalId;
|
||||||
|
|
||||||
|
@Column(name = "client_id")
|
||||||
|
private Long snapshotClientId;
|
||||||
|
|
||||||
|
@Column(name = "product_id")
|
||||||
|
private Long snapshotProductId;
|
||||||
|
|
||||||
|
@Column(name = "office_id")
|
||||||
|
private Long snapshotOfficeId;
|
||||||
|
|
||||||
|
@Column(name = "staff_id")
|
||||||
|
private Long snapshotStaffId;
|
||||||
|
|
||||||
|
@Column(name = "status_enum")
|
||||||
|
private Integer snapshotStatusEnum;
|
||||||
|
|
||||||
|
@Column(name = "sub_status_enum")
|
||||||
|
private Integer snapshotSubStatusEnum;
|
||||||
|
|
||||||
|
@Column(name = "account_type_enum")
|
||||||
|
private Integer snapshotAccountTypeEnum;
|
||||||
|
|
||||||
|
@Column(name = "submittedon_date")
|
||||||
|
private LocalDate snapshotSubmittedOnDate;
|
||||||
|
|
||||||
|
@Column(name = "approvedon_date")
|
||||||
|
private LocalDate snapshotApprovedOnDate;
|
||||||
|
|
||||||
|
@Column(name = "activatedon_date")
|
||||||
|
private LocalDate snapshotActivatedOnDate;
|
||||||
|
|
||||||
|
@Column(name = "rejectedon_date")
|
||||||
|
private LocalDate snapshotRejectedOnDate;
|
||||||
|
|
||||||
|
@Column(name = "withdrawnon_date")
|
||||||
|
private LocalDate snapshotWithdrawnOnDate;
|
||||||
|
|
||||||
|
@Column(name = "closedon_date")
|
||||||
|
private LocalDate snapshotClosedOnDate;
|
||||||
|
|
||||||
|
@Column(name = "submittedon_userid")
|
||||||
|
private Long snapshotSubmittedOnUserId;
|
||||||
|
|
||||||
|
@Column(name = "approvedon_userid")
|
||||||
|
private Long snapshotApprovedOnUserId;
|
||||||
|
|
||||||
|
@Column(name = "activatedon_userid")
|
||||||
|
private Long snapshotActivatedOnUserId;
|
||||||
|
|
||||||
|
@Column(name = "rejectedon_userid")
|
||||||
|
private Long snapshotRejectedOnUserId;
|
||||||
|
|
||||||
|
@Column(name = "withdrawnon_userid")
|
||||||
|
private Long snapshotWithdrawnOnUserId;
|
||||||
|
|
||||||
|
@Column(name = "closedon_userid")
|
||||||
|
private Long snapshotClosedOnUserId;
|
||||||
|
|
||||||
|
@Column(name = "currency_code", length = 3)
|
||||||
|
private String snapshotCurrencyCode;
|
||||||
|
|
||||||
|
@Column(name = "currency_digits")
|
||||||
|
private Integer snapshotCurrencyDigits;
|
||||||
|
|
||||||
|
@Column(name = "currency_multiplesof")
|
||||||
|
private Integer snapshotCurrencyMultiplesOf;
|
||||||
|
|
||||||
|
@Column(name = "nominal_annual_interest_rate", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotNominalAnnualInterestRate;
|
||||||
|
|
||||||
|
@Column(name = "interest_compounding_period_enum")
|
||||||
|
private Integer snapshotInterestCompoundingPeriodEnum;
|
||||||
|
|
||||||
|
@Column(name = "interest_posting_period_enum")
|
||||||
|
private Integer snapshotInterestPostingPeriodEnum;
|
||||||
|
|
||||||
|
@Column(name = "interest_calculation_type_enum")
|
||||||
|
private Integer snapshotInterestCalculationTypeEnum;
|
||||||
|
|
||||||
|
@Column(name = "interest_calculation_days_in_year_type_enum")
|
||||||
|
private Integer snapshotInterestCalculationDaysInYearTypeEnum;
|
||||||
|
|
||||||
|
@Column(name = "min_balance_for_interest_calculation", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotMinBalanceForInterestCalculation;
|
||||||
|
|
||||||
|
@Column(name = "start_interest_calculation_date")
|
||||||
|
private LocalDate snapshotStartInterestCalculationDate;
|
||||||
|
|
||||||
|
@Column(name = "accrued_till_date")
|
||||||
|
private LocalDate snapshotAccruedTillDate;
|
||||||
|
|
||||||
|
@Column(name = "last_interest_calculation_date")
|
||||||
|
private LocalDate snapshotLastInterestCalculationDate;
|
||||||
|
|
||||||
|
@Column(name = "last_closed_business_date")
|
||||||
|
private LocalDate snapshotLastClosedBusinessDate;
|
||||||
|
|
||||||
|
@Column(name = "withhold_tax")
|
||||||
|
private Boolean snapshotWithholdTax;
|
||||||
|
|
||||||
|
@Column(name = "min_required_opening_balance", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotMinRequiredOpeningBalance;
|
||||||
|
|
||||||
|
@Column(name = "lockin_period_frequency")
|
||||||
|
private Integer snapshotLockinPeriodFrequency;
|
||||||
|
|
||||||
|
@Column(name = "lockin_period_frequency_enum")
|
||||||
|
private Integer snapshotLockinPeriodFrequencyEnum;
|
||||||
|
|
||||||
|
@Column(name = "lockedin_until_date_derived")
|
||||||
|
private LocalDate snapshotLockedInUntilDate;
|
||||||
|
|
||||||
|
@Column(name = "enforce_min_required_balance")
|
||||||
|
private Boolean snapshotEnforceMinRequiredBalance;
|
||||||
|
|
||||||
|
@Column(name = "min_required_balance", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotMinRequiredBalance;
|
||||||
|
|
||||||
|
@Column(name = "allow_overdraft")
|
||||||
|
private Boolean snapshotAllowOverdraft;
|
||||||
|
|
||||||
|
@Column(name = "overdraft_limit", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotOverdraftLimit;
|
||||||
|
|
||||||
|
@Column(name = "nominal_annual_interest_rate_overdraft", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotNominalAnnualInterestRateOverdraft;
|
||||||
|
|
||||||
|
@Column(name = "min_overdraft_for_interest_calculation", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotMinOverdraftForInterestCalculation;
|
||||||
|
|
||||||
|
@Column(name = "is_lien_allowed")
|
||||||
|
private Boolean snapshotLienAllowed;
|
||||||
|
|
||||||
|
@Column(name = "max_allowed_lien_limit", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotMaxAllowedLienLimit;
|
||||||
|
|
||||||
|
@Column(name = "on_hold_funds_derived", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotOnHoldFundsDerived;
|
||||||
|
|
||||||
|
@Column(name = "account_balance_derived", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotAccountBalanceDerived;
|
||||||
|
|
||||||
|
@Column(name = "total_deposits_derived", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotTotalDepositsDerived;
|
||||||
|
|
||||||
|
@Column(name = "total_withdrawals_derived", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotTotalWithdrawalsDerived;
|
||||||
|
|
||||||
|
@Column(name = "total_interest_earned_derived", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotTotalInterestEarnedDerived;
|
||||||
|
|
||||||
|
@Column(name = "total_interest_posted_derived", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotTotalInterestPostedDerived;
|
||||||
|
|
||||||
|
@Column(name = "total_overdraft_interest_derived", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotTotalOverdraftInterestDerived;
|
||||||
|
|
||||||
|
@Column(name = "total_withhold_tax_derived", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotTotalWithholdTaxDerived;
|
||||||
|
|
||||||
|
@Column(name = "total_savings_amount_on_hold", precision = 19, scale = 6)
|
||||||
|
private BigDecimal snapshotTotalSavingsAmountOnHold;
|
||||||
|
|
||||||
|
@Column(name = "withdrawal_fee_for_transfer")
|
||||||
|
private Boolean snapshotWithdrawalFeeForTransfer;
|
||||||
|
|
||||||
|
@Column(name = "reason_for_block", length = 500)
|
||||||
|
private String snapshotReasonForBlock;
|
||||||
|
|
||||||
|
@Column(name = "msme")
|
||||||
|
private Boolean snapshotMsme;
|
||||||
|
|
||||||
|
@Column(name = "auto_debit_facility")
|
||||||
|
private Boolean snapshotAutoDebitFacility;
|
||||||
|
|
||||||
|
@Column(name = "balance_swap_option")
|
||||||
|
private Boolean snapshotBalanceSwapOption;
|
||||||
|
|
||||||
|
@Column(name = "establishment_date")
|
||||||
|
private LocalDate snapshotEstablishmentDate;
|
||||||
|
|
||||||
|
@Column(name = "start_operation_date")
|
||||||
|
private LocalDate snapshotStartOperationDate;
|
||||||
|
|
||||||
|
@Column(name = "is_non_individual_client")
|
||||||
|
private Boolean snapshotNonIndividualClient;
|
||||||
|
|
||||||
|
@Column(name = "signatory_rights", length = 100)
|
||||||
|
private String snapshotSignatoryRights;
|
||||||
|
|
||||||
|
@Column(name = "cbs_account_type", length = 100)
|
||||||
|
private String snapshotCbsAccountType;
|
||||||
|
|
||||||
|
@Column(name = "customer_segment", length = 100)
|
||||||
|
private String snapshotCustomerSegment;
|
||||||
|
|
||||||
|
@Column(name = "account_status", length = 50)
|
||||||
|
private String snapshotAccountStatus;
|
||||||
|
|
||||||
|
@Column(name = "mode_of_operation", length = 50)
|
||||||
|
private String snapshotModeOfOperation;
|
||||||
|
|
||||||
|
@Column(name = "special_category", length = 100)
|
||||||
|
private String snapshotSpecialCategory;
|
||||||
|
|
||||||
|
@Column(name = "introducer_client_id")
|
||||||
|
private Long snapshotIntroducerClientId;
|
||||||
|
|
||||||
|
@Column(name = "introducer_name", length = 200)
|
||||||
|
private String snapshotIntroducerName;
|
||||||
|
|
||||||
|
@Column(name = "introducer_cif_number", length = 50)
|
||||||
|
private String snapshotIntroducerCifNumber;
|
||||||
|
|
||||||
|
@Column(name = "nominee_data", columnDefinition = "TEXT")
|
||||||
|
private String snapshotNomineeData;
|
||||||
|
|
||||||
|
@Column(name = "interest_calculate_selector")
|
||||||
|
private Integer snapshotInterestCalculateSelector;
|
||||||
|
|
||||||
|
@Column(name = "average_period", length = 20)
|
||||||
|
private String snapshotAveragePeriod;
|
||||||
|
|
||||||
|
@Column(name = "auto_post")
|
||||||
|
private Boolean snapshotAutoPost;
|
||||||
|
|
||||||
|
@Column(name = "frequency_fix_day")
|
||||||
|
private Integer snapshotFrequencyFixDay;
|
||||||
|
|
||||||
|
@Column(name = "enable_lockin_period")
|
||||||
|
private Boolean snapshotEnableLockinPeriod;
|
||||||
|
|
||||||
|
@Column(name = "balance_frequency", length = 50)
|
||||||
|
private String snapshotBalanceFrequency;
|
||||||
|
|
||||||
|
@Column(name = "avg_balance_calc_period", length = 10)
|
||||||
|
private String snapshotAvgBalanceCalcPeriod;
|
||||||
|
|
||||||
|
@Column(name = "account_closed_zero_balance_in_day_closing")
|
||||||
|
private Boolean snapshotAccountClosedZeroBalanceInDayClosing;
|
||||||
|
|
||||||
|
@Column(name = "valid_for_period", length = 10)
|
||||||
|
private String snapshotValidForPeriod;
|
||||||
|
|
||||||
|
@Column(name = "od_account_closed_zero_balance_in_day_closing")
|
||||||
|
private Boolean snapshotOdAccountClosedZeroBalanceInDayClosing;
|
||||||
|
|
||||||
|
@Column(name = "is_linking_enable")
|
||||||
|
private Boolean snapshotShareLinkingEnabled;
|
||||||
|
|
||||||
|
@Column(name = "terminate_true")
|
||||||
|
private Boolean snapshotTerminateTrue;
|
||||||
|
|
||||||
|
protected CurrentAccountPendingEdit() {}
|
||||||
|
|
||||||
|
public CurrentAccountPendingEdit(final CurrentAccount currentAccount, final AppUser maker) {
|
||||||
|
this.currentAccount = currentAccount;
|
||||||
|
this.maker = maker;
|
||||||
|
this.createdOn = LocalDateTime.now();
|
||||||
|
this.status = STATUS_PENDING;
|
||||||
|
this.pendingLockKey = lockKeyFor(currentAccount.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String lockKeyFor(final Long currentAccountId) {
|
||||||
|
return "CURRENT_ACCOUNT_EDIT:" + currentAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new pending-edit row and copies every business column off the live {@code account} onto this
|
||||||
|
* request's wide snapshot fields, for audit/UI display purposes only.
|
||||||
|
*/
|
||||||
|
public static CurrentAccountPendingEdit snapshotOf(final CurrentAccount account, final AppUser maker) {
|
||||||
|
final CurrentAccountPendingEdit p = new CurrentAccountPendingEdit(account, maker);
|
||||||
|
|
||||||
|
p.snapshotAccountNo = account.getAccountNo();
|
||||||
|
p.snapshotExternalId = account.getExternalId();
|
||||||
|
p.snapshotClientId = account.getClient() != null ? account.getClient().getId() : null;
|
||||||
|
p.snapshotProductId = account.getProductId();
|
||||||
|
p.snapshotOfficeId = account.getOffice() != null ? account.getOffice().getId() : null;
|
||||||
|
p.snapshotStaffId = account.getStaff() != null ? account.getStaff().getId() : null;
|
||||||
|
p.snapshotStatusEnum = account.getStatusEnum();
|
||||||
|
p.snapshotSubStatusEnum = account.getSubStatusEnum();
|
||||||
|
p.snapshotAccountTypeEnum = account.getAccountTypeEnum();
|
||||||
|
|
||||||
|
p.snapshotSubmittedOnDate = account.getSubmittedOnDate();
|
||||||
|
p.snapshotApprovedOnDate = account.getApprovedOnDate();
|
||||||
|
p.snapshotActivatedOnDate = account.getActivatedOnDate();
|
||||||
|
p.snapshotRejectedOnDate = account.getRejectedOnDate();
|
||||||
|
p.snapshotWithdrawnOnDate = account.getWithdrawnOnDate();
|
||||||
|
p.snapshotClosedOnDate = account.getClosedOnDate();
|
||||||
|
|
||||||
|
p.snapshotSubmittedOnUserId = account.getSubmittedOnByUser() != null ? account.getSubmittedOnByUser().getId() : null;
|
||||||
|
p.snapshotApprovedOnUserId = account.getApprovedOnByUser() != null ? account.getApprovedOnByUser().getId() : null;
|
||||||
|
p.snapshotActivatedOnUserId = account.getActivatedOnByUser() != null ? account.getActivatedOnByUser().getId() : null;
|
||||||
|
p.snapshotRejectedOnUserId = account.getRejectedOnByUser() != null ? account.getRejectedOnByUser().getId() : null;
|
||||||
|
p.snapshotWithdrawnOnUserId = account.getWithdrawnOnByUser() != null ? account.getWithdrawnOnByUser().getId() : null;
|
||||||
|
p.snapshotClosedOnUserId = account.getClosedOnByUser() != null ? account.getClosedOnByUser().getId() : null;
|
||||||
|
|
||||||
|
p.snapshotCurrencyCode = account.getCurrencyCode();
|
||||||
|
p.snapshotCurrencyDigits = account.getCurrencyDigits();
|
||||||
|
p.snapshotCurrencyMultiplesOf = account.getCurrencyMultiplesOf();
|
||||||
|
|
||||||
|
p.snapshotNominalAnnualInterestRate = account.getNominalAnnualInterestRate();
|
||||||
|
p.snapshotInterestCompoundingPeriodEnum = account.getInterestCompoundingPeriodEnum();
|
||||||
|
p.snapshotInterestPostingPeriodEnum = account.getInterestPostingPeriodEnum();
|
||||||
|
p.snapshotInterestCalculationTypeEnum = account.getInterestCalculationTypeEnum();
|
||||||
|
p.snapshotInterestCalculationDaysInYearTypeEnum = account.getInterestCalculationDaysInYearTypeEnum();
|
||||||
|
p.snapshotMinBalanceForInterestCalculation = account.getMinBalanceForInterestCalculation();
|
||||||
|
p.snapshotStartInterestCalculationDate = account.getStartInterestCalculationDate();
|
||||||
|
p.snapshotAccruedTillDate = account.getAccruedTillDate();
|
||||||
|
p.snapshotLastInterestCalculationDate = account.getLastInterestCalculationDate();
|
||||||
|
p.snapshotLastClosedBusinessDate = account.getLastClosedBusinessDate();
|
||||||
|
|
||||||
|
p.snapshotWithholdTax = account.isWithholdTax();
|
||||||
|
|
||||||
|
p.snapshotMinRequiredOpeningBalance = account.getMinRequiredOpeningBalance();
|
||||||
|
p.snapshotLockinPeriodFrequency = account.getLockinPeriodFrequency();
|
||||||
|
p.snapshotLockinPeriodFrequencyEnum = account.getLockinPeriodFrequencyEnum();
|
||||||
|
p.snapshotLockedInUntilDate = account.getLockedInUntilDate();
|
||||||
|
p.snapshotEnforceMinRequiredBalance = account.isEnforceMinRequiredBalance();
|
||||||
|
p.snapshotMinRequiredBalance = account.getMinRequiredBalance();
|
||||||
|
|
||||||
|
p.snapshotAllowOverdraft = account.isAllowOverdraft();
|
||||||
|
p.snapshotOverdraftLimit = account.getOverdraftLimit();
|
||||||
|
p.snapshotNominalAnnualInterestRateOverdraft = account.getNominalAnnualInterestRateOverdraft();
|
||||||
|
p.snapshotMinOverdraftForInterestCalculation = account.getMinOverdraftForInterestCalculation();
|
||||||
|
|
||||||
|
p.snapshotLienAllowed = account.isLienAllowed();
|
||||||
|
p.snapshotMaxAllowedLienLimit = account.getMaxAllowedLienLimit();
|
||||||
|
p.snapshotOnHoldFundsDerived = account.getOnHoldFundsDerived();
|
||||||
|
|
||||||
|
p.snapshotAccountBalanceDerived = account.getAccountBalanceDerived();
|
||||||
|
p.snapshotTotalDepositsDerived = account.getTotalDepositsDerived();
|
||||||
|
p.snapshotTotalWithdrawalsDerived = account.getTotalWithdrawalsDerived();
|
||||||
|
p.snapshotTotalInterestEarnedDerived = account.getTotalInterestEarnedDerived();
|
||||||
|
p.snapshotTotalInterestPostedDerived = account.getTotalInterestPostedDerived();
|
||||||
|
p.snapshotTotalOverdraftInterestDerived = account.getTotalOverdraftInterestDerived();
|
||||||
|
p.snapshotTotalWithholdTaxDerived = account.getTotalWithholdTaxDerived();
|
||||||
|
p.snapshotTotalSavingsAmountOnHold = account.getTotalSavingsAmountOnHold();
|
||||||
|
|
||||||
|
p.snapshotWithdrawalFeeForTransfer = account.isWithdrawalFeeForTransfer();
|
||||||
|
p.snapshotReasonForBlock = account.getReasonForBlock();
|
||||||
|
|
||||||
|
p.snapshotMsme = account.isMsme();
|
||||||
|
p.snapshotAutoDebitFacility = account.isAutoDebitFacility();
|
||||||
|
p.snapshotBalanceSwapOption = account.isBalanceSwapOption();
|
||||||
|
p.snapshotEstablishmentDate = account.getEstablishmentDate();
|
||||||
|
p.snapshotStartOperationDate = account.getStartOperationDate();
|
||||||
|
p.snapshotNonIndividualClient = account.isNonIndividualClient();
|
||||||
|
p.snapshotSignatoryRights = account.getSignatoryRights();
|
||||||
|
|
||||||
|
p.snapshotCbsAccountType = account.getCbsAccountType();
|
||||||
|
p.snapshotCustomerSegment = account.getCustomerSegment();
|
||||||
|
p.snapshotAccountStatus = account.getAccountStatus();
|
||||||
|
p.snapshotModeOfOperation = account.getModeOfOperation();
|
||||||
|
p.snapshotSpecialCategory = account.getSpecialCategory();
|
||||||
|
|
||||||
|
p.snapshotIntroducerClientId = account.getIntroducerClientId();
|
||||||
|
p.snapshotIntroducerName = account.getIntroducerName();
|
||||||
|
p.snapshotIntroducerCifNumber = account.getIntroducerCifNumber();
|
||||||
|
|
||||||
|
p.snapshotNomineeData = account.getNomineeData();
|
||||||
|
|
||||||
|
p.snapshotInterestCalculateSelector = account.getInterestCalculateSelector();
|
||||||
|
p.snapshotAveragePeriod = account.getAveragePeriod();
|
||||||
|
p.snapshotAutoPost = account.isAutoPost();
|
||||||
|
p.snapshotFrequencyFixDay = account.getFrequencyFixDay();
|
||||||
|
p.snapshotEnableLockinPeriod = account.isEnableLockinPeriod();
|
||||||
|
p.snapshotBalanceFrequency = account.getBalanceFrequency();
|
||||||
|
p.snapshotAvgBalanceCalcPeriod = account.getAvgBalanceCalcPeriod();
|
||||||
|
|
||||||
|
p.snapshotAccountClosedZeroBalanceInDayClosing = account.getAccountClosedZeroBalanceInDayClosing();
|
||||||
|
p.snapshotValidForPeriod = account.getValidForPeriod();
|
||||||
|
p.snapshotOdAccountClosedZeroBalanceInDayClosing = account.isOdAccountClosedZeroBalanceInDayClosing();
|
||||||
|
|
||||||
|
p.snapshotShareLinkingEnabled = account.isShareLinkingEnabled();
|
||||||
|
p.snapshotTerminateTrue = account.isTerminateTrue();
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.currentaccount.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface CurrentAccountPendingEditRepository extends JpaRepository<CurrentAccountPendingEdit, Long> {
|
||||||
|
|
||||||
|
Optional<CurrentAccountPendingEdit> findByCurrentAccountIdAndStatus(Long currentAccountId, String status);
|
||||||
|
|
||||||
|
List<CurrentAccountPendingEdit> 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.currentaccount.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 CurrentAccountEditInsufficientBalanceException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public CurrentAccountEditInsufficientBalanceException(final Long currentAccountId, final BigDecimal availableBalance,
|
||||||
|
final BigDecimal requiredAmount) {
|
||||||
|
super("error.msg.currentaccount.edit.insufficient.balance",
|
||||||
|
"Account balance is insufficient for the requested feature(s). Available balance: " + availableBalance
|
||||||
|
+ ", required: " + requiredAmount,
|
||||||
|
currentAccountId, 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.currentaccount.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class CurrentAccountEditPendingApprovalException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public CurrentAccountEditPendingApprovalException(final Long currentAccountId) {
|
||||||
|
super("error.msg.currentaccount.edit.pending.approval.exists",
|
||||||
|
"Current account with identifier " + currentAccountId + " already has a Nominee/Digital-Feature edit pending approval",
|
||||||
|
currentAccountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+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.currentaccount.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class CurrentAccountEditSelfApprovalNotAllowedException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public CurrentAccountEditSelfApprovalNotAllowedException(final Long currentAccountId) {
|
||||||
|
super("error.msg.currentaccount.edit.self.approval.not.allowed",
|
||||||
|
"The maker of the pending Nominee/Digital-Feature edit for current account " + currentAccountId
|
||||||
|
+ " cannot also approve or reject it",
|
||||||
|
currentAccountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+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.currentaccount.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class CurrentAccountNoPendingEditException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public CurrentAccountNoPendingEditException(final Long currentAccountId) {
|
||||||
|
super("error.msg.currentaccount.no.pending.edit",
|
||||||
|
"Current account with identifier " + currentAccountId + " has no Nominee/Digital-Feature edit awaiting approval",
|
||||||
|
currentAccountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+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.currentaccount.service;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.data.CurrentAccountPendingEditData;
|
||||||
|
|
||||||
|
public interface CurrentAccountEditWritePlatformService {
|
||||||
|
|
||||||
|
CommandProcessingResult submitEditForApproval(Long currentAccountId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult approveEdit(Long currentAccountId);
|
||||||
|
|
||||||
|
CommandProcessingResult rejectEdit(Long currentAccountId, String rejectReason);
|
||||||
|
|
||||||
|
CurrentAccountPendingEditData retrievePendingEdit(Long currentAccountId);
|
||||||
|
|
||||||
|
Collection<CurrentAccountPendingEditData> retrieveAllPendingEdits();
|
||||||
|
}
|
||||||
+10
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.currentaccount.service;
|
package org.apache.fineract.portfolio.currentaccount.service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
|
||||||
@@ -42,4 +43,13 @@ public interface CurrentAccountWritePlatformService {
|
|||||||
CommandProcessingResult deleteApplication(Long accountId);
|
CommandProcessingResult deleteApplication(Long accountId);
|
||||||
|
|
||||||
CommandProcessingResult terminateAccount(Long accountId, JsonCommand command);
|
CommandProcessingResult terminateAccount(Long accountId, JsonCommand command);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves each of the given (newly-enabled) facility types to its mapped charge via
|
||||||
|
* {@code m_facility_charge_mapping}, creates a {@code CurrentAccountCharge} for it (skipping any facility type
|
||||||
|
* for which the account already has a paid, non-waived charge for the same mapped {@code Charge}), collects it
|
||||||
|
* from the account's running balance as of today, and posts the corresponding GL journal entries. Reuses the
|
||||||
|
* same private charge-creation-and-collection helper that {@code activate()} uses for activation-time charges.
|
||||||
|
*/
|
||||||
|
void applyAndCollectFacilityCharges(Long currentAccountId, Set<String> newlyEnabledFacilityTypes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,975 +0,0 @@
|
|||||||
> Task :buildSrc:extractPluginRequests UP-TO-DATE
|
|
||||||
> Task :buildSrc:generatePluginAdapters UP-TO-DATE
|
|
||||||
> Task :buildSrc:compileJava UP-TO-DATE
|
|
||||||
> Task :buildSrc:compileGroovy UP-TO-DATE
|
|
||||||
> Task :buildSrc:compileGroovyPlugins UP-TO-DATE
|
|
||||||
> Task :buildSrc:pluginDescriptors UP-TO-DATE
|
|
||||||
> Task :buildSrc:processResources UP-TO-DATE
|
|
||||||
> Task :buildSrc:classes UP-TO-DATE
|
|
||||||
> Task :buildSrc:jar UP-TO-DATE
|
|
||||||
|
|
||||||
> Configure project :
|
|
||||||
matching ref: COMMIT - 7dea88d30f72365945bed2160d2ba72dccdc80b7
|
|
||||||
ref configuration: COMMIT - pattern: null
|
|
||||||
version: ${describe.tag.version.major}.${describe.tag.version.minor.next}.0-SNAPSHOT
|
|
||||||
describeTagPattern: .*(\d+\.\d+\.\d+).*
|
|
||||||
describeTagFirstParent: false
|
|
||||||
|
|
||||||
project version: 1.15.0-SNAPSHOT
|
|
||||||
|
|
||||||
> Task :spotlessInternalRegisterDependencies UP-TO-DATE
|
|
||||||
> Task :fineract-current-product:processResources NO-SOURCE
|
|
||||||
> Task :fineract-mix:processResources NO-SOURCE
|
|
||||||
> Task :fineract-rates:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-core:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-cob:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-branch:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-security:processResources NO-SOURCE
|
|
||||||
> Task :fineract-loan-origination:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-savings:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-accounting:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-report:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-charge:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-investor:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-loan:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-client:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-progressive-loan:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-working-capital-loan:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-document:processResources NO-SOURCE
|
|
||||||
> Task :fineract-tax:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-avro-schemas:preprocessAvroSchemas UP-TO-DATE
|
|
||||||
> Task :fineract-validation:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-command:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-validation:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-validation:classes UP-TO-DATE
|
|
||||||
> Task :fineract-command:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-command:classes UP-TO-DATE
|
|
||||||
> Task :fineract-command:jar UP-TO-DATE
|
|
||||||
> Task :fineract-validation:jar UP-TO-DATE
|
|
||||||
> Task :fineract-avro-schemas:spotlessJson UP-TO-DATE
|
|
||||||
> Task :fineract-avro-schemas:spotlessJsonApply UP-TO-DATE
|
|
||||||
> Task :fineract-avro-schemas:buildJavaSdk UP-TO-DATE
|
|
||||||
> Task :fineract-avro-schemas:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-avro-schemas:processResources UP-TO-DATE
|
|
||||||
> Task :fineract-avro-schemas:classes UP-TO-DATE
|
|
||||||
> Task :fineract-avro-schemas:jar UP-TO-DATE
|
|
||||||
> Task :fineract-working-capital-loan:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-working-capital-loan:classes UP-TO-DATE
|
|
||||||
> Task :fineract-working-capital-loan:jar UP-TO-DATE
|
|
||||||
> Task :fineract-core:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-core:classes UP-TO-DATE
|
|
||||||
> Task :fineract-core:jar UP-TO-DATE
|
|
||||||
> Task :fineract-mix:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-tax:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-mix:classes UP-TO-DATE
|
|
||||||
> Task :fineract-report:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-tax:classes UP-TO-DATE
|
|
||||||
> Task :fineract-mix:jar UP-TO-DATE
|
|
||||||
> Task :fineract-rates:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-report:classes UP-TO-DATE
|
|
||||||
> Task :fineract-cob:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-rates:classes UP-TO-DATE
|
|
||||||
> Task :fineract-cob:classes UP-TO-DATE
|
|
||||||
> Task :fineract-tax:jar UP-TO-DATE
|
|
||||||
> Task :fineract-report:jar UP-TO-DATE
|
|
||||||
> Task :fineract-cob:jar UP-TO-DATE
|
|
||||||
> Task :fineract-rates:jar UP-TO-DATE
|
|
||||||
> Task :fineract-document:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-document:classes UP-TO-DATE
|
|
||||||
> Task :fineract-security:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-document:jar UP-TO-DATE
|
|
||||||
> Task :fineract-security:classes UP-TO-DATE
|
|
||||||
> Task :fineract-security:jar UP-TO-DATE
|
|
||||||
> Task :fineract-charge:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-charge:classes UP-TO-DATE
|
|
||||||
> Task :fineract-charge:jar UP-TO-DATE
|
|
||||||
> Task :fineract-accounting:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-accounting:classes UP-TO-DATE
|
|
||||||
> Task :fineract-accounting:jar UP-TO-DATE
|
|
||||||
> Task :fineract-branch:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-branch:classes UP-TO-DATE
|
|
||||||
> Task :fineract-loan:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-loan:classes UP-TO-DATE
|
|
||||||
> Task :fineract-branch:jar UP-TO-DATE
|
|
||||||
> Task :fineract-loan:jar UP-TO-DATE
|
|
||||||
> Task :fineract-savings:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-savings:classes UP-TO-DATE
|
|
||||||
> Task :fineract-savings:jar UP-TO-DATE
|
|
||||||
> Task :fineract-loan-origination:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-investor:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-progressive-loan:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-loan-origination:classes UP-TO-DATE
|
|
||||||
> Task :fineract-progressive-loan:classes UP-TO-DATE
|
|
||||||
> Task :fineract-investor:classes UP-TO-DATE
|
|
||||||
> Task :fineract-progressive-loan:jar UP-TO-DATE
|
|
||||||
> Task :fineract-loan-origination:jar UP-TO-DATE
|
|
||||||
> Task :fineract-investor:jar UP-TO-DATE
|
|
||||||
> Task :fineract-current-product:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-current-product:classes UP-TO-DATE
|
|
||||||
> Task :fineract-current-product:jar UP-TO-DATE
|
|
||||||
> Task :fineract-provider:generateGitProperties
|
|
||||||
> Task :fineract-provider:compileJava UP-TO-DATE
|
|
||||||
> Task :fineract-provider:generateRebelMain UP-TO-DATE
|
|
||||||
> Task :fineract-provider:processResources
|
|
||||||
> Task :fineract-provider:classes
|
|
||||||
> Task :fineract-provider:compileTestJava UP-TO-DATE
|
|
||||||
> Task :fineract-provider:prepareInputYaml UP-TO-DATE
|
|
||||||
> Task :fineract-provider:processTestResources UP-TO-DATE
|
|
||||||
> Task :fineract-provider:testClasses UP-TO-DATE
|
|
||||||
> Task :fineract-provider:resolve
|
|
||||||
> Task :fineract-provider:resolveMainClassName
|
|
||||||
|
|
||||||
> Task :fineract-provider:bootRun
|
|
||||||
19:17:05,132 |-WARN in ch.qos.logback.core.joran.action.ConversionRuleAction - [converterClass] attribute is deprecated and replaced by [class]. See element [conversionRule] near line 30
|
|
||||||
|
|
||||||
_ _ _____ _ _
|
|
||||||
/ \ _ __ __ _ ___| |__ ___ | ___(_)_ __ ___ _ __ __ _ ___| |_
|
|
||||||
/ _ \ | '_ \ / _` |/ __| '_ \ / _ \ | |_ | | '_ \ / _ \ '__/ _` |/ __| __|
|
|
||||||
/ ___ \| |_) | (_| | (__| | | | __/ | _| | | | | | __/ | | (_| | (__| |_
|
|
||||||
/_/ \_\ .__/ \__,_|\___|_| |_|\___| |_| |_|_| |_|\___|_| \__,_|\___|\__|
|
|
||||||
|_|
|
|
||||||
|
|
||||||
[2m[37m(c) 2015-2024 Apache Fineract (https://fineract.apache.org)[30m
|
|
||||||
|
|
||||||
[2m[37mPowered by Spring Boot 3.5.6[30m
|
|
||||||
|
|
||||||
|
|
||||||
[2m2026-05-29 19:17:05.618[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36morg.apache.fineract.ServerApplication [0;39m [2m:[0;39m Starting ServerApplication using Java 21.0.9 with PID 7760 (C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main started by adity in C:\Users\adity\Music\CBS\fineract\fineract-provider)
|
|
||||||
[2m2026-05-29 19:17:05.622[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36morg.apache.fineract.ServerApplication [0;39m [2m:[0;39m No active profile set, falling back to 1 default profile: "default"
|
|
||||||
[2m2026-05-29 19:17:10.836[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Multiple Spring Data modules found, entering strict repository configuration mode
|
|
||||||
[2m2026-05-29 19:17:10.836[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Bootstrapping Spring Data JPA repositories in DEFAULT mode.
|
|
||||||
[2m2026-05-29 19:17:11.794[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.DocumentRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
[2m2026-05-29 19:17:11.797[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.ImageRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
[2m2026-05-29 19:17:11.811[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixReportXBRLNamespaceRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
[2m2026-05-29 19:17:11.813[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyMappingRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
[2m2026-05-29 19:17:11.815[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
[2m2026-05-29 19:17:11.887[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.command.persistence.domain.CommandRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
|
||||||
[2m2026-05-29 19:17:13.120[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Finished Spring Data repository scanning in 2267 ms. Found 224 JPA repository interfaces.
|
|
||||||
[2m2026-05-29 19:17:13.130[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.f.s.DefaultListableBeanFactory [0;39m [2m:[0;39m Overriding bean definition for bean 'jpaAuditingHandler' with a different definition: replacing [Root bean: class=org.springframework.data.auditing.AuditingHandler; scope=; abstract=false; lazyInit=null; autowireMode=2; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=from; initMethodNames=null; destroyMethodNames=null] with [Root bean: class=org.apache.fineract.infrastructure.core.auditing.CustomAuditingHandler; scope=prototype; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null]
|
|
||||||
[2m2026-05-29 19:17:13.204[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.f.s.DefaultListableBeanFactory [0;39m [2m:[0;39m Overriding bean definition for bean 'transactionManager' with a different definition: replacing [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=JPAConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/core/config/jpa/JPAConfig.class]] with [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=scheduledJobRunnerConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/jobs/ScheduledJobRunnerConfig.class]]
|
|
||||||
[2m2026-05-29 19:17:13.206[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.c.annotation.BatchRegistrar [0;39m [2m:[0;39m Bean jobRepository already defined in the application context, skipping the registration of a jobRepository
|
|
||||||
[2m2026-05-29 19:17:13.206[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.c.annotation.BatchRegistrar [0;39m [2m:[0;39m Bean jobExplorer already defined in the application context, skipping the registration of a jobExplorer
|
|
||||||
[2m2026-05-29 19:17:13.206[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.c.annotation.BatchRegistrar [0;39m [2m:[0;39m Bean jobLauncher already defined in the application context, skipping the registration of a jobLauncher
|
|
||||||
[2m2026-05-29 19:17:13.208[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.c.annotation.BatchRegistrar [0;39m [2m:[0;39m Finished Spring Batch infrastructure beans configuration in 3 ms.
|
|
||||||
[2m2026-05-29 19:17:14.107[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Multiple Spring Data modules found, entering strict repository configuration mode
|
|
||||||
[2m2026-05-29 19:17:14.108[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Bootstrapping Spring Data JDBC repositories in DEFAULT mode.
|
|
||||||
[2m2026-05-29 19:17:14.408[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.provisioning.domain.ProvisioningEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.409[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.adhocquery.domain.AdHocRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.409[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.LoanAccountLockRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.bulkimport.domain.ImportDocumentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.ExternalServicesPropertiesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.412[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.412[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauLoanProductMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.412[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.413[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.413[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.OrganisationCreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.413[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.TokenRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.EntityDatatableChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.RegisteredDatatableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterUsageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityToEntityMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.416[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookTemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.416[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.imps.domain.ImpsTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.416[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.416[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.SchedulerDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntryAggregationTrackingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntrySummaryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.419[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.419[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.420[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.security.otp.domain.OtpLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.421[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.sms.domain.SmsMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.421[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.survey.domain.LikelihoodRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.421[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.interoperation.domain.InteropIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.421[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationMapperRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.422[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.422[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.holiday.domain.HolidayRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.422[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.423[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCategoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.423[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaDefinitionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.423[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.424[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.staff.domain.StaffRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.424[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.424[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountAssociationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.425[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.425[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.426[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.426[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.AddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.426[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.FieldConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.ckyc.domain.CkycBatchRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientAddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.428[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientFamilyMembersRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.428[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNonPersonRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.428[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.428[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransferDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.429[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.430[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.CollateralManagementDomainRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.430[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateral.domain.LoanCollateralRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.431[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.BankIfscRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.431[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.CustomerTransferLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.431[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.FundTransferEventRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.FundTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.TransferLimitConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.VerificationTokenRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fund.domain.FundRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupLevelRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.productmix.domain.ProductMixRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.attendance.domain.ClientAttendanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.domain.MeetingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.note.domain.NoteRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.rate.domain.RateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.435[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.438[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.438[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.438[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.account.domain.SelfBeneficiariesTPTRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.device.domain.DeviceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.registration.domain.SelfServiceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.441[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.441[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.LookupTableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.442[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.ScorecardRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.444[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.SurveyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.444[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.template.domain.TemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.445[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserPreviousPasswordRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.445[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PasswordValidationPolicyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PermissionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.RoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerLoanProductAttributesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.447[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.447[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.447[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.447[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.448[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.448[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.448[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.449[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.449[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.449[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAccrualActivityRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanApprovedAmountHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidByRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanStatusChangeHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgingParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.454[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.454[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.454[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.454[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlabRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FacilityChargeMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.BatchBusinessStepRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.closure.domain.GLClosureRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.459[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.459[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.rule.domain.AccountingRuleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.459[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.459[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.charge.domain.ChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.462[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxComponentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.462[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxGroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.462[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.security.domain.TFAccessTokenRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.462[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.security.domain.TwoFactorConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.464[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.GLAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.commands.domain.CommandSourceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.businessdate.domain.BusinessDateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.cache.domain.PlatformCacheRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeValueRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.GlobalConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.rbifeed.domain.RbiFeedItemRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.CfcfrmsSimulatorLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintAuditLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.468[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintTicketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.468[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientForm60Repository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.470[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.470[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNomineeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientEouRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientImportExportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMsmeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMudraBorrowerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientNonPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientStartupIndiaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.mobilelookup.domain.MobileLookupLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.473[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsScreeningLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.473[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.473[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.DscChallengeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.473[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.mfa.domain.UserMfaFactorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
|
||||||
[2m2026-05-29 19:17:14.768[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Finished Spring Data repository scanning in 657 ms. Found 6 JDBC repository interfaces.
|
|
||||||
[2m2026-05-29 19:17:16.886[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mfaultConfiguringBeanFactoryPostProcessor[0;39m [2m:[0;39m No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
|
|
||||||
[2m2026-05-29 19:17:16.895[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mfaultConfiguringBeanFactoryPostProcessor[0;39m [2m:[0;39m No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
|
|
||||||
[2m2026-05-29 19:17:18.104[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'fineract-org.apache.fineract.infrastructure.core.config.FineractProperties' of type [org.apache.fineract.infrastructure.core.config.FineractProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
|
||||||
[2m2026-05-29 19:17:18.117[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'cacheConfig' of type [org.apache.fineract.infrastructure.core.config.cache.CacheConfig$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
|
||||||
[2m2026-05-29 19:17:18.335[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36morg.reflections.Reflections [0;39m [2m:[0;39m Reflections took 7 ms to scan 1 urls, producing 0 keys and 0 values
|
|
||||||
[2m2026-05-29 19:17:18.339[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.a.f.i.core.config.cache.CacheConfig [0;39m [2m:[0;39m The following cache configurations are defined but cache does not exists: [userTFAccessToken]
|
|
||||||
[2m2026-05-29 19:17:18.343[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'ehCacheManager' of type [org.springframework.cache.jcache.JCacheCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
|
||||||
[2m2026-05-29 19:17:18.354[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'defaultCacheManager' of type [org.apache.fineract.infrastructure.core.config.cache.TransactionBoundCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
|
||||||
[2m2026-05-29 19:17:18.360[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'runtimeDelegatingCacheManager' of type [org.apache.fineract.infrastructure.cache.service.RuntimeDelegatingCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
|
||||||
[2m2026-05-29 19:17:18.364[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'platformCacheConfiguration' of type [org.apache.fineract.infrastructure.cache.PlatformCacheConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
|
||||||
[2m2026-05-29 19:17:18.407[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'samplingConfiguration' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
|
||||||
[2m2026-05-29 19:17:18.415[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'samplingServiceFactory' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingServiceFactory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
|
||||||
[2m2026-05-29 19:17:19.255[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.w.embedded.tomcat.TomcatWebServer [0;39m [2m:[0;39m Tomcat initialized with port 8443 (https)
|
|
||||||
[2m2026-05-29 19:17:19.278[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.apache.catalina.core.StandardService [0;39m [2m:[0;39m Starting service [Tomcat]
|
|
||||||
[2m2026-05-29 19:17:19.279[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.apache.catalina.core.StandardEngine [0;39m [2m:[0;39m Starting Servlet engine: [Apache Tomcat/10.1.49]
|
|
||||||
[2m2026-05-29 19:17:19.328[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.a.c.c.C.[.[.[/fineract-provider] [0;39m [2m:[0;39m Initializing Spring embedded WebApplicationContext
|
|
||||||
[2m2026-05-29 19:17:19.329[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mw.s.c.ServletWebServerApplicationContext[0;39m [2m:[0;39m Root WebApplicationContext: initialization completed in 13673 ms
|
|
||||||
[2m2026-05-29 19:17:19.577[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Starting...
|
|
||||||
[2m2026-05-29 19:17:19.860[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.pool.HikariPool [0;39m [2m:[0;39m HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@5917b984
|
|
||||||
[2m2026-05-29 19:17:19.861[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Start completed.
|
|
||||||
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Upgrading tenant store DB at localhost:5432
|
|
||||||
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.username: postgres
|
|
||||||
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.password: ****
|
|
||||||
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.parameters:
|
|
||||||
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.timezone: Asia/Kolkata
|
|
||||||
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.description: Default Demo Tenant
|
|
||||||
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.identifier: default
|
|
||||||
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.name: fineract_default
|
|
||||||
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - liquibase.analytics.enabled: false
|
|
||||||
[2m2026-05-29 19:17:25.487[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Tenant store upgrade finished
|
|
||||||
[2m2026-05-29 19:17:25.487[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Upgrading all tenants
|
|
||||||
[2m2026-05-29 19:17:25.501[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Upgrade for tenant default has started
|
|
||||||
[2m2026-05-29 19:17:25.842[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-2 - Starting...
|
|
||||||
[2m2026-05-29 19:17:25.946[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.pool.HikariPool [0;39m [2m:[0;39m HikariPool-2 - Added connection org.postgresql.jdbc.PgConnection@625805c1
|
|
||||||
[2m2026-05-29 19:17:25.946[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-2 - Start completed.
|
|
||||||
[2m2026-05-29 19:17:31.700[0;39m [31m-ERROR[0;39m [35m7760[0;39m [31m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mliquibase.changelog [0;39m [2m:[0;39m ChangeSet db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract encountered an exception.
|
|
||||||
|
|
||||||
liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
|
||||||
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
|
||||||
at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:520)
|
|
||||||
at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:85)
|
|
||||||
at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:188)
|
|
||||||
at liquibase.executor.AbstractExecutor.execute(AbstractExecutor.java:148)
|
|
||||||
at liquibase.database.AbstractJdbcDatabase.executeStatements(AbstractJdbcDatabase.java:1198)
|
|
||||||
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:816)
|
|
||||||
at liquibase.changelog.visitor.UpdateVisitor.executeAcceptedChange(UpdateVisitor.java:127)
|
|
||||||
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:71)
|
|
||||||
at liquibase.changelog.ChangeLogIterator.lambda$run$0(ChangeLogIterator.java:133)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.changelog.ChangeLogIterator.lambda$run$1(ChangeLogIterator.java:122)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.Scope.child(Scope.java:282)
|
|
||||||
at liquibase.Scope.child(Scope.java:286)
|
|
||||||
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:91)
|
|
||||||
at liquibase.command.core.AbstractUpdateCommandStep.lambda$run$0(AbstractUpdateCommandStep.java:114)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.command.core.AbstractUpdateCommandStep.run(AbstractUpdateCommandStep.java:112)
|
|
||||||
at liquibase.command.core.UpdateCommandStep.run(UpdateCommandStep.java:100)
|
|
||||||
at liquibase.command.CommandScope.lambda$execute$6(CommandScope.java:263)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:201)
|
|
||||||
at liquibase.command.CommandScope.execute(CommandScope.java:251)
|
|
||||||
at liquibase.Liquibase.lambda$update$0(Liquibase.java:216)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.Liquibase.runInScope(Liquibase.java:1366)
|
|
||||||
at liquibase.Liquibase.update(Liquibase.java:205)
|
|
||||||
at liquibase.Liquibase.update(Liquibase.java:188)
|
|
||||||
at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:327)
|
|
||||||
at liquibase.integration.spring.SpringLiquibase.lambda$afterPropertiesSet$0(SpringLiquibase.java:279)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:272)
|
|
||||||
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenant(TenantDatabaseUpgradeService.java:205)
|
|
||||||
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.lambda$upgradeIndividualTenants$1(TenantDatabaseUpgradeService.java:146)
|
|
||||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
|
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
|
|
||||||
at java.base/java.lang.Thread.run(Thread.java:1583)
|
|
||||||
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
|
||||||
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null).
|
|
||||||
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2736)
|
|
||||||
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2421)
|
|
||||||
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372)
|
|
||||||
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:525)
|
|
||||||
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:435)
|
|
||||||
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:357)
|
|
||||||
at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:342)
|
|
||||||
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:318)
|
|
||||||
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:313)
|
|
||||||
at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95)
|
|
||||||
at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java)
|
|
||||||
at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:512)
|
|
||||||
... 52 common frames omitted
|
|
||||||
|
|
||||||
[2m2026-05-29 19:17:31.748[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-2 - Shutdown initiated...
|
|
||||||
[2m2026-05-29 19:17:31.750[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-2 - Shutdown completed.
|
|
||||||
[2m2026-05-29 19:17:31.752[0;39m [31m-ERROR[0;39m [35m7760[0;39m [31m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Exception:
|
|
||||||
|
|
||||||
java.util.concurrent.ExecutionException: java.lang.RuntimeException: Exception while upgrading tenant default
|
|
||||||
at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
|
|
||||||
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
|
|
||||||
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenants(TenantDatabaseUpgradeService.java:155)
|
|
||||||
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.afterPropertiesSet(TenantDatabaseUpgradeService.java:93)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1873)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1822)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365)
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:691)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:513)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365)
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1725)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1474)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:606)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1698)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansWithAnnotation(DefaultListableBeanFactory.java:796)
|
|
||||||
at org.springframework.context.support.AbstractApplicationContext.getBeansWithAnnotation(AbstractApplicationContext.java:1440)
|
|
||||||
at org.apache.fineract.infrastructure.core.jersey.JerseyConfig.setup(JerseyConfig.java:58)
|
|
||||||
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
|
||||||
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
|
||||||
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMethod.invoke(InitDestroyAnnotationBeanPostProcessor.java:457)
|
|
||||||
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:401)
|
|
||||||
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:219)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:429)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1818)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:413)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
|
|
||||||
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:230)
|
|
||||||
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:221)
|
|
||||||
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:106)
|
|
||||||
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:95)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:271)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:245)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:52)
|
|
||||||
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4452)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201)
|
|
||||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191)
|
|
||||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
|
||||||
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81)
|
|
||||||
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
|
|
||||||
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747)
|
|
||||||
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:770)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201)
|
|
||||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191)
|
|
||||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
|
||||||
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81)
|
|
||||||
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
|
|
||||||
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747)
|
|
||||||
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:201)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:410)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:868)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:436)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:128)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:107)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:517)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:219)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:193)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:167)
|
|
||||||
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
|
|
||||||
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
|
|
||||||
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
|
|
||||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
|
|
||||||
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149)
|
|
||||||
at org.apache.fineract.ServerApplication.main(ServerApplication.java:57)
|
|
||||||
Caused by: java.lang.RuntimeException: Exception while upgrading tenant default
|
|
||||||
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenant(TenantDatabaseUpgradeService.java:208)
|
|
||||||
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.lambda$upgradeIndividualTenants$1(TenantDatabaseUpgradeService.java:146)
|
|
||||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
|
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
|
|
||||||
at java.base/java.lang.Thread.run(Thread.java:1583)
|
|
||||||
Caused by: liquibase.exception.LiquibaseException: liquibase.exception.CommandExecutionException: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract:
|
|
||||||
Reason: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
|
||||||
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
|
||||||
at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:289)
|
|
||||||
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenant(TenantDatabaseUpgradeService.java:205)
|
|
||||||
... 5 common frames omitted
|
|
||||||
Caused by: liquibase.exception.CommandExecutionException: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract:
|
|
||||||
Reason: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
|
||||||
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
|
||||||
at liquibase.command.CommandScope.lambda$execute$6(CommandScope.java:310)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:201)
|
|
||||||
at liquibase.command.CommandScope.execute(CommandScope.java:251)
|
|
||||||
at liquibase.Liquibase.lambda$update$0(Liquibase.java:216)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.Liquibase.runInScope(Liquibase.java:1366)
|
|
||||||
at liquibase.Liquibase.update(Liquibase.java:205)
|
|
||||||
at liquibase.Liquibase.update(Liquibase.java:188)
|
|
||||||
at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:327)
|
|
||||||
at liquibase.integration.spring.SpringLiquibase.lambda$afterPropertiesSet$0(SpringLiquibase.java:279)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:272)
|
|
||||||
... 6 common frames omitted
|
|
||||||
Caused by: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract:
|
|
||||||
Reason: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
|
||||||
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
|
||||||
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:155)
|
|
||||||
at liquibase.command.core.AbstractUpdateCommandStep.lambda$run$0(AbstractUpdateCommandStep.java:114)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.command.core.AbstractUpdateCommandStep.run(AbstractUpdateCommandStep.java:112)
|
|
||||||
at liquibase.command.core.UpdateCommandStep.run(UpdateCommandStep.java:100)
|
|
||||||
at liquibase.command.CommandScope.lambda$execute$6(CommandScope.java:263)
|
|
||||||
... 24 common frames omitted
|
|
||||||
Caused by: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract:
|
|
||||||
Reason: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
|
||||||
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
|
||||||
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:873)
|
|
||||||
at liquibase.changelog.visitor.UpdateVisitor.executeAcceptedChange(UpdateVisitor.java:127)
|
|
||||||
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:71)
|
|
||||||
at liquibase.changelog.ChangeLogIterator.lambda$run$0(ChangeLogIterator.java:133)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.changelog.ChangeLogIterator.lambda$run$1(ChangeLogIterator.java:122)
|
|
||||||
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
|
||||||
at liquibase.Scope.child(Scope.java:225)
|
|
||||||
at liquibase.Scope.child(Scope.java:215)
|
|
||||||
at liquibase.Scope.child(Scope.java:194)
|
|
||||||
at liquibase.Scope.child(Scope.java:282)
|
|
||||||
at liquibase.Scope.child(Scope.java:286)
|
|
||||||
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:91)
|
|
||||||
... 32 common frames omitted
|
|
||||||
Caused by: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
|
||||||
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
|
||||||
at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:520)
|
|
||||||
at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:85)
|
|
||||||
at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:188)
|
|
||||||
at liquibase.executor.AbstractExecutor.execute(AbstractExecutor.java:148)
|
|
||||||
at liquibase.database.AbstractJdbcDatabase.executeStatements(AbstractJdbcDatabase.java:1198)
|
|
||||||
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:816)
|
|
||||||
... 47 common frames omitted
|
|
||||||
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
|
||||||
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null).
|
|
||||||
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2736)
|
|
||||||
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2421)
|
|
||||||
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372)
|
|
||||||
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:525)
|
|
||||||
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:435)
|
|
||||||
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:357)
|
|
||||||
at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:342)
|
|
||||||
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:318)
|
|
||||||
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:313)
|
|
||||||
at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95)
|
|
||||||
at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java)
|
|
||||||
at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:512)
|
|
||||||
... 52 common frames omitted
|
|
||||||
|
|
||||||
[2m2026-05-29 19:17:31.761[0;39m [31m-ERROR[0;39m [35m7760[0;39m [31m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.web.embedded.tomcat.TomcatStarter [0;39m [2m:[0;39m Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'jerseyConfig': Invocation of init method failed
|
|
||||||
[2m2026-05-29 19:17:31.812[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.apache.catalina.core.StandardService [0;39m [2m:[0;39m Stopping service [Tomcat]
|
|
||||||
[2m2026-05-29 19:17:31.817[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.a.c.loader.WebappClassLoaderBase [0;39m [2m:[0;39m The web application [fineract-provider] appears to have started a thread named [HikariPool-1:housekeeper] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
|
|
||||||
java.base/jdk.internal.misc.Unsafe.park(Native Method)
|
|
||||||
java.base/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:269)
|
|
||||||
java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1797)
|
|
||||||
java.base/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1182)
|
|
||||||
java.base/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899)
|
|
||||||
java.base/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1070)
|
|
||||||
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
|
|
||||||
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
|
|
||||||
java.base/java.lang.Thread.run(Thread.java:1583)
|
|
||||||
[2m2026-05-29 19:17:31.820[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mConfigServletWebServerApplicationContext[0;39m [2m:[0;39m Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server
|
|
||||||
[2m2026-05-29 19:17:31.820[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Shutdown initiated...
|
|
||||||
[2m2026-05-29 19:17:31.822[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Shutdown completed.
|
|
||||||
[2m2026-05-29 19:17:31.847[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.b.a.l.ConditionEvaluationReportLogger[0;39m [2m:[0;39m
|
|
||||||
|
|
||||||
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
|
|
||||||
[2m2026-05-29 19:17:31.875[0;39m [31m-ERROR[0;39m [35m7760[0;39m [31m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.boot.SpringApplication [0;39m [2m:[0;39m Application run failed
|
|
||||||
|
|
||||||
org.springframework.context.ApplicationContextException: Unable to start web server
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:170)
|
|
||||||
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
|
|
||||||
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
|
|
||||||
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
|
|
||||||
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
|
|
||||||
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149)
|
|
||||||
at org.apache.fineract.ServerApplication.main(ServerApplication.java:57)
|
|
||||||
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:147)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:107)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:517)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:219)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:193)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:167)
|
|
||||||
... 7 common frames omitted
|
|
||||||
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'jerseyConfig': Invocation of init method failed
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:413)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
|
|
||||||
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:230)
|
|
||||||
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:221)
|
|
||||||
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:106)
|
|
||||||
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:95)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:271)
|
|
||||||
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:245)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:52)
|
|
||||||
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4452)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201)
|
|
||||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191)
|
|
||||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
|
||||||
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81)
|
|
||||||
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
|
|
||||||
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747)
|
|
||||||
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:770)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201)
|
|
||||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191)
|
|
||||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
|
||||||
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81)
|
|
||||||
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
|
|
||||||
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747)
|
|
||||||
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:201)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:410)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:868)
|
|
||||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
|
||||||
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:436)
|
|
||||||
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:128)
|
|
||||||
... 12 common frames omitted
|
|
||||||
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jerseyConfig': Invocation of init method failed
|
|
||||||
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:222)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:429)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1818)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
|
||||||
... 61 common frames omitted
|
|
||||||
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'journalEntriesApiResource' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\accounting\journalentry\api\JournalEntriesApiResource.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'springSecurityPlatformSecurityContext' defined in URL [jar:file:/C:/Users/adity/Music/CBS/fineract/fineract-security/build/libs/fineract-security-1.15.0-SNAPSHOT.jar!/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'configurationDomainServiceJpa' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\configuration\domain\ConfigurationDomainServiceJpa.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager'
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansWithAnnotation(DefaultListableBeanFactory.java:796)
|
|
||||||
at org.springframework.context.support.AbstractApplicationContext.getBeansWithAnnotation(AbstractApplicationContext.java:1440)
|
|
||||||
at org.apache.fineract.infrastructure.core.jersey.JerseyConfig.setup(JerseyConfig.java:58)
|
|
||||||
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
|
||||||
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
|
||||||
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMethod.invoke(InitDestroyAnnotationBeanPostProcessor.java:457)
|
|
||||||
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:401)
|
|
||||||
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:219)
|
|
||||||
... 74 common frames omitted
|
|
||||||
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'springSecurityPlatformSecurityContext' defined in URL [jar:file:/C:/Users/adity/Music/CBS/fineract/fineract-security/build/libs/fineract-security-1.15.0-SNAPSHOT.jar!/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'configurationDomainServiceJpa' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\configuration\domain\ConfigurationDomainServiceJpa.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager'
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
|
||||||
... 91 common frames omitted
|
|
||||||
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configurationDomainServiceJpa' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\configuration\domain\ConfigurationDomainServiceJpa.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager'
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
|
||||||
... 105 common frames omitted
|
|
||||||
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager'
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:377)
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1725)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1474)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:606)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1698)
|
|
||||||
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
|
||||||
... 119 common frames omitted
|
|
||||||
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaSharedEM_entityManagerFactory': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:377)
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:691)
|
|
||||||
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:513)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365)
|
|
||||||
... 132 common frames omitted
|
|
||||||
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tenantDatabaseUpgradeService' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\core\service\migration\TenantDatabaseUpgradeService.class]: Tenant upgrades had exceptions
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1826)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
|
||||||
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
|
|
||||||
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
|
||||||
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365)
|
|
||||||
... 144 common frames omitted
|
|
||||||
Caused by: java.lang.RuntimeException: Tenant upgrades had exceptions
|
|
||||||
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenants(TenantDatabaseUpgradeService.java:167)
|
|
||||||
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.afterPropertiesSet(TenantDatabaseUpgradeService.java:93)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1873)
|
|
||||||
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1822)
|
|
||||||
... 153 common frames omitted
|
|
||||||
|
|
||||||
|
|
||||||
> Task :fineract-provider:bootRun FAILED
|
|
||||||
|
|
||||||
[Incubating] Problems report is available at: file:///C:/Users/adity/Music/CBS/fineract/build/reports/problems/problems-report.html
|
|
||||||
|
|
||||||
FAILURE: Build failed with an exception.
|
|
||||||
|
|
||||||
* What went wrong:
|
|
||||||
Execution failed for task ':fineract-provider:bootRun'.
|
|
||||||
> Process 'command 'C:\Program Files\Eclipse Adoptium\jdk-21.0.9.10-hotspot\bin\java.exe'' finished with non-zero exit value 1
|
|
||||||
|
|
||||||
* Try:
|
|
||||||
> Run with --stacktrace option to get the stack trace.
|
|
||||||
> Run with --info or --debug option to get more log output.
|
|
||||||
> Get more help at https://help.gradle.org.
|
|
||||||
|
|
||||||
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
|
|
||||||
|
|
||||||
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
|
|
||||||
|
|
||||||
For more on this, please refer to https://docs.gradle.org/8.14.3/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
|
|
||||||
|
|
||||||
BUILD FAILED in 1m 30s
|
|
||||||
83 actionable tasks: 5 executed, 78 up-to-date
|
|
||||||
Resource usage collection shutdown not awaited
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
> Task :buildSrc:extractPluginRequests UP-TO-DATE
|
|
||||||
> Task :buildSrc:generatePluginAdapters UP-TO-DATE
|
|
||||||
> Task :buildSrc:compileJava UP-TO-DATE
|
|
||||||
> Task :buildSrc:compileGroovy UP-TO-DATE
|
|
||||||
> Task :buildSrc:compileGroovyPlugins UP-TO-DATE
|
|
||||||
> Task :buildSrc:pluginDescriptors UP-TO-DATE
|
|
||||||
> Task :buildSrc:processResources UP-TO-DATE
|
|
||||||
> Task :buildSrc:classes UP-TO-DATE
|
|
||||||
> Task :buildSrc:jar UP-TO-DATE
|
|
||||||
|
|
||||||
> Configure project :
|
|
||||||
matching ref: COMMIT - d7821c7824067a96d31c4c031ea538fc5e4107fc
|
|
||||||
ref configuration: COMMIT - pattern: null
|
|
||||||
version: ${describe.tag.version.major}.${describe.tag.version.minor.next}.0-SNAPSHOT
|
|
||||||
describeTagPattern: .*(\d+\.\d+\.\d+).*
|
|
||||||
describeTagFirstParent: false
|
|
||||||
|
|
||||||
project version: 0.1.0-SNAPSHOT
|
|
||||||
Binary file not shown.
+40
-6
@@ -101,6 +101,13 @@ public class CashierDepositRequestApiResource {
|
|||||||
final String bankName = fromJsonHelper.extractStringNamed("bankName", root);
|
final String bankName = fromJsonHelper.extractStringNamed("bankName", root);
|
||||||
final String txnDate = fromJsonHelper.extractStringNamed("txnDate", root);
|
final String txnDate = fromJsonHelper.extractStringNamed("txnDate", root);
|
||||||
final String denominationData = fromJsonHelper.extractStringNamed("denominationData", root);
|
final String denominationData = fromJsonHelper.extractStringNamed("denominationData", root);
|
||||||
|
// DD + Cash + GL breakdown from Transaction Entry - null for ordinary deposit requests.
|
||||||
|
// Persisted so the Sponsor DD issuance screen can read the breakdown back instead of
|
||||||
|
// retyping face value/charges once this request is approved and selected as cash evidence.
|
||||||
|
final BigDecimal ddFaceValue = parseBigDecimal(fromJsonHelper.extractStringNamed("ddFaceValue", root));
|
||||||
|
final BigDecimal ddCharges = parseBigDecimal(fromJsonHelper.extractStringNamed("ddCharges", root));
|
||||||
|
final BigDecimal ddGstPercentage = parseBigDecimal(fromJsonHelper.extractStringNamed("ddGstPercentage", root));
|
||||||
|
final BigDecimal ddGstAmount = parseBigDecimal(fromJsonHelper.extractStringNamed("ddGstAmount", root));
|
||||||
|
|
||||||
if (accountNo == null || accountNo.isBlank()) {
|
if (accountNo == null || accountNo.isBlank()) {
|
||||||
throw apiError(400, "Account number is required");
|
throw apiError(400, "Account number is required");
|
||||||
@@ -115,6 +122,7 @@ public class CashierDepositRequestApiResource {
|
|||||||
throw apiError(400, "Amount must be greater than zero");
|
throw apiError(400, "Amount must be greater than zero");
|
||||||
}
|
}
|
||||||
validateDenominationTotal(denominationData, amount);
|
validateDenominationTotal(denominationData, amount);
|
||||||
|
validateDdLimit(ddFaceValue);
|
||||||
|
|
||||||
final String activityLog = buildActivityLog(null, "SUBMITTED", currentUser.getUsername(), null);
|
final String activityLog = buildActivityLog(null, "SUBMITTED", currentUser.getUsername(), null);
|
||||||
|
|
||||||
@@ -123,8 +131,9 @@ public class CashierDepositRequestApiResource {
|
|||||||
(account_no, account_type, savings_account_id, gl_account_id, contra_gl_id, office_id, currency_code,
|
(account_no, account_type, savings_account_id, gl_account_id, contra_gl_id, office_id, currency_code,
|
||||||
client_id, client_name, amount,
|
client_id, client_name, amount,
|
||||||
payment_type_id, payment_type_name, description, cheque_no, bank_name, txn_date,
|
payment_type_id, payment_type_name, description, cheque_no, bank_name, txn_date,
|
||||||
status, cashier_id, cashier_name, cashier_username, denomination_data, activity_log, submitted_at)
|
status, cashier_id, cashier_name, cashier_username, denomination_data, activity_log, submitted_at,
|
||||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
dd_face_value, dd_charges, dd_gst_percentage, dd_gst_amount)
|
||||||
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||||
RETURNING id
|
RETURNING id
|
||||||
""",
|
""",
|
||||||
Long.class,
|
Long.class,
|
||||||
@@ -135,7 +144,8 @@ public class CashierDepositRequestApiResource {
|
|||||||
txnDate != null ? Date.valueOf(txnDate) : null,
|
txnDate != null ? Date.valueOf(txnDate) : null,
|
||||||
"PENDING",
|
"PENDING",
|
||||||
currentUser.getId(), currentUser.getDisplayName(), currentUser.getUsername(),
|
currentUser.getId(), currentUser.getDisplayName(), currentUser.getUsername(),
|
||||||
denominationData, activityLog, Timestamp.from(Instant.now())
|
denominationData, activityLog, Timestamp.from(Instant.now()),
|
||||||
|
ddFaceValue, ddCharges, ddGstPercentage, ddGstAmount
|
||||||
);
|
);
|
||||||
|
|
||||||
// Globally unique voucher number from the central m_voucher register
|
// Globally unique voucher number from the central m_voucher register
|
||||||
@@ -189,7 +199,8 @@ public class CashierDepositRequestApiResource {
|
|||||||
+ " amount, payment_type_id, payment_type_name, description, cheque_no, bank_name,"
|
+ " amount, payment_type_id, payment_type_name, description, cheque_no, bank_name,"
|
||||||
+ " txn_date, status, cashier_id, cashier_name, cashier_username,"
|
+ " txn_date, status, cashier_id, cashier_name, cashier_username,"
|
||||||
+ " manager_id, manager_name, manager_username, rejection_reason, txn_id,"
|
+ " manager_id, manager_name, manager_username, rejection_reason, txn_id,"
|
||||||
+ " denomination_data, activity_log, submitted_at, actioned_at"
|
+ " denomination_data, activity_log, submitted_at, actioned_at,"
|
||||||
|
+ " dd_face_value, dd_charges, dd_gst_percentage, dd_gst_amount"
|
||||||
+ " FROM m_cashier_deposit_request " + where
|
+ " FROM m_cashier_deposit_request " + where
|
||||||
+ " ORDER BY submitted_at DESC LIMIT ? OFFSET ?",
|
+ " ORDER BY submitted_at DESC LIMIT ? OFFSET ?",
|
||||||
pageParams.toArray());
|
pageParams.toArray());
|
||||||
@@ -242,7 +253,8 @@ public class CashierDepositRequestApiResource {
|
|||||||
SELECT id, voucher_no, account_no, account_type, client_name, amount,
|
SELECT id, voucher_no, account_no, account_type, client_name, amount,
|
||||||
payment_type_name, description, txn_date, status,
|
payment_type_name, description, txn_date, status,
|
||||||
cashier_name, manager_name, manager_username, rejection_reason, txn_id,
|
cashier_name, manager_name, manager_username, rejection_reason, txn_id,
|
||||||
submitted_at, actioned_at
|
submitted_at, actioned_at,
|
||||||
|
dd_face_value, dd_charges, dd_gst_percentage, dd_gst_amount
|
||||||
FROM m_cashier_deposit_request
|
FROM m_cashier_deposit_request
|
||||||
""" + where + " ORDER BY submitted_at DESC LIMIT ? OFFSET ?",
|
""" + where + " ORDER BY submitted_at DESC LIMIT ? OFFSET ?",
|
||||||
pageParams.toArray()
|
pageParams.toArray()
|
||||||
@@ -424,6 +436,7 @@ public class CashierDepositRequestApiResource {
|
|||||||
final JsonObject res = new JsonObject();
|
final JsonObject res = new JsonObject();
|
||||||
res.addProperty("resourceId", id);
|
res.addProperty("resourceId", id);
|
||||||
res.addProperty("status", "APPROVED");
|
res.addProperty("status", "APPROVED");
|
||||||
|
res.addProperty("voucherNo", Optional.ofNullable(record.get("voucher_no")).map(Object::toString).orElse(""));
|
||||||
if (txnId != null) res.addProperty("txnId", txnId);
|
if (txnId != null) res.addProperty("txnId", txnId);
|
||||||
return gson.toJson(res);
|
return gson.toJson(res);
|
||||||
}
|
}
|
||||||
@@ -532,7 +545,8 @@ public class CashierDepositRequestApiResource {
|
|||||||
amount, payment_type_id, payment_type_name, description, cheque_no, bank_name,
|
amount, payment_type_id, payment_type_name, description, cheque_no, bank_name,
|
||||||
txn_date, status, cashier_id, cashier_name, cashier_username,
|
txn_date, status, cashier_id, cashier_name, cashier_username,
|
||||||
manager_id, manager_name, manager_username, rejection_reason, txn_id,
|
manager_id, manager_name, manager_username, rejection_reason, txn_id,
|
||||||
denomination_data, activity_log, submitted_at, actioned_at
|
denomination_data, activity_log, submitted_at, actioned_at,
|
||||||
|
dd_face_value, dd_charges, dd_gst_percentage, dd_gst_amount
|
||||||
FROM m_cashier_deposit_request WHERE id = ?
|
FROM m_cashier_deposit_request WHERE id = ?
|
||||||
""", id);
|
""", id);
|
||||||
if (rows.isEmpty()) {
|
if (rows.isEmpty()) {
|
||||||
@@ -869,6 +883,26 @@ public class CashierDepositRequestApiResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DD + Cash + GL deposit requests carry a Face Value (ddFaceValue) - reject the cash intake
|
||||||
|
* itself, before the cashier even accepts the money, if it exceeds the admin-configured DD
|
||||||
|
* limit (m_sponsor_dd_gl_mapping.dd_limit_amount). The same limit is enforced again later at
|
||||||
|
* actual DD issuance (sponsordd module); this is a first line of defense so an over-limit
|
||||||
|
* amount never even gets accepted as cash. ddFaceValue is null for ordinary (non-DD) deposit
|
||||||
|
* requests, which this check skips entirely. Null/zero-or-negative limit means unconfigured.
|
||||||
|
*/
|
||||||
|
private void validateDdLimit(final BigDecimal ddFaceValue) {
|
||||||
|
if (ddFaceValue == null) return;
|
||||||
|
final List<Map<String, Object>> rows = jdbcTemplate.queryForList(
|
||||||
|
"SELECT dd_limit_amount FROM m_sponsor_dd_gl_mapping LIMIT 1");
|
||||||
|
if (rows.isEmpty()) return;
|
||||||
|
final BigDecimal ddLimitAmount = (BigDecimal) rows.get(0).get("dd_limit_amount");
|
||||||
|
if (ddLimitAmount == null || ddLimitAmount.compareTo(BigDecimal.ZERO) <= 0) return;
|
||||||
|
if (ddFaceValue.compareTo(ddLimitAmount) > 0) {
|
||||||
|
throw apiError(400, "DD Face Value ₹" + ddFaceValue + " exceeds the configured DD limit of ₹" + ddLimitAmount + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checker authorization: only manager-level users may approve or reject.
|
* Checker authorization: only manager-level users may approve or reject.
|
||||||
* Allowed: super users (ALL_FUNCTIONS), or any role whose name contains
|
* Allowed: super users (ALL_FUNCTIONS), or any role whose name contains
|
||||||
|
|||||||
+14
-1
@@ -44,6 +44,8 @@ import org.apache.fineract.portfolio.client.domain.LegalForm;
|
|||||||
import org.apache.fineract.portfolio.client.exception.ClientNotFoundException;
|
import org.apache.fineract.portfolio.client.exception.ClientNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -62,6 +64,7 @@ public class ClientNomineeWritePlatformServiceImpl implements ClientNomineeWrite
|
|||||||
private final ClientNomineeRepository nomineeRepository;
|
private final ClientNomineeRepository nomineeRepository;
|
||||||
private final CodeValueRepositoryWrapper codeValueRepository;
|
private final CodeValueRepositoryWrapper codeValueRepository;
|
||||||
private final SavingsAccountRepository savingsAccountRepository;
|
private final SavingsAccountRepository savingsAccountRepository;
|
||||||
|
private final CurrentAccountRepository currentAccountRepository;
|
||||||
private final LoanRepository loanRepository;
|
private final LoanRepository loanRepository;
|
||||||
private final ClientNonPersonRepositoryWrapper clientNonPersonRepository;
|
private final ClientNonPersonRepositoryWrapper clientNonPersonRepository;
|
||||||
|
|
||||||
@@ -299,8 +302,18 @@ public class ClientNomineeWritePlatformServiceImpl implements ClientNomineeWrite
|
|||||||
throw new PlatformDataIntegrityException("error.msg.nominee.loan.account.not.found",
|
throw new PlatformDataIntegrityException("error.msg.nominee.loan.account.not.found",
|
||||||
"Loan account '" + accountNo + "' not found for client " + clientId + ".", "accountNo");
|
"Loan account '" + accountNo + "' not found for client " + clientId + ".", "accountNo");
|
||||||
}
|
}
|
||||||
|
} else if ("CB".equals(accountType)) {
|
||||||
|
final CurrentAccount ca = this.currentAccountRepository.findByAccountNo(accountNo).orElse(null);
|
||||||
|
if (ca == null) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.nominee.current.account.not.found",
|
||||||
|
"Account '" + accountNo + "' not found.", "accountNo");
|
||||||
|
}
|
||||||
|
if (ca.getClient() == null || !ca.getClient().getId().equals(clientId)) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.nominee.account.does.not.belong.to.client",
|
||||||
|
"Account '" + accountNo + "' does not belong to client " + clientId + ".", "accountNo");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// SB, CB, FD, RD are all savings accounts with different deposit_type_enum
|
// SB, FD, RD are all savings accounts with different deposit_type_enum
|
||||||
final SavingsAccount sa = savingsAccountRepository.findSavingsAccountByAccountNumber(accountNo);
|
final SavingsAccount sa = savingsAccountRepository.findSavingsAccountByAccountNumber(accountNo);
|
||||||
if (sa == null) {
|
if (sa == null) {
|
||||||
throw new PlatformDataIntegrityException("error.msg.nominee.savings.account.not.found",
|
throw new PlatformDataIntegrityException("error.msg.nominee.savings.account.not.found",
|
||||||
|
|||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* 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.customersearch.api;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.WebApplicationException;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.portfolio.customersearch.data.CustomerSearchResultData;
|
||||||
|
import org.apache.fineract.portfolio.customersearch.service.CustomerSearchReadPlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/customer-search")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Customer Search", description = "Multi-field customer lookup by name, account number (savings/current/FD/RD), Aadhaar, PAN, and/or address.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CustomerSearchApiResource {
|
||||||
|
|
||||||
|
private final CustomerSearchReadPlatformService readPlatformService;
|
||||||
|
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Search for a customer", description = "AND-combines whichever of name/accountNumber/aadhaarNumber/panNumber/address/accountTypes are supplied - at least one is required. accountTypes is a comma-separated list (SAVINGS,CURRENT,FIXED_DEPOSIT,RECURRING_DEPOSIT).")
|
||||||
|
public Collection<CustomerSearchResultData> search(@QueryParam("name") final String name,
|
||||||
|
@QueryParam("accountNumber") final String accountNumber, @QueryParam("aadhaarNumber") final String aadhaarNumber,
|
||||||
|
@QueryParam("panNumber") final String panNumber, @QueryParam("address") final String address,
|
||||||
|
@QueryParam("accountTypes") final String accountTypes) {
|
||||||
|
final List<String> accountTypeList = accountTypes == null || accountTypes.isBlank() ? List.of()
|
||||||
|
: Arrays.asList(accountTypes.split(","));
|
||||||
|
return safely(() -> this.readPlatformService.search(name, accountNumber, aadhaarNumber, panNumber, address, accountTypeList));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Surfaces IllegalArgumentException as a clean 400 instead of an opaque 500. */
|
||||||
|
private <T> T safely(final Supplier<T> action) {
|
||||||
|
try {
|
||||||
|
return action.get();
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
final JsonObject body = new JsonObject();
|
||||||
|
body.addProperty("message", e.getMessage());
|
||||||
|
body.addProperty("defaultUserMessage", e.getMessage());
|
||||||
|
body.addProperty("httpStatus", 400);
|
||||||
|
final JsonArray errors = new JsonArray();
|
||||||
|
final JsonObject err = new JsonObject();
|
||||||
|
err.addProperty("defaultUserMessage", e.getMessage());
|
||||||
|
errors.add(err);
|
||||||
|
body.add("errors", errors);
|
||||||
|
throw new WebApplicationException(
|
||||||
|
Response.status(400).entity(this.gson.toJson(body)).type(MediaType.APPLICATION_JSON).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* 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.customersearch.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One matching (client, account) combination from a multi-field customer search. A client with
|
||||||
|
* several accounts appears once per matching account; a client with no accounts at all still
|
||||||
|
* appears once, with accountNo/accountType left null.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CustomerSearchResultData implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long clientId;
|
||||||
|
private String displayName;
|
||||||
|
private String mobileNo;
|
||||||
|
private String officeName;
|
||||||
|
private String accountNo;
|
||||||
|
/** SAVINGS, CURRENT, FIXED_DEPOSIT, or RECURRING_DEPOSIT. */
|
||||||
|
private String accountType;
|
||||||
|
private String panNumber;
|
||||||
|
private String aadhaarNumber;
|
||||||
|
private String address;
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* 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.customersearch.service;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.fineract.portfolio.customersearch.data.CustomerSearchResultData;
|
||||||
|
|
||||||
|
public interface CustomerSearchReadPlatformService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multi-field AND search - only non-blank parameters are applied. At least one must be
|
||||||
|
* supplied. name/address are partial, case-insensitive matches; accountNumber/aadhaarNumber/
|
||||||
|
* panNumber are exact matches (they're precise identifiers, not free text). accountTypes, if
|
||||||
|
* non-empty, restricts results to those account types only (SAVINGS/CURRENT/FIXED_DEPOSIT/
|
||||||
|
* RECURRING_DEPOSIT) - a client with no account, or only accounts of other types, is excluded
|
||||||
|
* entirely when this filter is active (unlike the unfiltered case, where a client with no
|
||||||
|
* account at all still appears once for informational purposes).
|
||||||
|
*/
|
||||||
|
Collection<CustomerSearchResultData> search(String name, String accountNumber, String aadhaarNumber, String panNumber,
|
||||||
|
String address, List<String> accountTypes);
|
||||||
|
}
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* 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.customersearch.service;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.portfolio.customersearch.data.CustomerSearchResultData;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CustomerSearchReadPlatformServiceImpl implements CustomerSearchReadPlatformService {
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
private static final Set<String> VALID_ACCOUNT_TYPES = Set.of("SAVINGS", "CURRENT", "FIXED_DEPOSIT", "RECURRING_DEPOSIT");
|
||||||
|
|
||||||
|
private static final String BASE_SELECT = """
|
||||||
|
SELECT DISTINCT c.id AS client_id, c.display_name, c.mobile_no, o.name AS office_name,
|
||||||
|
acct.account_no, acct.account_type,
|
||||||
|
pan.document_key AS pan_number, aad.document_key AS aadhaar_number,
|
||||||
|
addr.address_line_1, addr.address_line_2, addr.town_village, addr.city,
|
||||||
|
addr.county_district, addr.postal_code
|
||||||
|
FROM m_client c
|
||||||
|
LEFT JOIN m_office o ON o.id = c.office_id
|
||||||
|
LEFT JOIN m_client_identifier pan ON pan.client_id = c.id AND pan.document_type_id = (
|
||||||
|
SELECT id FROM m_code_value WHERE code_value = 'PAN Card'
|
||||||
|
AND code_id = (SELECT id FROM m_code WHERE code_name = 'KYCIdProofType'))
|
||||||
|
LEFT JOIN m_client_identifier aad ON aad.client_id = c.id AND aad.document_type_id = (
|
||||||
|
SELECT id FROM m_code_value WHERE code_value = 'Aadhaar Card'
|
||||||
|
AND code_id = (SELECT id FROM m_code WHERE code_name = 'KYCIdProofType'))
|
||||||
|
LEFT JOIN m_address addr ON addr.client_id = c.id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT client_id, account_no,
|
||||||
|
CASE deposit_type_enum WHEN 100 THEN 'SAVINGS' WHEN 200 THEN 'FIXED_DEPOSIT'
|
||||||
|
WHEN 300 THEN 'RECURRING_DEPOSIT' ELSE 'SAVINGS' END AS account_type
|
||||||
|
FROM m_savings_account
|
||||||
|
UNION ALL
|
||||||
|
SELECT client_id, account_no, 'CURRENT' AS account_type FROM m_current_account
|
||||||
|
) acct ON acct.client_id = c.id
|
||||||
|
""";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<CustomerSearchResultData> search(final String name, final String accountNumber, final String aadhaarNumber,
|
||||||
|
final String panNumber, final String address, final List<String> accountTypes) {
|
||||||
|
final boolean hasName = name != null && !name.isBlank();
|
||||||
|
final boolean hasAccountNumber = accountNumber != null && !accountNumber.isBlank();
|
||||||
|
final boolean hasAadhaar = aadhaarNumber != null && !aadhaarNumber.isBlank();
|
||||||
|
final boolean hasPan = panNumber != null && !panNumber.isBlank();
|
||||||
|
final boolean hasAddress = address != null && !address.isBlank();
|
||||||
|
final List<String> effectiveAccountTypes = accountTypes == null ? List.of()
|
||||||
|
: accountTypes.stream().filter(t -> t != null && !t.isBlank()).map(String::trim).map(String::toUpperCase).toList();
|
||||||
|
|
||||||
|
if (!hasName && !hasAccountNumber && !hasAadhaar && !hasPan && !hasAddress && effectiveAccountTypes.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Enter at least one search field - name, account number, Aadhaar, PAN, or address.");
|
||||||
|
}
|
||||||
|
for (final String type : effectiveAccountTypes) {
|
||||||
|
if (!VALID_ACCOUNT_TYPES.contains(type)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unknown account type: " + type + ". Valid values: SAVINGS, CURRENT, FIXED_DEPOSIT, RECURRING_DEPOSIT.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder sql = new StringBuilder(BASE_SELECT).append(" WHERE 1=1 ");
|
||||||
|
final List<Object> params = new ArrayList<>();
|
||||||
|
|
||||||
|
if (hasName) {
|
||||||
|
sql.append(" AND LOWER(c.display_name) LIKE LOWER(?) ");
|
||||||
|
params.add("%" + name.trim() + "%");
|
||||||
|
}
|
||||||
|
if (hasAccountNumber) {
|
||||||
|
sql.append(" AND acct.account_no = ? ");
|
||||||
|
params.add(accountNumber.trim());
|
||||||
|
}
|
||||||
|
if (hasAadhaar) {
|
||||||
|
sql.append(" AND aad.document_key = ? ");
|
||||||
|
params.add(aadhaarNumber.trim());
|
||||||
|
}
|
||||||
|
if (hasPan) {
|
||||||
|
sql.append(" AND UPPER(pan.document_key) = UPPER(?) ");
|
||||||
|
params.add(panNumber.trim());
|
||||||
|
}
|
||||||
|
if (hasAddress) {
|
||||||
|
sql.append(" AND (LOWER(addr.address_line_1) LIKE LOWER(?) OR LOWER(addr.address_line_2) LIKE LOWER(?) "
|
||||||
|
+ "OR LOWER(addr.town_village) LIKE LOWER(?) OR LOWER(addr.city) LIKE LOWER(?) "
|
||||||
|
+ "OR LOWER(addr.county_district) LIKE LOWER(?) OR LOWER(addr.postal_code) LIKE LOWER(?)) ");
|
||||||
|
final String likeAddress = "%" + address.trim() + "%";
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
params.add(likeAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!effectiveAccountTypes.isEmpty()) {
|
||||||
|
// Restricting to specific account types excludes both non-matching-type accounts AND
|
||||||
|
// clients with no account at all (acct.account_type would be NULL for those, which can
|
||||||
|
// never equal one of the listed types) - that's the point of the filter.
|
||||||
|
final String placeholders = String.join(",", effectiveAccountTypes.stream().map(t -> "?").toList());
|
||||||
|
sql.append(" AND acct.account_type IN (").append(placeholders).append(") ");
|
||||||
|
params.addAll(effectiveAccountTypes);
|
||||||
|
}
|
||||||
|
sql.append(" ORDER BY c.display_name ASC LIMIT 100");
|
||||||
|
|
||||||
|
return this.jdbcTemplate.query(sql.toString(), new CustomerSearchResultMapper(), params.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CustomerSearchResultMapper implements RowMapper<CustomerSearchResultData> {
|
||||||
|
@Override
|
||||||
|
public CustomerSearchResultData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||||
|
final Long clientId = rs.getLong("client_id");
|
||||||
|
final String displayName = rs.getString("display_name");
|
||||||
|
final String mobileNo = rs.getString("mobile_no");
|
||||||
|
final String officeName = rs.getString("office_name");
|
||||||
|
final String accountNo = rs.getString("account_no");
|
||||||
|
final String accountType = rs.getString("account_type");
|
||||||
|
final String panNumber = rs.getString("pan_number");
|
||||||
|
final String aadhaarNumber = rs.getString("aadhaar_number");
|
||||||
|
|
||||||
|
final List<String> addressParts = new ArrayList<>();
|
||||||
|
addPart(addressParts, rs.getString("address_line_1"));
|
||||||
|
addPart(addressParts, rs.getString("address_line_2"));
|
||||||
|
addPart(addressParts, rs.getString("town_village"));
|
||||||
|
addPart(addressParts, rs.getString("city"));
|
||||||
|
addPart(addressParts, rs.getString("county_district"));
|
||||||
|
addPart(addressParts, rs.getString("postal_code"));
|
||||||
|
final String address = addressParts.isEmpty() ? null : String.join(", ", addressParts);
|
||||||
|
|
||||||
|
return new CustomerSearchResultData(clientId, displayName, mobileNo, officeName, accountNo, accountType, panNumber,
|
||||||
|
aadhaarNumber, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPart(final List<String> parts, final String value) {
|
||||||
|
if (value != null && !value.isBlank()) {
|
||||||
|
parts.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+109
@@ -39,23 +39,29 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.data.CurrentAccountPendingEditData;
|
||||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
||||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountEditWritePlatformService;
|
||||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||||
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
|
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -76,6 +82,9 @@ public class CurrentAccountsApiResource {
|
|||||||
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||||
private final CurrentAccountReadPlatformService currentAccountReadPlatformService;
|
private final CurrentAccountReadPlatformService currentAccountReadPlatformService;
|
||||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
private final CurrentAccountEditWritePlatformService currentAccountEditWritePlatformService;
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
private final DefaultToApiJsonSerializer<CurrentAccountPendingEditData> pendingEditApiJsonSerializer;
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("template")
|
@Path("template")
|
||||||
@@ -229,4 +238,104 @@ public class CurrentAccountsApiResource {
|
|||||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{accountId}/edit")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Submit a Nominee/Digital-Feature edit for a Current Account",
|
||||||
|
description = "Edits are held in m_current_account_pending_edit (a pending row there is itself the lock) and only "
|
||||||
|
+ "applied to the real account once a checker approves - see /currentaccounts/{accountId}/edit/approve and /reject below.")
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK")
|
||||||
|
public String submitEdit(@PathParam("accountId") final Long accountId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasPermissionTo("SUBMITEDIT_CURRENTACCOUNT");
|
||||||
|
|
||||||
|
final JsonCommand command = JsonCommand.fromJsonElement(accountId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||||
|
this.fromApiJsonHelper);
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.currentAccountEditWritePlatformService.submitEditForApproval(accountId, command);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("edit/pending")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "List Current Account Nominee/Digital-Feature edits pending approval")
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK")
|
||||||
|
public String retrieveAllPendingEdits(@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||||
|
|
||||||
|
final Collection<CurrentAccountPendingEditData> pendingEdits = this.currentAccountEditWritePlatformService
|
||||||
|
.retrieveAllPendingEdits();
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
|
return this.pendingEditApiJsonSerializer.serialize(settings, pendingEdits);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{accountId}/edit/pending")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve the pending Nominee/Digital-Feature edit for a Current Account, if any")
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK")
|
||||||
|
public String retrievePendingEdit(@PathParam("accountId") final Long accountId, @Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||||
|
|
||||||
|
final CurrentAccountPendingEditData pendingEdit = this.currentAccountEditWritePlatformService.retrievePendingEdit(accountId);
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
|
return this.pendingEditApiJsonSerializer.serialize(settings, pendingEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{accountId}/edit/approve")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Approve a pending Current Account Nominee/Digital-Feature edit",
|
||||||
|
description = "Releases the submission-time hold, posts any newly-enabled feature charges, and applies the "
|
||||||
|
+ "Nominee/Digital-Feature changes to the account. The maker of the edit cannot also approve it.")
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK")
|
||||||
|
public String approvePendingEdit(@PathParam("accountId") final Long accountId) {
|
||||||
|
|
||||||
|
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVEEDIT_CURRENTACCOUNT")) {
|
||||||
|
throw new NoAuthorizationException("User has no authority to approve current account edits");
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.currentAccountEditWritePlatformService.approveEdit(accountId);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{accountId}/edit/reject")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Reject a pending Current Account Nominee/Digital-Feature edit",
|
||||||
|
description = "Releases the submission-time hold without applying any change. The maker of the edit cannot also reject it.")
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK")
|
||||||
|
public String rejectPendingEdit(@PathParam("accountId") final Long accountId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECTEDIT_CURRENTACCOUNT")) {
|
||||||
|
throw new NoAuthorizationException("User has no authority to reject current account edits");
|
||||||
|
}
|
||||||
|
|
||||||
|
String rejectReason = null;
|
||||||
|
if (apiRequestBodyAsJson != null && !apiRequestBodyAsJson.isBlank()) {
|
||||||
|
final var parsed = this.fromApiJsonHelper.parse(apiRequestBodyAsJson);
|
||||||
|
if (parsed != null && parsed.isJsonObject() && parsed.getAsJsonObject().has("rejectReason")) {
|
||||||
|
rejectReason = parsed.getAsJsonObject().get("rejectReason").getAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.currentAccountEditWritePlatformService.rejectEdit(accountId, rejectReason);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+104
@@ -58,8 +58,11 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
|||||||
import org.apache.fineract.infrastructure.core.data.UploadRequest;
|
import org.apache.fineract.infrastructure.core.data.UploadRequest;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||||
|
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
import org.apache.fineract.infrastructure.core.service.Page;
|
import org.apache.fineract.infrastructure.core.service.Page;
|
||||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||||
@@ -69,9 +72,11 @@ import org.apache.fineract.portfolio.savings.DepositAccountType;
|
|||||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||||
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountPendingEditData;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||||
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
|
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
|
||||||
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountEditWritePlatformService;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformService;
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformService;
|
||||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||||
@@ -95,6 +100,9 @@ public class SavingsAccountsApiResource {
|
|||||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||||
private final SqlValidator sqlValidator;
|
private final SqlValidator sqlValidator;
|
||||||
|
private final SavingsAccountEditWritePlatformService savingsAccountEditWritePlatformService;
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
private final DefaultToApiJsonSerializer<SavingsAccountPendingEditData> pendingEditApiJsonSerializer;
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("template")
|
@Path("template")
|
||||||
@@ -413,6 +421,102 @@ public class SavingsAccountsApiResource {
|
|||||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{accountId}/edit")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Submit a Nominee/Digital-Feature edit for a Savings Account",
|
||||||
|
description = "Edits are held in m_savings_account_pending_edit (a pending row there is itself the lock) and only "
|
||||||
|
+ "applied to the real account once a checker approves - see /savingsaccounts/{accountId}/edit/approve and /reject below.")
|
||||||
|
public String submitEdit(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasPermissionTo("SUBMITEDIT_SAVINGSACCOUNT");
|
||||||
|
|
||||||
|
final JsonCommand command = JsonCommand.fromJsonElement(accountId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||||
|
this.fromApiJsonHelper);
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.savingsAccountEditWritePlatformService.submitEditForApproval(accountId, command);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("edit/pending")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "List Savings Account Nominee/Digital-Feature edits pending approval")
|
||||||
|
public String retrieveAllPendingEdits(@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
|
||||||
|
|
||||||
|
final Collection<SavingsAccountPendingEditData> pendingEdits = this.savingsAccountEditWritePlatformService
|
||||||
|
.retrieveAllPendingEdits();
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
|
return this.pendingEditApiJsonSerializer.serialize(settings, pendingEdits);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{accountId}/edit/pending")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve the pending Nominee/Digital-Feature edit for a Savings Account, if any")
|
||||||
|
public String retrievePendingEdit(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||||
|
@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
|
||||||
|
|
||||||
|
final SavingsAccountPendingEditData pendingEdit = this.savingsAccountEditWritePlatformService.retrievePendingEdit(accountId);
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
|
return this.pendingEditApiJsonSerializer.serialize(settings, pendingEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{accountId}/edit/approve")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Approve a pending Savings Account Nominee/Digital-Feature edit",
|
||||||
|
description = "Releases the submission-time hold, posts any newly-enabled feature charges, and applies the "
|
||||||
|
+ "Nominee/Digital-Feature changes to the account. The maker of the edit cannot also approve it.")
|
||||||
|
public String approvePendingEdit(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId) {
|
||||||
|
|
||||||
|
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVEEDIT_SAVINGSACCOUNT")) {
|
||||||
|
throw new NoAuthorizationException("User has no authority to approve savings account edits");
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.savingsAccountEditWritePlatformService.approveEdit(accountId);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{accountId}/edit/reject")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Reject a pending Savings Account Nominee/Digital-Feature edit",
|
||||||
|
description = "Releases the submission-time hold without applying any change. The maker of the edit cannot also reject it.")
|
||||||
|
public String rejectPendingEdit(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECTEDIT_SAVINGSACCOUNT")) {
|
||||||
|
throw new NoAuthorizationException("User has no authority to reject savings account edits");
|
||||||
|
}
|
||||||
|
|
||||||
|
String rejectReason = null;
|
||||||
|
if (apiRequestBodyAsJson != null && !apiRequestBodyAsJson.isBlank()) {
|
||||||
|
final var parsed = this.fromApiJsonHelper.parse(apiRequestBodyAsJson);
|
||||||
|
if (parsed != null && parsed.isJsonObject() && parsed.getAsJsonObject().has("rejectReason")) {
|
||||||
|
rejectReason = parsed.getAsJsonObject().get("rejectReason").getAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.savingsAccountEditWritePlatformService.rejectEdit(accountId, rejectReason);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("{accountId}")
|
@Path("{accountId}")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
|||||||
+569
@@ -0,0 +1,569 @@
|
|||||||
|
/**
|
||||||
|
* 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.JsonObject;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.commands.domain.CommandProcessingResultType;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSource;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||||
|
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.core.serialization.FromJsonHelper;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.client.service.ClientNomineeWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.data.CurrentAccountPendingEditData;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountFacilityConfig;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountFacilityConfigRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountPendingEdit;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountPendingEditRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountEditInsufficientBalanceException;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountEditPendingApprovalException;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountEditSelfApprovalNotAllowedException;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNoPendingEditException;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountEditWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maker-checker edit flow for an ACTIVE current account's Nominee + Digital & Account Features. Mirrors
|
||||||
|
* {@code SavingsAccountEditWritePlatformServiceJpaRepositoryImpl}'s submitEditForApproval/approveEdit/rejectEdit
|
||||||
|
* pattern: a row in {@code m_current_account_pending_edit} is itself the lock, and nothing is applied to the real
|
||||||
|
* {@code m_current_account_facility_config} / {@code m_client_nominee} tables until a checker approves.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Charge handling for a newly-enabled facility (one with a mapped charge in m_facility_charge_mapping): at
|
||||||
|
* submission a HOLD is placed for the total charge amount (blocking that much of the available balance without
|
||||||
|
* touching it); on approval the hold is released and the charge is posted for real (via
|
||||||
|
* {@link CurrentAccountWritePlatformService#applyAndCollectFacilityCharges(Long, Set)}); on rejection the hold is
|
||||||
|
* simply released.
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CurrentAccountEditWritePlatformServiceJpaRepositoryImpl implements CurrentAccountEditWritePlatformService {
|
||||||
|
|
||||||
|
/** field name (as sent in accountFacilityConfig JSON / stored on the pending edit) -> facility_type code. */
|
||||||
|
private static final Map<String, String> FACILITY_TYPES = new LinkedHashMap<>();
|
||||||
|
static {
|
||||||
|
FACILITY_TYPES.put("atmFacility", "ATM_FACILITY");
|
||||||
|
FACILITY_TYPES.put("atmWithdrawalFacility", "ATM_WITHDRAWAL");
|
||||||
|
FACILITY_TYPES.put("upiFacility", "UPI_FACILITY");
|
||||||
|
FACILITY_TYPES.put("posTransactionsFacility", "POS_TRANSACTIONS");
|
||||||
|
FACILITY_TYPES.put("chequeBook", "CHEQUE_BOOK");
|
||||||
|
FACILITY_TYPES.put("impsFacility", "IMPS_FACILITY");
|
||||||
|
FACILITY_TYPES.put("smsAlerts", "SMS_ALERTS");
|
||||||
|
FACILITY_TYPES.put("bbpsFacility", "BBPS_FACILITY");
|
||||||
|
FACILITY_TYPES.put("insuranceCover", "INSURANCE_COVER");
|
||||||
|
FACILITY_TYPES.put("bepgFacility", "BEPG_FACILITY");
|
||||||
|
FACILITY_TYPES.put("neftFacility", "NEFT_FACILITY");
|
||||||
|
FACILITY_TYPES.put("rtgsFacility", "RTGS_FACILITY");
|
||||||
|
FACILITY_TYPES.put("interBankTotalCapFacility", "INTER_BANK_TOTAL_CAP");
|
||||||
|
FACILITY_TYPES.put("withdrawalFacility", "WITHDRAWAL_FACILITY");
|
||||||
|
FACILITY_TYPES.put("depositFacilityKyc", "DEPOSIT_FACILITY_KYC");
|
||||||
|
FACILITY_TYPES.put("virtualDebitCard", "VIRTUAL_DEBIT_CARD");
|
||||||
|
FACILITY_TYPES.put("internetBanking", "INTERNET_BANKING");
|
||||||
|
FACILITY_TYPES.put("mobileBanking", "MOBILE_BANKING");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final CurrentAccountRepository currentAccountRepository;
|
||||||
|
private final CurrentAccountPendingEditRepository pendingEditRepository;
|
||||||
|
private final CurrentAccountFacilityConfigRepository facilityConfigRepository;
|
||||||
|
private final org.apache.fineract.portfolio.savings.domain.FacilityChargeMappingRepository facilityChargeMappingRepository;
|
||||||
|
private final CurrentAccountTransactionWritePlatformService currentAccountTransactionWritePlatformService;
|
||||||
|
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||||
|
private final ClientNomineeWritePlatformService clientNomineeWritePlatformService;
|
||||||
|
private final CommandSourceRepository commandSourceRepository;
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult submitEditForApproval(final Long currentAccountId, final JsonCommand command) {
|
||||||
|
|
||||||
|
final AppUser maker = this.context.authenticatedUser();
|
||||||
|
final CurrentAccount account = this.currentAccountRepository.findById(currentAccountId)
|
||||||
|
.orElseThrow(() -> new CurrentAccountNotFoundException(currentAccountId));
|
||||||
|
|
||||||
|
if (this.pendingEditRepository.findByCurrentAccountIdAndStatus(currentAccountId, CurrentAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.isPresent()) {
|
||||||
|
throw new CurrentAccountEditPendingApprovalException(currentAccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonObject root = command.parsedJson().getAsJsonObject();
|
||||||
|
final CurrentAccountFacilityConfig currentConfig = account.getFacilityConfig();
|
||||||
|
|
||||||
|
final CurrentAccountPendingEdit pendingEdit = CurrentAccountPendingEdit.snapshotOf(account, maker);
|
||||||
|
|
||||||
|
// ── Digital & Account Features ──
|
||||||
|
final Set<String> newlyEnabledFacilityTypes = new HashSet<>();
|
||||||
|
if (root.has("accountFacilityConfig") && root.get("accountFacilityConfig").isJsonObject()) {
|
||||||
|
final JsonObject fc = root.getAsJsonObject("accountFacilityConfig");
|
||||||
|
pendingEdit.setFacilityConfigSubmitted(true);
|
||||||
|
populateFacilityField(fc, "atmFacility", currentConfig != null && currentConfig.isAtmFacility(), pendingEdit::setAtmFacility,
|
||||||
|
newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "atmWithdrawalFacility", currentConfig != null && currentConfig.isAtmWithdrawalFacility(),
|
||||||
|
pendingEdit::setAtmWithdrawalFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "upiFacility", currentConfig != null && currentConfig.isUpiFacility(), pendingEdit::setUpiFacility,
|
||||||
|
newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "posTransactionsFacility", currentConfig != null && currentConfig.isPosTransactionsFacility(),
|
||||||
|
pendingEdit::setPosTransactionsFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "chequeBook", currentConfig != null && currentConfig.isChequeBook(), pendingEdit::setChequeBook,
|
||||||
|
newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "impsFacility", currentConfig != null && currentConfig.isImpsFacility(),
|
||||||
|
pendingEdit::setImpsFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "smsAlerts", currentConfig != null && currentConfig.isSmsAlerts(), pendingEdit::setSmsAlerts,
|
||||||
|
newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "bbpsFacility", currentConfig != null && currentConfig.isBbpsFacility(),
|
||||||
|
pendingEdit::setBbpsFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "insuranceCover", currentConfig != null && currentConfig.isInsuranceCover(),
|
||||||
|
pendingEdit::setInsuranceCover, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "bepgFacility", currentConfig != null && currentConfig.isBepgFacility(),
|
||||||
|
pendingEdit::setBepgFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "neftFacility", currentConfig != null && currentConfig.isNeftFacility(),
|
||||||
|
pendingEdit::setNeftFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "rtgsFacility", currentConfig != null && currentConfig.isRtgsFacility(),
|
||||||
|
pendingEdit::setRtgsFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "interBankTotalCapFacility", currentConfig != null && currentConfig.isInterBankTotalCapFacility(),
|
||||||
|
pendingEdit::setInterBankTotalCapFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "withdrawalFacility", currentConfig != null && currentConfig.isWithdrawalFacility(),
|
||||||
|
pendingEdit::setWithdrawalFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "depositFacilityKyc", currentConfig != null && currentConfig.isDepositFacilityKyc(),
|
||||||
|
pendingEdit::setDepositFacilityKyc, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "virtualDebitCard", currentConfig != null && currentConfig.isVirtualDebitCard(),
|
||||||
|
pendingEdit::setVirtualDebitCard, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "internetBanking", currentConfig != null && currentConfig.isInternetBanking(),
|
||||||
|
pendingEdit::setInternetBanking, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "mobileBanking", currentConfig != null && currentConfig.isMobileBanking(),
|
||||||
|
pendingEdit::setMobileBanking, newlyEnabledFacilityTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Nominee (single add / update / delete per request) ──
|
||||||
|
if (root.has("nominee") && root.get("nominee").isJsonObject()) {
|
||||||
|
final JsonObject nom = root.getAsJsonObject("nominee");
|
||||||
|
pendingEdit.setNomineeSubmitted(true);
|
||||||
|
pendingEdit.setNomineeOperation(nom.has("operation") ? nom.get("operation").getAsString() : null);
|
||||||
|
pendingEdit.setNomineeId(nom.has("nomineeId") && !nom.get("nomineeId").isJsonNull() ? nom.get("nomineeId").getAsLong() : null);
|
||||||
|
pendingEdit.setNomineeClientId(
|
||||||
|
nom.has("nomineeClientId") && !nom.get("nomineeClientId").isJsonNull() ? nom.get("nomineeClientId").getAsLong()
|
||||||
|
: null);
|
||||||
|
pendingEdit.setRelationshipCvId(
|
||||||
|
nom.has("relationshipId") && !nom.get("relationshipId").isJsonNull() ? nom.get("relationshipId").getAsLong() : null);
|
||||||
|
pendingEdit.setPercentageShare(
|
||||||
|
nom.has("percentageShare") && !nom.get("percentageShare").isJsonNull() ? nom.get("percentageShare").getAsBigDecimal()
|
||||||
|
: null);
|
||||||
|
pendingEdit.setNomineeIsActive(
|
||||||
|
nom.has("isActive") && !nom.get("isActive").isJsonNull() ? nom.get("isActive").getAsBoolean() : null);
|
||||||
|
pendingEdit.setNomineeIsMinor(
|
||||||
|
nom.has("nomineeIsMinor") && !nom.get("nomineeIsMinor").isJsonNull() ? nom.get("nomineeIsMinor").getAsBoolean()
|
||||||
|
: null);
|
||||||
|
pendingEdit.setGuardianClientId(
|
||||||
|
nom.has("guardianClientId") && !nom.get("guardianClientId").isJsonNull() ? nom.get("guardianClientId").getAsLong()
|
||||||
|
: null);
|
||||||
|
pendingEdit.setGuardianRelationshipCvId(
|
||||||
|
nom.has("guardianRelationshipId") && !nom.get("guardianRelationshipId").isJsonNull()
|
||||||
|
? nom.get("guardianRelationshipId").getAsLong()
|
||||||
|
: null);
|
||||||
|
pendingEdit.setEffectiveDate(
|
||||||
|
nom.has("effectiveDate") && !nom.get("effectiveDate").isJsonNull() ? LocalDate.parse(nom.get("effectiveDate").getAsString())
|
||||||
|
: null);
|
||||||
|
pendingEdit.setNomineeRemarks(nom.has("remarks") && !nom.get("remarks").isJsonNull() ? nom.get("remarks").getAsString() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Hold the charge amount for newly-enabled facilities before anything is persisted ──
|
||||||
|
final BigDecimal holdNeeded = totalChargeAmountFor(newlyEnabledFacilityTypes);
|
||||||
|
if (holdNeeded.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
final BigDecimal currentBalance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived()
|
||||||
|
: BigDecimal.ZERO;
|
||||||
|
final BigDecimal onHold = account.getOnHoldFundsDerived() != null ? account.getOnHoldFundsDerived() : BigDecimal.ZERO;
|
||||||
|
final BigDecimal availableBalance = currentBalance.subtract(onHold);
|
||||||
|
if (availableBalance.compareTo(holdNeeded) < 0) {
|
||||||
|
throw new CurrentAccountEditInsufficientBalanceException(currentAccountId, availableBalance, holdNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonObject holdJson = new JsonObject();
|
||||||
|
holdJson.addProperty("locale", "en");
|
||||||
|
holdJson.addProperty("dateFormat", "yyyy-MM-dd");
|
||||||
|
holdJson.addProperty("transactionDate", DateUtils.getBusinessLocalDate().toString());
|
||||||
|
holdJson.addProperty("transactionAmount", holdNeeded);
|
||||||
|
holdJson.addProperty("reasonForBlock", "Pending approval of Nominee/Digital-Feature edit");
|
||||||
|
final JsonCommand holdCommand = JsonCommand.fromJsonElement(currentAccountId, holdJson, this.fromApiJsonHelper);
|
||||||
|
|
||||||
|
final CommandProcessingResult holdResult = this.currentAccountTransactionWritePlatformService.holdAmount(currentAccountId,
|
||||||
|
holdCommand);
|
||||||
|
pendingEdit.setHoldTransactionId(holdResult.getResourceId());
|
||||||
|
pendingEdit.setHoldAmount(holdNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandSource auditEntry = CommandSource.builder() //
|
||||||
|
.actionName("SUBMITEDIT") //
|
||||||
|
.entityName("CURRENTACCOUNT") //
|
||||||
|
.resourceGetUrl("/currentaccounts/" + currentAccountId) //
|
||||||
|
.resourceId(currentAccountId) //
|
||||||
|
.commandAsJson(command.json()) //
|
||||||
|
.maker(maker) //
|
||||||
|
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||||
|
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||||
|
.idempotencyKey(UUID.randomUUID().toString()) //
|
||||||
|
.sanitized(false) //
|
||||||
|
.build();
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||||
|
|
||||||
|
this.pendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(currentAccountId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult approveEdit(final Long currentAccountId) {
|
||||||
|
|
||||||
|
final AppUser checker = this.context.authenticatedUser();
|
||||||
|
final CurrentAccountPendingEdit pendingEdit = this.pendingEditRepository
|
||||||
|
.findByCurrentAccountIdAndStatus(currentAccountId, CurrentAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.orElseThrow(() -> new CurrentAccountNoPendingEditException(currentAccountId));
|
||||||
|
|
||||||
|
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||||
|
throw new CurrentAccountEditSelfApprovalNotAllowedException(currentAccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final CurrentAccount account = this.currentAccountRepository.findById(currentAccountId)
|
||||||
|
.orElseThrow(() -> new CurrentAccountNotFoundException(currentAccountId));
|
||||||
|
final CurrentAccountFacilityConfig currentConfig = account.getFacilityConfig();
|
||||||
|
final Set<String> newlyEnabledFacilityTypes = pendingEdit.isFacilityConfigSubmitted()
|
||||||
|
? computeNewlyEnabled(pendingEdit, currentConfig)
|
||||||
|
: Set.of();
|
||||||
|
|
||||||
|
// 1) Release the hold placed at submission time.
|
||||||
|
if (pendingEdit.getHoldTransactionId() != null) {
|
||||||
|
this.currentAccountTransactionWritePlatformService.releaseAmount(currentAccountId, pendingEdit.getHoldTransactionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Post the real charge(s) for newly-enabled facilities, today.
|
||||||
|
if (!newlyEnabledFacilityTypes.isEmpty()) {
|
||||||
|
this.currentAccountWritePlatformService.applyAndCollectFacilityCharges(currentAccountId, newlyEnabledFacilityTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Apply the facility-config toggles to the real row.
|
||||||
|
if (pendingEdit.isFacilityConfigSubmitted()) {
|
||||||
|
CurrentAccountFacilityConfig config = account.getFacilityConfig();
|
||||||
|
if (config == null) {
|
||||||
|
config = CurrentAccountFacilityConfig.createFor(account);
|
||||||
|
account.setFacilityConfig(config);
|
||||||
|
}
|
||||||
|
config.updateFrom(buildFacilityConfigJson(pendingEdit));
|
||||||
|
this.facilityConfigRepository.saveAndFlush(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) Apply the nominee change to the real table.
|
||||||
|
if (pendingEdit.isNomineeSubmitted()) {
|
||||||
|
applyNomineeChange(account, pendingEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentAccountRepository.saveAndFlush(account);
|
||||||
|
|
||||||
|
pendingEdit.markApproved(checker);
|
||||||
|
this.pendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
updateAuditEntry(pendingEdit, checker, false);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(currentAccountId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult rejectEdit(final Long currentAccountId, final String rejectReason) {
|
||||||
|
|
||||||
|
final AppUser checker = this.context.authenticatedUser();
|
||||||
|
final CurrentAccountPendingEdit pendingEdit = this.pendingEditRepository
|
||||||
|
.findByCurrentAccountIdAndStatus(currentAccountId, CurrentAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.orElseThrow(() -> new CurrentAccountNoPendingEditException(currentAccountId));
|
||||||
|
|
||||||
|
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||||
|
throw new CurrentAccountEditSelfApprovalNotAllowedException(currentAccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pendingEdit.getHoldTransactionId() != null) {
|
||||||
|
this.currentAccountTransactionWritePlatformService.releaseAmount(currentAccountId, pendingEdit.getHoldTransactionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingEdit.markRejected(checker, rejectReason);
|
||||||
|
this.pendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
updateAuditEntry(pendingEdit, checker, true);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(currentAccountId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CurrentAccountPendingEditData retrievePendingEdit(final Long currentAccountId) {
|
||||||
|
return this.pendingEditRepository.findByCurrentAccountIdAndStatus(currentAccountId, CurrentAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.map(this::toData).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.util.Collection<CurrentAccountPendingEditData> retrieveAllPendingEdits() {
|
||||||
|
return this.pendingEditRepository.findByStatus(CurrentAccountPendingEdit.STATUS_PENDING).stream().map(this::toData).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sum of the mapped charge amounts for the given facility types - used to size the hold placed at
|
||||||
|
* edit-submission time, before any charge is actually applied.
|
||||||
|
*/
|
||||||
|
private BigDecimal totalChargeAmountFor(final Set<String> facilityTypes) {
|
||||||
|
BigDecimal total = BigDecimal.ZERO;
|
||||||
|
for (final String facilityType : facilityTypes) {
|
||||||
|
final var mapping = this.facilityChargeMappingRepository.findByFacilityType(facilityType);
|
||||||
|
if (mapping.isPresent() && mapping.get().getCharge() != null) {
|
||||||
|
total = total.add(mapping.get().getCharge().getAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyNomineeChange(final CurrentAccount account, final CurrentAccountPendingEdit pendingEdit) {
|
||||||
|
final Long clientId = account.getClient() != null ? account.getClient().getId() : null;
|
||||||
|
final String operation = pendingEdit.getNomineeOperation();
|
||||||
|
|
||||||
|
if (CurrentAccountPendingEdit.NOMINEE_OP_DELETE.equals(operation)) {
|
||||||
|
this.clientNomineeWritePlatformService.deleteNominee(clientId, pendingEdit.getNomineeId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonObject nomineeJson = new JsonObject();
|
||||||
|
nomineeJson.addProperty("locale", "en");
|
||||||
|
nomineeJson.addProperty("dateFormat", "yyyy-MM-dd");
|
||||||
|
nomineeJson.addProperty("accountNo", account.getAccountNo());
|
||||||
|
nomineeJson.addProperty("accountType", "CB");
|
||||||
|
if (pendingEdit.getNomineeClientId() != null) {
|
||||||
|
nomineeJson.addProperty("nomineeClientId", pendingEdit.getNomineeClientId());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getRelationshipCvId() != null) {
|
||||||
|
nomineeJson.addProperty("relationshipId", pendingEdit.getRelationshipCvId());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getPercentageShare() != null) {
|
||||||
|
nomineeJson.addProperty("percentageShare", pendingEdit.getPercentageShare());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getNomineeIsActive() != null) {
|
||||||
|
nomineeJson.addProperty("isActive", pendingEdit.getNomineeIsActive());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getNomineeIsMinor() != null) {
|
||||||
|
nomineeJson.addProperty("nomineeIsMinor", pendingEdit.getNomineeIsMinor());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getGuardianClientId() != null) {
|
||||||
|
nomineeJson.addProperty("guardianClientId", pendingEdit.getGuardianClientId());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getGuardianRelationshipCvId() != null) {
|
||||||
|
nomineeJson.addProperty("guardianRelationshipId", pendingEdit.getGuardianRelationshipCvId());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getEffectiveDate() != null) {
|
||||||
|
nomineeJson.addProperty("effectiveDate", pendingEdit.getEffectiveDate().toString());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getNomineeRemarks() != null) {
|
||||||
|
nomineeJson.addProperty("remarks", pendingEdit.getNomineeRemarks());
|
||||||
|
}
|
||||||
|
final JsonCommand nomineeCommand = JsonCommand.fromJsonElement(clientId, nomineeJson, this.fromApiJsonHelper);
|
||||||
|
|
||||||
|
if (CurrentAccountPendingEdit.NOMINEE_OP_ADD.equals(operation)) {
|
||||||
|
this.clientNomineeWritePlatformService.addNominee(clientId, nomineeCommand);
|
||||||
|
} else if (CurrentAccountPendingEdit.NOMINEE_OP_UPDATE.equals(operation)) {
|
||||||
|
this.clientNomineeWritePlatformService.updateNominee(clientId, pendingEdit.getNomineeId(), nomineeCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonObject buildFacilityConfigJson(final CurrentAccountPendingEdit p) {
|
||||||
|
final JsonObject fc = new JsonObject();
|
||||||
|
putIfPresent(fc, "atmFacility", p.getAtmFacility());
|
||||||
|
putIfPresent(fc, "atmWithdrawalFacility", p.getAtmWithdrawalFacility());
|
||||||
|
putIfPresent(fc, "upiFacility", p.getUpiFacility());
|
||||||
|
putIfPresent(fc, "posTransactionsFacility", p.getPosTransactionsFacility());
|
||||||
|
putIfPresent(fc, "chequeBook", p.getChequeBook());
|
||||||
|
putIfPresent(fc, "impsFacility", p.getImpsFacility());
|
||||||
|
putIfPresent(fc, "smsAlerts", p.getSmsAlerts());
|
||||||
|
putIfPresent(fc, "bbpsFacility", p.getBbpsFacility());
|
||||||
|
putIfPresent(fc, "insuranceCover", p.getInsuranceCover());
|
||||||
|
putIfPresent(fc, "bepgFacility", p.getBepgFacility());
|
||||||
|
putIfPresent(fc, "neftFacility", p.getNeftFacility());
|
||||||
|
putIfPresent(fc, "rtgsFacility", p.getRtgsFacility());
|
||||||
|
putIfPresent(fc, "interBankTotalCapFacility", p.getInterBankTotalCapFacility());
|
||||||
|
putIfPresent(fc, "withdrawalFacility", p.getWithdrawalFacility());
|
||||||
|
putIfPresent(fc, "depositFacilityKyc", p.getDepositFacilityKyc());
|
||||||
|
putIfPresent(fc, "virtualDebitCard", p.getVirtualDebitCard());
|
||||||
|
putIfPresent(fc, "internetBanking", p.getInternetBanking());
|
||||||
|
putIfPresent(fc, "mobileBanking", p.getMobileBanking());
|
||||||
|
return fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putIfPresent(final JsonObject json, final String field, final Boolean value) {
|
||||||
|
if (value != null) {
|
||||||
|
json.addProperty(field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> computeNewlyEnabled(final CurrentAccountPendingEdit p, final CurrentAccountFacilityConfig current) {
|
||||||
|
final Set<String> result = new HashSet<>();
|
||||||
|
addIfNewlyEnabled(result, "atmFacility", p.getAtmFacility(), current != null && current.isAtmFacility());
|
||||||
|
addIfNewlyEnabled(result, "atmWithdrawalFacility", p.getAtmWithdrawalFacility(), current != null && current.isAtmWithdrawalFacility());
|
||||||
|
addIfNewlyEnabled(result, "upiFacility", p.getUpiFacility(), current != null && current.isUpiFacility());
|
||||||
|
addIfNewlyEnabled(result, "posTransactionsFacility", p.getPosTransactionsFacility(),
|
||||||
|
current != null && current.isPosTransactionsFacility());
|
||||||
|
addIfNewlyEnabled(result, "chequeBook", p.getChequeBook(), current != null && current.isChequeBook());
|
||||||
|
addIfNewlyEnabled(result, "impsFacility", p.getImpsFacility(), current != null && current.isImpsFacility());
|
||||||
|
addIfNewlyEnabled(result, "smsAlerts", p.getSmsAlerts(), current != null && current.isSmsAlerts());
|
||||||
|
addIfNewlyEnabled(result, "bbpsFacility", p.getBbpsFacility(), current != null && current.isBbpsFacility());
|
||||||
|
addIfNewlyEnabled(result, "insuranceCover", p.getInsuranceCover(), current != null && current.isInsuranceCover());
|
||||||
|
addIfNewlyEnabled(result, "bepgFacility", p.getBepgFacility(), current != null && current.isBepgFacility());
|
||||||
|
addIfNewlyEnabled(result, "neftFacility", p.getNeftFacility(), current != null && current.isNeftFacility());
|
||||||
|
addIfNewlyEnabled(result, "rtgsFacility", p.getRtgsFacility(), current != null && current.isRtgsFacility());
|
||||||
|
addIfNewlyEnabled(result, "interBankTotalCapFacility", p.getInterBankTotalCapFacility(),
|
||||||
|
current != null && current.isInterBankTotalCapFacility());
|
||||||
|
addIfNewlyEnabled(result, "withdrawalFacility", p.getWithdrawalFacility(), current != null && current.isWithdrawalFacility());
|
||||||
|
addIfNewlyEnabled(result, "depositFacilityKyc", p.getDepositFacilityKyc(), current != null && current.isDepositFacilityKyc());
|
||||||
|
addIfNewlyEnabled(result, "virtualDebitCard", p.getVirtualDebitCard(), current != null && current.isVirtualDebitCard());
|
||||||
|
addIfNewlyEnabled(result, "internetBanking", p.getInternetBanking(), current != null && current.isInternetBanking());
|
||||||
|
addIfNewlyEnabled(result, "mobileBanking", p.getMobileBanking(), current != null && current.isMobileBanking());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIfNewlyEnabled(final Set<String> result, final String field, final Boolean proposed, final boolean current) {
|
||||||
|
if (Boolean.TRUE.equals(proposed) && !current) {
|
||||||
|
result.add(FACILITY_TYPES.get(field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reads a single submitted field out of the incoming JSON, tracks whether it enables a previously-off facility. */
|
||||||
|
private void populateFacilityField(final JsonObject fc, final String field, final boolean currentValue,
|
||||||
|
final java.util.function.Consumer<Boolean> setter, final Set<String> newlyEnabledCollector) {
|
||||||
|
if (!fc.has(field) || fc.get(field).isJsonNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final boolean proposed = fc.get(field).getAsBoolean();
|
||||||
|
setter.accept(proposed);
|
||||||
|
if (proposed && !currentValue) {
|
||||||
|
newlyEnabledCollector.add(FACILITY_TYPES.get(field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAuditEntry(final CurrentAccountPendingEdit pendingEdit, final AppUser checker, final boolean rejected) {
|
||||||
|
Optional<CommandSource> existing = Optional.empty();
|
||||||
|
if (pendingEdit.getCommandSourceId() != null) {
|
||||||
|
existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||||
|
}
|
||||||
|
final CommandSource auditEntry = existing.orElseGet(() -> CommandSource.builder() //
|
||||||
|
.actionName(rejected ? "REJECTEDIT" : "APPROVEEDIT") //
|
||||||
|
.entityName("CURRENTACCOUNT") //
|
||||||
|
.resourceGetUrl("/currentaccounts/" + pendingEdit.getCurrentAccount().getId()) //
|
||||||
|
.resourceId(pendingEdit.getCurrentAccount().getId()) //
|
||||||
|
.maker(pendingEdit.getMaker()) //
|
||||||
|
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||||
|
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||||
|
.idempotencyKey(UUID.randomUUID().toString()) //
|
||||||
|
.sanitized(false) //
|
||||||
|
.build());
|
||||||
|
if (rejected) {
|
||||||
|
auditEntry.markAsRejected(checker);
|
||||||
|
} else {
|
||||||
|
auditEntry.markAsChecked(checker);
|
||||||
|
}
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CurrentAccountPendingEditData toData(final CurrentAccountPendingEdit p) {
|
||||||
|
final CurrentAccountPendingEditData data = new CurrentAccountPendingEditData();
|
||||||
|
data.setId(p.getId());
|
||||||
|
data.setCurrentAccountId(p.getCurrentAccount().getId());
|
||||||
|
data.setAccountNo(p.getCurrentAccount().getAccountNo());
|
||||||
|
data.setMakerId(p.getMaker().getId());
|
||||||
|
data.setMakerUsername(p.getMaker().getUsername());
|
||||||
|
if (p.getChecker() != null) {
|
||||||
|
data.setCheckerId(p.getChecker().getId());
|
||||||
|
data.setCheckerUsername(p.getChecker().getUsername());
|
||||||
|
}
|
||||||
|
data.setCreatedOn(p.getCreatedOn());
|
||||||
|
data.setCheckedOn(p.getCheckedOn());
|
||||||
|
data.setStatus(p.getStatus());
|
||||||
|
data.setRejectReason(p.getRejectReason());
|
||||||
|
data.setHoldAmount(p.getHoldAmount());
|
||||||
|
data.setFacilityConfigSubmitted(p.isFacilityConfigSubmitted());
|
||||||
|
data.setNomineeSubmitted(p.isNomineeSubmitted());
|
||||||
|
|
||||||
|
if (p.isFacilityConfigSubmitted()) {
|
||||||
|
final Map<String, Object> fc = new LinkedHashMap<>();
|
||||||
|
putIfNotNull(fc, "atmFacility", p.getAtmFacility());
|
||||||
|
putIfNotNull(fc, "atmWithdrawalFacility", p.getAtmWithdrawalFacility());
|
||||||
|
putIfNotNull(fc, "upiFacility", p.getUpiFacility());
|
||||||
|
putIfNotNull(fc, "posTransactionsFacility", p.getPosTransactionsFacility());
|
||||||
|
putIfNotNull(fc, "chequeBook", p.getChequeBook());
|
||||||
|
putIfNotNull(fc, "impsFacility", p.getImpsFacility());
|
||||||
|
putIfNotNull(fc, "smsAlerts", p.getSmsAlerts());
|
||||||
|
putIfNotNull(fc, "bbpsFacility", p.getBbpsFacility());
|
||||||
|
putIfNotNull(fc, "insuranceCover", p.getInsuranceCover());
|
||||||
|
putIfNotNull(fc, "bepgFacility", p.getBepgFacility());
|
||||||
|
putIfNotNull(fc, "neftFacility", p.getNeftFacility());
|
||||||
|
putIfNotNull(fc, "rtgsFacility", p.getRtgsFacility());
|
||||||
|
putIfNotNull(fc, "interBankTotalCapFacility", p.getInterBankTotalCapFacility());
|
||||||
|
putIfNotNull(fc, "withdrawalFacility", p.getWithdrawalFacility());
|
||||||
|
putIfNotNull(fc, "depositFacilityKyc", p.getDepositFacilityKyc());
|
||||||
|
putIfNotNull(fc, "virtualDebitCard", p.getVirtualDebitCard());
|
||||||
|
putIfNotNull(fc, "internetBanking", p.getInternetBanking());
|
||||||
|
putIfNotNull(fc, "mobileBanking", p.getMobileBanking());
|
||||||
|
data.setProposedFacilityConfig(fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.isNomineeSubmitted()) {
|
||||||
|
final Map<String, Object> nom = new LinkedHashMap<>();
|
||||||
|
putIfNotNull(nom, "operation", p.getNomineeOperation());
|
||||||
|
putIfNotNull(nom, "nomineeId", p.getNomineeId());
|
||||||
|
putIfNotNull(nom, "nomineeClientId", p.getNomineeClientId());
|
||||||
|
putIfNotNull(nom, "relationshipId", p.getRelationshipCvId());
|
||||||
|
putIfNotNull(nom, "percentageShare", p.getPercentageShare());
|
||||||
|
putIfNotNull(nom, "isActive", p.getNomineeIsActive());
|
||||||
|
putIfNotNull(nom, "nomineeIsMinor", p.getNomineeIsMinor());
|
||||||
|
putIfNotNull(nom, "guardianClientId", p.getGuardianClientId());
|
||||||
|
putIfNotNull(nom, "guardianRelationshipId", p.getGuardianRelationshipCvId());
|
||||||
|
putIfNotNull(nom, "effectiveDate", p.getEffectiveDate());
|
||||||
|
putIfNotNull(nom, "remarks", p.getNomineeRemarks());
|
||||||
|
data.setProposedNominee(nom);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putIfNotNull(final Map<String, Object> map, final String key, final Object value) {
|
||||||
|
if (value != null) {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
-3
@@ -70,9 +70,37 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
|||||||
response.put("partners", retrievePartners(accountId));
|
response.put("partners", retrievePartners(accountId));
|
||||||
response.put("charges", retrieveCharges(accountId));
|
response.put("charges", retrieveCharges(accountId));
|
||||||
response.put("transactions", retrieveTransactions(accountId));
|
response.put("transactions", retrieveTransactions(accountId));
|
||||||
|
response.put("accountFacilityConfig", retrieveFacilityConfig(accountId));
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> retrieveFacilityConfig(Long accountId) {
|
||||||
|
final String sql = "SELECT * FROM m_current_account_facility_config WHERE current_account_id = ?";
|
||||||
|
List<Map<String, Object>> rows = jdbcTemplate.query(sql, (rs, rowNum) -> {
|
||||||
|
final Map<String, Object> cfg = new LinkedHashMap<>();
|
||||||
|
cfg.put("atmFacility", rs.getBoolean("atm_facility"));
|
||||||
|
cfg.put("atmWithdrawalFacility", rs.getBoolean("atm_withdrawal_facility"));
|
||||||
|
cfg.put("upiFacility", rs.getBoolean("upi_facility"));
|
||||||
|
cfg.put("posTransactionsFacility", rs.getBoolean("pos_transactions_facility"));
|
||||||
|
cfg.put("chequeBook", rs.getBoolean("cheque_book"));
|
||||||
|
cfg.put("impsFacility", rs.getBoolean("imps_facility"));
|
||||||
|
cfg.put("smsAlerts", rs.getBoolean("sms_alerts"));
|
||||||
|
cfg.put("bbpsFacility", rs.getBoolean("bbps_facility"));
|
||||||
|
cfg.put("insuranceCover", rs.getBoolean("insurance_cover"));
|
||||||
|
cfg.put("bepgFacility", rs.getBoolean("bepg_facility"));
|
||||||
|
cfg.put("neftFacility", rs.getBoolean("neft_facility"));
|
||||||
|
cfg.put("rtgsFacility", rs.getBoolean("rtgs_facility"));
|
||||||
|
cfg.put("interBankTotalCapFacility", rs.getBoolean("inter_bank_total_cap_facility"));
|
||||||
|
cfg.put("withdrawalFacility", rs.getBoolean("withdrawal_facility"));
|
||||||
|
cfg.put("depositFacilityKyc", rs.getBoolean("deposit_facility_kyc"));
|
||||||
|
cfg.put("virtualDebitCard", rs.getBoolean("virtual_debit_card"));
|
||||||
|
cfg.put("internetBanking", rs.getBoolean("internet_banking"));
|
||||||
|
cfg.put("mobileBanking", rs.getBoolean("mobile_banking"));
|
||||||
|
return cfg;
|
||||||
|
}, accountId);
|
||||||
|
return rows.isEmpty() ? null : rows.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
private List<Map<String, Object>> retrieveJointHolders(Long accountId) {
|
private List<Map<String, Object>> retrieveJointHolders(Long accountId) {
|
||||||
final String sql =
|
final String sql =
|
||||||
"SELECT jh.id, jh.client_id, jh.cif_number, jh.display_name, "
|
"SELECT jh.id, jh.client_id, jh.cif_number, jh.display_name, "
|
||||||
@@ -607,8 +635,18 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
|||||||
|
|
||||||
private Map<String, Object> buildSummary(Map<String, Object> row) {
|
private Map<String, Object> buildSummary(Map<String, Object> row) {
|
||||||
Map<String, Object> s = new LinkedHashMap<>();
|
Map<String, Object> s = new LinkedHashMap<>();
|
||||||
s.put("accountBalance", row.get("account_balance_derived"));
|
BigDecimal balance = toBigDecimal(row.get("account_balance_derived"));
|
||||||
s.put("availableBalance", row.get("account_balance_derived"));
|
if (balance == null) {
|
||||||
|
balance = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
BigDecimal onHold = toBigDecimal(row.get("on_hold_funds_derived"));
|
||||||
|
if (onHold == null) {
|
||||||
|
onHold = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
BigDecimal available = balance.subtract(onHold);
|
||||||
|
|
||||||
|
s.put("accountBalance", balance);
|
||||||
|
s.put("availableBalance", available);
|
||||||
s.put("totalDeposits", row.get("total_deposits_derived"));
|
s.put("totalDeposits", row.get("total_deposits_derived"));
|
||||||
s.put("totalWithdrawals", row.get("total_withdrawals_derived"));
|
s.put("totalWithdrawals", row.get("total_withdrawals_derived"));
|
||||||
s.put("totalInterestEarned", row.get("total_interest_earned_derived"));
|
s.put("totalInterestEarned", row.get("total_interest_earned_derived"));
|
||||||
@@ -619,7 +657,7 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
|||||||
s.put("totalWithdrawalFees", null);
|
s.put("totalWithdrawalFees", null);
|
||||||
s.put("totalAnnualFees", null);
|
s.put("totalAnnualFees", null);
|
||||||
s.put("lastInterestCalculationDate", row.get("last_interest_calculation_date"));
|
s.put("lastInterestCalculationDate", row.get("last_interest_calculation_date"));
|
||||||
s.put("onHoldFunds", row.get("on_hold_funds_derived"));
|
s.put("onHoldFunds", onHold);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+78
-19
@@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
|||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||||
@@ -335,25 +336,11 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auto-deduct activation charges (chargeTimeEnum=3) from opening balance
|
// Auto-deduct activation charges (chargeTimeEnum=3) from opening balance
|
||||||
for (final org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge charge : account.getCharges()) {
|
final java.util.List<org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge> activationCharges =
|
||||||
if (charge.getChargeTimeEnum() != null && charge.getChargeTimeEnum() == 3
|
account.getCharges().stream()
|
||||||
&& charge.getAmount() != null
|
.filter(c -> c.getChargeTimeEnum() != null && c.getChargeTimeEnum() == 3 && !c.isPaid())
|
||||||
&& charge.getAmount().compareTo(java.math.BigDecimal.ZERO) > 0
|
.toList();
|
||||||
&& charge.getCharge() != null) {
|
currentBalance = collectChargesAndPost(account, activationCharges, currentBalance, activatedOnDate, chargeEntries);
|
||||||
charge.setAmountPaidDerived(charge.getAmount());
|
|
||||||
charge.setAmountOutstandingDerived(java.math.BigDecimal.ZERO);
|
|
||||||
charge.setPaid(true);
|
|
||||||
currentBalance = currentBalance.subtract(charge.getAmount());
|
|
||||||
final org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction chargeTxn =
|
|
||||||
org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction
|
|
||||||
.chargeDeduction(account, account.getOffice(), charge.getAmount(), activatedOnDate);
|
|
||||||
chargeTxn.setRunningBalanceDerived(currentBalance);
|
|
||||||
currentAccountTransactionRepository.saveAndFlush(chargeTxn);
|
|
||||||
chargeEntries.add(new ChargeGlEntry(chargeTxn, charge));
|
|
||||||
log.info("Auto-deducted activation charge '{}' {} from account id={}, running balance={}",
|
|
||||||
charge.getCharge().getName(), charge.getAmount(), accountId, currentBalance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
account.setAccountBalanceDerived(currentBalance);
|
account.setAccountBalanceDerived(currentBalance);
|
||||||
currentAccountRepository.saveAndFlush(account);
|
currentAccountRepository.saveAndFlush(account);
|
||||||
@@ -718,6 +705,78 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
|||||||
org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction txn,
|
org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction txn,
|
||||||
org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge charge) {}
|
org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge charge) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks each of the given (unpaid) charges as paid, deducts its amount from {@code runningBalance}, creates a
|
||||||
|
* {@code CurrentAccountTransaction.chargeDeduction(...)} row for it, and records a {@link ChargeGlEntry} for GL
|
||||||
|
* posting. Shared by {@link #activate(Long, JsonCommand)} (activation-time charges) and
|
||||||
|
* {@link #applyAndCollectFacilityCharges(Long, java.util.Set)} (edit-approval-time charges) so the
|
||||||
|
* charge-collection logic isn't duplicated. Returns the resulting balance.
|
||||||
|
*/
|
||||||
|
private java.math.BigDecimal collectChargesAndPost(final CurrentAccount account,
|
||||||
|
final java.util.List<org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge> charges,
|
||||||
|
java.math.BigDecimal runningBalance, final LocalDate transactionDate,
|
||||||
|
final java.util.List<ChargeGlEntry> chargeEntriesOut) {
|
||||||
|
for (final org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge charge : charges) {
|
||||||
|
if (charge.getAmount() == null || charge.getAmount().compareTo(java.math.BigDecimal.ZERO) <= 0 || charge.getCharge() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
charge.setAmountPaidDerived(charge.getAmount());
|
||||||
|
charge.setAmountOutstandingDerived(java.math.BigDecimal.ZERO);
|
||||||
|
charge.setPaid(true);
|
||||||
|
runningBalance = runningBalance.subtract(charge.getAmount());
|
||||||
|
final org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction chargeTxn =
|
||||||
|
org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction
|
||||||
|
.chargeDeduction(account, account.getOffice(), charge.getAmount(), transactionDate);
|
||||||
|
chargeTxn.setRunningBalanceDerived(runningBalance);
|
||||||
|
currentAccountTransactionRepository.saveAndFlush(chargeTxn);
|
||||||
|
chargeEntriesOut.add(new ChargeGlEntry(chargeTxn, charge));
|
||||||
|
log.info("[FacilityCharge] Collected charge '{}' {} from account id={}, running balance={}",
|
||||||
|
charge.getCharge().getName(), charge.getAmount(), account.getId(), runningBalance);
|
||||||
|
}
|
||||||
|
return runningBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void applyAndCollectFacilityCharges(final Long currentAccountId, final java.util.Set<String> newlyEnabledFacilityTypes) {
|
||||||
|
if (newlyEnabledFacilityTypes == null || newlyEnabledFacilityTypes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final CurrentAccount account = findAccount(currentAccountId);
|
||||||
|
final java.util.List<org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge> newCharges = new java.util.ArrayList<>();
|
||||||
|
|
||||||
|
for (final String facilityType : newlyEnabledFacilityTypes) {
|
||||||
|
final var mappingOpt = facilityChargeMappingRepository.findByFacilityType(facilityType);
|
||||||
|
if (mappingOpt.isEmpty()) {
|
||||||
|
log.warn("[FacilityCharge] {} newly-enabled via edit-approval but NO mapping found in m_facility_charge_mapping",
|
||||||
|
facilityType);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final org.apache.fineract.portfolio.charge.domain.Charge charge = mappingOpt.get().getCharge();
|
||||||
|
if (charge == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge newCharge =
|
||||||
|
org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge.createFromFacility(account, charge);
|
||||||
|
account.getCharges().add(newCharge);
|
||||||
|
newCharges.add(newCharge);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newCharges.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
java.math.BigDecimal currentBalance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived()
|
||||||
|
: java.math.BigDecimal.ZERO;
|
||||||
|
final java.util.List<ChargeGlEntry> chargeEntries = new java.util.ArrayList<>();
|
||||||
|
currentBalance = collectChargesAndPost(account, newCharges, currentBalance, DateUtils.getBusinessLocalDate(), chargeEntries);
|
||||||
|
|
||||||
|
account.setAccountBalanceDerived(currentBalance);
|
||||||
|
currentAccountRepository.saveAndFlush(account);
|
||||||
|
|
||||||
|
postGlEntries(account, java.util.List.of(), chargeEntries);
|
||||||
|
}
|
||||||
|
|
||||||
// entity type for savings/current accounts in acc_gl_journal_entry
|
// entity type for savings/current accounts in acc_gl_journal_entry
|
||||||
private static final int ENTITY_TYPE_SAVINGS = 2;
|
private static final int ENTITY_TYPE_SAVINGS = 2;
|
||||||
|
|
||||||
|
|||||||
+510
@@ -0,0 +1,510 @@
|
|||||||
|
/**
|
||||||
|
* 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.JsonObject;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.commands.domain.CommandProcessingResultType;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSource;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||||
|
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.core.serialization.FromJsonHelper;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.client.service.ClientNomineeWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountPendingEditData;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfig;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfigRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountPendingEdit;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountPendingEditRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
|
||||||
|
import org.apache.fineract.portfolio.savings.exception.SavingsAccountEditInsufficientBalanceException;
|
||||||
|
import org.apache.fineract.portfolio.savings.exception.SavingsAccountEditPendingApprovalException;
|
||||||
|
import org.apache.fineract.portfolio.savings.exception.SavingsAccountEditSelfApprovalNotAllowedException;
|
||||||
|
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNoPendingEditException;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maker-checker edit flow for an ACTIVE savings account's Nominee + Digital & Account Features. Mirrors
|
||||||
|
* {@code SavingsProductWritePlatformServiceJpaRepositoryImpl}'s submitEditForApproval/approveEdit/rejectEdit
|
||||||
|
* pattern: a row in {@code m_savings_account_pending_edit} is itself the lock, and nothing is applied to the real
|
||||||
|
* {@code m_savings_account_facility_config} / {@code m_client_nominee} tables until a checker approves.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Charge handling for a newly-enabled facility (one with a mapped charge in m_facility_charge_mapping): at
|
||||||
|
* submission a HOLD is placed for the total charge amount (blocking that much of the available balance without
|
||||||
|
* touching it); on approval the hold is released and the charge is posted for real; on rejection the hold is simply
|
||||||
|
* released.
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SavingsAccountEditWritePlatformServiceJpaRepositoryImpl implements SavingsAccountEditWritePlatformService {
|
||||||
|
|
||||||
|
/** field name (as sent in accountFacilityConfig JSON / stored on the pending edit) -> facility_type code. */
|
||||||
|
private static final Map<String, String> FACILITY_TYPES = new LinkedHashMap<>();
|
||||||
|
static {
|
||||||
|
FACILITY_TYPES.put("atmFacility", "ATM_FACILITY");
|
||||||
|
FACILITY_TYPES.put("atmWithdrawalFacility", "ATM_WITHDRAWAL");
|
||||||
|
FACILITY_TYPES.put("upiFacility", "UPI_FACILITY");
|
||||||
|
FACILITY_TYPES.put("posTransactionsFacility", "POS_TRANSACTIONS");
|
||||||
|
FACILITY_TYPES.put("chequeBook", "CHEQUE_BOOK");
|
||||||
|
FACILITY_TYPES.put("impsFacility", "IMPS_FACILITY");
|
||||||
|
FACILITY_TYPES.put("smsAlerts", "SMS_ALERTS");
|
||||||
|
FACILITY_TYPES.put("bbpsFacility", "BBPS_FACILITY");
|
||||||
|
FACILITY_TYPES.put("insuranceCover", "INSURANCE_COVER");
|
||||||
|
FACILITY_TYPES.put("bepgFacility", "BEPG_FACILITY");
|
||||||
|
FACILITY_TYPES.put("neftFacility", "NEFT_FACILITY");
|
||||||
|
FACILITY_TYPES.put("rtgsFacility", "RTGS_FACILITY");
|
||||||
|
FACILITY_TYPES.put("interBankTotalCapFacility", "INTER_BANK_TOTAL_CAP");
|
||||||
|
FACILITY_TYPES.put("withdrawalFacility", "WITHDRAWAL_FACILITY");
|
||||||
|
FACILITY_TYPES.put("depositFacilityKyc", "DEPOSIT_FACILITY_KYC");
|
||||||
|
FACILITY_TYPES.put("virtualDebitCard", "VIRTUAL_DEBIT_CARD");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final SavingsAccountRepositoryWrapper savingAccountRepositoryWrapper;
|
||||||
|
private final SavingsAccountPendingEditRepository pendingEditRepository;
|
||||||
|
private final SavingsAccountFacilityConfigRepository facilityConfigRepository;
|
||||||
|
private final FacilityChargeAutoApplyService facilityChargeAutoApplyService;
|
||||||
|
private final SavingsAccountWritePlatformService savingsAccountWritePlatformService;
|
||||||
|
private final ClientNomineeWritePlatformService clientNomineeWritePlatformService;
|
||||||
|
private final CommandSourceRepository commandSourceRepository;
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult submitEditForApproval(final Long savingsId, final JsonCommand command) {
|
||||||
|
|
||||||
|
final AppUser maker = this.context.authenticatedUser();
|
||||||
|
final SavingsAccount account = this.savingAccountRepositoryWrapper.findOneWithNotFoundDetection(savingsId);
|
||||||
|
|
||||||
|
if (this.pendingEditRepository.findBySavingsAccountIdAndStatus(savingsId, SavingsAccountPendingEdit.STATUS_PENDING).isPresent()) {
|
||||||
|
throw new SavingsAccountEditPendingApprovalException(savingsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonObject root = command.parsedJson().getAsJsonObject();
|
||||||
|
final SavingsAccountFacilityConfig currentConfig = account.getFacilityConfig();
|
||||||
|
|
||||||
|
final SavingsAccountPendingEdit pendingEdit = new SavingsAccountPendingEdit(account, maker);
|
||||||
|
|
||||||
|
// ── Digital & Account Features ──
|
||||||
|
final Set<String> newlyEnabledFacilityTypes = new HashSet<>();
|
||||||
|
if (root.has("accountFacilityConfig") && root.get("accountFacilityConfig").isJsonObject()) {
|
||||||
|
final JsonObject fc = root.getAsJsonObject("accountFacilityConfig");
|
||||||
|
pendingEdit.setFacilityConfigSubmitted(true);
|
||||||
|
populateFacilityField(fc, "atmFacility", currentConfig == null ? null : currentConfig.getAtmFacility(), pendingEdit::setAtmFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "atmWithdrawalFacility", currentConfig == null ? null : currentConfig.getAtmWithdrawalFacility(), pendingEdit::setAtmWithdrawalFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "upiFacility", currentConfig == null ? null : currentConfig.getUpiFacility(), pendingEdit::setUpiFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "posTransactionsFacility", currentConfig == null ? null : currentConfig.getPosTransactionsFacility(), pendingEdit::setPosTransactionsFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "chequeBook", currentConfig == null ? null : currentConfig.getChequeBook(), pendingEdit::setChequeBook, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "impsFacility", currentConfig == null ? null : currentConfig.getImpsFacility(), pendingEdit::setImpsFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "smsAlerts", currentConfig == null ? null : currentConfig.getSmsAlerts(), pendingEdit::setSmsAlerts, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "bbpsFacility", currentConfig == null ? null : currentConfig.getBbpsFacility(), pendingEdit::setBbpsFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "insuranceCover", currentConfig == null ? null : currentConfig.getInsuranceCover(), pendingEdit::setInsuranceCover, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "bepgFacility", currentConfig == null ? null : currentConfig.getBepgFacility(), pendingEdit::setBepgFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "neftFacility", currentConfig == null ? null : currentConfig.getNeftFacility(), pendingEdit::setNeftFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "rtgsFacility", currentConfig == null ? null : currentConfig.getRtgsFacility(), pendingEdit::setRtgsFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "interBankTotalCapFacility", currentConfig == null ? null : currentConfig.getInterBankTotalCapFacility(), pendingEdit::setInterBankTotalCapFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "withdrawalFacility", currentConfig == null ? null : currentConfig.getWithdrawalFacility(), pendingEdit::setWithdrawalFacility, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "depositFacilityKyc", currentConfig == null ? null : currentConfig.getDepositFacilityKyc(), pendingEdit::setDepositFacilityKyc, newlyEnabledFacilityTypes);
|
||||||
|
populateFacilityField(fc, "virtualDebitCard", currentConfig == null ? null : currentConfig.getVirtualDebitCard(), pendingEdit::setVirtualDebitCard, newlyEnabledFacilityTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Nominee (single add / update / delete per request) ──
|
||||||
|
if (root.has("nominee") && root.get("nominee").isJsonObject()) {
|
||||||
|
final JsonObject nom = root.getAsJsonObject("nominee");
|
||||||
|
pendingEdit.setNomineeSubmitted(true);
|
||||||
|
pendingEdit.setNomineeOperation(nom.has("operation") ? nom.get("operation").getAsString() : null);
|
||||||
|
pendingEdit.setNomineeId(nom.has("nomineeId") && !nom.get("nomineeId").isJsonNull() ? nom.get("nomineeId").getAsLong() : null);
|
||||||
|
pendingEdit.setNomineeClientId(nom.has("nomineeClientId") && !nom.get("nomineeClientId").isJsonNull() ? nom.get("nomineeClientId").getAsLong() : null);
|
||||||
|
pendingEdit.setRelationshipCvId(nom.has("relationshipId") && !nom.get("relationshipId").isJsonNull() ? nom.get("relationshipId").getAsLong() : null);
|
||||||
|
pendingEdit.setPercentageShare(nom.has("percentageShare") && !nom.get("percentageShare").isJsonNull() ? nom.get("percentageShare").getAsBigDecimal() : null);
|
||||||
|
pendingEdit.setNomineeIsActive(nom.has("isActive") && !nom.get("isActive").isJsonNull() ? nom.get("isActive").getAsBoolean() : null);
|
||||||
|
pendingEdit.setNomineeIsMinor(nom.has("nomineeIsMinor") && !nom.get("nomineeIsMinor").isJsonNull() ? nom.get("nomineeIsMinor").getAsBoolean() : null);
|
||||||
|
pendingEdit.setGuardianClientId(nom.has("guardianClientId") && !nom.get("guardianClientId").isJsonNull() ? nom.get("guardianClientId").getAsLong() : null);
|
||||||
|
pendingEdit.setGuardianRelationshipCvId(nom.has("guardianRelationshipId") && !nom.get("guardianRelationshipId").isJsonNull() ? nom.get("guardianRelationshipId").getAsLong() : null);
|
||||||
|
pendingEdit.setEffectiveDate(nom.has("effectiveDate") && !nom.get("effectiveDate").isJsonNull() ? LocalDate.parse(nom.get("effectiveDate").getAsString()) : null);
|
||||||
|
pendingEdit.setNomineeRemarks(nom.has("remarks") && !nom.get("remarks").isJsonNull() ? nom.get("remarks").getAsString() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Hold the charge amount for newly-enabled facilities before anything is persisted ──
|
||||||
|
final BigDecimal holdNeeded = this.facilityChargeAutoApplyService.totalChargeAmountFor(newlyEnabledFacilityTypes);
|
||||||
|
if (holdNeeded.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
final BigDecimal availableBalance = account.getWithdrawableBalance();
|
||||||
|
if (availableBalance.compareTo(holdNeeded) < 0) {
|
||||||
|
throw new SavingsAccountEditInsufficientBalanceException(savingsId, availableBalance, holdNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonObject holdJson = new JsonObject();
|
||||||
|
holdJson.addProperty("locale", "en");
|
||||||
|
holdJson.addProperty("dateFormat", "yyyy-MM-dd");
|
||||||
|
holdJson.addProperty("transactionDate", DateUtils.getBusinessLocalDate().toString());
|
||||||
|
holdJson.addProperty("transactionAmount", holdNeeded);
|
||||||
|
holdJson.addProperty("lienAllowed", false);
|
||||||
|
holdJson.addProperty("reasonForBlock", "Pending approval of Nominee/Digital-Feature edit");
|
||||||
|
final JsonCommand holdCommand = JsonCommand.fromJsonElement(savingsId, holdJson, this.fromApiJsonHelper);
|
||||||
|
|
||||||
|
final CommandProcessingResult holdResult = this.savingsAccountWritePlatformService.holdAmount(savingsId, holdCommand);
|
||||||
|
pendingEdit.setHoldTransactionId(holdResult.getResourceId());
|
||||||
|
pendingEdit.setHoldAmount(holdNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandSource auditEntry = CommandSource.builder() //
|
||||||
|
.actionName("SUBMITEDIT") //
|
||||||
|
.entityName("SAVINGSACCOUNT") //
|
||||||
|
.resourceGetUrl("/savingsaccounts/" + savingsId) //
|
||||||
|
.resourceId(savingsId) //
|
||||||
|
.commandAsJson(command.json()) //
|
||||||
|
.maker(maker) //
|
||||||
|
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||||
|
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||||
|
.idempotencyKey(UUID.randomUUID().toString()) //
|
||||||
|
.sanitized(false) //
|
||||||
|
.build();
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||||
|
|
||||||
|
this.pendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(savingsId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult approveEdit(final Long savingsId) {
|
||||||
|
|
||||||
|
final AppUser checker = this.context.authenticatedUser();
|
||||||
|
final SavingsAccountPendingEdit pendingEdit = this.pendingEditRepository
|
||||||
|
.findBySavingsAccountIdAndStatus(savingsId, SavingsAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.orElseThrow(() -> new SavingsAccountNoPendingEditException(savingsId));
|
||||||
|
|
||||||
|
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||||
|
throw new SavingsAccountEditSelfApprovalNotAllowedException(savingsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final SavingsAccount account = this.savingAccountRepositoryWrapper.findOneWithNotFoundDetection(savingsId);
|
||||||
|
final SavingsAccountFacilityConfig currentConfig = account.getFacilityConfig();
|
||||||
|
final Set<String> newlyEnabledFacilityTypes = pendingEdit.isFacilityConfigSubmitted()
|
||||||
|
? computeNewlyEnabled(pendingEdit, currentConfig)
|
||||||
|
: Set.of();
|
||||||
|
|
||||||
|
// 1) Release the hold placed at submission time.
|
||||||
|
if (pendingEdit.getHoldTransactionId() != null) {
|
||||||
|
this.savingsAccountWritePlatformService.releaseAmount(savingsId, pendingEdit.getHoldTransactionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Post the real charge(s) for newly-enabled facilities, today.
|
||||||
|
if (!newlyEnabledFacilityTypes.isEmpty()) {
|
||||||
|
final var chargesAdded = this.facilityChargeAutoApplyService.applyChargesForFacilities(account, newlyEnabledFacilityTypes);
|
||||||
|
this.facilityChargeAutoApplyService.collectCharges(account, chargesAdded, DateUtils.getBusinessLocalDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Apply the facility-config toggles to the real row.
|
||||||
|
if (pendingEdit.isFacilityConfigSubmitted()) {
|
||||||
|
SavingsAccountFacilityConfig config = account.getFacilityConfig();
|
||||||
|
if (config == null) {
|
||||||
|
config = new SavingsAccountFacilityConfig(account);
|
||||||
|
account.setFacilityConfig(config);
|
||||||
|
}
|
||||||
|
config.updateFrom(buildFacilityConfigJson(pendingEdit));
|
||||||
|
this.facilityConfigRepository.saveAndFlush(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) Apply the nominee change to the real table.
|
||||||
|
if (pendingEdit.isNomineeSubmitted()) {
|
||||||
|
applyNomineeChange(account, pendingEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.savingAccountRepositoryWrapper.saveAndFlush(account);
|
||||||
|
|
||||||
|
pendingEdit.markApproved(checker);
|
||||||
|
this.pendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
updateAuditEntry(pendingEdit, checker, false);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(savingsId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult rejectEdit(final Long savingsId, final String rejectReason) {
|
||||||
|
|
||||||
|
final AppUser checker = this.context.authenticatedUser();
|
||||||
|
final SavingsAccountPendingEdit pendingEdit = this.pendingEditRepository
|
||||||
|
.findBySavingsAccountIdAndStatus(savingsId, SavingsAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.orElseThrow(() -> new SavingsAccountNoPendingEditException(savingsId));
|
||||||
|
|
||||||
|
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||||
|
throw new SavingsAccountEditSelfApprovalNotAllowedException(savingsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pendingEdit.getHoldTransactionId() != null) {
|
||||||
|
this.savingsAccountWritePlatformService.releaseAmount(savingsId, pendingEdit.getHoldTransactionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingEdit.markRejected(checker, rejectReason);
|
||||||
|
this.pendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
updateAuditEntry(pendingEdit, checker, true);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(savingsId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SavingsAccountPendingEditData retrievePendingEdit(final Long savingsId) {
|
||||||
|
return this.pendingEditRepository.findBySavingsAccountIdAndStatus(savingsId, SavingsAccountPendingEdit.STATUS_PENDING)
|
||||||
|
.map(this::toData).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.util.Collection<SavingsAccountPendingEditData> retrieveAllPendingEdits() {
|
||||||
|
return this.pendingEditRepository.findByStatus(SavingsAccountPendingEdit.STATUS_PENDING).stream().map(this::toData).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void applyNomineeChange(final SavingsAccount account, final SavingsAccountPendingEdit pendingEdit) {
|
||||||
|
final Long clientId = account.clientId();
|
||||||
|
final String operation = pendingEdit.getNomineeOperation();
|
||||||
|
|
||||||
|
if (SavingsAccountPendingEdit.NOMINEE_OP_DELETE.equals(operation)) {
|
||||||
|
this.clientNomineeWritePlatformService.deleteNominee(clientId, pendingEdit.getNomineeId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String accountType;
|
||||||
|
if (account.depositAccountType().isFixedDeposit()) {
|
||||||
|
accountType = "FD";
|
||||||
|
} else if (account.depositAccountType().isRecurringDeposit()) {
|
||||||
|
accountType = "RD";
|
||||||
|
} else {
|
||||||
|
accountType = "SB";
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonObject nomineeJson = new JsonObject();
|
||||||
|
nomineeJson.addProperty("locale", "en");
|
||||||
|
nomineeJson.addProperty("dateFormat", "yyyy-MM-dd");
|
||||||
|
nomineeJson.addProperty("accountNo", account.getAccountNumber());
|
||||||
|
nomineeJson.addProperty("accountType", accountType);
|
||||||
|
if (pendingEdit.getNomineeClientId() != null) {
|
||||||
|
nomineeJson.addProperty("nomineeClientId", pendingEdit.getNomineeClientId());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getRelationshipCvId() != null) {
|
||||||
|
nomineeJson.addProperty("relationshipId", pendingEdit.getRelationshipCvId());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getPercentageShare() != null) {
|
||||||
|
nomineeJson.addProperty("percentageShare", pendingEdit.getPercentageShare());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getNomineeIsActive() != null) {
|
||||||
|
nomineeJson.addProperty("isActive", pendingEdit.getNomineeIsActive());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getNomineeIsMinor() != null) {
|
||||||
|
nomineeJson.addProperty("nomineeIsMinor", pendingEdit.getNomineeIsMinor());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getGuardianClientId() != null) {
|
||||||
|
nomineeJson.addProperty("guardianClientId", pendingEdit.getGuardianClientId());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getGuardianRelationshipCvId() != null) {
|
||||||
|
nomineeJson.addProperty("guardianRelationshipId", pendingEdit.getGuardianRelationshipCvId());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getEffectiveDate() != null) {
|
||||||
|
nomineeJson.addProperty("effectiveDate", pendingEdit.getEffectiveDate().toString());
|
||||||
|
}
|
||||||
|
if (pendingEdit.getNomineeRemarks() != null) {
|
||||||
|
nomineeJson.addProperty("remarks", pendingEdit.getNomineeRemarks());
|
||||||
|
}
|
||||||
|
final JsonCommand nomineeCommand = JsonCommand.fromJsonElement(clientId, nomineeJson, this.fromApiJsonHelper);
|
||||||
|
|
||||||
|
if (SavingsAccountPendingEdit.NOMINEE_OP_ADD.equals(operation)) {
|
||||||
|
this.clientNomineeWritePlatformService.addNominee(clientId, nomineeCommand);
|
||||||
|
} else if (SavingsAccountPendingEdit.NOMINEE_OP_UPDATE.equals(operation)) {
|
||||||
|
this.clientNomineeWritePlatformService.updateNominee(clientId, pendingEdit.getNomineeId(), nomineeCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonObject buildFacilityConfigJson(final SavingsAccountPendingEdit p) {
|
||||||
|
final JsonObject fc = new JsonObject();
|
||||||
|
putIfPresent(fc, "atmFacility", p.getAtmFacility());
|
||||||
|
putIfPresent(fc, "atmWithdrawalFacility", p.getAtmWithdrawalFacility());
|
||||||
|
putIfPresent(fc, "upiFacility", p.getUpiFacility());
|
||||||
|
putIfPresent(fc, "posTransactionsFacility", p.getPosTransactionsFacility());
|
||||||
|
putIfPresent(fc, "chequeBook", p.getChequeBook());
|
||||||
|
putIfPresent(fc, "impsFacility", p.getImpsFacility());
|
||||||
|
putIfPresent(fc, "smsAlerts", p.getSmsAlerts());
|
||||||
|
putIfPresent(fc, "bbpsFacility", p.getBbpsFacility());
|
||||||
|
putIfPresent(fc, "insuranceCover", p.getInsuranceCover());
|
||||||
|
putIfPresent(fc, "bepgFacility", p.getBepgFacility());
|
||||||
|
putIfPresent(fc, "neftFacility", p.getNeftFacility());
|
||||||
|
putIfPresent(fc, "rtgsFacility", p.getRtgsFacility());
|
||||||
|
putIfPresent(fc, "interBankTotalCapFacility", p.getInterBankTotalCapFacility());
|
||||||
|
putIfPresent(fc, "withdrawalFacility", p.getWithdrawalFacility());
|
||||||
|
putIfPresent(fc, "depositFacilityKyc", p.getDepositFacilityKyc());
|
||||||
|
putIfPresent(fc, "virtualDebitCard", p.getVirtualDebitCard());
|
||||||
|
return fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putIfPresent(final JsonObject json, final String field, final Boolean value) {
|
||||||
|
if (value != null) {
|
||||||
|
json.addProperty(field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> computeNewlyEnabled(final SavingsAccountPendingEdit p, final SavingsAccountFacilityConfig current) {
|
||||||
|
final Set<String> result = new HashSet<>();
|
||||||
|
addIfNewlyEnabled(result, "atmFacility", p.getAtmFacility(), current == null ? null : current.getAtmFacility());
|
||||||
|
addIfNewlyEnabled(result, "atmWithdrawalFacility", p.getAtmWithdrawalFacility(), current == null ? null : current.getAtmWithdrawalFacility());
|
||||||
|
addIfNewlyEnabled(result, "upiFacility", p.getUpiFacility(), current == null ? null : current.getUpiFacility());
|
||||||
|
addIfNewlyEnabled(result, "posTransactionsFacility", p.getPosTransactionsFacility(), current == null ? null : current.getPosTransactionsFacility());
|
||||||
|
addIfNewlyEnabled(result, "chequeBook", p.getChequeBook(), current == null ? null : current.getChequeBook());
|
||||||
|
addIfNewlyEnabled(result, "impsFacility", p.getImpsFacility(), current == null ? null : current.getImpsFacility());
|
||||||
|
addIfNewlyEnabled(result, "smsAlerts", p.getSmsAlerts(), current == null ? null : current.getSmsAlerts());
|
||||||
|
addIfNewlyEnabled(result, "bbpsFacility", p.getBbpsFacility(), current == null ? null : current.getBbpsFacility());
|
||||||
|
addIfNewlyEnabled(result, "insuranceCover", p.getInsuranceCover(), current == null ? null : current.getInsuranceCover());
|
||||||
|
addIfNewlyEnabled(result, "bepgFacility", p.getBepgFacility(), current == null ? null : current.getBepgFacility());
|
||||||
|
addIfNewlyEnabled(result, "neftFacility", p.getNeftFacility(), current == null ? null : current.getNeftFacility());
|
||||||
|
addIfNewlyEnabled(result, "rtgsFacility", p.getRtgsFacility(), current == null ? null : current.getRtgsFacility());
|
||||||
|
addIfNewlyEnabled(result, "interBankTotalCapFacility", p.getInterBankTotalCapFacility(), current == null ? null : current.getInterBankTotalCapFacility());
|
||||||
|
addIfNewlyEnabled(result, "withdrawalFacility", p.getWithdrawalFacility(), current == null ? null : current.getWithdrawalFacility());
|
||||||
|
addIfNewlyEnabled(result, "depositFacilityKyc", p.getDepositFacilityKyc(), current == null ? null : current.getDepositFacilityKyc());
|
||||||
|
addIfNewlyEnabled(result, "virtualDebitCard", p.getVirtualDebitCard(), current == null ? null : current.getVirtualDebitCard());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIfNewlyEnabled(final Set<String> result, final String field, final Boolean proposed, final Boolean current) {
|
||||||
|
if (Boolean.TRUE.equals(proposed) && !Boolean.TRUE.equals(current)) {
|
||||||
|
result.add(FACILITY_TYPES.get(field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reads a single submitted field out of the incoming JSON, tracks whether it enables a previously-off facility. */
|
||||||
|
private void populateFacilityField(final JsonObject fc, final String field, final Boolean currentValue,
|
||||||
|
final java.util.function.Consumer<Boolean> setter, final Set<String> newlyEnabledCollector) {
|
||||||
|
if (!fc.has(field) || fc.get(field).isJsonNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final boolean proposed = fc.get(field).getAsBoolean();
|
||||||
|
setter.accept(proposed);
|
||||||
|
if (proposed && !Boolean.TRUE.equals(currentValue)) {
|
||||||
|
newlyEnabledCollector.add(FACILITY_TYPES.get(field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAuditEntry(final SavingsAccountPendingEdit pendingEdit, final AppUser checker, final boolean rejected) {
|
||||||
|
Optional<CommandSource> existing = Optional.empty();
|
||||||
|
if (pendingEdit.getCommandSourceId() != null) {
|
||||||
|
existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||||
|
}
|
||||||
|
final CommandSource auditEntry = existing.orElseGet(() -> CommandSource.builder() //
|
||||||
|
.actionName(rejected ? "REJECTEDIT" : "APPROVEEDIT") //
|
||||||
|
.entityName("SAVINGSACCOUNT") //
|
||||||
|
.resourceGetUrl("/savingsaccounts/" + pendingEdit.getSavingsAccount().getId()) //
|
||||||
|
.resourceId(pendingEdit.getSavingsAccount().getId()) //
|
||||||
|
.maker(pendingEdit.getMaker()) //
|
||||||
|
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||||
|
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||||
|
.idempotencyKey(UUID.randomUUID().toString()) //
|
||||||
|
.sanitized(false) //
|
||||||
|
.build());
|
||||||
|
if (rejected) {
|
||||||
|
auditEntry.markAsRejected(checker);
|
||||||
|
} else {
|
||||||
|
auditEntry.markAsChecked(checker);
|
||||||
|
}
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SavingsAccountPendingEditData toData(final SavingsAccountPendingEdit p) {
|
||||||
|
final SavingsAccountPendingEditData data = new SavingsAccountPendingEditData();
|
||||||
|
data.setId(p.getId());
|
||||||
|
data.setSavingsAccountId(p.getSavingsAccount().getId());
|
||||||
|
data.setAccountNo(p.getSavingsAccount().getAccountNumber());
|
||||||
|
data.setMakerId(p.getMaker().getId());
|
||||||
|
data.setMakerUsername(p.getMaker().getUsername());
|
||||||
|
if (p.getChecker() != null) {
|
||||||
|
data.setCheckerId(p.getChecker().getId());
|
||||||
|
data.setCheckerUsername(p.getChecker().getUsername());
|
||||||
|
}
|
||||||
|
data.setCreatedOn(p.getCreatedOn());
|
||||||
|
data.setCheckedOn(p.getCheckedOn());
|
||||||
|
data.setStatus(p.getStatus());
|
||||||
|
data.setRejectReason(p.getRejectReason());
|
||||||
|
data.setHoldAmount(p.getHoldAmount());
|
||||||
|
data.setFacilityConfigSubmitted(p.isFacilityConfigSubmitted());
|
||||||
|
data.setNomineeSubmitted(p.isNomineeSubmitted());
|
||||||
|
if (p.getSavingsAccount() != null) {
|
||||||
|
data.setDepositAccountType(p.getSavingsAccount().depositAccountType().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.isFacilityConfigSubmitted()) {
|
||||||
|
final Map<String, Object> fc = new LinkedHashMap<>();
|
||||||
|
putIfNotNull(fc, "atmFacility", p.getAtmFacility());
|
||||||
|
putIfNotNull(fc, "atmWithdrawalFacility", p.getAtmWithdrawalFacility());
|
||||||
|
putIfNotNull(fc, "upiFacility", p.getUpiFacility());
|
||||||
|
putIfNotNull(fc, "posTransactionsFacility", p.getPosTransactionsFacility());
|
||||||
|
putIfNotNull(fc, "chequeBook", p.getChequeBook());
|
||||||
|
putIfNotNull(fc, "impsFacility", p.getImpsFacility());
|
||||||
|
putIfNotNull(fc, "smsAlerts", p.getSmsAlerts());
|
||||||
|
putIfNotNull(fc, "bbpsFacility", p.getBbpsFacility());
|
||||||
|
putIfNotNull(fc, "insuranceCover", p.getInsuranceCover());
|
||||||
|
putIfNotNull(fc, "bepgFacility", p.getBepgFacility());
|
||||||
|
putIfNotNull(fc, "neftFacility", p.getNeftFacility());
|
||||||
|
putIfNotNull(fc, "rtgsFacility", p.getRtgsFacility());
|
||||||
|
putIfNotNull(fc, "interBankTotalCapFacility", p.getInterBankTotalCapFacility());
|
||||||
|
putIfNotNull(fc, "withdrawalFacility", p.getWithdrawalFacility());
|
||||||
|
putIfNotNull(fc, "depositFacilityKyc", p.getDepositFacilityKyc());
|
||||||
|
putIfNotNull(fc, "virtualDebitCard", p.getVirtualDebitCard());
|
||||||
|
data.setProposedFacilityConfig(fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.isNomineeSubmitted()) {
|
||||||
|
final Map<String, Object> nom = new LinkedHashMap<>();
|
||||||
|
putIfNotNull(nom, "operation", p.getNomineeOperation());
|
||||||
|
putIfNotNull(nom, "nomineeId", p.getNomineeId());
|
||||||
|
putIfNotNull(nom, "nomineeClientId", p.getNomineeClientId());
|
||||||
|
putIfNotNull(nom, "relationshipId", p.getRelationshipCvId());
|
||||||
|
putIfNotNull(nom, "percentageShare", p.getPercentageShare());
|
||||||
|
putIfNotNull(nom, "isActive", p.getNomineeIsActive());
|
||||||
|
putIfNotNull(nom, "nomineeIsMinor", p.getNomineeIsMinor());
|
||||||
|
putIfNotNull(nom, "guardianClientId", p.getGuardianClientId());
|
||||||
|
putIfNotNull(nom, "guardianRelationshipId", p.getGuardianRelationshipCvId());
|
||||||
|
putIfNotNull(nom, "effectiveDate", p.getEffectiveDate());
|
||||||
|
putIfNotNull(nom, "remarks", p.getNomineeRemarks());
|
||||||
|
data.setProposedNominee(nom);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putIfNotNull(final Map<String, Object> map, final String key, final Object value) {
|
||||||
|
if (value != null) {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -152,6 +152,16 @@ import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatform
|
|||||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
|
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformServiceImpl;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfigRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountEditWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountEditWritePlatformServiceJpaRepositoryImpl;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountFacilityConfigRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountPendingEditRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountEditWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.savings.service.CurrentAccountEditWritePlatformServiceJpaRepositoryImpl;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
|
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformServiceJpaRepositoryImpl;
|
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformServiceJpaRepositoryImpl;
|
||||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository;
|
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository;
|
||||||
@@ -468,6 +478,35 @@ public class SavingsConfiguration {
|
|||||||
errorHandler, facilityChargeAutoApplyService, denominationBreakdownWriter);
|
errorHandler, facilityChargeAutoApplyService, denominationBreakdownWriter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(SavingsAccountEditWritePlatformService.class)
|
||||||
|
public SavingsAccountEditWritePlatformService savingsAccountEditWritePlatformService(PlatformSecurityContext context,
|
||||||
|
SavingsAccountRepositoryWrapper savingAccountRepositoryWrapper,
|
||||||
|
org.apache.fineract.portfolio.savings.domain.SavingsAccountPendingEditRepository pendingEditRepository,
|
||||||
|
SavingsAccountFacilityConfigRepository facilityConfigRepository, FacilityChargeAutoApplyService facilityChargeAutoApplyService,
|
||||||
|
SavingsAccountWritePlatformService savingsAccountWritePlatformService,
|
||||||
|
org.apache.fineract.portfolio.client.service.ClientNomineeWritePlatformService clientNomineeWritePlatformService,
|
||||||
|
org.apache.fineract.commands.domain.CommandSourceRepository commandSourceRepository, FromJsonHelper fromApiJsonHelper) {
|
||||||
|
return new SavingsAccountEditWritePlatformServiceJpaRepositoryImpl(context, savingAccountRepositoryWrapper,
|
||||||
|
pendingEditRepository, facilityConfigRepository, facilityChargeAutoApplyService, savingsAccountWritePlatformService,
|
||||||
|
clientNomineeWritePlatformService, commandSourceRepository, fromApiJsonHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(CurrentAccountEditWritePlatformService.class)
|
||||||
|
public CurrentAccountEditWritePlatformService currentAccountEditWritePlatformService(PlatformSecurityContext context,
|
||||||
|
CurrentAccountRepository currentAccountRepository, CurrentAccountPendingEditRepository pendingEditRepository,
|
||||||
|
CurrentAccountFacilityConfigRepository facilityConfigRepository,
|
||||||
|
org.apache.fineract.portfolio.savings.domain.FacilityChargeMappingRepository facilityChargeMappingRepository,
|
||||||
|
CurrentAccountTransactionWritePlatformService currentAccountTransactionWritePlatformService,
|
||||||
|
CurrentAccountWritePlatformService currentAccountWritePlatformService,
|
||||||
|
ClientNomineeWritePlatformService clientNomineeWritePlatformService, CommandSourceRepository commandSourceRepository,
|
||||||
|
FromJsonHelper fromApiJsonHelper) {
|
||||||
|
return new CurrentAccountEditWritePlatformServiceJpaRepositoryImpl(context, currentAccountRepository, pendingEditRepository,
|
||||||
|
facilityConfigRepository, facilityChargeMappingRepository, currentAccountTransactionWritePlatformService,
|
||||||
|
currentAccountWritePlatformService, clientNomineeWritePlatformService, commandSourceRepository, fromApiJsonHelper);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(SavingsApplicationProcessWritePlatformService.class)
|
@ConditionalOnMissingBean(SavingsApplicationProcessWritePlatformService.class)
|
||||||
public SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService(PlatformSecurityContext context,
|
public SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService(PlatformSecurityContext context,
|
||||||
|
|||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.api;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.WebApplicationException;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.portfolio.sponsordd.domain.SponsorBank;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorBankRepository;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/sponsorbanks")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Sponsor Bank", description = "Endpoints for managing sponsor banks.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SponsorBankApiResource {
|
||||||
|
|
||||||
|
private final SponsorBankRepository sponsorBankRepository;
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||||
|
|
||||||
|
/** Mirrors SponsorDDApiResource.safely() - see that class for the full rationale. */
|
||||||
|
private <T> T safely(final Supplier<T> action) {
|
||||||
|
try {
|
||||||
|
return action.get();
|
||||||
|
} catch (final IllegalArgumentException | IllegalStateException e) {
|
||||||
|
final JsonObject body = new JsonObject();
|
||||||
|
body.addProperty("message", e.getMessage());
|
||||||
|
body.addProperty("defaultUserMessage", e.getMessage());
|
||||||
|
body.addProperty("httpStatus", 400);
|
||||||
|
final JsonArray errors = new JsonArray();
|
||||||
|
final JsonObject err = new JsonObject();
|
||||||
|
err.addProperty("defaultUserMessage", e.getMessage());
|
||||||
|
errors.add(err);
|
||||||
|
body.add("errors", errors);
|
||||||
|
throw new WebApplicationException(
|
||||||
|
Response.status(400).entity(this.gson.toJson(body)).type(MediaType.APPLICATION_JSON).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve all Sponsor Banks", description = "Returns a list of all registered Sponsor Banks.")
|
||||||
|
public Collection<SponsorBankData> retrieveAllSponsorBanks() {
|
||||||
|
return this.sponsorBankRepository.findAll().stream()
|
||||||
|
.map(sb -> new SponsorBankData(sb.getId(), sb.getName(), sb.getDescription(), sb.getIfscCode(), sb.getBankCode(),
|
||||||
|
sb.getSwiftCode()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Create a Sponsor Bank", description = "Creates a new Sponsor Bank.")
|
||||||
|
public CommandProcessingResult createSponsorBank(final SponsorBankRequest request) {
|
||||||
|
return safely(() -> {
|
||||||
|
if (request.getIfscCode() != null && !request.getIfscCode().isBlank()
|
||||||
|
&& !request.getIfscCode().trim().toUpperCase().matches("^[A-Z]{4}0[A-Z0-9]{6}$")) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"IFSC Code must be 11 characters: 4 letters, then '0', then 6 alphanumeric characters (e.g. SBIN0001234).");
|
||||||
|
}
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
final SponsorBank sponsorBank = new SponsorBank(
|
||||||
|
request.getName(),
|
||||||
|
request.getDescription(),
|
||||||
|
request.getIfscCode() != null ? request.getIfscCode().trim().toUpperCase() : null,
|
||||||
|
request.getBankCode(),
|
||||||
|
request.getSwiftCode() != null ? request.getSwiftCode().trim().toUpperCase() : null,
|
||||||
|
currentUser,
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.sponsorBankRepository.saveAndFlush(sponsorBank);
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(sponsorBank.getId()).build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class SponsorBankData {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String ifscCode;
|
||||||
|
private String bankCode;
|
||||||
|
private String swiftCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class SponsorBankRequest {
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String ifscCode;
|
||||||
|
private String bankCode;
|
||||||
|
private String swiftCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
+633
@@ -0,0 +1,633 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.api;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.WebApplicationException;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDBatchIssueResult;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDCashierDepositOptionData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDGlMappingData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDInventoryData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDIssueRequestData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDLeafletData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDSlipLine;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDRecoBatch;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDReprintRequestData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.service.SponsorDDIssueRequestWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.service.SponsorDDReadPlatformService;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.service.SponsorDDReconciliationService;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.service.SponsorDDReprintRequestWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.service.SponsorDDWritePlatformService;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/sponsordd")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Sponsor Demand Draft", description = "Endpoints for managing sponsor bank demand draft inventory, leaflet lifecycles, and clearing reconciliation.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SponsorDDApiResource {
|
||||||
|
|
||||||
|
private final SponsorDDReadPlatformService readPlatformService;
|
||||||
|
private final SponsorDDWritePlatformService writePlatformService;
|
||||||
|
private final SponsorDDReconciliationService reconciliationService;
|
||||||
|
private final SponsorDDIssueRequestWritePlatformService issueRequestWritePlatformService;
|
||||||
|
private final SponsorDDReprintRequestWritePlatformService reprintRequestWritePlatformService;
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Business-rule validation in this module is expressed with plain IllegalArgumentException /
|
||||||
|
* IllegalStateException (see SponsorDDWritePlatformServiceImpl / SponsorDDIssueRequestWritePlatformServiceImpl).
|
||||||
|
* Those aren't mapped by Fineract's global exception handler, so left uncaught they surface to the
|
||||||
|
* frontend as an opaque 500. This wraps every write endpoint so validation failures come back as a
|
||||||
|
* proper 400 with the actual message.
|
||||||
|
*/
|
||||||
|
private <T> T safely(final Supplier<T> action) {
|
||||||
|
try {
|
||||||
|
return action.get();
|
||||||
|
} catch (final IllegalArgumentException | IllegalStateException e) {
|
||||||
|
throw apiError(400, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebApplicationException apiError(final int httpStatus, final String message) {
|
||||||
|
final JsonObject body = new JsonObject();
|
||||||
|
body.addProperty("message", message);
|
||||||
|
body.addProperty("defaultUserMessage", message);
|
||||||
|
body.addProperty("httpStatus", httpStatus);
|
||||||
|
final JsonArray errors = new JsonArray();
|
||||||
|
final JsonObject err = new JsonObject();
|
||||||
|
err.addProperty("defaultUserMessage", message);
|
||||||
|
errors.add(err);
|
||||||
|
body.add("errors", errors);
|
||||||
|
return new WebApplicationException(
|
||||||
|
Response.status(httpStatus).entity(this.gson.toJson(body)).type(MediaType.APPLICATION_JSON).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("inventory")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve all DD Inventories", description = "Returns a list of all registered Sponsor DD Inventory blocks.")
|
||||||
|
public Collection<SponsorDDInventoryData> retrieveAllInventories() {
|
||||||
|
return this.readPlatformService.retrieveAllInventories();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("inventory/{inventoryId}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve DD Inventory block details", description = "Returns detail metadata for a specific DD inventory block.")
|
||||||
|
public SponsorDDInventoryData retrieveInventory(@PathParam("inventoryId") final Long inventoryId) {
|
||||||
|
return this.readPlatformService.retrieveInventory(inventoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("inventory")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Register new DD Inventory block", description = "Creates a new DD inventory series and auto-generates associated available leaflets.")
|
||||||
|
public CommandProcessingResult registerInventory(final RegisterInventoryRequest request) {
|
||||||
|
final LocalDate receivedDate = LocalDate.parse(request.getReceivedDate(), this.dateFormatter);
|
||||||
|
return safely(() -> this.writePlatformService.registerInventory(
|
||||||
|
request.getSponsorBankId(),
|
||||||
|
request.getBranchId(),
|
||||||
|
request.getSeriesStart(),
|
||||||
|
request.getSeriesEnd(),
|
||||||
|
receivedDate
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("inventory/{inventoryId}/leaflets")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve all leaflets in an inventory", description = "Returns status registry list of all leaflets under a specific inventory batch.")
|
||||||
|
public Collection<SponsorDDLeafletData> retrieveAllLeafletsByInventory(@PathParam("inventoryId") final Long inventoryId) {
|
||||||
|
return this.readPlatformService.retrieveAllLeafletsByInventory(inventoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("leaflets/{leafletId}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve specific leaflet details", description = "Returns detailed metadata and audit properties for a single leaflet.")
|
||||||
|
public SponsorDDLeafletData retrieveLeaflet(@PathParam("leafletId") final Long leafletId) {
|
||||||
|
return this.readPlatformService.retrieveLeaflet(leafletId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("leaflets/by-number/{ddNumber}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Find leaflet by DD Number", description = "Finds and returns a leaflet's data structure searching by its pre-printed DD Number.")
|
||||||
|
public SponsorDDLeafletData retrieveLeafletByDdNumber(@PathParam("ddNumber") final String ddNumber) {
|
||||||
|
return this.readPlatformService.retrieveLeafletByDdNumber(ddNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("leaflets/{leafletId}/issue")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Issue a demand draft", description = "Transitions an AVAILABLE leaflet to ISSUED and links it to customer debit transactions.")
|
||||||
|
public CommandProcessingResult issueDemandDraft(@PathParam("leafletId") final Long leafletId, final IssueDDRequest request) {
|
||||||
|
final LocalDate issuedDate = LocalDate.parse(request.getIssuedDate(), this.dateFormatter);
|
||||||
|
return safely(() -> this.writePlatformService.issueDemandDraft(
|
||||||
|
leafletId,
|
||||||
|
request.getClientId(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
request.getBeneficiaryName(),
|
||||||
|
request.getFaceValue(),
|
||||||
|
request.getIssuingBranchId(),
|
||||||
|
issuedDate,
|
||||||
|
request.getSavingsTransactionId(),
|
||||||
|
request.getCurrentTransactionId(),
|
||||||
|
request.getJournalEntryId(),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("leaflets/{leafletId}/cancel")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Cancel a demand draft", description = "Transitions an ISSUED leaflet to CANCELLED and maps customer refund details.")
|
||||||
|
public CommandProcessingResult cancelDemandDraft(@PathParam("leafletId") final Long leafletId, final CancelDDRequest request) {
|
||||||
|
return safely(() -> this.writePlatformService.cancelDemandDraft(
|
||||||
|
leafletId,
|
||||||
|
request.getRemarks(),
|
||||||
|
request.getRefundSavingsTransactionId(),
|
||||||
|
request.getRefundCurrentTransactionId()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("leaflets/{leafletId}/stop")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Process Stop Payment instruction", description = "Transitions an ISSUED leaflet to STOPPED.")
|
||||||
|
public CommandProcessingResult stopPaymentDemandDraft(@PathParam("leafletId") final Long leafletId, final StopDDRequest request) {
|
||||||
|
return safely(() -> this.writePlatformService.stopPaymentDemandDraft(leafletId, request.getRemarks()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("leaflets")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve leaflets with filter", description = "Returns status registry list of leaflets filtered by sponsor bank, status, and advice status.")
|
||||||
|
public Collection<SponsorDDLeafletData> retrieveLeaflets(
|
||||||
|
@QueryParam("sponsorBankId") final Long sponsorBankId,
|
||||||
|
@QueryParam("status") final String status,
|
||||||
|
@QueryParam("adviceStatus") final String adviceStatus) {
|
||||||
|
return this.readPlatformService.retrieveLeaflets(sponsorBankId, status, adviceStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("leaflets/send-advice")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Send advice to sponsor bank", description = "Marks all issued, unsent leaflets for a specific sponsor bank as sent.")
|
||||||
|
public CommandProcessingResult sendAdvice(final SendAdviceRequest request) {
|
||||||
|
return safely(() -> this.writePlatformService.sendAdvice(request.getSponsorBankId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Maker-checker DD issuance (clerk submits, manager/head-cashier approves) ──
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("leaflets/{leafletId}/issue-request")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Submit a DD issuance request", description = "Clerk (maker) step: records a pending DD issuance request funded either by an account debit or a cheque. No money moves until a manager approves it.")
|
||||||
|
public CommandProcessingResult submitIssueRequest(@PathParam("leafletId") final Long leafletId, final IssueRequestRequest request) {
|
||||||
|
final LocalDate issuedDate = LocalDate.parse(request.getIssuedDate(), this.dateFormatter);
|
||||||
|
return safely(() -> this.issueRequestWritePlatformService.submitIssueRequest(
|
||||||
|
leafletId,
|
||||||
|
request.getClientId(),
|
||||||
|
request.getWalkinPurchaserName(),
|
||||||
|
request.getWalkinPurchaserAddress(),
|
||||||
|
request.getWalkinPurchaserAadhaar(),
|
||||||
|
request.getBeneficiaryName(),
|
||||||
|
request.getPayableAtBranch(),
|
||||||
|
request.getPayableAtCity(),
|
||||||
|
request.getFaceValue(),
|
||||||
|
request.getChargesAmount(),
|
||||||
|
request.getIssuingBranchId(),
|
||||||
|
issuedDate,
|
||||||
|
request.getPaymentMethod(),
|
||||||
|
request.getSavingsAccountId(),
|
||||||
|
request.getCurrentAccountId(),
|
||||||
|
request.getChequeNumber(),
|
||||||
|
request.getChequeBankName(),
|
||||||
|
request.getCashierDepositRequestId()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("inventory/{inventoryId}/issue-request-batch")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Submit a multi-DD slip", description = "Clerk (maker) step: one account debit or cheque funds several drafts (different beneficiaries/amounts, charges optional per line) submitted together. A physical leaflet is auto-allocated per line from this inventory - the response shows exactly which DD number and beneficiary/amount was assigned to each line. No money moves until a manager approves the whole slip.")
|
||||||
|
public SponsorDDBatchIssueResult submitIssueRequestBatch(@PathParam("inventoryId") final Long inventoryId,
|
||||||
|
final IssueRequestBatchRequest request) {
|
||||||
|
final LocalDate issuedDate = LocalDate.parse(request.getIssuedDate(), this.dateFormatter);
|
||||||
|
final List<SponsorDDSlipLine> lines = request.getLines() == null ? List.of()
|
||||||
|
: request.getLines().stream()
|
||||||
|
.map(l -> new SponsorDDSlipLine(l.getBeneficiaryName(), l.getPayableAtBranch(), l.getPayableAtCity(),
|
||||||
|
l.getFaceValue(), l.getChargesAmount()))
|
||||||
|
.collect(java.util.stream.Collectors.toList());
|
||||||
|
return safely(() -> this.issueRequestWritePlatformService.submitIssueRequestBatch(
|
||||||
|
inventoryId,
|
||||||
|
request.getClientId(),
|
||||||
|
request.getIssuingBranchId(),
|
||||||
|
issuedDate,
|
||||||
|
request.getPaymentMethod(),
|
||||||
|
request.getSavingsAccountId(),
|
||||||
|
request.getCurrentAccountId(),
|
||||||
|
request.getChequeNumber(),
|
||||||
|
request.getChequeBankName(),
|
||||||
|
lines
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("issue-requests/batch/{batchId}/approve")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Approve a multi-DD slip", description = "Manager/head-cashier (checker) step: one combined GL posting for the whole slip's total, then issues every draft in it. Approves every line together, not one at a time.")
|
||||||
|
public CommandProcessingResult approveIssueRequestBatch(@PathParam("batchId") final Long batchId) {
|
||||||
|
return safely(() -> this.issueRequestWritePlatformService.approveIssueRequestBatch(batchId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("issue-requests/batch/{batchId}/reject")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Reject a multi-DD slip", description = "Manager/head-cashier (checker) step: rejects every line of a pending slip together. All leaflets remain AVAILABLE.")
|
||||||
|
public CommandProcessingResult rejectIssueRequestBatch(@PathParam("batchId") final Long batchId, final RejectIssueRequestRequest request) {
|
||||||
|
return safely(() -> this.issueRequestWritePlatformService.rejectIssueRequestBatch(batchId, request.getRejectionReason()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("cashier-deposit-requests/available")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve available cashier deposit requests for CASH DD issuance", description = "Returns approved cashier Transaction Entry (AC.TP=GL) deposit requests credited to the DD Cash Suspense GL account, not yet linked to any DD issuance request.")
|
||||||
|
public Collection<SponsorDDCashierDepositOptionData> retrieveAvailableCashierDepositRequests() {
|
||||||
|
return this.readPlatformService.retrieveAvailableCashierDepositRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("issue-requests")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve DD issuance requests", description = "Returns all DD issuance requests, optionally filtered by status (PENDING/APPROVED/REJECTED), for the manager approval queue.")
|
||||||
|
public Collection<SponsorDDIssueRequestData> retrieveAllIssueRequests(@QueryParam("status") final String status) {
|
||||||
|
return this.readPlatformService.retrieveAllIssueRequests(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("issue-requests/mine")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve my DD issuance requests", description = "Returns the DD issuance requests submitted by the current clerk.")
|
||||||
|
public Collection<SponsorDDIssueRequestData> retrieveMyIssueRequests() {
|
||||||
|
return this.readPlatformService.retrieveMyIssueRequests(this.context.authenticatedUser().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("issue-requests/{issueRequestId}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve a DD issuance request", description = "Returns detail for a single DD issuance request.")
|
||||||
|
public SponsorDDIssueRequestData retrieveIssueRequest(@PathParam("issueRequestId") final Long issueRequestId) {
|
||||||
|
return this.readPlatformService.retrieveIssueRequest(issueRequestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("issue-requests/{issueRequestId}/approve")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Approve a DD issuance request", description = "Manager/head-cashier (checker) step: performs the real GL posting (client account debit or cheque-clearing debit, credited to the Sponsor DD Payable GL account) and issues the demand draft.")
|
||||||
|
public CommandProcessingResult approveIssueRequest(@PathParam("issueRequestId") final Long issueRequestId) {
|
||||||
|
return safely(() -> this.issueRequestWritePlatformService.approveIssueRequest(issueRequestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("issue-requests/{issueRequestId}/reject")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Reject a DD issuance request", description = "Manager/head-cashier (checker) step: rejects a pending DD issuance request. The leaflet remains AVAILABLE.")
|
||||||
|
public CommandProcessingResult rejectIssueRequest(@PathParam("issueRequestId") final Long issueRequestId, final RejectIssueRequestRequest request) {
|
||||||
|
return safely(() -> this.issueRequestWritePlatformService.rejectIssueRequest(issueRequestId, request.getRejectionReason()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── DD reprint requests (maker-checker) ─────────────────────────────────
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("leaflets/{leafletId}/print")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Record a print of an issued DD", description = "First print of a leaflet is always allowed. Any print after that consumes a previously-approved reprint request, or fails if none exists.")
|
||||||
|
public CommandProcessingResult recordPrint(@PathParam("leafletId") final Long leafletId) {
|
||||||
|
return safely(() -> this.reprintRequestWritePlatformService.recordPrint(leafletId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("leaflets/{leafletId}/reprint-request")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Request permission to reprint a DD", description = "Clerk (maker) step: requests manager approval to print an already-printed leaflet again.")
|
||||||
|
public CommandProcessingResult requestReprint(@PathParam("leafletId") final Long leafletId, final ReprintRequestRequest request) {
|
||||||
|
return safely(() -> this.reprintRequestWritePlatformService.requestReprint(leafletId, request.getReason()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("reprint-requests")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve DD reprint requests", description = "Returns all DD reprint requests, optionally filtered by status (PENDING/APPROVED/REJECTED), for the manager approval queue.")
|
||||||
|
public Collection<SponsorDDReprintRequestData> retrieveAllReprintRequests(@QueryParam("status") final String status) {
|
||||||
|
return this.readPlatformService.retrieveAllReprintRequests(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("reprint-requests/{reprintRequestId}/approve")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Approve a DD reprint request", description = "Manager/head-cashier (checker) step: allows one further print of the leaflet.")
|
||||||
|
public CommandProcessingResult approveReprintRequest(@PathParam("reprintRequestId") final Long reprintRequestId) {
|
||||||
|
return safely(() -> this.reprintRequestWritePlatformService.approveReprintRequest(reprintRequestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("reprint-requests/{reprintRequestId}/reject")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Reject a DD reprint request", description = "Manager/head-cashier (checker) step: rejects a pending DD reprint request.")
|
||||||
|
public CommandProcessingResult rejectReprintRequest(@PathParam("reprintRequestId") final Long reprintRequestId, final RejectIssueRequestRequest request) {
|
||||||
|
return safely(() -> this.reprintRequestWritePlatformService.rejectReprintRequest(reprintRequestId, request.getRejectionReason()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Sponsor DD GL account configuration ───────────────────────────────
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("gl-mapping")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve Sponsor DD GL mappings", description = "Returns the configured GL accounts (DD Payable liability + cheque-clearing suspense) for each sponsor bank / payment type.")
|
||||||
|
public Collection<SponsorDDGlMappingData> retrieveAllGlMappings() {
|
||||||
|
return this.readPlatformService.retrieveAllGlMappings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("gl-mapping")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Configure a Sponsor DD GL mapping", description = "Creates or updates the DD Payable / cheque-clearing GL accounts for a sponsor bank / payment type.")
|
||||||
|
public CommandProcessingResult upsertGlMapping(final GlMappingRequest request) {
|
||||||
|
return safely(() -> this.issueRequestWritePlatformService.upsertGlMapping(
|
||||||
|
request.getSponsorBankId(),
|
||||||
|
request.getDdPayableGlAccountId(),
|
||||||
|
request.getChequeClearingGlAccountId(),
|
||||||
|
request.getCashGlAccountId(),
|
||||||
|
request.getChargesIncomeGlAccountId(),
|
||||||
|
request.getCashSuspenseGlAccountId(),
|
||||||
|
request.getGstPercentage(),
|
||||||
|
request.getGstPayableGlAccountId(),
|
||||||
|
request.getDdLimitAmount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("reconcile/upload")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Upload and process clearing scroll", description = "Imports Sponsor Bank clearing scroll content, matching DD numbers and face values.")
|
||||||
|
public RecoBatchResponse uploadAndReconcile(final RecoUploadRequest request) {
|
||||||
|
return safely(() -> {
|
||||||
|
final SponsorDDRecoBatch batch = this.reconciliationService.uploadAndReconcile(request.getFileName(), request.getFileContent());
|
||||||
|
return new RecoBatchResponse(
|
||||||
|
batch.getId(),
|
||||||
|
batch.getBatchReference(),
|
||||||
|
batch.getStatus(),
|
||||||
|
batch.getTotalRecords(),
|
||||||
|
batch.getSuccessCount(),
|
||||||
|
batch.getMismatchCount()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("reconcile/resolve/{recordId}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Resolve matching exception", description = "Manually resolves a reconciliation mismatch by approving force-match matching.")
|
||||||
|
public CommandProcessingResult resolveMismatch(@PathParam("recordId") final Long recordId, final ResolveMismatchRequest request) {
|
||||||
|
return safely(() -> this.reconciliationService.resolveMismatch(
|
||||||
|
recordId,
|
||||||
|
request.getActionType(),
|
||||||
|
request.getResolvedSavingsTxnId(),
|
||||||
|
request.getResolvedCurrentTxnId()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("reconcile/batches")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve all reconciliation batches", description = "Returns a list of all clearing scrolls processed.")
|
||||||
|
public Collection<org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoBatchData> retrieveAllReconciliationBatches() {
|
||||||
|
return this.readPlatformService.retrieveAllReconciliationBatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("reconcile/batches/{batchId}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve reconciliation batch detail", description = "Returns metadata for a specific reconciliation upload batch.")
|
||||||
|
public org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoBatchData retrieveReconciliationBatch(@PathParam("batchId") final Long batchId) {
|
||||||
|
return this.readPlatformService.retrieveReconciliationBatch(batchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("reconcile/batches/{batchId}/records")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve records in a reconciliation batch", description = "Returns all matched and exception scroll records within a specific batch.")
|
||||||
|
public Collection<org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoRecordData> retrieveReconciliationRecordsByBatch(@PathParam("batchId") final Long batchId) {
|
||||||
|
return this.readPlatformService.retrieveReconciliationRecordsByBatch(batchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requests DTOs
|
||||||
|
@lombok.Data
|
||||||
|
public static class RegisterInventoryRequest {
|
||||||
|
private Long sponsorBankId;
|
||||||
|
private Long branchId;
|
||||||
|
private Long seriesStart;
|
||||||
|
private Long seriesEnd;
|
||||||
|
private String receivedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class IssueDDRequest {
|
||||||
|
private Long clientId;
|
||||||
|
private String beneficiaryName;
|
||||||
|
private BigDecimal faceValue;
|
||||||
|
private Long issuingBranchId;
|
||||||
|
private String issuedDate;
|
||||||
|
private Long savingsTransactionId;
|
||||||
|
private Long currentTransactionId;
|
||||||
|
private Long journalEntryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class CancelDDRequest {
|
||||||
|
private String remarks;
|
||||||
|
private Long refundSavingsTransactionId;
|
||||||
|
private Long refundCurrentTransactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class StopDDRequest {
|
||||||
|
private String remarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class RecoUploadRequest {
|
||||||
|
private String fileName;
|
||||||
|
private String fileContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class ResolveMismatchRequest {
|
||||||
|
private String actionType; // FORCE_MATCH
|
||||||
|
private Long resolvedSavingsTxnId;
|
||||||
|
private Long resolvedCurrentTxnId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class IssueRequestRequest {
|
||||||
|
private Long clientId;
|
||||||
|
// Walk-in (non-client) purchaser details — required instead of clientId when paymentMethod is CASH.
|
||||||
|
private String walkinPurchaserName;
|
||||||
|
private String walkinPurchaserAddress;
|
||||||
|
private String walkinPurchaserAadhaar; // raw 12-digit Aadhaar — masked to last 4 digits before storage
|
||||||
|
private String beneficiaryName;
|
||||||
|
private String payableAtBranch;
|
||||||
|
private String payableAtCity;
|
||||||
|
private BigDecimal faceValue;
|
||||||
|
private BigDecimal chargesAmount; // DD issuance commission/charges, if any
|
||||||
|
private Long issuingBranchId;
|
||||||
|
private String issuedDate;
|
||||||
|
private String paymentMethod; // ACCOUNT_DEBIT, CHEQUE, or CASH
|
||||||
|
private Long savingsAccountId;
|
||||||
|
private Long currentAccountId;
|
||||||
|
private String chequeNumber;
|
||||||
|
private String chequeBankName;
|
||||||
|
// CASH only — the cashier's already-APPROVED Transaction Entry (AC.TP=GL) deposit request
|
||||||
|
// that received this cash, credited to the DD Cash Suspense GL account.
|
||||||
|
private Long cashierDepositRequestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class IssueRequestBatchRequest {
|
||||||
|
private Long clientId;
|
||||||
|
private Long issuingBranchId;
|
||||||
|
private String issuedDate;
|
||||||
|
private String paymentMethod; // ACCOUNT_DEBIT or CHEQUE only — a slip does not support CASH
|
||||||
|
private Long savingsAccountId;
|
||||||
|
private Long currentAccountId;
|
||||||
|
private String chequeNumber;
|
||||||
|
private String chequeBankName;
|
||||||
|
private List<SlipLineRequest> lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class SlipLineRequest {
|
||||||
|
private String beneficiaryName;
|
||||||
|
private String payableAtBranch;
|
||||||
|
private String payableAtCity;
|
||||||
|
private BigDecimal faceValue;
|
||||||
|
private BigDecimal chargesAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class RejectIssueRequestRequest {
|
||||||
|
private String rejectionReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class ReprintRequestRequest {
|
||||||
|
private String reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class GlMappingRequest {
|
||||||
|
private Long sponsorBankId;
|
||||||
|
private Long ddPayableGlAccountId;
|
||||||
|
private Long chequeClearingGlAccountId;
|
||||||
|
private Long cashGlAccountId;
|
||||||
|
private Long chargesIncomeGlAccountId;
|
||||||
|
private Long cashSuspenseGlAccountId;
|
||||||
|
private BigDecimal gstPercentage;
|
||||||
|
private Long gstPayableGlAccountId;
|
||||||
|
private BigDecimal ddLimitAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
@lombok.AllArgsConstructor
|
||||||
|
public static class RecoBatchResponse {
|
||||||
|
private Long id;
|
||||||
|
private String batchReference;
|
||||||
|
private String status;
|
||||||
|
private Integer totalRecords;
|
||||||
|
private Integer successCount;
|
||||||
|
private Integer mismatchCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public static class SendAdviceRequest {
|
||||||
|
private Long sponsorBankId;
|
||||||
|
}
|
||||||
|
}
|
||||||
+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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response for submitIssueRequestBatch - tells the maker exactly which physical leaflet (DD number)
|
||||||
|
* was auto-allocated to each line and for what amount, since that allocation only happens inside the
|
||||||
|
* submit transaction (locked against concurrent slips) and can't be known beforehand.
|
||||||
|
*/
|
||||||
|
public record SponsorDDBatchIssueResult(Long batchId, List<Line> lines) {
|
||||||
|
|
||||||
|
public record Line(Long issueRequestId, Long leafletId, String ddNumber, String beneficiaryName, BigDecimal faceValue) {}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cashier's Transaction Entry (AC.TP=GL) deposit request, already approved, credited to the DD
|
||||||
|
* Cash Suspense GL account, and not yet linked to any Sponsor DD issue request - i.e. available to
|
||||||
|
* be selected as the cash-receipt evidence for a new CASH DD issuance request.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDCashierDepositOptionData implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String voucherNo;
|
||||||
|
private BigDecimal amount;
|
||||||
|
private String description;
|
||||||
|
private String cashierUsername;
|
||||||
|
private LocalDateTime actionedAt;
|
||||||
|
// DD face value / charges / GST breakdown captured at Transaction Entry submit time - null for
|
||||||
|
// deposit requests that were not raised as a DD + Cash + GL row.
|
||||||
|
private BigDecimal ddFaceValue;
|
||||||
|
private BigDecimal ddCharges;
|
||||||
|
private BigDecimal ddGstPercentage;
|
||||||
|
private BigDecimal ddGstAmount;
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDGlMappingData implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long sponsorBankId;
|
||||||
|
private String sponsorBankName;
|
||||||
|
private Long ddPayableGlAccountId;
|
||||||
|
private String ddPayableGlAccountName;
|
||||||
|
private Long chequeClearingGlAccountId;
|
||||||
|
private String chequeClearingGlAccountName;
|
||||||
|
private Long cashGlAccountId;
|
||||||
|
private String cashGlAccountName;
|
||||||
|
private Long chargesIncomeGlAccountId;
|
||||||
|
private String chargesIncomeGlAccountName;
|
||||||
|
private Long cashSuspenseGlAccountId;
|
||||||
|
private String cashSuspenseGlAccountName;
|
||||||
|
private BigDecimal gstPercentage;
|
||||||
|
private Long gstPayableGlAccountId;
|
||||||
|
private String gstPayableGlAccountName;
|
||||||
|
private BigDecimal ddLimitAmount;
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDInventoryData implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long sponsorBankId;
|
||||||
|
private String sponsorBankName;
|
||||||
|
private Long branchId;
|
||||||
|
private String branchName;
|
||||||
|
private Long seriesStart;
|
||||||
|
private Long seriesEnd;
|
||||||
|
private Integer totalLeaves;
|
||||||
|
private LocalDate receivedDate;
|
||||||
|
private String status;
|
||||||
|
/** Leaflets in this inventory still in AVAILABLE status (not yet issued/cancelled/stopped). */
|
||||||
|
private Integer availableLeaves;
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDIssueRequestData implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long leafletId;
|
||||||
|
private String ddNumber;
|
||||||
|
private Long clientId;
|
||||||
|
private String clientName;
|
||||||
|
private String beneficiaryName;
|
||||||
|
private String payableAtBranch;
|
||||||
|
private String payableAtCity;
|
||||||
|
private BigDecimal faceValue;
|
||||||
|
private Long issuingBranchId;
|
||||||
|
private String issuingBranchName;
|
||||||
|
private LocalDate issuedDate;
|
||||||
|
private String paymentMethod;
|
||||||
|
private Long savingsAccountId;
|
||||||
|
private Long currentAccountId;
|
||||||
|
private String chequeNumber;
|
||||||
|
private String chequeBankName;
|
||||||
|
private String walkinPurchaserName;
|
||||||
|
private String walkinPurchaserAddress;
|
||||||
|
private String walkinPurchaserAadhaarMasked;
|
||||||
|
private BigDecimal chargesAmount;
|
||||||
|
private BigDecimal gstAmount;
|
||||||
|
private BigDecimal gstPercentageApplied;
|
||||||
|
private String status;
|
||||||
|
private Long makerId;
|
||||||
|
private String makerUsername;
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
private Long checkerId;
|
||||||
|
private String checkerUsername;
|
||||||
|
private LocalDateTime checkerDate;
|
||||||
|
private String rejectionReason;
|
||||||
|
private Long resultingSavingsTransactionId;
|
||||||
|
private Long resultingCurrentTransactionId;
|
||||||
|
private Long resultingJournalEntryId;
|
||||||
|
private Long cashReceiptJournalEntryId;
|
||||||
|
private Long cashRefundJournalEntryId;
|
||||||
|
/** Groups rows submitted together as one multi-DD slip (see submitIssueRequestBatch). Null for an ordinary single-DD request. */
|
||||||
|
private Long batchId;
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDLeafletData implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long inventoryId;
|
||||||
|
private String ddNumber;
|
||||||
|
private String status;
|
||||||
|
private BigDecimal faceValue;
|
||||||
|
private Long issuedToClientId;
|
||||||
|
private String issuedToClientName;
|
||||||
|
private String walkinPurchaserName;
|
||||||
|
private String walkinPurchaserAddress;
|
||||||
|
private String walkinPurchaserAadhaarMasked;
|
||||||
|
private String beneficiaryName;
|
||||||
|
private String payableAtBranch;
|
||||||
|
private String payableAtCity;
|
||||||
|
private Long issuingBranchId;
|
||||||
|
private String issuingBranchName;
|
||||||
|
private LocalDate issuedDate;
|
||||||
|
private LocalDate clearedDate;
|
||||||
|
private String adviceStatus;
|
||||||
|
private LocalDateTime adviceSentDate;
|
||||||
|
private Long pendingIssueRequestId;
|
||||||
|
private Integer printCount;
|
||||||
|
private Long pendingReprintRequestId;
|
||||||
|
private Long approvedUnconsumedReprintRequestId;
|
||||||
|
private Long approverUserId;
|
||||||
|
private String approverUsername;
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDRecoBatchData implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String batchReference;
|
||||||
|
private String fileName;
|
||||||
|
private String fileHash;
|
||||||
|
private String status;
|
||||||
|
private Integer totalRecords;
|
||||||
|
private BigDecimal totalAmount;
|
||||||
|
private Integer successCount;
|
||||||
|
private Integer mismatchCount;
|
||||||
|
private String createdByUsername;
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDRecoRecordData implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long batchId;
|
||||||
|
private String ddNumber;
|
||||||
|
private BigDecimal clearingAmount;
|
||||||
|
private LocalDate clearingDate;
|
||||||
|
private String clearingRefNumber;
|
||||||
|
private String matchStatus;
|
||||||
|
private Long matchedLeafletId;
|
||||||
|
private String remarks;
|
||||||
|
private String resolvedByUsername;
|
||||||
|
private LocalDateTime resolvedDate;
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDReprintRequestData implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long leafletId;
|
||||||
|
private String ddNumber;
|
||||||
|
private String reason;
|
||||||
|
private String status;
|
||||||
|
private Boolean consumed;
|
||||||
|
private Long makerId;
|
||||||
|
private String makerUsername;
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
private Long checkerId;
|
||||||
|
private String checkerUsername;
|
||||||
|
private LocalDateTime checkerDate;
|
||||||
|
private String rejectionReason;
|
||||||
|
}
|
||||||
+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.sponsordd.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One line of a multi-DD "slip" - a customer fills one slip listing several drafts (different
|
||||||
|
* beneficiaries/amounts, charges optional per line) funded from one account debit or one cheque; the
|
||||||
|
* combined total across every line is what actually gets deducted. Everything else (payment method,
|
||||||
|
* account, cheque details, issuing branch, issued date) is shared across the whole slip and carried
|
||||||
|
* separately, not per line.
|
||||||
|
*/
|
||||||
|
public record SponsorDDSlipLine(String beneficiaryName, String payableAtBranch, String payableAtCity, BigDecimal faceValue,
|
||||||
|
BigDecimal chargesAmount) {}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_sponsor_bank")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorBank extends AbstractPersistableCustom<Long> {
|
||||||
|
|
||||||
|
@Column(name = "name", nullable = false, unique = true, length = 100)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "description", length = 255)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "ifsc_code", length = 11)
|
||||||
|
private String ifscCode;
|
||||||
|
|
||||||
|
@Column(name = "bank_code", length = 20)
|
||||||
|
private String bankCode;
|
||||||
|
|
||||||
|
@Column(name = "swift_code", length = 11)
|
||||||
|
private String swiftCode;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "createdby_id", nullable = false)
|
||||||
|
private AppUser createdBy;
|
||||||
|
|
||||||
|
@Column(name = "created_date", nullable = false)
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
}
|
||||||
+27
@@ -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.sponsordd.domain;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface SponsorBankRepository extends JpaRepository<SponsorBank, Long>, JpaSpecificationExecutor<SponsorBank> {
|
||||||
|
Optional<SponsorBank> findByName(String name);
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_sponsor_dd_audit_log")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDAuditLog extends AbstractPersistableCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "leaflet_id", nullable = false)
|
||||||
|
private SponsorDDLeaflet leaflet;
|
||||||
|
|
||||||
|
@Column(name = "action_type", nullable = false, length = 30)
|
||||||
|
private String actionType;
|
||||||
|
|
||||||
|
@Column(name = "pre_status", length = 20)
|
||||||
|
private String preStatus;
|
||||||
|
|
||||||
|
@Column(name = "post_status", nullable = false, length = 20)
|
||||||
|
private String postStatus;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "changed_by_id", nullable = false)
|
||||||
|
private AppUser changedBy;
|
||||||
|
|
||||||
|
@Column(name = "client_ip", nullable = false, length = 45)
|
||||||
|
private String clientIp;
|
||||||
|
|
||||||
|
@Column(name = "payload_snapshot")
|
||||||
|
private String payloadSnapshot;
|
||||||
|
|
||||||
|
@Column(name = "remarks", length = 255)
|
||||||
|
private String remarks;
|
||||||
|
|
||||||
|
@Column(name = "created_date", nullable = false)
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.domain;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface SponsorDDAuditLogRepository extends JpaRepository<SponsorDDAuditLog, Long>, JpaSpecificationExecutor<SponsorDDAuditLog> {
|
||||||
|
}
|
||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_sponsor_dd_inventory")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDInventory extends AbstractPersistableCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "sponsor_bank_id", nullable = false)
|
||||||
|
private SponsorBank sponsorBank;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "branch_id", nullable = false)
|
||||||
|
private Office branch;
|
||||||
|
|
||||||
|
@Column(name = "series_start", nullable = false)
|
||||||
|
private Long seriesStart;
|
||||||
|
|
||||||
|
@Column(name = "series_end", nullable = false)
|
||||||
|
private Long seriesEnd;
|
||||||
|
|
||||||
|
@Column(name = "total_leaves", nullable = false)
|
||||||
|
private Integer totalLeaves;
|
||||||
|
|
||||||
|
@Column(name = "received_date", nullable = false)
|
||||||
|
private LocalDate receivedDate;
|
||||||
|
|
||||||
|
@Column(name = "status", nullable = false, length = 20)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "createdby_id", nullable = false)
|
||||||
|
private AppUser createdBy;
|
||||||
|
|
||||||
|
@Column(name = "created_date", nullable = false)
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "lastmodifiedby_id")
|
||||||
|
private AppUser lastModifiedBy;
|
||||||
|
|
||||||
|
@Column(name = "lastmodified_date")
|
||||||
|
private LocalDateTime lastModifiedDate;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.domain;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface SponsorDDInventoryRepository extends JpaRepository<SponsorDDInventory, Long>, JpaSpecificationExecutor<SponsorDDInventory> {
|
||||||
|
}
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_sponsor_dd_leaflet")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDLeaflet extends AbstractPersistableCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "inventory_id", nullable = false)
|
||||||
|
private SponsorDDInventory inventory;
|
||||||
|
|
||||||
|
@Column(name = "dd_number", nullable = false, unique = true, length = 30)
|
||||||
|
private String ddNumber;
|
||||||
|
|
||||||
|
@Column(name = "status", nullable = false, length = 20)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Column(name = "face_value", nullable = false, precision = 19, scale = 6)
|
||||||
|
private BigDecimal faceValue;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "issued_to_client_id")
|
||||||
|
private Client issuedToClient;
|
||||||
|
|
||||||
|
@Column(name = "beneficiary_name", length = 150)
|
||||||
|
private String beneficiaryName;
|
||||||
|
|
||||||
|
@Column(name = "payable_at_branch", length = 150)
|
||||||
|
private String payableAtBranch;
|
||||||
|
|
||||||
|
@Column(name = "payable_at_city", length = 100)
|
||||||
|
private String payableAtCity;
|
||||||
|
|
||||||
|
/** Set instead of issuedToClient when the purchaser is a walk-in, not a bank client (cash payment). */
|
||||||
|
@Column(name = "walkin_purchaser_name", length = 150)
|
||||||
|
private String walkinPurchaserName;
|
||||||
|
|
||||||
|
@Column(name = "walkin_purchaser_address", length = 255)
|
||||||
|
private String walkinPurchaserAddress;
|
||||||
|
|
||||||
|
/** Stored masked (only the last 4 digits kept) — never the full Aadhaar number. */
|
||||||
|
@Column(name = "walkin_purchaser_aadhaar_masked", length = 20)
|
||||||
|
private String walkinPurchaserAadhaarMasked;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "issuing_branch_id")
|
||||||
|
private Office issuingBranch;
|
||||||
|
|
||||||
|
@Column(name = "issued_date")
|
||||||
|
private LocalDate issuedDate;
|
||||||
|
|
||||||
|
@Column(name = "cleared_date")
|
||||||
|
private LocalDate clearedDate;
|
||||||
|
|
||||||
|
@Column(name = "advice_status", nullable = false, length = 20)
|
||||||
|
private String adviceStatus;
|
||||||
|
|
||||||
|
@Column(name = "advice_sent_date")
|
||||||
|
private LocalDateTime adviceSentDate;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
@Column(name = "version", nullable = false)
|
||||||
|
private Integer version;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "createdby_id", nullable = false)
|
||||||
|
private AppUser createdBy;
|
||||||
|
|
||||||
|
@Column(name = "created_date", nullable = false)
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "lastmodifiedby_id")
|
||||||
|
private AppUser lastModifiedBy;
|
||||||
|
|
||||||
|
@Column(name = "lastmodified_date")
|
||||||
|
private LocalDateTime lastModifiedDate;
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.domain;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface SponsorDDLeafletRepository extends JpaRepository<SponsorDDLeaflet, Long>, JpaSpecificationExecutor<SponsorDDLeaflet> {
|
||||||
|
|
||||||
|
Optional<SponsorDDLeaflet> findByDdNumber(String ddNumber);
|
||||||
|
}
|
||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.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.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_sponsor_dd_reco_batch")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDRecoBatch extends AbstractPersistableCustom<Long> {
|
||||||
|
|
||||||
|
@Column(name = "batch_reference", nullable = false, unique = true, length = 50)
|
||||||
|
private String batchReference;
|
||||||
|
|
||||||
|
@Column(name = "file_name", nullable = false, length = 150)
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Column(name = "file_hash", nullable = false, length = 64)
|
||||||
|
private String fileHash;
|
||||||
|
|
||||||
|
@Column(name = "status", nullable = false, length = 20)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Column(name = "total_records", nullable = false)
|
||||||
|
private Integer totalRecords;
|
||||||
|
|
||||||
|
@Column(name = "total_amount", nullable = false, precision = 19, scale = 6)
|
||||||
|
private BigDecimal totalAmount;
|
||||||
|
|
||||||
|
@Column(name = "success_count", nullable = false)
|
||||||
|
private Integer successCount;
|
||||||
|
|
||||||
|
@Column(name = "mismatch_count", nullable = false)
|
||||||
|
private Integer mismatchCount;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "createdby_id", nullable = false)
|
||||||
|
private AppUser createdBy;
|
||||||
|
|
||||||
|
@Column(name = "created_date", nullable = false)
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "lastmodifiedby_id")
|
||||||
|
private AppUser lastModifiedBy;
|
||||||
|
|
||||||
|
@Column(name = "lastmodified_date")
|
||||||
|
private LocalDateTime lastModifiedDate;
|
||||||
|
}
|
||||||
+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.sponsordd.domain;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface SponsorDDRecoBatchRepository extends JpaRepository<SponsorDDRecoBatch, Long>, JpaSpecificationExecutor<SponsorDDRecoBatch> {
|
||||||
|
|
||||||
|
Optional<SponsorDDRecoBatch> findByFileHash(String fileHash);
|
||||||
|
Optional<SponsorDDRecoBatch> findByBatchReference(String batchReference);
|
||||||
|
}
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.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.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_sponsor_dd_reco_record")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDRecoRecord extends AbstractPersistableCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "batch_id", nullable = false)
|
||||||
|
private SponsorDDRecoBatch batch;
|
||||||
|
|
||||||
|
@Column(name = "dd_number", nullable = false, length = 30)
|
||||||
|
private String ddNumber;
|
||||||
|
|
||||||
|
@Column(name = "clearing_amount", nullable = false, precision = 19, scale = 6)
|
||||||
|
private BigDecimal clearingAmount;
|
||||||
|
|
||||||
|
@Column(name = "clearing_date", nullable = false)
|
||||||
|
private LocalDate clearingDate;
|
||||||
|
|
||||||
|
@Column(name = "clearing_ref_number", nullable = false, length = 50)
|
||||||
|
private String clearingRefNumber;
|
||||||
|
|
||||||
|
@Column(name = "match_status", nullable = false, length = 25)
|
||||||
|
private String matchStatus;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "matched_leaflet_id")
|
||||||
|
private SponsorDDLeaflet matchedLeaflet;
|
||||||
|
|
||||||
|
@Column(name = "remarks", length = 255)
|
||||||
|
private String remarks;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "resolved_by_id")
|
||||||
|
private AppUser resolvedBy;
|
||||||
|
|
||||||
|
@Column(name = "resolved_date")
|
||||||
|
private LocalDateTime resolvedDate;
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface SponsorDDRecoRecordRepository extends JpaRepository<SponsorDDRecoRecord, Long>, JpaSpecificationExecutor<SponsorDDRecoRecord> {
|
||||||
|
|
||||||
|
List<SponsorDDRecoRecord> findByBatchId(Long batchId);
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.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.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_sponsor_dd_transaction")
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SponsorDDTransaction extends AbstractPersistableCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "leaflet_id", nullable = false)
|
||||||
|
private SponsorDDLeaflet leaflet;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "savings_transaction_id")
|
||||||
|
private SavingsAccountTransaction savingsTransaction;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "current_transaction_id")
|
||||||
|
private CurrentAccountTransaction currentTransaction;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "journal_entry_id")
|
||||||
|
private JournalEntry journalEntry;
|
||||||
|
|
||||||
|
@Column(name = "transaction_type", nullable = false, length = 20)
|
||||||
|
private String transactionType;
|
||||||
|
|
||||||
|
@Column(name = "amount", nullable = false, precision = 19, scale = 6)
|
||||||
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
@Column(name = "created_date", nullable = false)
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.domain;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
|
public interface SponsorDDTransactionRepository extends JpaRepository<SponsorDDTransaction, Long>, JpaSpecificationExecutor<SponsorDDTransaction> {
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDBatchIssueResult;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDSlipLine;
|
||||||
|
|
||||||
|
public interface SponsorDDIssueRequestWritePlatformService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clerk (maker) step: records a PENDING issue request.
|
||||||
|
* clientId is required for ACCOUNT_DEBIT/CHEQUE; for CASH it must be null and the walk-in
|
||||||
|
* purchaser's own details (name, address, raw Aadhaar — masked to last 4 digits before storage)
|
||||||
|
* are supplied instead, since the purchaser need not be a bank client at all. For CASH,
|
||||||
|
* cashierDepositRequestId must reference an already-APPROVED cashier deposit request (submitted
|
||||||
|
* by the cashier via Transaction Entry, AC.TP=GL, target = the DD Cash Suspense GL account) for
|
||||||
|
* exactly the face value + charges + GST total - the cash itself is never posted by this class;
|
||||||
|
* it's only validated and linked here, since the cashier's own maker-checker flow already moved
|
||||||
|
* the money and updated their tracked cash position.
|
||||||
|
*/
|
||||||
|
CommandProcessingResult submitIssueRequest(Long leafletId, Long clientId, String walkinPurchaserName,
|
||||||
|
String walkinPurchaserAddress, String walkinPurchaserAadhaar, String beneficiaryName, String payableAtBranch,
|
||||||
|
String payableAtCity, BigDecimal faceValue, BigDecimal chargesAmount, Long issuingBranchId, LocalDate issuedDate,
|
||||||
|
String paymentMethod, Long savingsAccountId, Long currentAccountId, String chequeNumber, String chequeBankName,
|
||||||
|
Long cashierDepositRequestId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager/head-cashier (checker) step: performs the real debit/credit GL posting
|
||||||
|
* (client account debit, cheque-clearing GL debit, or cash-in-hand GL debit, credited to
|
||||||
|
* Sponsor DD Payable — plus a Charges Income credit leg if charges apply), then issues the
|
||||||
|
* demand draft.
|
||||||
|
*/
|
||||||
|
CommandProcessingResult approveIssueRequest(Long issueRequestId);
|
||||||
|
|
||||||
|
CommandProcessingResult rejectIssueRequest(Long issueRequestId, String rejectionReason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clerk (maker) step for a multi-DD "slip": one customer account debit (or one cheque) funds
|
||||||
|
* several drafts in one submission - a physical leaflet is auto-allocated per line from the given
|
||||||
|
* inventory, and every resulting row shares one batch id so the checker approves/rejects the whole
|
||||||
|
* slip as a unit rather than line by line. CASH is not supported here - only ACCOUNT_DEBIT/CHEQUE,
|
||||||
|
* since a cash slip would also need Transaction Entry changes to record one lump intake for N
|
||||||
|
* drafts, which is a separate piece of work. Returns exactly which physical leaflet (DD number)
|
||||||
|
* was auto-allocated to each line and for what amount, since that allocation only happens inside
|
||||||
|
* this call (locked against a concurrent slip grabbing the same leaflet) and can't be known by the
|
||||||
|
* caller beforehand.
|
||||||
|
*/
|
||||||
|
SponsorDDBatchIssueResult submitIssueRequestBatch(Long inventoryId, Long clientId, Long issuingBranchId, LocalDate issuedDate,
|
||||||
|
String paymentMethod, Long savingsAccountId, Long currentAccountId, String chequeNumber, String chequeBankName,
|
||||||
|
List<SponsorDDSlipLine> lines);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checker step for a multi-DD slip: one combined withdrawal for the slip's total face value, one
|
||||||
|
* combined GL journal entry (Dr control/clearing GL for total face value + charges + GST, Cr DD
|
||||||
|
* Payable / Charges Income / GST Payable), then every line's leaflet is issued and every row in the
|
||||||
|
* batch moves to APPROVED together.
|
||||||
|
*/
|
||||||
|
CommandProcessingResult approveIssueRequestBatch(Long batchId);
|
||||||
|
|
||||||
|
/** Rejects every line of a pending multi-DD slip together; no money has moved yet for ACCOUNT_DEBIT/CHEQUE. */
|
||||||
|
CommandProcessingResult rejectIssueRequestBatch(Long batchId, String rejectionReason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Back-office configuration: which GL accounts to use for a given sponsor bank / payment type,
|
||||||
|
* the GST rate applied to DD issuance charges, and the DD limit amount - kept here as data so it
|
||||||
|
* can be updated at any time (e.g. when the government revises the GST rate, or compliance
|
||||||
|
* revises the DD limit) without a code deployment. ddLimitAmount, if set, caps both a single DD's
|
||||||
|
* face value and the combined face value of all DDs issued to the same beneficiary on the same
|
||||||
|
* day; null/unset means no limit is enforced.
|
||||||
|
*/
|
||||||
|
CommandProcessingResult upsertGlMapping(Long paymentTypeId, Long ddPayableGlAccountId, Long chequeClearingGlAccountId,
|
||||||
|
Long cashGlAccountId, Long chargesIncomeGlAccountId, Long cashSuspenseGlAccountId, BigDecimal gstPercentage,
|
||||||
|
Long gstPayableGlAccountId, BigDecimal ddLimitAmount);
|
||||||
|
}
|
||||||
+1213
File diff suppressed because it is too large
Load Diff
+67
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.service;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDCashierDepositOptionData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDGlMappingData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDInventoryData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDIssueRequestData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDLeafletData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDReprintRequestData;
|
||||||
|
|
||||||
|
public interface SponsorDDReadPlatformService {
|
||||||
|
|
||||||
|
Collection<SponsorDDInventoryData> retrieveAllInventories();
|
||||||
|
|
||||||
|
SponsorDDInventoryData retrieveInventory(Long inventoryId);
|
||||||
|
|
||||||
|
Collection<SponsorDDLeafletData> retrieveAllLeafletsByInventory(Long inventoryId);
|
||||||
|
|
||||||
|
SponsorDDLeafletData retrieveLeaflet(Long leafletId);
|
||||||
|
|
||||||
|
SponsorDDLeafletData retrieveLeafletByDdNumber(String ddNumber);
|
||||||
|
|
||||||
|
Collection<SponsorDDLeafletData> retrieveLeaflets(Long sponsorBankId, String status, String adviceStatus);
|
||||||
|
|
||||||
|
Collection<org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoBatchData> retrieveAllReconciliationBatches();
|
||||||
|
|
||||||
|
org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoBatchData retrieveReconciliationBatch(Long batchId);
|
||||||
|
|
||||||
|
Collection<org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoRecordData> retrieveReconciliationRecordsByBatch(Long batchId);
|
||||||
|
|
||||||
|
Collection<SponsorDDGlMappingData> retrieveAllGlMappings();
|
||||||
|
|
||||||
|
SponsorDDGlMappingData retrieveGlMappingBySponsorBank(Long sponsorBankId);
|
||||||
|
|
||||||
|
Collection<SponsorDDIssueRequestData> retrieveAllIssueRequests(String status);
|
||||||
|
|
||||||
|
Collection<SponsorDDIssueRequestData> retrieveMyIssueRequests(Long makerId);
|
||||||
|
|
||||||
|
SponsorDDIssueRequestData retrieveIssueRequest(Long issueRequestId);
|
||||||
|
|
||||||
|
Collection<SponsorDDReprintRequestData> retrieveAllReprintRequests(String status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approved cashier Transaction Entry (AC.TP=GL) deposit requests, credited to the DD Cash
|
||||||
|
* Suspense GL account, and not yet linked to any Sponsor DD issue request - candidates a clerk
|
||||||
|
* can pick from when submitting a new CASH DD issuance request.
|
||||||
|
*/
|
||||||
|
Collection<SponsorDDCashierDepositOptionData> retrieveAvailableCashierDepositRequests();
|
||||||
|
}
|
||||||
+465
@@ -0,0 +1,465 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDCashierDepositOptionData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDGlMappingData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDInventoryData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDIssueRequestData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDLeafletData;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.data.SponsorDDReprintRequestData;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SponsorDDReadPlatformServiceImpl implements SponsorDDReadPlatformService {
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
private final InventoryMapper inventoryMapper = new InventoryMapper();
|
||||||
|
private final LeafletMapper leafletMapper = new LeafletMapper();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SponsorDDReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate) {
|
||||||
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String INVENTORY_SELECT =
|
||||||
|
"select inv.id, inv.sponsor_bank_id, sb.name as sponsor_bank_name, inv.branch_id, o.name as branch_name, "
|
||||||
|
+ "inv.series_start, inv.series_end, inv.total_leaves, inv.received_date, inv.status, "
|
||||||
|
+ "(select count(*) from m_sponsor_dd_leaflet lf where lf.inventory_id = inv.id and lf.status = 'AVAILABLE') as available_leaves "
|
||||||
|
+ "from m_sponsor_dd_inventory inv "
|
||||||
|
+ "join m_sponsor_bank sb on sb.id = inv.sponsor_bank_id "
|
||||||
|
+ "join m_office o on o.id = inv.branch_id ";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SponsorDDInventoryData> retrieveAllInventories() {
|
||||||
|
final String sql = INVENTORY_SELECT + "order by inv.id desc";
|
||||||
|
return this.jdbcTemplate.query(sql, this.inventoryMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SponsorDDInventoryData retrieveInventory(final Long inventoryId) {
|
||||||
|
try {
|
||||||
|
final String sql = INVENTORY_SELECT + "where inv.id = ?";
|
||||||
|
return this.jdbcTemplate.queryForObject(sql, this.inventoryMapper, inventoryId);
|
||||||
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String LEAFLET_SELECT =
|
||||||
|
"select leaf.id, leaf.inventory_id, leaf.dd_number, leaf.status, leaf.face_value, leaf.issued_to_client_id, "
|
||||||
|
+ "c.display_name as client_name, leaf.walkin_purchaser_name, leaf.walkin_purchaser_address, "
|
||||||
|
+ "leaf.walkin_purchaser_aadhaar_masked, leaf.beneficiary_name, leaf.payable_at_branch, leaf.payable_at_city, "
|
||||||
|
+ "leaf.issuing_branch_id, o.name as branch_name, "
|
||||||
|
+ "leaf.issued_date, leaf.cleared_date, leaf.advice_status, leaf.advice_sent_date, pir.id as pending_issue_request_id, "
|
||||||
|
+ "leaf.print_count, prr.id as pending_reprint_request_id, arr.id as approved_reprint_request_id, "
|
||||||
|
+ "apr.checker_id as approver_user_id, approver.username as approver_username "
|
||||||
|
+ "from m_sponsor_dd_leaflet leaf "
|
||||||
|
+ "left join m_client c on c.id = leaf.issued_to_client_id "
|
||||||
|
+ "left join m_office o on o.id = leaf.issuing_branch_id "
|
||||||
|
+ "left join m_sponsor_dd_issue_request pir on pir.leaflet_id = leaf.id and pir.status = 'PENDING' "
|
||||||
|
+ "left join m_sponsor_dd_reprint_request prr on prr.leaflet_id = leaf.id and prr.status = 'PENDING' "
|
||||||
|
+ "left join m_sponsor_dd_reprint_request arr on arr.leaflet_id = leaf.id and arr.status = 'APPROVED' and arr.consumed = false "
|
||||||
|
+ "left join m_sponsor_dd_issue_request apr on apr.leaflet_id = leaf.id and apr.status = 'APPROVED' "
|
||||||
|
+ "left join m_appuser approver on approver.id = apr.checker_id ";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SponsorDDLeafletData> retrieveAllLeafletsByInventory(final Long inventoryId) {
|
||||||
|
final String sql = LEAFLET_SELECT + "where leaf.inventory_id = ? order by leaf.dd_number asc";
|
||||||
|
return this.jdbcTemplate.query(sql, this.leafletMapper, inventoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SponsorDDLeafletData retrieveLeaflet(final Long leafletId) {
|
||||||
|
try {
|
||||||
|
final String sql = LEAFLET_SELECT + "where leaf.id = ?";
|
||||||
|
return this.jdbcTemplate.queryForObject(sql, this.leafletMapper, leafletId);
|
||||||
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SponsorDDLeafletData retrieveLeafletByDdNumber(final String ddNumber) {
|
||||||
|
try {
|
||||||
|
final String sql = LEAFLET_SELECT + "where leaf.dd_number = ?";
|
||||||
|
return this.jdbcTemplate.queryForObject(sql, this.leafletMapper, ddNumber);
|
||||||
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class InventoryMapper implements RowMapper<SponsorDDInventoryData> {
|
||||||
|
@Override
|
||||||
|
public SponsorDDInventoryData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final Long sponsorBankId = rs.getLong("sponsor_bank_id");
|
||||||
|
final String sponsorBankName = rs.getString("sponsor_bank_name");
|
||||||
|
final Long branchId = rs.getLong("branch_id");
|
||||||
|
final String branchName = rs.getString("branch_name");
|
||||||
|
final Long seriesStart = rs.getLong("series_start");
|
||||||
|
final Long seriesEnd = rs.getLong("series_end");
|
||||||
|
final Integer totalLeaves = rs.getInt("total_leaves");
|
||||||
|
final LocalDate receivedDate = rs.getDate("received_date") != null ? rs.getDate("received_date").toLocalDate() : null;
|
||||||
|
final String status = rs.getString("status");
|
||||||
|
final Integer availableLeaves = rs.getInt("available_leaves");
|
||||||
|
return new SponsorDDInventoryData(id, sponsorBankId, sponsorBankName, branchId, branchName, seriesStart, seriesEnd, totalLeaves, receivedDate, status, availableLeaves);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class LeafletMapper implements RowMapper<SponsorDDLeafletData> {
|
||||||
|
@Override
|
||||||
|
public SponsorDDLeafletData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final Long inventoryId = rs.getLong("inventory_id");
|
||||||
|
final String ddNumber = rs.getString("dd_number");
|
||||||
|
final String status = rs.getString("status");
|
||||||
|
final BigDecimal faceValue = rs.getBigDecimal("face_value");
|
||||||
|
final Long issuedToClientId = rs.getObject("issued_to_client_id") != null ? rs.getLong("issued_to_client_id") : null;
|
||||||
|
final String clientName = rs.getString("client_name");
|
||||||
|
final String walkinPurchaserName = rs.getString("walkin_purchaser_name");
|
||||||
|
final String walkinPurchaserAddress = rs.getString("walkin_purchaser_address");
|
||||||
|
final String walkinPurchaserAadhaarMasked = rs.getString("walkin_purchaser_aadhaar_masked");
|
||||||
|
final String beneficiaryName = rs.getString("beneficiary_name");
|
||||||
|
final String payableAtBranch = rs.getString("payable_at_branch");
|
||||||
|
final String payableAtCity = rs.getString("payable_at_city");
|
||||||
|
final Long issuingBranchId = rs.getObject("issuing_branch_id") != null ? rs.getLong("issuing_branch_id") : null;
|
||||||
|
final String branchName = rs.getString("branch_name");
|
||||||
|
final LocalDate issuedDate = rs.getDate("issued_date") != null ? rs.getDate("issued_date").toLocalDate() : null;
|
||||||
|
final LocalDate clearedDate = rs.getDate("cleared_date") != null ? rs.getDate("cleared_date").toLocalDate() : null;
|
||||||
|
final String adviceStatus = rs.getString("advice_status");
|
||||||
|
final LocalDateTime adviceSentDate = rs.getTimestamp("advice_sent_date") != null ? rs.getTimestamp("advice_sent_date").toLocalDateTime() : null;
|
||||||
|
final Long pendingIssueRequestId = rs.getObject("pending_issue_request_id") != null ? rs.getLong("pending_issue_request_id") : null;
|
||||||
|
final Integer printCount = rs.getObject("print_count") != null ? rs.getInt("print_count") : 0;
|
||||||
|
final Long pendingReprintRequestId = rs.getObject("pending_reprint_request_id") != null ? rs.getLong("pending_reprint_request_id") : null;
|
||||||
|
final Long approvedUnconsumedReprintRequestId = rs.getObject("approved_reprint_request_id") != null ? rs.getLong("approved_reprint_request_id") : null;
|
||||||
|
final Long approverUserId = rs.getObject("approver_user_id") != null ? rs.getLong("approver_user_id") : null;
|
||||||
|
final String approverUsername = rs.getString("approver_username");
|
||||||
|
return new SponsorDDLeafletData(id, inventoryId, ddNumber, status, faceValue, issuedToClientId, clientName,
|
||||||
|
walkinPurchaserName, walkinPurchaserAddress, walkinPurchaserAadhaarMasked, beneficiaryName, payableAtBranch,
|
||||||
|
payableAtCity, issuingBranchId, branchName, issuedDate, clearedDate, adviceStatus, adviceSentDate,
|
||||||
|
pendingIssueRequestId, printCount, pendingReprintRequestId, approvedUnconsumedReprintRequestId,
|
||||||
|
approverUserId, approverUsername);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoBatchData> retrieveAllReconciliationBatches() {
|
||||||
|
final String sql = "select b.id, b.batch_reference, b.file_name, b.file_hash, b.status, b.total_records, b.total_amount, b.success_count, b.mismatch_count, u.username as maker_username, b.created_date from m_sponsor_dd_reco_batch b join m_appuser u on u.id = b.createdby_id order by b.id desc";
|
||||||
|
return this.jdbcTemplate.query(sql, new RecoBatchMapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoBatchData retrieveReconciliationBatch(final Long batchId) {
|
||||||
|
try {
|
||||||
|
final String sql = "select b.id, b.batch_reference, b.file_name, b.file_hash, b.status, b.total_records, b.total_amount, b.success_count, b.mismatch_count, u.username as maker_username, b.created_date from m_sponsor_dd_reco_batch b join m_appuser u on u.id = b.createdby_id where b.id = ?";
|
||||||
|
return this.jdbcTemplate.queryForObject(sql, new RecoBatchMapper(), batchId);
|
||||||
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoRecordData> retrieveReconciliationRecordsByBatch(final Long batchId) {
|
||||||
|
final String sql = "select r.id, r.batch_id, r.dd_number, r.clearing_amount, r.clearing_date, r.clearing_ref_number, r.match_status, r.matched_leaflet_id, r.remarks, u.username as resolver_username, r.resolved_date from m_sponsor_dd_reco_record r left join m_appuser u on u.id = r.resolved_by_id where r.batch_id = ? order by r.id asc";
|
||||||
|
return this.jdbcTemplate.query(sql, new RecoRecordMapper(), batchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RecoBatchMapper implements RowMapper<org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoBatchData> {
|
||||||
|
@Override
|
||||||
|
public org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoBatchData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final String batchReference = rs.getString("batch_reference");
|
||||||
|
final String fileName = rs.getString("file_name");
|
||||||
|
final String fileHash = rs.getString("file_hash");
|
||||||
|
final String status = rs.getString("status");
|
||||||
|
final Integer totalRecords = rs.getInt("total_records");
|
||||||
|
final BigDecimal totalAmount = rs.getBigDecimal("total_amount");
|
||||||
|
final Integer successCount = rs.getInt("success_count");
|
||||||
|
final Integer mismatchCount = rs.getInt("mismatch_count");
|
||||||
|
final String makerUsername = rs.getString("maker_username");
|
||||||
|
final LocalDateTime createdDate = rs.getTimestamp("created_date") != null ? rs.getTimestamp("created_date").toLocalDateTime() : null;
|
||||||
|
return new org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoBatchData(id, batchReference, fileName, fileHash, status, totalRecords, totalAmount, successCount, mismatchCount, makerUsername, createdDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RecoRecordMapper implements RowMapper<org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoRecordData> {
|
||||||
|
@Override
|
||||||
|
public org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoRecordData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final Long batchId = rs.getLong("batch_id");
|
||||||
|
final String ddNumber = rs.getString("dd_number");
|
||||||
|
final BigDecimal clearingAmount = rs.getBigDecimal("clearing_amount");
|
||||||
|
final LocalDate clearingDate = rs.getDate("clearing_date") != null ? rs.getDate("clearing_date").toLocalDate() : null;
|
||||||
|
final String clearingRefNumber = rs.getString("clearing_ref_number");
|
||||||
|
final String matchStatus = rs.getString("match_status");
|
||||||
|
final Long matchedLeafletId = rs.getObject("matched_leaflet_id") != null ? rs.getLong("matched_leaflet_id") : null;
|
||||||
|
final String remarks = rs.getString("remarks");
|
||||||
|
final String resolverUsername = rs.getString("resolver_username");
|
||||||
|
final LocalDateTime resolvedDate = rs.getTimestamp("resolved_date") != null ? rs.getTimestamp("resolved_date").toLocalDateTime() : null;
|
||||||
|
return new org.apache.fineract.portfolio.sponsordd.data.SponsorDDRecoRecordData(id, batchId, ddNumber, clearingAmount, clearingDate, clearingRefNumber, matchStatus, matchedLeafletId, remarks, resolverUsername, resolvedDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String GL_MAPPING_SELECT =
|
||||||
|
"select m.id, null as sponsor_bank_id, 'Global' as sponsor_bank_name, m.dd_payable_gl_account_id, "
|
||||||
|
+ "payable.name as dd_payable_gl_account_name, m.cheque_clearing_gl_account_id, clearing.name as cheque_clearing_gl_account_name, "
|
||||||
|
+ "m.cash_gl_account_id, cash.name as cash_gl_account_name, m.charges_income_gl_account_id, charges.name as charges_income_gl_account_name, "
|
||||||
|
+ "m.cash_suspense_gl_account_id, suspense.name as cash_suspense_gl_account_name, "
|
||||||
|
+ "m.gst_percentage, m.gst_payable_gl_account_id, gst.name as gst_payable_gl_account_name, "
|
||||||
|
+ "m.dd_limit_amount "
|
||||||
|
+ "from m_sponsor_dd_gl_mapping m "
|
||||||
|
+ "join acc_gl_account payable on payable.id = m.dd_payable_gl_account_id "
|
||||||
|
+ "left join acc_gl_account clearing on clearing.id = m.cheque_clearing_gl_account_id "
|
||||||
|
+ "left join acc_gl_account cash on cash.id = m.cash_gl_account_id "
|
||||||
|
+ "left join acc_gl_account charges on charges.id = m.charges_income_gl_account_id "
|
||||||
|
+ "left join acc_gl_account suspense on suspense.id = m.cash_suspense_gl_account_id "
|
||||||
|
+ "left join acc_gl_account gst on gst.id = m.gst_payable_gl_account_id ";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SponsorDDGlMappingData> retrieveAllGlMappings() {
|
||||||
|
final String sql = GL_MAPPING_SELECT;
|
||||||
|
return this.jdbcTemplate.query(sql, new GlMappingMapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SponsorDDGlMappingData retrieveGlMappingBySponsorBank(final Long sponsorBankId) {
|
||||||
|
try {
|
||||||
|
final String sql = GL_MAPPING_SELECT + "limit 1";
|
||||||
|
return this.jdbcTemplate.queryForObject(sql, new GlMappingMapper());
|
||||||
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class GlMappingMapper implements RowMapper<SponsorDDGlMappingData> {
|
||||||
|
@Override
|
||||||
|
public SponsorDDGlMappingData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final Long sponsorBankId = rs.getObject("sponsor_bank_id") != null ? rs.getLong("sponsor_bank_id") : null;
|
||||||
|
final String sponsorBankName = rs.getString("sponsor_bank_name");
|
||||||
|
final Long ddPayableGlAccountId = rs.getLong("dd_payable_gl_account_id");
|
||||||
|
final String ddPayableGlAccountName = rs.getString("dd_payable_gl_account_name");
|
||||||
|
final Long chequeClearingGlAccountId = rs.getObject("cheque_clearing_gl_account_id") != null ? rs.getLong("cheque_clearing_gl_account_id") : null;
|
||||||
|
final String chequeClearingGlAccountName = rs.getString("cheque_clearing_gl_account_name");
|
||||||
|
final Long cashGlAccountId = rs.getObject("cash_gl_account_id") != null ? rs.getLong("cash_gl_account_id") : null;
|
||||||
|
final String cashGlAccountName = rs.getString("cash_gl_account_name");
|
||||||
|
final Long chargesIncomeGlAccountId = rs.getObject("charges_income_gl_account_id") != null ? rs.getLong("charges_income_gl_account_id") : null;
|
||||||
|
final String chargesIncomeGlAccountName = rs.getString("charges_income_gl_account_name");
|
||||||
|
final Long cashSuspenseGlAccountId = rs.getObject("cash_suspense_gl_account_id") != null ? rs.getLong("cash_suspense_gl_account_id") : null;
|
||||||
|
final String cashSuspenseGlAccountName = rs.getString("cash_suspense_gl_account_name");
|
||||||
|
final BigDecimal gstPercentage = rs.getBigDecimal("gst_percentage");
|
||||||
|
final Long gstPayableGlAccountId = rs.getObject("gst_payable_gl_account_id") != null ? rs.getLong("gst_payable_gl_account_id") : null;
|
||||||
|
final String gstPayableGlAccountName = rs.getString("gst_payable_gl_account_name");
|
||||||
|
final BigDecimal ddLimitAmount = rs.getBigDecimal("dd_limit_amount");
|
||||||
|
return new SponsorDDGlMappingData(id, sponsorBankId, sponsorBankName, ddPayableGlAccountId, ddPayableGlAccountName,
|
||||||
|
chequeClearingGlAccountId, chequeClearingGlAccountName, cashGlAccountId, cashGlAccountName,
|
||||||
|
chargesIncomeGlAccountId, chargesIncomeGlAccountName, cashSuspenseGlAccountId, cashSuspenseGlAccountName,
|
||||||
|
gstPercentage, gstPayableGlAccountId, gstPayableGlAccountName, ddLimitAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String ISSUE_REQUEST_SELECT =
|
||||||
|
"select r.id, r.leaflet_id, leaf.dd_number, r.client_id, c.display_name as client_name, r.walkin_purchaser_name, "
|
||||||
|
+ "r.walkin_purchaser_address, r.walkin_purchaser_aadhaar_masked, r.beneficiary_name, r.payable_at_branch, "
|
||||||
|
+ "r.payable_at_city, "
|
||||||
|
+ "r.face_value, r.issuing_branch_id, o.name as branch_name, r.issued_date, r.payment_method, r.savings_account_id, "
|
||||||
|
+ "r.current_account_id, r.cheque_number, r.cheque_bank_name, r.charges_amount, r.gst_amount, r.gst_percentage_applied, "
|
||||||
|
+ "r.status, r.maker_id, maker.username as maker_username, "
|
||||||
|
+ "r.created_date, r.checker_id, checker.username as checker_username, r.checker_date, r.rejection_reason, "
|
||||||
|
+ "r.resulting_savings_transaction_id, r.resulting_current_transaction_id, r.resulting_journal_entry_id, "
|
||||||
|
+ "r.cash_receipt_journal_entry_id, r.cash_refund_journal_entry_id, r.batch_id "
|
||||||
|
+ "from m_sponsor_dd_issue_request r "
|
||||||
|
+ "join m_sponsor_dd_leaflet leaf on leaf.id = r.leaflet_id "
|
||||||
|
+ "left join m_client c on c.id = r.client_id "
|
||||||
|
+ "join m_office o on o.id = r.issuing_branch_id "
|
||||||
|
+ "join m_appuser maker on maker.id = r.maker_id "
|
||||||
|
+ "left join m_appuser checker on checker.id = r.checker_id ";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SponsorDDIssueRequestData> retrieveAllIssueRequests(final String status) {
|
||||||
|
if (status != null && !status.isBlank()) {
|
||||||
|
final String sql = ISSUE_REQUEST_SELECT + "where r.status = ? order by r.id desc";
|
||||||
|
return this.jdbcTemplate.query(sql, new IssueRequestMapper(), status);
|
||||||
|
}
|
||||||
|
final String sql = ISSUE_REQUEST_SELECT + "order by r.id desc";
|
||||||
|
return this.jdbcTemplate.query(sql, new IssueRequestMapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SponsorDDIssueRequestData> retrieveMyIssueRequests(final Long makerId) {
|
||||||
|
final String sql = ISSUE_REQUEST_SELECT + "where r.maker_id = ? order by r.id desc";
|
||||||
|
return this.jdbcTemplate.query(sql, new IssueRequestMapper(), makerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SponsorDDIssueRequestData retrieveIssueRequest(final Long issueRequestId) {
|
||||||
|
try {
|
||||||
|
final String sql = ISSUE_REQUEST_SELECT + "where r.id = ?";
|
||||||
|
return this.jdbcTemplate.queryForObject(sql, new IssueRequestMapper(), issueRequestId);
|
||||||
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class IssueRequestMapper implements RowMapper<SponsorDDIssueRequestData> {
|
||||||
|
@Override
|
||||||
|
public SponsorDDIssueRequestData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final Long leafletId = rs.getLong("leaflet_id");
|
||||||
|
final String ddNumber = rs.getString("dd_number");
|
||||||
|
final Long clientId = rs.getObject("client_id") != null ? rs.getLong("client_id") : null;
|
||||||
|
final String clientName = rs.getString("client_name");
|
||||||
|
final String walkinPurchaserName = rs.getString("walkin_purchaser_name");
|
||||||
|
final String walkinPurchaserAddress = rs.getString("walkin_purchaser_address");
|
||||||
|
final String walkinPurchaserAadhaarMasked = rs.getString("walkin_purchaser_aadhaar_masked");
|
||||||
|
final String beneficiaryName = rs.getString("beneficiary_name");
|
||||||
|
final String payableAtBranch = rs.getString("payable_at_branch");
|
||||||
|
final String payableAtCity = rs.getString("payable_at_city");
|
||||||
|
final BigDecimal faceValue = rs.getBigDecimal("face_value");
|
||||||
|
final Long issuingBranchId = rs.getLong("issuing_branch_id");
|
||||||
|
final String branchName = rs.getString("branch_name");
|
||||||
|
final LocalDate issuedDate = rs.getDate("issued_date") != null ? rs.getDate("issued_date").toLocalDate() : null;
|
||||||
|
final String paymentMethod = rs.getString("payment_method");
|
||||||
|
final Long savingsAccountId = rs.getObject("savings_account_id") != null ? rs.getLong("savings_account_id") : null;
|
||||||
|
final Long currentAccountId = rs.getObject("current_account_id") != null ? rs.getLong("current_account_id") : null;
|
||||||
|
final String chequeNumber = rs.getString("cheque_number");
|
||||||
|
final String chequeBankName = rs.getString("cheque_bank_name");
|
||||||
|
final BigDecimal chargesAmount = rs.getBigDecimal("charges_amount");
|
||||||
|
final BigDecimal gstAmount = rs.getBigDecimal("gst_amount");
|
||||||
|
final BigDecimal gstPercentageApplied = rs.getBigDecimal("gst_percentage_applied");
|
||||||
|
final String status = rs.getString("status");
|
||||||
|
final Long makerId = rs.getLong("maker_id");
|
||||||
|
final String makerUsername = rs.getString("maker_username");
|
||||||
|
final LocalDateTime createdDate = rs.getTimestamp("created_date") != null ? rs.getTimestamp("created_date").toLocalDateTime() : null;
|
||||||
|
final Long checkerId = rs.getObject("checker_id") != null ? rs.getLong("checker_id") : null;
|
||||||
|
final String checkerUsername = rs.getString("checker_username");
|
||||||
|
final LocalDateTime checkerDate = rs.getTimestamp("checker_date") != null ? rs.getTimestamp("checker_date").toLocalDateTime() : null;
|
||||||
|
final String rejectionReason = rs.getString("rejection_reason");
|
||||||
|
final Long resultingSavingsTransactionId = rs.getObject("resulting_savings_transaction_id") != null ? rs.getLong("resulting_savings_transaction_id") : null;
|
||||||
|
final Long resultingCurrentTransactionId = rs.getObject("resulting_current_transaction_id") != null ? rs.getLong("resulting_current_transaction_id") : null;
|
||||||
|
final Long resultingJournalEntryId = rs.getObject("resulting_journal_entry_id") != null ? rs.getLong("resulting_journal_entry_id") : null;
|
||||||
|
final Long cashReceiptJournalEntryId = rs.getObject("cash_receipt_journal_entry_id") != null ? rs.getLong("cash_receipt_journal_entry_id") : null;
|
||||||
|
final Long cashRefundJournalEntryId = rs.getObject("cash_refund_journal_entry_id") != null ? rs.getLong("cash_refund_journal_entry_id") : null;
|
||||||
|
final Long batchId = rs.getObject("batch_id") != null ? rs.getLong("batch_id") : null;
|
||||||
|
return new SponsorDDIssueRequestData(id, leafletId, ddNumber, clientId, clientName, beneficiaryName, payableAtBranch,
|
||||||
|
payableAtCity, faceValue, issuingBranchId, branchName, issuedDate, paymentMethod, savingsAccountId,
|
||||||
|
currentAccountId, chequeNumber, chequeBankName, walkinPurchaserName, walkinPurchaserAddress,
|
||||||
|
walkinPurchaserAadhaarMasked, chargesAmount, gstAmount, gstPercentageApplied, status, makerId, makerUsername,
|
||||||
|
createdDate, checkerId, checkerUsername, checkerDate, rejectionReason, resultingSavingsTransactionId,
|
||||||
|
resultingCurrentTransactionId, resultingJournalEntryId, cashReceiptJournalEntryId, cashRefundJournalEntryId,
|
||||||
|
batchId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String REPRINT_REQUEST_SELECT =
|
||||||
|
"select rr.id, rr.leaflet_id, leaf.dd_number, rr.reason, rr.status, rr.consumed, rr.maker_id, "
|
||||||
|
+ "maker.username as maker_username, rr.created_date, rr.checker_id, checker.username as checker_username, "
|
||||||
|
+ "rr.checker_date, rr.rejection_reason "
|
||||||
|
+ "from m_sponsor_dd_reprint_request rr "
|
||||||
|
+ "join m_sponsor_dd_leaflet leaf on leaf.id = rr.leaflet_id "
|
||||||
|
+ "join m_appuser maker on maker.id = rr.maker_id "
|
||||||
|
+ "left join m_appuser checker on checker.id = rr.checker_id ";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SponsorDDReprintRequestData> retrieveAllReprintRequests(final String status) {
|
||||||
|
if (status != null && !status.isBlank()) {
|
||||||
|
final String sql = REPRINT_REQUEST_SELECT + "where rr.status = ? order by rr.id desc";
|
||||||
|
return this.jdbcTemplate.query(sql, new ReprintRequestMapper(), status);
|
||||||
|
}
|
||||||
|
final String sql = REPRINT_REQUEST_SELECT + "order by rr.id desc";
|
||||||
|
return this.jdbcTemplate.query(sql, new ReprintRequestMapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ReprintRequestMapper implements RowMapper<SponsorDDReprintRequestData> {
|
||||||
|
@Override
|
||||||
|
public SponsorDDReprintRequestData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final Long leafletId = rs.getLong("leaflet_id");
|
||||||
|
final String ddNumber = rs.getString("dd_number");
|
||||||
|
final String reason = rs.getString("reason");
|
||||||
|
final String status = rs.getString("status");
|
||||||
|
final Boolean consumed = rs.getBoolean("consumed");
|
||||||
|
final Long makerId = rs.getLong("maker_id");
|
||||||
|
final String makerUsername = rs.getString("maker_username");
|
||||||
|
final LocalDateTime createdDate = rs.getTimestamp("created_date") != null ? rs.getTimestamp("created_date").toLocalDateTime() : null;
|
||||||
|
final Long checkerId = rs.getObject("checker_id") != null ? rs.getLong("checker_id") : null;
|
||||||
|
final String checkerUsername = rs.getString("checker_username");
|
||||||
|
final LocalDateTime checkerDate = rs.getTimestamp("checker_date") != null ? rs.getTimestamp("checker_date").toLocalDateTime() : null;
|
||||||
|
final String rejectionReason = rs.getString("rejection_reason");
|
||||||
|
return new SponsorDDReprintRequestData(id, leafletId, ddNumber, reason, status, consumed, makerId, makerUsername,
|
||||||
|
createdDate, checkerId, checkerUsername, checkerDate, rejectionReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SponsorDDLeafletData> retrieveLeaflets(final Long sponsorBankId, final String status, final String adviceStatus) {
|
||||||
|
final List<Object> params = new ArrayList<>();
|
||||||
|
String sql = LEAFLET_SELECT + "join m_sponsor_dd_inventory inv on inv.id = leaf.inventory_id WHERE 1=1 ";
|
||||||
|
if (sponsorBankId != null) {
|
||||||
|
sql += " AND inv.sponsor_bank_id = ? ";
|
||||||
|
params.add(sponsorBankId);
|
||||||
|
}
|
||||||
|
if (status != null) {
|
||||||
|
sql += " AND leaf.status = ? ";
|
||||||
|
params.add(status);
|
||||||
|
}
|
||||||
|
if (adviceStatus != null) {
|
||||||
|
sql += " AND leaf.advice_status = ? ";
|
||||||
|
params.add(adviceStatus);
|
||||||
|
}
|
||||||
|
sql += " ORDER BY leaf.dd_number ASC";
|
||||||
|
return this.jdbcTemplate.query(sql, this.leafletMapper, params.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SponsorDDCashierDepositOptionData> retrieveAvailableCashierDepositRequests() {
|
||||||
|
final String sql = "select cdr.id, cdr.voucher_no, cdr.amount, cdr.description, cdr.cashier_username, cdr.actioned_at, "
|
||||||
|
+ "cdr.dd_face_value, cdr.dd_charges, cdr.dd_gst_percentage, cdr.dd_gst_amount "
|
||||||
|
+ "from m_cashier_deposit_request cdr "
|
||||||
|
+ "where cdr.status = 'APPROVED' "
|
||||||
|
+ "and cdr.gl_account_id = (select cash_suspense_gl_account_id from m_sponsor_dd_gl_mapping limit 1) "
|
||||||
|
+ "and cdr.id not in (select cashier_deposit_request_id from m_sponsor_dd_issue_request "
|
||||||
|
+ "where cashier_deposit_request_id is not null) "
|
||||||
|
+ "order by cdr.actioned_at desc";
|
||||||
|
return this.jdbcTemplate.query(sql, (rs, rowNum) -> new SponsorDDCashierDepositOptionData(
|
||||||
|
rs.getLong("id"),
|
||||||
|
rs.getString("voucher_no"),
|
||||||
|
rs.getBigDecimal("amount"),
|
||||||
|
rs.getString("description"),
|
||||||
|
rs.getString("cashier_username"),
|
||||||
|
rs.getTimestamp("actioned_at") != null ? rs.getTimestamp("actioned_at").toLocalDateTime() : null,
|
||||||
|
rs.getBigDecimal("dd_face_value"),
|
||||||
|
rs.getBigDecimal("dd_charges"),
|
||||||
|
rs.getBigDecimal("dd_gst_percentage"),
|
||||||
|
rs.getBigDecimal("dd_gst_amount")));
|
||||||
|
}
|
||||||
|
}
|
||||||
+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.sponsordd.service;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDRecoBatch;
|
||||||
|
|
||||||
|
public interface SponsorDDReconciliationService {
|
||||||
|
|
||||||
|
SponsorDDRecoBatch uploadAndReconcile(String fileName, String fileContent);
|
||||||
|
|
||||||
|
CommandProcessingResult resolveMismatch(Long recordId, String actionType, Long resolvedSavingsTxnId, Long resolvedCurrentTxnId);
|
||||||
|
}
|
||||||
+361
@@ -0,0 +1,361 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
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.portfolio.currentaccount.domain.CurrentAccountTransaction;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDAuditLog;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDAuditLogRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDLeaflet;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDLeafletRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDRecoBatch;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDRecoBatchRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDRecoRecord;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDRecoRecordRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDTransaction;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDTransactionRepository;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SponsorDDReconciliationServiceImpl implements SponsorDDReconciliationService {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final SponsorDDRecoBatchRepository batchRepository;
|
||||||
|
private final SponsorDDRecoRecordRepository recordRepository;
|
||||||
|
private final SponsorDDLeafletRepository leafletRepository;
|
||||||
|
private final SponsorDDTransactionRepository transactionRepository;
|
||||||
|
private final SponsorDDAuditLogRepository auditLogRepository;
|
||||||
|
private final SavingsAccountTransactionRepository savingsTransactionRepository;
|
||||||
|
private final CurrentAccountTransactionRepository currentTransactionRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SponsorDDReconciliationServiceImpl(
|
||||||
|
final PlatformSecurityContext context,
|
||||||
|
final SponsorDDRecoBatchRepository batchRepository,
|
||||||
|
final SponsorDDRecoRecordRepository recordRepository,
|
||||||
|
final SponsorDDLeafletRepository leafletRepository,
|
||||||
|
final SponsorDDTransactionRepository transactionRepository,
|
||||||
|
final SponsorDDAuditLogRepository auditLogRepository,
|
||||||
|
final SavingsAccountTransactionRepository savingsTransactionRepository,
|
||||||
|
final CurrentAccountTransactionRepository currentTransactionRepository) {
|
||||||
|
this.context = context;
|
||||||
|
this.batchRepository = batchRepository;
|
||||||
|
this.recordRepository = recordRepository;
|
||||||
|
this.leafletRepository = leafletRepository;
|
||||||
|
this.transactionRepository = transactionRepository;
|
||||||
|
this.auditLogRepository = auditLogRepository;
|
||||||
|
this.savingsTransactionRepository = savingsTransactionRepository;
|
||||||
|
this.currentTransactionRepository = currentTransactionRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public SponsorDDRecoBatch uploadAndReconcile(final String fileName, final String fileContent) {
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
|
||||||
|
// 1. Check file uniqueness using SHA-256 hash
|
||||||
|
final String fileHash = calculateSHA256(fileContent);
|
||||||
|
final Optional<SponsorDDRecoBatch> existingBatch = this.batchRepository.findByFileHash(fileHash);
|
||||||
|
if (existingBatch.isPresent()) {
|
||||||
|
throw new IllegalStateException("This clearing file has already been uploaded and processed under reference: "
|
||||||
|
+ existingBatch.get().getBatchReference());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Parse CSV rows
|
||||||
|
final String[] rows = fileContent.split("\\r?\\n");
|
||||||
|
final List<ParsedRecord> parsedRecords = new ArrayList<>();
|
||||||
|
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
|
||||||
|
for (int i = 0; i < rows.length; i++) {
|
||||||
|
final String row = rows[i].trim();
|
||||||
|
if (row.isEmpty() || i == 0 && row.toLowerCase().contains("dd_number")) {
|
||||||
|
// Skip empty lines or CSV header
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] columns = row.split(",");
|
||||||
|
if (columns.length < 4) {
|
||||||
|
// Malformed row
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String ddNumber = columns[0].trim();
|
||||||
|
final BigDecimal clearingAmount = new BigDecimal(columns[1].trim());
|
||||||
|
final LocalDate clearingDate = LocalDate.parse(columns[2].trim(), dateFormatter);
|
||||||
|
final String clearingRef = columns[3].trim();
|
||||||
|
|
||||||
|
parsedRecords.add(new ParsedRecord(ddNumber, clearingAmount, clearingDate, clearingRef));
|
||||||
|
totalAmount = totalAmount.add(clearingAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Create batch record
|
||||||
|
final String batchRef = "RECO-" + LocalDate.now().toString().replace("-", "") + "-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
|
||||||
|
SponsorDDRecoBatch batch = new SponsorDDRecoBatch(
|
||||||
|
batchRef,
|
||||||
|
fileName,
|
||||||
|
fileHash,
|
||||||
|
"PROCESSING",
|
||||||
|
parsedRecords.size(),
|
||||||
|
totalAmount,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
currentUser,
|
||||||
|
LocalDateTime.now(),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
batch = this.batchRepository.saveAndFlush(batch);
|
||||||
|
|
||||||
|
int successCount = 0;
|
||||||
|
int mismatchCount = 0;
|
||||||
|
|
||||||
|
final List<SponsorDDRecoRecord> recoRecords = new ArrayList<>();
|
||||||
|
|
||||||
|
// 4. Run matching rules
|
||||||
|
for (final ParsedRecord pr : parsedRecords) {
|
||||||
|
final Optional<SponsorDDLeaflet> optLeaflet = this.leafletRepository.findByDdNumber(pr.ddNumber);
|
||||||
|
|
||||||
|
String matchStatus = "MATCHED";
|
||||||
|
SponsorDDLeaflet leaflet = null;
|
||||||
|
String remarks = null;
|
||||||
|
|
||||||
|
if (optLeaflet.isPresent()) {
|
||||||
|
leaflet = optLeaflet.get();
|
||||||
|
|
||||||
|
if ("CLEARED".equals(leaflet.getStatus())) {
|
||||||
|
matchStatus = "DUPLICATE_CLEARING";
|
||||||
|
remarks = "Instrument was already cleared on " + leaflet.getClearedDate();
|
||||||
|
mismatchCount++;
|
||||||
|
} else if (!"ISSUED".equals(leaflet.getStatus())) {
|
||||||
|
matchStatus = "UNADVISED_CLEARING";
|
||||||
|
remarks = "Leaflet status is " + leaflet.getStatus() + " instead of ISSUED";
|
||||||
|
mismatchCount++;
|
||||||
|
} else if (leaflet.getFaceValue().compareTo(pr.clearingAmount) != 0) {
|
||||||
|
matchStatus = "AMOUNT_MISMATCH";
|
||||||
|
remarks = "Amount mismatch: expected " + leaflet.getFaceValue() + ", cleared " + pr.clearingAmount;
|
||||||
|
mismatchCount++;
|
||||||
|
} else {
|
||||||
|
// Rule 1: Exact Match (Success)
|
||||||
|
successCount++;
|
||||||
|
leaflet.setStatus("CLEARED");
|
||||||
|
leaflet.setClearedDate(pr.clearingDate);
|
||||||
|
leaflet.setLastModifiedBy(currentUser);
|
||||||
|
leaflet.setLastModifiedDate(LocalDateTime.now());
|
||||||
|
this.leafletRepository.save(leaflet);
|
||||||
|
|
||||||
|
// Create clearing transaction mapping
|
||||||
|
final SponsorDDTransaction ddTxn = new SponsorDDTransaction(
|
||||||
|
leaflet,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"CLEARING",
|
||||||
|
pr.clearingAmount,
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.transactionRepository.save(ddTxn);
|
||||||
|
|
||||||
|
// Audit Log
|
||||||
|
final SponsorDDAuditLog auditLog = new SponsorDDAuditLog(
|
||||||
|
leaflet,
|
||||||
|
"CLEARING_POSTED",
|
||||||
|
"ISSUED",
|
||||||
|
"CLEARED",
|
||||||
|
currentUser,
|
||||||
|
"127.0.0.1",
|
||||||
|
null,
|
||||||
|
"Sponsor clearing scroll auto-match. Ref: " + pr.clearingRef,
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.auditLogRepository.save(auditLog);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Rule 3: Unadvised / Not found
|
||||||
|
matchStatus = "UNADVISED_CLEARING";
|
||||||
|
remarks = "DD number not found in internal leaflet registry";
|
||||||
|
mismatchCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SponsorDDRecoRecord record = new SponsorDDRecoRecord(
|
||||||
|
batch,
|
||||||
|
pr.ddNumber,
|
||||||
|
pr.clearingAmount,
|
||||||
|
pr.clearingDate,
|
||||||
|
pr.clearingRef,
|
||||||
|
matchStatus,
|
||||||
|
leaflet,
|
||||||
|
remarks,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
recoRecords.add(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.recordRepository.saveAllAndFlush(recoRecords);
|
||||||
|
|
||||||
|
// 5. Update batch totals
|
||||||
|
batch.setSuccessCount(successCount);
|
||||||
|
batch.setMismatchCount(mismatchCount);
|
||||||
|
batch.setStatus(mismatchCount > 0 ? "PARTIALLY_RECONCILED" : "RECONCILED");
|
||||||
|
batch.setLastModifiedBy(currentUser);
|
||||||
|
batch.setLastModifiedDate(LocalDateTime.now());
|
||||||
|
this.batchRepository.save(batch);
|
||||||
|
|
||||||
|
return batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult resolveMismatch(final Long recordId, final String actionType,
|
||||||
|
final Long resolvedSavingsTxnId, final Long resolvedCurrentTxnId) {
|
||||||
|
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
final SponsorDDRecoRecord record = this.recordRepository.findById(recordId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Reconciliation record not found."));
|
||||||
|
|
||||||
|
if (!"AMOUNT_MISMATCH".equals(record.getMatchStatus()) && !"UNADVISED_CLEARING".equals(record.getMatchStatus())) {
|
||||||
|
throw new IllegalStateException("Only exception records can be resolved. Current status: " + record.getMatchStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
final SponsorDDRecoBatch batch = record.getBatch();
|
||||||
|
|
||||||
|
SavingsAccountTransaction savingsTxn = null;
|
||||||
|
if (resolvedSavingsTxnId != null) {
|
||||||
|
savingsTxn = this.savingsTransactionRepository.findById(resolvedSavingsTxnId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Resolved savings transaction not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentAccountTransaction currentTxn = null;
|
||||||
|
if (resolvedCurrentTxnId != null) {
|
||||||
|
currentTxn = this.currentTransactionRepository.findById(resolvedCurrentTxnId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Resolved current account transaction not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("FORCE_MATCH".equals(actionType)) {
|
||||||
|
final SponsorDDLeaflet leaflet = record.getMatchedLeaflet();
|
||||||
|
if (leaflet != null) {
|
||||||
|
final String preStatus = leaflet.getStatus();
|
||||||
|
leaflet.setStatus("CLEARED");
|
||||||
|
leaflet.setClearedDate(record.getClearingDate());
|
||||||
|
leaflet.setLastModifiedBy(currentUser);
|
||||||
|
leaflet.setLastModifiedDate(LocalDateTime.now());
|
||||||
|
this.leafletRepository.saveAndFlush(leaflet);
|
||||||
|
|
||||||
|
// Create transaction link
|
||||||
|
final SponsorDDTransaction ddTxn = new SponsorDDTransaction(
|
||||||
|
leaflet,
|
||||||
|
savingsTxn,
|
||||||
|
currentTxn,
|
||||||
|
null,
|
||||||
|
"CLEARING",
|
||||||
|
record.getClearingAmount(),
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.transactionRepository.save(ddTxn);
|
||||||
|
|
||||||
|
// Audit Log
|
||||||
|
final SponsorDDAuditLog auditLog = new SponsorDDAuditLog(
|
||||||
|
leaflet,
|
||||||
|
"CLEARING_POSTED",
|
||||||
|
preStatus,
|
||||||
|
"CLEARED",
|
||||||
|
currentUser,
|
||||||
|
"127.0.0.1",
|
||||||
|
null,
|
||||||
|
"Manual reconciliation override (Force Match) approved by checker.",
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.auditLogRepository.save(auditLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
record.setMatchStatus("RESOLVED");
|
||||||
|
record.setRemarks("Manually resolved by force-match override.");
|
||||||
|
record.setResolvedBy(currentUser);
|
||||||
|
record.setResolvedDate(LocalDateTime.now());
|
||||||
|
this.recordRepository.save(record);
|
||||||
|
|
||||||
|
// Update batch counts
|
||||||
|
batch.setSuccessCount(batch.getSuccessCount() + 1);
|
||||||
|
batch.setMismatchCount(batch.getMismatchCount() - 1);
|
||||||
|
if (batch.getMismatchCount() == 0) {
|
||||||
|
batch.setStatus("RECONCILED");
|
||||||
|
}
|
||||||
|
batch.setLastModifiedBy(currentUser);
|
||||||
|
batch.setLastModifiedDate(LocalDateTime.now());
|
||||||
|
this.batchRepository.save(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(record.getId())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String calculateSHA256(final String text) {
|
||||||
|
try {
|
||||||
|
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
final byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
|
||||||
|
final StringBuilder hexString = new StringBuilder();
|
||||||
|
for (final byte b : hash) {
|
||||||
|
final String hex = Integer.toHexString(0xff & b);
|
||||||
|
if (hex.length() == 1) {
|
||||||
|
hexString.append('0');
|
||||||
|
}
|
||||||
|
hexString.append(hex);
|
||||||
|
}
|
||||||
|
return hexString.toString();
|
||||||
|
} catch (final NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException("SHA-256 algorithm not available.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ParsedRecord {
|
||||||
|
private final String ddNumber;
|
||||||
|
private final BigDecimal clearingAmount;
|
||||||
|
private final LocalDate clearingDate;
|
||||||
|
private final String clearingRef;
|
||||||
|
|
||||||
|
public ParsedRecord(final String ddNumber, final BigDecimal clearingAmount, final LocalDate clearingDate, final String clearingRef) {
|
||||||
|
this.ddNumber = ddNumber;
|
||||||
|
this.clearingAmount = clearingAmount;
|
||||||
|
this.clearingDate = clearingDate;
|
||||||
|
this.clearingRef = clearingRef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -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.sponsordd.service;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
|
||||||
|
public interface SponsorDDReprintRequestWritePlatformService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records a print of the given leaflet. The first print of a leaflet is always allowed. Any
|
||||||
|
* print after that requires a previously APPROVED, not-yet-consumed reprint request - if one
|
||||||
|
* exists it is consumed by this call; otherwise this throws, and the caller should submit a
|
||||||
|
* reprint request instead via {@link #requestReprint}.
|
||||||
|
*/
|
||||||
|
CommandProcessingResult recordPrint(Long leafletId);
|
||||||
|
|
||||||
|
/** Clerk (maker) step: requests permission to print an already-printed leaflet again. */
|
||||||
|
CommandProcessingResult requestReprint(Long leafletId, String reason);
|
||||||
|
|
||||||
|
/** Manager (checker) step: approves a pending reprint request, allowing one further print. */
|
||||||
|
CommandProcessingResult approveReprintRequest(Long reprintRequestId);
|
||||||
|
|
||||||
|
CommandProcessingResult rejectReprintRequest(Long reprintRequestId, String rejectionReason);
|
||||||
|
}
|
||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||||
|
import org.springframework.jdbc.support.KeyHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SponsorDDReprintRequestWritePlatformServiceImpl implements SponsorDDReprintRequestWritePlatformService {
|
||||||
|
|
||||||
|
private static final String STATUS_PENDING = "PENDING";
|
||||||
|
private static final String STATUS_APPROVED = "APPROVED";
|
||||||
|
private static final String STATUS_REJECTED = "REJECTED";
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult recordPrint(final Long leafletId) {
|
||||||
|
final Map<String, Object> leaflet = findLeafletOrThrow(leafletId);
|
||||||
|
if (!"ISSUED".equals(leaflet.get("status"))) {
|
||||||
|
throw new IllegalStateException("Only an issued Demand Draft can be printed. Current status: " + leaflet.get("status"));
|
||||||
|
}
|
||||||
|
final int printCount = ((Number) leaflet.get("print_count")).intValue();
|
||||||
|
final LocalDateTime now = LocalDateTime.now(java.time.Clock.systemDefaultZone());
|
||||||
|
|
||||||
|
if (printCount == 0) {
|
||||||
|
this.jdbcTemplate.update(
|
||||||
|
"UPDATE m_sponsor_dd_leaflet SET print_count = print_count + 1, last_printed_date = ? WHERE id = ?", now, leafletId);
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(leafletId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Map<String, Object>> approved = this.jdbcTemplate.queryForList(
|
||||||
|
"SELECT id FROM m_sponsor_dd_reprint_request WHERE leaflet_id = ? AND status = 'APPROVED' AND consumed = false "
|
||||||
|
+ "ORDER BY id DESC LIMIT 1", leafletId);
|
||||||
|
if (approved.isEmpty()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"This Demand Draft has already been printed. Submit a reprint request for manager approval before printing again.");
|
||||||
|
}
|
||||||
|
final long reprintRequestId = ((Number) approved.get(0).get("id")).longValue();
|
||||||
|
this.jdbcTemplate.update("UPDATE m_sponsor_dd_reprint_request SET consumed = true WHERE id = ?", reprintRequestId);
|
||||||
|
this.jdbcTemplate.update(
|
||||||
|
"UPDATE m_sponsor_dd_leaflet SET print_count = print_count + 1, last_printed_date = ? WHERE id = ?", now, leafletId);
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(leafletId).withSubEntityId(reprintRequestId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult requestReprint(final Long leafletId, final String reason) {
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
final Map<String, Object> leaflet = findLeafletOrThrow(leafletId);
|
||||||
|
if (!"ISSUED".equals(leaflet.get("status"))) {
|
||||||
|
throw new IllegalStateException("Only an issued Demand Draft can be reprinted. Current status: " + leaflet.get("status"));
|
||||||
|
}
|
||||||
|
final int printCount = ((Number) leaflet.get("print_count")).intValue();
|
||||||
|
if (printCount == 0) {
|
||||||
|
throw new IllegalStateException("This Demand Draft has not been printed yet - use Print, not Request Reprint.");
|
||||||
|
}
|
||||||
|
if (reason == null || reason.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("A reason is required to request a reprint.");
|
||||||
|
}
|
||||||
|
if (!this.jdbcTemplate.queryForList(
|
||||||
|
"SELECT id FROM m_sponsor_dd_reprint_request WHERE leaflet_id = ? AND status = 'PENDING'", leafletId).isEmpty()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"A reprint request is already pending for this Demand Draft. Wait for it to be approved or rejected.");
|
||||||
|
}
|
||||||
|
if (!this.jdbcTemplate.queryForList(
|
||||||
|
"SELECT id FROM m_sponsor_dd_reprint_request WHERE leaflet_id = ? AND status = 'APPROVED' AND consumed = false",
|
||||||
|
leafletId).isEmpty()) {
|
||||||
|
throw new IllegalStateException("A reprint has already been approved for this Demand Draft - use Print to consume it.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final KeyHolder kh = new GeneratedKeyHolder();
|
||||||
|
final LocalDateTime now = LocalDateTime.now(java.time.Clock.systemDefaultZone());
|
||||||
|
this.jdbcTemplate.update(con -> {
|
||||||
|
final java.sql.PreparedStatement ps = con.prepareStatement(
|
||||||
|
"INSERT INTO m_sponsor_dd_reprint_request (leaflet_id, reason, status, maker_id, created_date) "
|
||||||
|
+ "VALUES (?, ?, ?, ?, ?)",
|
||||||
|
new String[] { "id" });
|
||||||
|
ps.setLong(1, leafletId);
|
||||||
|
ps.setString(2, reason);
|
||||||
|
ps.setString(3, STATUS_PENDING);
|
||||||
|
ps.setLong(4, currentUser.getId());
|
||||||
|
ps.setObject(5, now);
|
||||||
|
return ps;
|
||||||
|
}, kh);
|
||||||
|
final long newId = Objects.requireNonNull(kh.getKey()).longValue();
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(newId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult approveReprintRequest(final Long reprintRequestId) {
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
validateCheckerRole(currentUser);
|
||||||
|
|
||||||
|
final Map<String, Object> request = findReprintRequestOrThrow(reprintRequestId);
|
||||||
|
if (!STATUS_PENDING.equals(request.get("status"))) {
|
||||||
|
throw new IllegalStateException("Reprint request is not pending. Current status: " + request.get("status"));
|
||||||
|
}
|
||||||
|
final Long makerId = ((Number) request.get("maker_id")).longValue();
|
||||||
|
if (makerId.equals(currentUser.getId()) && !currentUser.hasAnyPermission("ALL_FUNCTIONS")) {
|
||||||
|
throw new IllegalStateException("You cannot approve your own reprint request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final LocalDateTime now = LocalDateTime.now(java.time.Clock.systemDefaultZone());
|
||||||
|
this.jdbcTemplate.update(
|
||||||
|
"UPDATE m_sponsor_dd_reprint_request SET status = ?, checker_id = ?, checker_date = ? WHERE id = ?",
|
||||||
|
STATUS_APPROVED, currentUser.getId(), now, reprintRequestId);
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(reprintRequestId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult rejectReprintRequest(final Long reprintRequestId, final String rejectionReason) {
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
validateCheckerRole(currentUser);
|
||||||
|
|
||||||
|
final Map<String, Object> request = findReprintRequestOrThrow(reprintRequestId);
|
||||||
|
if (!STATUS_PENDING.equals(request.get("status"))) {
|
||||||
|
throw new IllegalStateException("Reprint request is not pending. Current status: " + request.get("status"));
|
||||||
|
}
|
||||||
|
final Long makerId = ((Number) request.get("maker_id")).longValue();
|
||||||
|
if (makerId.equals(currentUser.getId()) && !currentUser.hasAnyPermission("ALL_FUNCTIONS")) {
|
||||||
|
throw new IllegalStateException("You cannot reject your own reprint request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final LocalDateTime now = LocalDateTime.now(java.time.Clock.systemDefaultZone());
|
||||||
|
this.jdbcTemplate.update(
|
||||||
|
"UPDATE m_sponsor_dd_reprint_request SET status = ?, checker_id = ?, checker_date = ?, rejection_reason = ? WHERE id = ?",
|
||||||
|
STATUS_REJECTED, currentUser.getId(), now, rejectionReason, reprintRequestId);
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(reprintRequestId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> findLeafletOrThrow(final Long leafletId) {
|
||||||
|
final List<Map<String, Object>> rows = this.jdbcTemplate.queryForList(
|
||||||
|
"SELECT id, status, print_count FROM m_sponsor_dd_leaflet WHERE id = ?", leafletId);
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Demand Draft leaflet not found.");
|
||||||
|
}
|
||||||
|
return rows.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> findReprintRequestOrThrow(final Long reprintRequestId) {
|
||||||
|
final List<Map<String, Object>> rows = this.jdbcTemplate.queryForList(
|
||||||
|
"SELECT * FROM m_sponsor_dd_reprint_request WHERE id = ?", reprintRequestId);
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Sponsor DD reprint request not found.");
|
||||||
|
}
|
||||||
|
return rows.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCheckerRole(final AppUser user) {
|
||||||
|
if (user.hasAnyPermission("ALL_FUNCTIONS")) return true;
|
||||||
|
return user.getRoles().stream()
|
||||||
|
.map(r -> r.getName() == null ? "" : r.getName().toLowerCase().trim())
|
||||||
|
.anyMatch(n -> n.contains("manager") || n.contains("head cashier"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateCheckerRole(final AppUser user) {
|
||||||
|
if (!isCheckerRole(user)) {
|
||||||
|
throw new IllegalStateException("Only a Bank Manager (or Head Cashier) can approve or reject reprint requests.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -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.sponsordd.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
|
||||||
|
public interface SponsorDDWritePlatformService {
|
||||||
|
|
||||||
|
CommandProcessingResult registerInventory(Long sponsorBankId, Long branchId, Long seriesStart, Long seriesEnd, LocalDate receivedDate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clientId may be null for a walk-in purchaser who isn't a bank client (cash payment) — in that
|
||||||
|
* case walkinPurchaserName/Address/AadhaarMasked identify the purchaser instead.
|
||||||
|
*/
|
||||||
|
CommandProcessingResult issueDemandDraft(Long leafletId, Long clientId, String walkinPurchaserName,
|
||||||
|
String walkinPurchaserAddress, String walkinPurchaserAadhaarMasked, String beneficiaryName, BigDecimal faceValue,
|
||||||
|
Long issuingBranchId, LocalDate issuedDate, Long savingsTransactionId, Long currentTransactionId, Long journalEntryId,
|
||||||
|
String payableAtBranch, String payableAtCity);
|
||||||
|
|
||||||
|
CommandProcessingResult cancelDemandDraft(Long leafletId, String remarks, Long refundSavingsTransactionId, Long refundCurrentTransactionId);
|
||||||
|
|
||||||
|
CommandProcessingResult stopPaymentDemandDraft(Long leafletId, String remarks);
|
||||||
|
|
||||||
|
CommandProcessingResult sendAdvice(Long sponsorBankId);
|
||||||
|
}
|
||||||
+403
@@ -0,0 +1,403 @@
|
|||||||
|
/**
|
||||||
|
* 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.sponsordd.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
|
||||||
|
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
|
||||||
|
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.portfolio.client.domain.Client;
|
||||||
|
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorBank;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorBankRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDAuditLog;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDAuditLogRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDInventory;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDInventoryRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDLeaflet;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDLeafletRepository;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDTransaction;
|
||||||
|
import org.apache.fineract.portfolio.sponsordd.domain.SponsorDDTransactionRepository;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SponsorDDWritePlatformServiceImpl implements SponsorDDWritePlatformService {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final SponsorDDInventoryRepository inventoryRepository;
|
||||||
|
private final SponsorDDLeafletRepository leafletRepository;
|
||||||
|
private final SponsorDDTransactionRepository transactionRepository;
|
||||||
|
private final SponsorDDAuditLogRepository auditLogRepository;
|
||||||
|
private final SponsorBankRepository sponsorBankRepository;
|
||||||
|
private final OfficeRepositoryWrapper officeRepositoryWrapper;
|
||||||
|
private final ClientRepositoryWrapper clientRepositoryWrapper;
|
||||||
|
private final SavingsAccountTransactionRepository savingsTransactionRepository;
|
||||||
|
private final CurrentAccountTransactionRepository currentTransactionRepository;
|
||||||
|
private final JournalEntryRepository journalEntryRepository;
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SponsorDDWritePlatformServiceImpl(
|
||||||
|
final PlatformSecurityContext context,
|
||||||
|
final SponsorDDInventoryRepository inventoryRepository,
|
||||||
|
final SponsorDDLeafletRepository leafletRepository,
|
||||||
|
final SponsorDDTransactionRepository transactionRepository,
|
||||||
|
final SponsorDDAuditLogRepository auditLogRepository,
|
||||||
|
final SponsorBankRepository sponsorBankRepository,
|
||||||
|
final OfficeRepositoryWrapper officeRepositoryWrapper,
|
||||||
|
final ClientRepositoryWrapper clientRepositoryWrapper,
|
||||||
|
final SavingsAccountTransactionRepository savingsTransactionRepository,
|
||||||
|
final CurrentAccountTransactionRepository currentTransactionRepository,
|
||||||
|
final JournalEntryRepository journalEntryRepository,
|
||||||
|
final JdbcTemplate jdbcTemplate) {
|
||||||
|
this.context = context;
|
||||||
|
this.inventoryRepository = inventoryRepository;
|
||||||
|
this.leafletRepository = leafletRepository;
|
||||||
|
this.transactionRepository = transactionRepository;
|
||||||
|
this.auditLogRepository = auditLogRepository;
|
||||||
|
this.sponsorBankRepository = sponsorBankRepository;
|
||||||
|
this.officeRepositoryWrapper = officeRepositoryWrapper;
|
||||||
|
this.clientRepositoryWrapper = clientRepositoryWrapper;
|
||||||
|
this.savingsTransactionRepository = savingsTransactionRepository;
|
||||||
|
this.currentTransactionRepository = currentTransactionRepository;
|
||||||
|
this.journalEntryRepository = journalEntryRepository;
|
||||||
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult registerInventory(final Long sponsorBankId, final Long branchId, final Long seriesStart,
|
||||||
|
final Long seriesEnd, final LocalDate receivedDate) {
|
||||||
|
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
|
||||||
|
if (seriesStart > seriesEnd) {
|
||||||
|
throw new IllegalArgumentException("Series start number must be less than or equal to series end number.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final SponsorBank sponsorBank = this.sponsorBankRepository.findById(sponsorBankId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Sponsor Bank not found with ID: " + sponsorBankId));
|
||||||
|
final Office branch = this.officeRepositoryWrapper.findOneWithNotFoundDetection(branchId);
|
||||||
|
final int totalLeaves = (int) (seriesEnd - seriesStart) + 1;
|
||||||
|
|
||||||
|
final SponsorDDInventory inventory = new SponsorDDInventory(
|
||||||
|
sponsorBank,
|
||||||
|
branch,
|
||||||
|
seriesStart,
|
||||||
|
seriesEnd,
|
||||||
|
totalLeaves,
|
||||||
|
receivedDate,
|
||||||
|
"ACTIVE",
|
||||||
|
currentUser,
|
||||||
|
LocalDateTime.now(),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
this.inventoryRepository.saveAndFlush(inventory);
|
||||||
|
|
||||||
|
// Pre-create leaflets in bulk
|
||||||
|
final List<SponsorDDLeaflet> leaflets = new ArrayList<>();
|
||||||
|
for (long currentVal = seriesStart; currentVal <= seriesEnd; currentVal++) {
|
||||||
|
final String ddNumber = String.valueOf(currentVal);
|
||||||
|
final SponsorDDLeaflet leaflet = new SponsorDDLeaflet(
|
||||||
|
inventory,
|
||||||
|
ddNumber,
|
||||||
|
"AVAILABLE",
|
||||||
|
BigDecimal.ZERO,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"NOT_SENT",
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
currentUser,
|
||||||
|
LocalDateTime.now(),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
leaflets.add(leaflet);
|
||||||
|
}
|
||||||
|
this.leafletRepository.saveAllAndFlush(leaflets);
|
||||||
|
|
||||||
|
// Audit the creation of each leaflet
|
||||||
|
final List<SponsorDDAuditLog> auditLogs = new ArrayList<>();
|
||||||
|
for (final SponsorDDLeaflet leaflet : leaflets) {
|
||||||
|
final SponsorDDAuditLog auditLog = new SponsorDDAuditLog(
|
||||||
|
leaflet,
|
||||||
|
"STOCK_RECEIPT",
|
||||||
|
null,
|
||||||
|
"AVAILABLE",
|
||||||
|
currentUser,
|
||||||
|
"127.0.0.1",
|
||||||
|
null,
|
||||||
|
"Draft leaflet registered into inventory.",
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
auditLogs.add(auditLog);
|
||||||
|
}
|
||||||
|
this.auditLogRepository.saveAll(auditLogs);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(inventory.getId())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult issueDemandDraft(final Long leafletId, final Long clientId, final String walkinPurchaserName,
|
||||||
|
final String walkinPurchaserAddress, final String walkinPurchaserAadhaarMasked, final String beneficiaryName,
|
||||||
|
final BigDecimal faceValue, final Long issuingBranchId, final LocalDate issuedDate,
|
||||||
|
final Long savingsTransactionId, final Long currentTransactionId, final Long journalEntryId,
|
||||||
|
final String payableAtBranch, final String payableAtCity) {
|
||||||
|
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
final SponsorDDLeaflet leaflet = this.leafletRepository.findById(leafletId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Demand Draft leaflet not found."));
|
||||||
|
|
||||||
|
if (!"AVAILABLE".equals(leaflet.getStatus())) {
|
||||||
|
throw new IllegalStateException("Demand Draft leaflet is not available. Current status: " + leaflet.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
final Client client = clientId != null ? this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId) : null;
|
||||||
|
final Office branch = this.officeRepositoryWrapper.findOneWithNotFoundDetection(issuingBranchId);
|
||||||
|
|
||||||
|
// Find linked financial items
|
||||||
|
SavingsAccountTransaction savingsTxn = null;
|
||||||
|
if (savingsTransactionId != null) {
|
||||||
|
savingsTxn = this.savingsTransactionRepository.findById(savingsTransactionId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Savings transaction not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentAccountTransaction currentTxn = null;
|
||||||
|
if (currentTransactionId != null) {
|
||||||
|
currentTxn = this.currentTransactionRepository.findById(currentTransactionId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Current account transaction not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
JournalEntry journalEntry = null;
|
||||||
|
if (journalEntryId != null) {
|
||||||
|
journalEntry = this.journalEntryRepository.findById(journalEntryId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Journal Entry not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
final String preStatus = leaflet.getStatus();
|
||||||
|
leaflet.setStatus("ISSUED");
|
||||||
|
leaflet.setFaceValue(faceValue);
|
||||||
|
leaflet.setIssuedToClient(client);
|
||||||
|
leaflet.setWalkinPurchaserName(walkinPurchaserName);
|
||||||
|
leaflet.setWalkinPurchaserAddress(walkinPurchaserAddress);
|
||||||
|
leaflet.setWalkinPurchaserAadhaarMasked(walkinPurchaserAadhaarMasked);
|
||||||
|
leaflet.setBeneficiaryName(beneficiaryName);
|
||||||
|
leaflet.setPayableAtBranch(payableAtBranch);
|
||||||
|
leaflet.setPayableAtCity(payableAtCity);
|
||||||
|
leaflet.setIssuingBranch(branch);
|
||||||
|
leaflet.setIssuedDate(issuedDate);
|
||||||
|
leaflet.setAdviceStatus("NOT_SENT");
|
||||||
|
leaflet.setLastModifiedBy(currentUser);
|
||||||
|
leaflet.setLastModifiedDate(LocalDateTime.now());
|
||||||
|
|
||||||
|
this.leafletRepository.saveAndFlush(leaflet);
|
||||||
|
|
||||||
|
// Link transaction
|
||||||
|
final SponsorDDTransaction ddTransaction = new SponsorDDTransaction(
|
||||||
|
leaflet,
|
||||||
|
savingsTxn,
|
||||||
|
currentTxn,
|
||||||
|
journalEntry,
|
||||||
|
"ISSUANCE",
|
||||||
|
faceValue,
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.transactionRepository.save(ddTransaction);
|
||||||
|
|
||||||
|
// Audit Log
|
||||||
|
final SponsorDDAuditLog auditLog = new SponsorDDAuditLog(
|
||||||
|
leaflet,
|
||||||
|
"ISSUED",
|
||||||
|
preStatus,
|
||||||
|
"ISSUED",
|
||||||
|
currentUser,
|
||||||
|
"127.0.0.1",
|
||||||
|
null,
|
||||||
|
"Demand draft issued to customer.",
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.auditLogRepository.save(auditLog);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(leaflet.getId())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult cancelDemandDraft(final Long leafletId, final String remarks,
|
||||||
|
final Long refundSavingsTransactionId, final Long refundCurrentTransactionId) {
|
||||||
|
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
final SponsorDDLeaflet leaflet = this.leafletRepository.findById(leafletId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Demand Draft leaflet not found."));
|
||||||
|
|
||||||
|
if (!"ISSUED".equals(leaflet.getStatus())) {
|
||||||
|
throw new IllegalStateException("Only ISSUED Demand Drafts can be cancelled. Current status: " + leaflet.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
SavingsAccountTransaction savingsTxn = null;
|
||||||
|
if (refundSavingsTransactionId != null) {
|
||||||
|
savingsTxn = this.savingsTransactionRepository.findById(refundSavingsTransactionId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Refund savings transaction not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentAccountTransaction currentTxn = null;
|
||||||
|
if (refundCurrentTransactionId != null) {
|
||||||
|
currentTxn = this.currentTransactionRepository.findById(refundCurrentTransactionId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Refund current account transaction not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
final String preStatus = leaflet.getStatus();
|
||||||
|
leaflet.setStatus("CANCELLED");
|
||||||
|
leaflet.setLastModifiedBy(currentUser);
|
||||||
|
leaflet.setLastModifiedDate(LocalDateTime.now());
|
||||||
|
|
||||||
|
this.leafletRepository.saveAndFlush(leaflet);
|
||||||
|
|
||||||
|
// Link refund transaction
|
||||||
|
final SponsorDDTransaction ddTransaction = new SponsorDDTransaction(
|
||||||
|
leaflet,
|
||||||
|
savingsTxn,
|
||||||
|
currentTxn,
|
||||||
|
null,
|
||||||
|
"CANCELLATION",
|
||||||
|
leaflet.getFaceValue(),
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.transactionRepository.save(ddTransaction);
|
||||||
|
|
||||||
|
// Audit Log
|
||||||
|
final SponsorDDAuditLog auditLog = new SponsorDDAuditLog(
|
||||||
|
leaflet,
|
||||||
|
"CANCELLED",
|
||||||
|
preStatus,
|
||||||
|
"CANCELLED",
|
||||||
|
currentUser,
|
||||||
|
"127.0.0.1",
|
||||||
|
null,
|
||||||
|
remarks != null ? remarks : "Demand draft cancelled and refunded.",
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.auditLogRepository.save(auditLog);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(leaflet.getId())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult stopPaymentDemandDraft(final Long leafletId, final String remarks) {
|
||||||
|
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
final SponsorDDLeaflet leaflet = this.leafletRepository.findById(leafletId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Demand Draft leaflet not found."));
|
||||||
|
|
||||||
|
if (!"ISSUED".equals(leaflet.getStatus())) {
|
||||||
|
throw new IllegalStateException("Only ISSUED Demand Drafts can be stopped. Current status: " + leaflet.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String preStatus = leaflet.getStatus();
|
||||||
|
leaflet.setStatus("STOPPED");
|
||||||
|
leaflet.setLastModifiedBy(currentUser);
|
||||||
|
leaflet.setLastModifiedDate(LocalDateTime.now());
|
||||||
|
|
||||||
|
this.leafletRepository.saveAndFlush(leaflet);
|
||||||
|
|
||||||
|
// Link stop transaction
|
||||||
|
final SponsorDDTransaction ddTransaction = new SponsorDDTransaction(
|
||||||
|
leaflet,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"STOP_PAYMENT",
|
||||||
|
leaflet.getFaceValue(),
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.transactionRepository.save(ddTransaction);
|
||||||
|
|
||||||
|
// Audit Log
|
||||||
|
final SponsorDDAuditLog auditLog = new SponsorDDAuditLog(
|
||||||
|
leaflet,
|
||||||
|
"STOP_PAYMENT",
|
||||||
|
preStatus,
|
||||||
|
"STOPPED",
|
||||||
|
currentUser,
|
||||||
|
"127.0.0.1",
|
||||||
|
null,
|
||||||
|
remarks != null ? remarks : "Stop payment instruction processed.",
|
||||||
|
LocalDateTime.now()
|
||||||
|
);
|
||||||
|
this.auditLogRepository.save(auditLog);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(leaflet.getId())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult sendAdvice(final Long sponsorBankId) {
|
||||||
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
final LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
// Update m_sponsor_dd_leaflet for the sponsor bank where status is ISSUED and advice_status is NOT_SENT
|
||||||
|
final String sql = "UPDATE m_sponsor_dd_leaflet leaf "
|
||||||
|
+ "SET advice_status = 'SENT', advice_sent_date = ?, lastmodifiedby_id = ?, lastmodified_date = ? "
|
||||||
|
+ "WHERE leaf.status = 'ISSUED' AND leaf.advice_status = 'NOT_SENT' "
|
||||||
|
+ "AND leaf.inventory_id IN (SELECT id FROM m_sponsor_dd_inventory WHERE sponsor_bank_id = ?)";
|
||||||
|
|
||||||
|
this.jdbcTemplate.update(sql, now, currentUser.getId(), now, sponsorBankId);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(sponsorBankId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -435,4 +435,29 @@
|
|||||||
<include file="parts/0412_activate_outward_clearing_batch_job.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0412_activate_outward_clearing_batch_job.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="parts/0413_inward_item_verify_signature_permission.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0413_inward_item_verify_signature_permission.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="parts/0414_inward_item_full_source_details.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0414_inward_item_full_source_details.xml" relativeToChangelogFile="true"/>
|
||||||
|
<!-- NOTE: the numbers below (0341-0359) re-use prefixes already used above by the chequebook/CTS
|
||||||
|
clearing catchup migrations — these come from a long-diverged branch (uate) that assigned its own
|
||||||
|
sequence independently. Liquibase tracks changesets by filename + id + author, not by the numeric
|
||||||
|
prefix, so this causes no functional collision; the prefixes are just no longer strictly sequential
|
||||||
|
across the whole file. Left as-is rather than renumbering, since renumbering an already-created
|
||||||
|
migration risks Liquibase re-running it on any environment where it was already applied under its
|
||||||
|
original filename/id. -->
|
||||||
|
<include file="parts/0341_add_current_account_pending_edit_table.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0342_add_current_account_edit_approval_permissions.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0343_create_sponsor_bank_dd_tables.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0344_create_sponsor_dd_issue_request_tables.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0345_add_sponsor_dd_payable_gl_account_sbi.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0347_add_sponsor_dd_walkin_cash_support.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0348_add_sponsor_dd_cash_charges_gl_mapping.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0349_replace_payment_type_with_sponsor_bank.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0350_add_sponsor_dd_cash_receipt_suspense.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0351_add_sponsor_dd_reprint_approval.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0352_add_sponsor_dd_payable_at.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0353_add_sponsor_dd_gst_on_charges.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0354_add_sponsor_bank_identification_codes.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0355_add_sponsor_dd_cashier_deposit_link.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0356_add_dd_breakdown_to_cashier_deposit_request.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0357_add_dd_limit_to_sponsor_dd_gl_mapping.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0358_add_batch_id_to_sponsor_dd_issue_request.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0359_reclassify_dd_gst_payable_as_income.xml" relativeToChangelogFile="true"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+218
@@ -0,0 +1,218 @@
|
|||||||
|
<?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 current account's Nominee + Digital & Account
|
||||||
|
Features, mirroring m_savings_account_pending_edit's 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_current_account_facility_config / m_client_nominee rows until
|
||||||
|
a checker approves (see CurrentAccountEditWritePlatformServiceJpaRepositoryImpl). 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. Also carries a
|
||||||
|
full point-in-time "wide snapshot" of every business column on m_current_account at submission
|
||||||
|
time, for audit/UI display purposes only.
|
||||||
|
-->
|
||||||
|
<changeSet author="fineract" id="0341-1">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<tableExists tableName="m_current_account_pending_edit" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<createTable tableName="m_current_account_pending_edit">
|
||||||
|
<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_caoe_current_account_id" references="m_current_account(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="createdby_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_caoe_maker_id" references="m_appuser(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="lastmodifiedby_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_caoe_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="caoe_unq_pending_lock_key" />
|
||||||
|
</column>
|
||||||
|
<column name="command_source_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_caoe_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_caoe_hold_txn_id" references="m_current_account_transaction(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="hold_amount" type="DECIMAL(19,6)" />
|
||||||
|
|
||||||
|
<!-- Digital & Account Features - same 18 columns/names as m_current_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" />
|
||||||
|
<column name="internet_banking" type="BOOLEAN" />
|
||||||
|
<column name="mobile_banking" 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_caoe_nominee_id" references="m_client_nominee(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="nominee_client_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_caoe_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_caoe_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)" />
|
||||||
|
|
||||||
|
<!-- Wide point-in-time snapshot of every business column on m_current_account at submission
|
||||||
|
time (audit/UI display only - approve()/reject() logic reads the live account, not this).
|
||||||
|
All nullable, no unique constraints, no defaults - this is a frozen copy. -->
|
||||||
|
<column name="account_no" type="VARCHAR(20)" />
|
||||||
|
<column name="external_id" type="VARCHAR(100)" />
|
||||||
|
<column name="client_id" type="BIGINT" />
|
||||||
|
<column name="product_id" type="BIGINT" />
|
||||||
|
<column name="office_id" type="BIGINT" />
|
||||||
|
<column name="staff_id" type="BIGINT" />
|
||||||
|
<column name="status_enum" type="INTEGER" />
|
||||||
|
<column name="sub_status_enum" type="INTEGER" />
|
||||||
|
<column name="account_type_enum" type="INTEGER" />
|
||||||
|
<column name="submittedon_date" type="DATE" />
|
||||||
|
<column name="approvedon_date" type="DATE" />
|
||||||
|
<column name="activatedon_date" type="DATE" />
|
||||||
|
<column name="rejectedon_date" type="DATE" />
|
||||||
|
<column name="withdrawnon_date" type="DATE" />
|
||||||
|
<column name="closedon_date" type="DATE" />
|
||||||
|
<column name="submittedon_userid" type="BIGINT" />
|
||||||
|
<column name="approvedon_userid" type="BIGINT" />
|
||||||
|
<column name="activatedon_userid" type="BIGINT" />
|
||||||
|
<column name="rejectedon_userid" type="BIGINT" />
|
||||||
|
<column name="withdrawnon_userid" type="BIGINT" />
|
||||||
|
<column name="closedon_userid" type="BIGINT" />
|
||||||
|
<column name="currency_code" type="VARCHAR(3)" />
|
||||||
|
<column name="currency_digits" type="INTEGER" />
|
||||||
|
<column name="currency_multiplesof" type="INTEGER" />
|
||||||
|
<column name="nominal_annual_interest_rate" type="DECIMAL(19,6)" />
|
||||||
|
<column name="interest_compounding_period_enum" type="INTEGER" />
|
||||||
|
<column name="interest_posting_period_enum" type="INTEGER" />
|
||||||
|
<column name="interest_calculation_type_enum" type="INTEGER" />
|
||||||
|
<column name="interest_calculation_days_in_year_type_enum" type="INTEGER" />
|
||||||
|
<column name="min_balance_for_interest_calculation" type="DECIMAL(19,6)" />
|
||||||
|
<column name="start_interest_calculation_date" type="DATE" />
|
||||||
|
<column name="accrued_till_date" type="DATE" />
|
||||||
|
<column name="last_interest_calculation_date" type="DATE" />
|
||||||
|
<column name="last_closed_business_date" type="DATE" />
|
||||||
|
<column name="withhold_tax" type="BOOLEAN" />
|
||||||
|
<column name="min_required_opening_balance" type="DECIMAL(19,6)" />
|
||||||
|
<column name="lockin_period_frequency" type="INTEGER" />
|
||||||
|
<column name="lockin_period_frequency_enum" type="INTEGER" />
|
||||||
|
<column name="lockedin_until_date_derived" type="DATE" />
|
||||||
|
<column name="enforce_min_required_balance" type="BOOLEAN" />
|
||||||
|
<column name="min_required_balance" type="DECIMAL(19,6)" />
|
||||||
|
<column name="allow_overdraft" type="BOOLEAN" />
|
||||||
|
<column name="overdraft_limit" type="DECIMAL(19,6)" />
|
||||||
|
<column name="nominal_annual_interest_rate_overdraft" type="DECIMAL(19,6)" />
|
||||||
|
<column name="min_overdraft_for_interest_calculation" type="DECIMAL(19,6)" />
|
||||||
|
<column name="is_lien_allowed" type="BOOLEAN" />
|
||||||
|
<column name="max_allowed_lien_limit" type="DECIMAL(19,6)" />
|
||||||
|
<column name="on_hold_funds_derived" type="DECIMAL(19,6)" />
|
||||||
|
<column name="account_balance_derived" type="DECIMAL(19,6)" />
|
||||||
|
<column name="total_deposits_derived" type="DECIMAL(19,6)" />
|
||||||
|
<column name="total_withdrawals_derived" type="DECIMAL(19,6)" />
|
||||||
|
<column name="total_interest_earned_derived" type="DECIMAL(19,6)" />
|
||||||
|
<column name="total_interest_posted_derived" type="DECIMAL(19,6)" />
|
||||||
|
<column name="total_overdraft_interest_derived" type="DECIMAL(19,6)" />
|
||||||
|
<column name="total_withhold_tax_derived" type="DECIMAL(19,6)" />
|
||||||
|
<column name="total_savings_amount_on_hold" type="DECIMAL(19,6)" />
|
||||||
|
<column name="withdrawal_fee_for_transfer" type="BOOLEAN" />
|
||||||
|
<column name="reason_for_block" type="VARCHAR(500)" />
|
||||||
|
<column name="msme" type="BOOLEAN" />
|
||||||
|
<column name="auto_debit_facility" type="BOOLEAN" />
|
||||||
|
<column name="balance_swap_option" type="BOOLEAN" />
|
||||||
|
<column name="establishment_date" type="DATE" />
|
||||||
|
<column name="start_operation_date" type="DATE" />
|
||||||
|
<column name="is_non_individual_client" type="BOOLEAN" />
|
||||||
|
<column name="signatory_rights" type="VARCHAR(100)" />
|
||||||
|
<column name="cbs_account_type" type="VARCHAR(100)" />
|
||||||
|
<column name="customer_segment" type="VARCHAR(100)" />
|
||||||
|
<column name="account_status" type="VARCHAR(50)" />
|
||||||
|
<column name="mode_of_operation" type="VARCHAR(50)" />
|
||||||
|
<column name="special_category" type="VARCHAR(100)" />
|
||||||
|
<column name="introducer_client_id" type="BIGINT" />
|
||||||
|
<column name="introducer_name" type="VARCHAR(200)" />
|
||||||
|
<column name="introducer_cif_number" type="VARCHAR(50)" />
|
||||||
|
<column name="nominee_data" type="TEXT" />
|
||||||
|
<column name="interest_calculate_selector" type="INTEGER" />
|
||||||
|
<column name="average_period" type="VARCHAR(20)" />
|
||||||
|
<column name="auto_post" type="BOOLEAN" />
|
||||||
|
<column name="frequency_fix_day" type="INTEGER" />
|
||||||
|
<column name="enable_lockin_period" type="BOOLEAN" />
|
||||||
|
<column name="balance_frequency" type="VARCHAR(50)" />
|
||||||
|
<column name="avg_balance_calc_period" type="VARCHAR(10)" />
|
||||||
|
<column name="account_closed_zero_balance_in_day_closing" type="BOOLEAN" />
|
||||||
|
<column name="valid_for_period" type="VARCHAR(10)" />
|
||||||
|
<column name="od_account_closed_zero_balance_in_day_closing" type="BOOLEAN" />
|
||||||
|
<column name="is_linking_enable" type="BOOLEAN" />
|
||||||
|
<column name="terminate_true" type="BOOLEAN" />
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
<?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
|
||||||
|
current 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 so the pending request also surfaces in the
|
||||||
|
generic Checker Inbox for limited checkers. Grouping matches the rest of CURRENTACCOUNT's
|
||||||
|
permissions in 0225_add_current_account_support.xml ("portfolio").
|
||||||
|
-->
|
||||||
|
<changeSet author="fineract" id="0342-1">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'SUBMITEDIT_CURRENTACCOUNT'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="portfolio" />
|
||||||
|
<column name="code" value="SUBMITEDIT_CURRENTACCOUNT" />
|
||||||
|
<column name="entity_name" value="CURRENTACCOUNT" />
|
||||||
|
<column name="action_name" value="SUBMITEDIT" />
|
||||||
|
<column name="can_maker_checker" valueBoolean="false" />
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0342-2">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_CURRENTACCOUNT'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="portfolio" />
|
||||||
|
<column name="code" value="APPROVEEDIT_CURRENTACCOUNT" />
|
||||||
|
<column name="entity_name" value="CURRENTACCOUNT" />
|
||||||
|
<column name="action_name" value="APPROVEEDIT" />
|
||||||
|
<column name="can_maker_checker" valueBoolean="false" />
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0342-3">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'REJECTEDIT_CURRENTACCOUNT'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="portfolio" />
|
||||||
|
<column name="code" value="REJECTEDIT_CURRENTACCOUNT" />
|
||||||
|
<column name="entity_name" value="CURRENTACCOUNT" />
|
||||||
|
<column name="action_name" value="REJECTEDIT" />
|
||||||
|
<column name="can_maker_checker" valueBoolean="false" />
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0342-4">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'APPROVEEDIT_CURRENTACCOUNT_CHECKER'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="portfolio" />
|
||||||
|
<column name="code" value="APPROVEEDIT_CURRENTACCOUNT_CHECKER" />
|
||||||
|
<column name="entity_name" value="CURRENTACCOUNT" />
|
||||||
|
<column name="action_name" value="APPROVEEDIT_CHECKER" />
|
||||||
|
<column name="can_maker_checker" valueBoolean="false" />
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+1290
File diff suppressed because it is too large
Load Diff
+143
@@ -0,0 +1,143 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Per sponsor-bank GL account configuration: which GL account is credited when a DD is
|
||||||
|
issued (liability owed to the sponsor bank), and which suspense GL account is debited
|
||||||
|
when the customer pays by cheque instead of an account debit. -->
|
||||||
|
<changeSet author="fineract" id="0344-1-create-gl-mapping">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<tableExists tableName="m_sponsor_dd_gl_mapping" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<createTable tableName="m_sponsor_dd_gl_mapping">
|
||||||
|
<column name="id" type="BIGINT" autoIncrement="true">
|
||||||
|
<constraints primaryKey="true" nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="payment_type_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" unique="true" uniqueConstraintName="sdd_glmap_unq_payment_type"
|
||||||
|
foreignKeyName="fk_sdd_glmap_payment_type_id" references="m_payment_type(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="dd_payable_gl_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_sdd_glmap_payable_gl_id" references="acc_gl_account(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="cheque_clearing_gl_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_clearing_gl_id" references="acc_gl_account(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="created_date" type="DATETIME" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- Maker-checker queue: clerk submits a PENDING request when a customer wants a DD issued
|
||||||
|
(either by debiting their own savings/current account, or by handing over a cheque);
|
||||||
|
a manager/head-cashier approval performs the actual GL posting and flips the leaflet
|
||||||
|
to ISSUED via the existing SponsorDDWritePlatformService.issueDemandDraft(). -->
|
||||||
|
<changeSet author="fineract" id="0344-2-create-issue-request">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<tableExists tableName="m_sponsor_dd_issue_request" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<createTable tableName="m_sponsor_dd_issue_request">
|
||||||
|
<column name="id" type="BIGINT" autoIncrement="true">
|
||||||
|
<constraints primaryKey="true" nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="leaflet_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_sdd_issuereq_leaflet_id" references="m_sponsor_dd_leaflet(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="client_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_sdd_issuereq_client_id" references="m_client(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="beneficiary_name" type="VARCHAR(150)">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="face_value" type="DECIMAL(19,6)">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="issuing_branch_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_sdd_issuereq_branch_id" references="m_office(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="issued_date" type="DATE">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<!-- ACCOUNT_DEBIT or CHEQUE -->
|
||||||
|
<column name="payment_method" type="VARCHAR(20)">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="savings_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_savings_id" references="m_savings_account(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="current_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_current_id" references="m_current_account(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="cheque_number" type="VARCHAR(30)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="cheque_bank_name" type="VARCHAR(100)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<!-- PENDING, APPROVED, REJECTED -->
|
||||||
|
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="maker_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_sdd_issuereq_maker_id" references="m_appuser(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="created_date" type="DATETIME" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="checker_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_checker_id" references="m_appuser(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="checker_date" type="DATETIME">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="rejection_reason" type="VARCHAR(255)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="resulting_savings_transaction_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_result_savings_txn" references="m_savings_account_transaction(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="resulting_current_transaction_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_result_current_txn" references="m_current_account_transaction(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="resulting_journal_entry_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_result_journal" references="acc_gl_journal_entry(id)" />
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0344-3-index-issue-request-status">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<indexExists tableName="m_sponsor_dd_issue_request" indexName="idx_sdd_issuereq_status" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<createIndex indexName="idx_sdd_issuereq_status" tableName="m_sponsor_dd_issue_request">
|
||||||
|
<column name="status" />
|
||||||
|
</createIndex>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Dedicated liability GL account for the Sponsor DD Payable balance owed to SBI, as a child
|
||||||
|
of the existing "OTHER LIABILITY" account (gl_code 9), matching the pattern already used
|
||||||
|
by "INTEREST PAYABLE" (gl_code 9.2). -->
|
||||||
|
<changeSet author="fineract" id="0345-1-insert-sponsor-dd-payable-sbi-gl-account">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.3'</sqlCheck>
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="acc_gl_account">
|
||||||
|
<column name="name" value="Sponsor DD Payable - SBI"/>
|
||||||
|
<column name="gl_code" value="9.3"/>
|
||||||
|
<column name="parent_id" valueNumeric="22"/>
|
||||||
|
<column name="hierarchy" value="."/>
|
||||||
|
<column name="account_usage" valueNumeric="1"/>
|
||||||
|
<column name="classification_enum" valueNumeric="2"/>
|
||||||
|
<column name="disabled" valueBoolean="false"/>
|
||||||
|
<column name="manual_journal_entries_allowed" valueBoolean="true"/>
|
||||||
|
<column name="description" value="Liability owed to SBI for sponsor bank demand drafts issued on their behalf, pending clearing."/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- Fineract's GL hierarchy path embeds the row's own auto-generated id, so it can only be
|
||||||
|
set after the insert above. -->
|
||||||
|
<changeSet author="fineract" id="0345-2-fix-sponsor-dd-payable-sbi-hierarchy">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.3' and hierarchy = '.'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
UPDATE acc_gl_account SET hierarchy = CONCAT('.', id, '.') WHERE gl_code = '9.3' AND hierarchy = '.';
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- Wire the new GL account into the Sponsor DD GL mapping for the SBI payment type, so
|
||||||
|
DD issuance requests for SBI can be approved without manual configuration. -->
|
||||||
|
<changeSet author="fineract" id="0345-3-map-sponsor-dd-payable-sbi">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="0">
|
||||||
|
select count(*) from m_sponsor_dd_gl_mapping m join m_payment_type pt on pt.id = m.payment_type_id where pt.value = 'SBI'
|
||||||
|
</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
INSERT INTO m_sponsor_dd_gl_mapping (payment_type_id, dd_payable_gl_account_id, created_date)
|
||||||
|
SELECT pt.id, gl.id, CURRENT_TIMESTAMP
|
||||||
|
FROM m_payment_type pt, acc_gl_account gl
|
||||||
|
WHERE pt.value = 'SBI' AND gl.gl_code = '9.3';
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+97
@@ -0,0 +1,97 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Support DD issuance to a walk-in person paying cash who is not a bank client at all
|
||||||
|
(not just a different-branch customer). client_id becomes optional; when absent, the
|
||||||
|
walk-in purchaser's own KYC details are captured instead. Aadhaar is stored masked only
|
||||||
|
(last 4 digits) per UIDAI/RBI guidance — the full number is never persisted. -->
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0347-1-issue-request-client-id-nullable">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<columnExists tableName="m_sponsor_dd_issue_request" columnName="client_id" />
|
||||||
|
</preConditions>
|
||||||
|
<dropNotNullConstraint tableName="m_sponsor_dd_issue_request" columnName="client_id" columnDataType="BIGINT" />
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0347-2-issue-request-add-walkin-fields">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_issue_request" columnName="walkin_purchaser_name" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_issue_request">
|
||||||
|
<column name="walkin_purchaser_name" type="VARCHAR(150)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="walkin_purchaser_address" type="VARCHAR(255)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="walkin_purchaser_aadhaar_masked" type="VARCHAR(20)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="charges_amount" type="DECIMAL(19,6)" defaultValueNumeric="0">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0347-3-leaflet-add-walkin-fields">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_leaflet" columnName="walkin_purchaser_name" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_leaflet">
|
||||||
|
<column name="walkin_purchaser_name" type="VARCHAR(150)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="walkin_purchaser_address" type="VARCHAR(255)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="walkin_purchaser_aadhaar_masked" type="VARCHAR(20)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- GL accounts for a CASH payment: Dr Cash-in-Hand (face value + charges) / Cr Sponsor DD
|
||||||
|
Payable (face value) / Cr Charges Income (charges, if any). Both optional — CASH issuance
|
||||||
|
is blocked with a clear error until these are configured, matching the existing pattern
|
||||||
|
for cheque_clearing_gl_account_id. -->
|
||||||
|
<changeSet author="fineract" id="0347-4-gl-mapping-add-cash-and-charges-accounts">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="cash_gl_account_id" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_gl_mapping">
|
||||||
|
<column name="cash_gl_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_cash_gl_id" references="acc_gl_account(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="charges_income_gl_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_charges_gl_id" references="acc_gl_account(id)" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Dedicated income GL account for charges/commission collected on sponsor bank DD issuance,
|
||||||
|
as a child of the existing "INCOME HEAD" account (gl_code 10), matching the pattern
|
||||||
|
already used by "INCOME FROM LOAN" (gl_code 10.1) / "INCOME FROM ALL" (gl_code 10.2). -->
|
||||||
|
<changeSet author="fineract" id="0348-1-insert-dd-charges-income-gl-account">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '10.3'</sqlCheck>
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="acc_gl_account">
|
||||||
|
<column name="name" value="DD Issuance Charges Income"/>
|
||||||
|
<column name="gl_code" value="10.3"/>
|
||||||
|
<column name="parent_id" valueNumeric="25"/>
|
||||||
|
<column name="hierarchy" value="."/>
|
||||||
|
<column name="account_usage" valueNumeric="1"/>
|
||||||
|
<column name="classification_enum" valueNumeric="4"/>
|
||||||
|
<column name="disabled" valueBoolean="false"/>
|
||||||
|
<column name="manual_journal_entries_allowed" valueBoolean="true"/>
|
||||||
|
<column name="description" value="Commission/charges income collected from customers and walk-in purchasers when issuing a sponsor bank demand draft."/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- Fineract's GL hierarchy path embeds the row's own auto-generated id, so it can only be
|
||||||
|
set after the insert above. -->
|
||||||
|
<changeSet author="fineract" id="0348-2-fix-dd-charges-income-hierarchy">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '10.3' and hierarchy = '.'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
UPDATE acc_gl_account SET hierarchy = CONCAT('.', id, '.') WHERE gl_code = '10.3' AND hierarchy = '.';
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- Wire "Cash at Tellers" (the standard Fineract GL account for cash physically received at
|
||||||
|
the teller counter, gl_code 2) and the new charges income account into the existing
|
||||||
|
Sponsor DD GL mapping row(s), so CASH-method walk-in DD issuance can be approved without
|
||||||
|
further manual configuration. -->
|
||||||
|
<changeSet author="fineract" id="0348-3-map-dd-cash-and-charges-accounts">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<sqlCheck expectedResult="0">
|
||||||
|
select count(*) from m_sponsor_dd_gl_mapping where cash_gl_account_id is null or charges_income_gl_account_id is null
|
||||||
|
</sqlCheck>
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
UPDATE m_sponsor_dd_gl_mapping
|
||||||
|
SET cash_gl_account_id = (select id from acc_gl_account where gl_code = '2'),
|
||||||
|
charges_income_gl_account_id = (select id from acc_gl_account where gl_code = '10.3')
|
||||||
|
WHERE cash_gl_account_id is null or charges_income_gl_account_id is null;
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
<?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.1.xsd">
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0349-0-drop-old-sponsor-bank-table">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_bank" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_bank" columnName="createdby_id" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
DROP TABLE IF EXISTS m_sponsor_bank CASCADE;
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0349-1-create-sponsor-bank-table-v3">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<tableExists tableName="m_sponsor_bank" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<createTable tableName="m_sponsor_bank">
|
||||||
|
<column name="id" type="BIGINT" autoIncrement="true">
|
||||||
|
<constraints primaryKey="true" nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="name" type="VARCHAR(100)">
|
||||||
|
<constraints nullable="false" unique="true" uniqueConstraintName="sdd_bank_unq_name" />
|
||||||
|
</column>
|
||||||
|
<column name="description" type="VARCHAR(255)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="createdby_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_sdd_bank_maker_id" references="m_appuser(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="created_date" type="DATETIME" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0349-2-migrate-existing-payment-types">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_bank" />
|
||||||
|
<tableExists tableName="m_payment_type" />
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
-- Insert unique sponsor banks from payment types into m_sponsor_bank
|
||||||
|
INSERT INTO m_sponsor_bank (name, description, createdby_id, created_date)
|
||||||
|
SELECT DISTINCT pt.value, pt.description, 1, CURRENT_TIMESTAMP
|
||||||
|
FROM m_payment_type pt
|
||||||
|
WHERE (pt.description = 'Sponsor Bank'
|
||||||
|
OR pt.id IN (SELECT DISTINCT payment_type_id FROM m_sponsor_dd_inventory))
|
||||||
|
AND pt.value NOT IN (SELECT name FROM m_sponsor_bank);
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0349-3-add-sponsor-bank-id-to-inventory">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_inventory" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_inventory" columnName="sponsor_bank_id" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_inventory">
|
||||||
|
<column name="sponsor_bank_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_inv_sponsor_bank_id" references="m_sponsor_bank(id)" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0349-4-update-inventory-sponsor-bank-ids">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<columnExists tableName="m_sponsor_dd_inventory" columnName="sponsor_bank_id" />
|
||||||
|
<columnExists tableName="m_sponsor_dd_inventory" columnName="payment_type_id" />
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
-- Update sponsor_bank_id based on name matching from m_payment_type
|
||||||
|
UPDATE m_sponsor_dd_inventory
|
||||||
|
SET sponsor_bank_id = (
|
||||||
|
SELECT sb.id
|
||||||
|
FROM m_sponsor_bank sb
|
||||||
|
JOIN m_payment_type pt ON pt.value = sb.name
|
||||||
|
WHERE pt.id = m_sponsor_dd_inventory.payment_type_id
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
WHERE sponsor_bank_id IS NULL;
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0349-5-set-sponsor-bank-id-not-null">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<columnExists tableName="m_sponsor_dd_inventory" columnName="sponsor_bank_id" />
|
||||||
|
</preConditions>
|
||||||
|
<addNotNullConstraint tableName="m_sponsor_dd_inventory" columnName="sponsor_bank_id" columnDataType="BIGINT" />
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0349-6-drop-payment-type-from-inventory">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<columnExists tableName="m_sponsor_dd_inventory" columnName="payment_type_id" />
|
||||||
|
</preConditions>
|
||||||
|
<dropForeignKeyConstraint baseTableName="m_sponsor_dd_inventory" constraintName="fk_sdd_inv_payment_type_id" />
|
||||||
|
<dropColumn tableName="m_sponsor_dd_inventory" columnName="payment_type_id" />
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0349-7-decouple-gl-mapping-payment-type">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_gl_mapping" />
|
||||||
|
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="payment_type_id" />
|
||||||
|
</preConditions>
|
||||||
|
<dropForeignKeyConstraint baseTableName="m_sponsor_dd_gl_mapping" constraintName="fk_sdd_glmap_payment_type_id" />
|
||||||
|
<dropUniqueConstraint tableName="m_sponsor_dd_gl_mapping" constraintName="sdd_glmap_unq_payment_type" />
|
||||||
|
<dropColumn tableName="m_sponsor_dd_gl_mapping" columnName="payment_type_id" />
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Interim liability GL account for cash physically received from a walk-in DD purchaser at
|
||||||
|
submission time, before the manager has approved the request. Real-world equivalent of
|
||||||
|
"advance received against demand draft application" - keeps the cash movement auditable
|
||||||
|
immediately (the teller's cash-in-hand balance is correct as soon as cash is taken),
|
||||||
|
without prematurely crediting the Sponsor DD Payable liability, which is only actually
|
||||||
|
owed to the sponsor bank once the draft is approved/issued. -->
|
||||||
|
<changeSet author="fineract" id="0350-1-insert-dd-cash-suspense-gl-account">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.4'</sqlCheck>
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="acc_gl_account">
|
||||||
|
<column name="name" value="DD Cash Received - Pending Approval"/>
|
||||||
|
<column name="gl_code" value="9.4"/>
|
||||||
|
<column name="parent_id" valueNumeric="22"/>
|
||||||
|
<column name="hierarchy" value="."/>
|
||||||
|
<column name="account_usage" valueNumeric="1"/>
|
||||||
|
<column name="classification_enum" valueNumeric="2"/>
|
||||||
|
<column name="disabled" valueBoolean="false"/>
|
||||||
|
<column name="manual_journal_entries_allowed" valueBoolean="true"/>
|
||||||
|
<column name="description" value="Interim liability for cash physically received from a walk-in DD purchaser at submission time, pending manager approval of the issuance request. Cleared into Sponsor DD Payable + Charges Income on approval, or reversed back to Cash at Tellers on rejection."/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0350-2-fix-dd-cash-suspense-hierarchy">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.4' and hierarchy = '.'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
UPDATE acc_gl_account SET hierarchy = CONCAT('.', id, '.') WHERE gl_code = '9.4' AND hierarchy = '.';
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0350-3-add-cash-suspense-to-gl-mapping">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_gl_mapping" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="cash_suspense_gl_account_id" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_gl_mapping">
|
||||||
|
<column name="cash_suspense_gl_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_cash_suspense_gl_id" references="acc_gl_account(id)" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0350-4-map-dd-cash-suspense-account">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<sqlCheck expectedResult="0">
|
||||||
|
select count(*) from m_sponsor_dd_gl_mapping where cash_suspense_gl_account_id is null
|
||||||
|
</sqlCheck>
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
UPDATE m_sponsor_dd_gl_mapping
|
||||||
|
SET cash_suspense_gl_account_id = (select id from acc_gl_account where gl_code = '9.4')
|
||||||
|
WHERE cash_suspense_gl_account_id is null;
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- Audit trail: which journal entry recorded the initial cash receipt (posted at submission),
|
||||||
|
and which one reversed it (posted only if the request is later rejected). Both are nullable
|
||||||
|
since ACCOUNT_DEBIT/CHEQUE requests never populate them, and an approved CASH request only
|
||||||
|
ever gets a receipt entry (never a refund one). -->
|
||||||
|
<changeSet author="fineract" id="0350-5-add-cash-receipt-journal-entry-column">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_issue_request" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_issue_request" columnName="cash_receipt_journal_entry_id" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_issue_request">
|
||||||
|
<column name="cash_receipt_journal_entry_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0350-6-add-cash-refund-journal-entry-column">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_issue_request" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_issue_request" columnName="cash_refund_journal_entry_id" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_issue_request">
|
||||||
|
<column name="cash_refund_journal_entry_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</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">
|
||||||
|
|
||||||
|
<!-- A DD leaflet is a negotiable instrument - printing it a second time is a control risk
|
||||||
|
(duplicate physical drafts). The first print is free; any print after that requires a
|
||||||
|
reprint request approved by a higher authority (maker-checker), same pattern as issuance. -->
|
||||||
|
<changeSet author="fineract" id="0351-1-add-print-tracking-to-leaflet">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_leaflet" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_leaflet" columnName="print_count" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_leaflet">
|
||||||
|
<column name="print_count" type="INT" defaultValueNumeric="0">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="last_printed_date" type="DATETIME">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0351-2-create-reprint-request-table">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<tableExists tableName="m_sponsor_dd_reprint_request" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<createTable tableName="m_sponsor_dd_reprint_request">
|
||||||
|
<column name="id" type="BIGINT" autoIncrement="true">
|
||||||
|
<constraints primaryKey="true" nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="leaflet_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_sdd_reprint_leaflet_id" references="m_sponsor_dd_leaflet(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="reason" type="VARCHAR(500)">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="consumed" type="BOOLEAN" defaultValueBoolean="false">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="maker_id" type="BIGINT">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_sdd_reprint_maker_id" references="m_appuser(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="created_date" type="DATETIME" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="checker_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_reprint_checker_id" references="m_appuser(id)" />
|
||||||
|
</column>
|
||||||
|
<column name="checker_date" type="DATETIME">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="rejection_reason" type="VARCHAR(1000)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- "Payable at [Branch], [City]" - standard field on a demand draft telling the beneficiary
|
||||||
|
where it can be encashed. Nullable at the DB level (existing rows have neither); the
|
||||||
|
write service enforces it as required for new submissions. -->
|
||||||
|
<changeSet author="fineract" id="0352-1-add-payable-at-to-issue-request">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_issue_request" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_issue_request" columnName="payable_at_branch" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_issue_request">
|
||||||
|
<column name="payable_at_branch" type="VARCHAR(150)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="payable_at_city" type="VARCHAR(100)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0352-2-add-payable-at-to-leaflet">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_leaflet" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_leaflet" columnName="payable_at_branch" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_leaflet">
|
||||||
|
<column name="payable_at_branch" type="VARCHAR(150)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="payable_at_city" type="VARCHAR(100)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- GST collected on DD issuance charges is money owed to the government, not bank income -
|
||||||
|
it gets its own liability GL account, separate from Charges Income. -->
|
||||||
|
<changeSet author="fineract" id="0353-1-insert-gst-payable-gl-account">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.5'</sqlCheck>
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<insert tableName="acc_gl_account">
|
||||||
|
<column name="name" value="GST Payable on DD Charges"/>
|
||||||
|
<column name="gl_code" value="9.5"/>
|
||||||
|
<column name="parent_id" valueNumeric="22"/>
|
||||||
|
<column name="hierarchy" value="."/>
|
||||||
|
<column name="account_usage" valueNumeric="1"/>
|
||||||
|
<column name="classification_enum" valueNumeric="2"/>
|
||||||
|
<column name="disabled" valueBoolean="false"/>
|
||||||
|
<column name="manual_journal_entries_allowed" valueBoolean="true"/>
|
||||||
|
<column name="description" value="GST collected from customers/purchasers on Sponsor DD issuance charges, held pending remittance to the government - not bank income."/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0353-2-fix-gst-payable-hierarchy">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.5' and hierarchy = '.'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
UPDATE acc_gl_account SET hierarchy = CONCAT('.', id, '.') WHERE gl_code = '9.5' AND hierarchy = '.';
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- gst_percentage is deliberately a configurable column, not a hardcoded constant in Java -
|
||||||
|
it can be changed at any time (e.g. when the government revises the GST rate) via the
|
||||||
|
existing GL mapping admin endpoint, without a code deployment. Defaults to 18.00 (the
|
||||||
|
current GST rate on Indian banking service charges) for existing/new rows. -->
|
||||||
|
<changeSet author="fineract" id="0353-3-add-gst-columns-to-gl-mapping">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_gl_mapping" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="gst_percentage" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_gl_mapping">
|
||||||
|
<column name="gst_percentage" type="DECIMAL(5,2)" defaultValueNumeric="18.00">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="gst_payable_gl_account_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_glmap_gst_gl_id" references="acc_gl_account(id)" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0353-4-map-gst-payable-account">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<not>
|
||||||
|
<sqlCheck expectedResult="0">
|
||||||
|
select count(*) from m_sponsor_dd_gl_mapping where gst_payable_gl_account_id is null
|
||||||
|
</sqlCheck>
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
UPDATE m_sponsor_dd_gl_mapping
|
||||||
|
SET gst_payable_gl_account_id = (select id from acc_gl_account where gl_code = '9.5')
|
||||||
|
WHERE gst_payable_gl_account_id is null;
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- Locked in at submission time (not recomputed at approval), so a rate change between
|
||||||
|
submission and approval never silently alters a request already in flight. -->
|
||||||
|
<changeSet author="fineract" id="0353-5-add-gst-columns-to-issue-request">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_issue_request" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_issue_request" columnName="gst_amount" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_issue_request">
|
||||||
|
<column name="gst_amount" type="DECIMAL(19,6)" defaultValueNumeric="0">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
<column name="gst_percentage_applied" type="DECIMAL(5,2)" defaultValueNumeric="0">
|
||||||
|
<constraints nullable="false" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Identification codes needed to route/reconcile settlement with the sponsor bank and
|
||||||
|
confirm its identity unambiguously - IFSC (settlement branch), RBI/NPCI bank code, and
|
||||||
|
SWIFT/BIC (only relevant if it's ever a foreign correspondent bank). All optional at the
|
||||||
|
DB level since existing rows have none, but the API validates IFSC format when supplied. -->
|
||||||
|
<changeSet author="fineract" id="0354-1-add-sponsor-bank-identification-codes">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_bank" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_bank" columnName="ifsc_code" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_bank">
|
||||||
|
<column name="ifsc_code" type="VARCHAR(11)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="bank_code" type="VARCHAR(20)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
<column name="swift_code" type="VARCHAR(11)">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- CASH DD requests no longer post their own "cash received" GL entry - the cashier records
|
||||||
|
the physical cash intake themselves via Transaction Entry (AC.TP=GL, target = the DD Cash
|
||||||
|
Suspense GL account), which is the existing, real cashier-position-tracked mechanism
|
||||||
|
(m_cashier_deposit_request + m_savings_txn_denomination). This column links a sponsor DD
|
||||||
|
issue request to the specific approved cashier deposit request that received its cash, so
|
||||||
|
we can validate the amount/account match and prevent the same cash being reused twice. -->
|
||||||
|
<changeSet author="fineract" id="0355-1-add-cashier-deposit-request-link">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_issue_request" />
|
||||||
|
<tableExists tableName="m_cashier_deposit_request" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_issue_request" columnName="cashier_deposit_request_id" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_issue_request">
|
||||||
|
<column name="cashier_deposit_request_id" type="BIGINT">
|
||||||
|
<constraints nullable="true" foreignKeyName="fk_sdd_issuereq_cashier_deposit_id"
|
||||||
|
references="m_cashier_deposit_request(id)" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<!-- The cashier's Transaction Entry GL-deposit flow always contras cash against "Cash at Vault"
|
||||||
|
(gl_code 1), not "Cash at Teller" (gl_code 2) - see CashierDepositRequestApiResource's
|
||||||
|
executeGlJournalEntry / the frontend's loadCashVaultGl(). Repoint our cash_gl_account_id to
|
||||||
|
match, since the reject/refund leg (Dr DD Cash Suspense / Cr this account) must reverse
|
||||||
|
against the same account the cashier's approved deposit actually touched. -->
|
||||||
|
<changeSet author="fineract" id="0355-2-repoint-cash-account-to-vault">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_sponsor_dd_gl_mapping" />
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '1'</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
UPDATE m_sponsor_dd_gl_mapping
|
||||||
|
SET cash_gl_account_id = (select id from acc_gl_account where gl_code = '1')
|
||||||
|
WHERE cash_gl_account_id = (select id from acc_gl_account where gl_code = '2');
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Transaction Entry's DD + Cash + GL row splits the single deposit amount into DD face value,
|
||||||
|
issuance charges and GST on those charges (Amount + Charges + GST, rounded up to the target
|
||||||
|
cash-collected total). Today m_cashier_deposit_request only stores that combined "amount" -
|
||||||
|
the breakdown is lost once the request is submitted, forcing whoever later issues the actual
|
||||||
|
DD leaflet in the Sponsor DD module to retype face value/charges from scratch. These columns
|
||||||
|
persist the breakdown at submit time (null for ordinary, non-DD deposit requests) so the DD
|
||||||
|
issuance screen can read it back via the linked cashier_deposit_request_id and pre-fill its
|
||||||
|
form instead of retyping. -->
|
||||||
|
<changeSet author="fineract" id="0356-1-add-dd-breakdown-to-cashier-deposit-request">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<tableExists tableName="m_cashier_deposit_request" />
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_cashier_deposit_request" columnName="dd_face_value" />
|
||||||
|
</not>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_cashier_deposit_request">
|
||||||
|
<column name="dd_face_value" type="DECIMAL(19,6)" />
|
||||||
|
<column name="dd_charges" type="DECIMAL(19,6)" />
|
||||||
|
<column name="dd_gst_percentage" type="DECIMAL(19,6)" />
|
||||||
|
<column name="dd_gst_amount" type="DECIMAL(19,6)" />
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Admin-configurable ceiling for a single DD's face value, also used as the ceiling for the
|
||||||
|
combined face value of all DDs issued to the same beneficiary on the same day (structuring
|
||||||
|
control for walk-in/large-cash DD issuance). Null/unset means no limit is enforced. -->
|
||||||
|
<changeSet author="fineract" id="0357-1-add-dd-limit-amount">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<and>
|
||||||
|
<tableExists tableName="m_sponsor_dd_gl_mapping"/>
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_gl_mapping" columnName="dd_limit_amount"/>
|
||||||
|
</not>
|
||||||
|
</and>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_gl_mapping">
|
||||||
|
<column name="dd_limit_amount" type="DECIMAL(19,6)">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Groups several DD issue requests submitted together as one "DD slip" (one customer account
|
||||||
|
debited/one cheque, several drafts for different beneficiaries/amounts). Set to the id of the
|
||||||
|
first row inserted for the slip - every row in the slip, including that first row, carries the
|
||||||
|
same batch_id, so the group can be queried/approved/rejected as one unit. Null for an ordinary
|
||||||
|
single-DD request (unchanged behaviour). No FK - it's a grouping tag, not a relationship to a
|
||||||
|
separate header row. -->
|
||||||
|
<changeSet author="fineract" id="0358-1-add-batch-id">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<and>
|
||||||
|
<tableExists tableName="m_sponsor_dd_issue_request"/>
|
||||||
|
<not>
|
||||||
|
<columnExists tableName="m_sponsor_dd_issue_request" columnName="batch_id"/>
|
||||||
|
</not>
|
||||||
|
</and>
|
||||||
|
</preConditions>
|
||||||
|
<addColumn tableName="m_sponsor_dd_issue_request">
|
||||||
|
<column name="batch_id" type="BIGINT">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="fineract" id="0358-2-index-batch-id">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<and>
|
||||||
|
<tableExists tableName="m_sponsor_dd_issue_request"/>
|
||||||
|
<not>
|
||||||
|
<indexExists tableName="m_sponsor_dd_issue_request" indexName="idx_sdd_issue_req_batch_id"/>
|
||||||
|
</not>
|
||||||
|
</and>
|
||||||
|
</preConditions>
|
||||||
|
<createIndex tableName="m_sponsor_dd_issue_request" indexName="idx_sdd_issue_req_batch_id">
|
||||||
|
<column name="batch_id"/>
|
||||||
|
</createIndex>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<!-- Bank's own choice: book GST collected on DD charges as income rather than tracking it as a
|
||||||
|
payable owed to the government. Same GL account (gl_code 9.5), same id, so
|
||||||
|
m_sponsor_dd_gl_mapping.gst_payable_gl_account_id and the DD-issuance GL posting code need no
|
||||||
|
change at all - this only re-tags how the account is classified/grouped for reporting.
|
||||||
|
parent_id=25 matches the parent already used by "DD Issuance Charges Income" (gl_code 10.3,
|
||||||
|
see 0348), so both DD-related income accounts group together under the same income parent. -->
|
||||||
|
<changeSet author="fineract" id="0359-1-reclassify-gst-payable-as-income">
|
||||||
|
<preConditions onFail="MARK_RAN">
|
||||||
|
<sqlCheck expectedResult="1">select count(*) from acc_gl_account where gl_code = '9.5' and classification_enum = 2</sqlCheck>
|
||||||
|
</preConditions>
|
||||||
|
<sql>
|
||||||
|
UPDATE acc_gl_account
|
||||||
|
SET classification_enum = 4, parent_id = 25
|
||||||
|
WHERE gl_code = '9.5';
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
+3
@@ -932,6 +932,9 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal getEffectiveOverdraftInterestRateAsFraction(MathContext mc) {
|
private BigDecimal getEffectiveOverdraftInterestRateAsFraction(MathContext mc) {
|
||||||
|
if (this.nominalAnnualInterestRateOverdraft == null) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
return this.nominalAnnualInterestRateOverdraft.divide(BigDecimal.valueOf(100L), mc);
|
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
@@ -90,6 +90,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.
|
* Collects all unpaid SavingsActivation charges on the account.
|
||||||
* Called after facility charges are saved and flushed, so amounts are properly initialized.
|
* 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/2027_add_pending_edit_satellite_tables.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/2028_update_saving_product_temp_table.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/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>
|
</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