feat(loan-products): add product master table and GL account seeding for loan interest rate registe
This commit is contained in:
+1
@@ -157,6 +157,7 @@ public class CommandSourceService {
|
||||
}
|
||||
if ("GLACCOUNT".equalsIgnoreCase(commandSource.getEntityName())
|
||||
|| "CBSBASEINTERESTRATE".equalsIgnoreCase(commandSource.getEntityName())
|
||||
|| "LOANBASEINTERESTRATE".equalsIgnoreCase(commandSource.getEntityName())
|
||||
|| "CHARGE".equalsIgnoreCase(commandSource.getEntityName())
|
||||
|| "LOANPRODUCT".equalsIgnoreCase(commandSource.getEntityName())
|
||||
|| "SAVINGSPRODUCT".equalsIgnoreCase(commandSource.getEntityName())
|
||||
|
||||
+2
-2
@@ -2299,9 +2299,9 @@ public class CommandWrapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createCbsLoanBaseInterestRate() {
|
||||
public CommandWrapperBuilder createLoanBaseInterestRate() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "CBSLOANBASEINTERESTRATE";
|
||||
this.entityName = "LOANBASEINTERESTRATE";
|
||||
this.entityId = null;
|
||||
this.href = "/loanproducts/baseinterestrates";
|
||||
return this;
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanproduct.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* A "particular" (loan type) row of the Loan Interest Rate Register chart. The GL account is a
|
||||
* property of the particular, so it lives here rather than on each rate-register version. Kept as a
|
||||
* plain {@code gl_account_id} column (no relation) to avoid pulling the accounting module's entity
|
||||
* graph into the loan module.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_loan_rate_register_product")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class LoanRateRegisterProduct {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "sr_no", nullable = false)
|
||||
private Integer srNo;
|
||||
|
||||
@Column(name = "name", length = 200, nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "gl_account_id")
|
||||
private Long glAccountId;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private Boolean isActive;
|
||||
|
||||
@Column(name = "created_date")
|
||||
private LocalDateTime createdDate;
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanproduct.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface LoanRateRegisterProductRepository extends JpaRepository<LoanRateRegisterProduct, Long> {
|
||||
|
||||
/** Active particulars in chart order. */
|
||||
List<LoanRateRegisterProduct> findByIsActiveTrueOrderBySrNoAsc();
|
||||
}
|
||||
+8
-3
@@ -41,6 +41,10 @@ public class LoanRateRegisterSlabHistory {
|
||||
private Long effectiveId;
|
||||
|
||||
// ---- product dimension ----------------------------------------------
|
||||
/** Stable link to the master particular (and its GL). Null only for rows written before the master existed. */
|
||||
@Column(name = "product_id")
|
||||
private Long productId;
|
||||
|
||||
@Column(name = "product_sr_no", nullable = false)
|
||||
private Integer productSrNo;
|
||||
|
||||
@@ -73,11 +77,12 @@ public class LoanRateRegisterSlabHistory {
|
||||
@Column(name = "created_date")
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
public static LoanRateRegisterSlabHistory create(final Long effectiveId, final Integer productSrNo, final String productName,
|
||||
final Integer slabOrder, final String slabLabel, final BigDecimal amountUpto, final BigDecimal rateValue,
|
||||
final String rateFormula, final boolean isFormula, final boolean isNa) {
|
||||
public static LoanRateRegisterSlabHistory create(final Long effectiveId, final Long productId, final Integer productSrNo,
|
||||
final String productName, final Integer slabOrder, final String slabLabel, final BigDecimal amountUpto,
|
||||
final BigDecimal rateValue, final String rateFormula, final boolean isFormula, final boolean isNa) {
|
||||
final LoanRateRegisterSlabHistory s = new LoanRateRegisterSlabHistory();
|
||||
s.effectiveId = effectiveId;
|
||||
s.productId = productId;
|
||||
s.productSrNo = productSrNo;
|
||||
s.productName = productName;
|
||||
s.slabOrder = slabOrder;
|
||||
|
||||
+15
-3
@@ -44,7 +44,7 @@ import org.springframework.stereotype.Component;
|
||||
* a POST that goes through Fineract's maker-checker pipeline.
|
||||
*
|
||||
* Reads are gated by the pre-existing {@code LOANPRODUCT} resource; writes by the two seeded
|
||||
* permissions {@code CREATE_CBSLOANBASEINTERESTRATE} (maker) and {@code CREATE_CBSLOANBASEINTERESTRATE_CHECKER}.
|
||||
* permissions {@code CREATE_LOANBASEINTERESTRATE} (maker) and {@code CREATE_LOANBASEINTERESTRATE_CHECKER}.
|
||||
*/
|
||||
@Path("/v1/loanproducts/baseinterestrates")
|
||||
@Component
|
||||
@@ -76,6 +76,18 @@ public class LoanProductsBaseInterestRatesApiResource {
|
||||
return this.gson.toJson(data);
|
||||
}
|
||||
|
||||
/** Master list of particulars (loan types) that make up the chart rows. Literal path wins over {id}. */
|
||||
@GET
|
||||
@Path("products")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Get Loan Interest Rate Register particulars", description = "Returns the master list of loan types (chart rows) with their mapped GL account id, in chart order.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public String retrieveProducts() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
return this.gson.toJson(this.readService.retrieveProducts());
|
||||
}
|
||||
|
||||
/** The currently in-force version, with its full chart + CIBIL. */
|
||||
@GET
|
||||
@Path("active")
|
||||
@@ -103,7 +115,7 @@ public class LoanProductsBaseInterestRatesApiResource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a new version. If {@code CREATE_CBSLOANBASEINTERESTRATE.can_maker_checker=true}, this
|
||||
* Submit a new version. If {@code CREATE_LOANBASEINTERESTRATE.can_maker_checker=true}, this
|
||||
* returns {@code rollbackTransaction=true} and the row sits in {@code m_portfolio_command_source}
|
||||
* until a checker approves it from the Approvals tab. Otherwise it commits immediately.
|
||||
*/
|
||||
@@ -114,7 +126,7 @@ public class LoanProductsBaseInterestRatesApiResource {
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public CommandProcessingResult submitRateRegister(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.createCbsLoanBaseInterestRate()
|
||||
.createLoanBaseInterestRate()
|
||||
.withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
+14
@@ -30,9 +30,23 @@ public class LoanRateRegisterSaveRequest {
|
||||
private String fallbackBelowMin;
|
||||
private String note;
|
||||
|
||||
/**
|
||||
* Particulars (chart rows) with their GL. A row whose srNo is not yet in the master is created on
|
||||
* approval and must carry a glAccountId; for an existing row a changed glAccountId/name updates the master.
|
||||
*/
|
||||
private List<ProductRequest> products;
|
||||
private List<SlabRequest> slabs;
|
||||
private List<CibilSlabRequest> cibilSlabs;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public static class ProductRequest {
|
||||
private Integer srNo;
|
||||
private String name;
|
||||
private Long glAccountId;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanproduct.data;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** Response DTO for one particular (loan type) of the Loan Interest Rate Register. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class LoanRateRegisterProductData {
|
||||
|
||||
private Long id;
|
||||
private Integer srNo;
|
||||
private String name;
|
||||
/** Null until the bank maps the particular to a GL account. */
|
||||
private Long glAccountId;
|
||||
private Boolean isActive;
|
||||
}
|
||||
+1
@@ -20,6 +20,7 @@ import lombok.Getter;
|
||||
public class LoanRateRegisterSlabData {
|
||||
|
||||
private Long id;
|
||||
private Long productId;
|
||||
private Integer productSrNo;
|
||||
private String productName;
|
||||
private Integer slabOrder;
|
||||
|
||||
+4
-4
@@ -17,15 +17,15 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Dispatched by Fineract when a {@code CBSLOANBASEINTERESTRATE} / {@code CREATE} command is executed —
|
||||
* Dispatched by Fineract when a {@code LOANBASEINTERESTRATE} / {@code CREATE} command is executed —
|
||||
* on first submit (maker-checker off) or on checker approval (maker-checker on). The {@code @CommandType}
|
||||
* entity string here MUST match the one registered by {@code CommandWrapperBuilder.createCbsLoanBaseInterestRate()}
|
||||
* entity string here MUST match the one registered by {@code CommandWrapperBuilder.createLoanBaseInterestRate()}
|
||||
* and the permission codes seeded by {@code 0581_enable_loan_rate_register_maker_checker.xml}.
|
||||
*/
|
||||
@Service
|
||||
@CommandType(entity = "CBSLOANBASEINTERESTRATE", action = "CREATE")
|
||||
@CommandType(entity = "LOANBASEINTERESTRATE", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateCbsLoanBaseInterestRateCommandHandler implements NewCommandSourceHandler {
|
||||
public class CreateLoanBaseInterestRateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanRateRegisterWritePlatformService writePlatformService;
|
||||
|
||||
+4
@@ -10,6 +10,7 @@ package org.apache.fineract.portfolio.loanproduct.service;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanRateRegisterData;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanRateRegisterProductData;
|
||||
|
||||
public interface LoanRateRegisterReadPlatformService {
|
||||
|
||||
@@ -21,4 +22,7 @@ public interface LoanRateRegisterReadPlatformService {
|
||||
|
||||
/** A specific version by id. */
|
||||
Optional<LoanRateRegisterData> retrieveById(Long id);
|
||||
|
||||
/** Master list of particulars (loan types) that make up the chart rows, in chart order. */
|
||||
List<LoanRateRegisterProductData> retrieveProducts();
|
||||
}
|
||||
|
||||
+21
@@ -13,11 +13,14 @@ import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanRateRegisterCibilSlabData;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanRateRegisterData;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanRateRegisterProductData;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanRateRegisterSlabData;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterCibilSlab;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterCibilSlabRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterEffective;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterEffectiveRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterProductRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterSlabHistory;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterSlabHistoryRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -33,6 +36,23 @@ public class LoanRateRegisterReadPlatformServiceImpl implements LoanRateRegister
|
||||
private final LoanRateRegisterEffectiveRepository effectiveRepository;
|
||||
private final LoanRateRegisterSlabHistoryRepository slabRepository;
|
||||
private final LoanRateRegisterCibilSlabRepository cibilRepository;
|
||||
private final LoanRateRegisterProductRepository productRepository;
|
||||
|
||||
@Override
|
||||
public List<LoanRateRegisterProductData> retrieveProducts() {
|
||||
final List<LoanRateRegisterProduct> products = this.productRepository.findByIsActiveTrueOrderBySrNoAsc();
|
||||
final List<LoanRateRegisterProductData> out = new ArrayList<>(products.size());
|
||||
for (final LoanRateRegisterProduct p : products) {
|
||||
out.add(LoanRateRegisterProductData.builder()
|
||||
.id(p.getId())
|
||||
.srNo(p.getSrNo())
|
||||
.name(p.getName())
|
||||
.glAccountId(p.getGlAccountId())
|
||||
.isActive(p.getIsActive())
|
||||
.build());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoanRateRegisterData> retrieveAll() {
|
||||
@@ -65,6 +85,7 @@ public class LoanRateRegisterReadPlatformServiceImpl implements LoanRateRegister
|
||||
for (final LoanRateRegisterSlabHistory c : cells) {
|
||||
slabData.add(LoanRateRegisterSlabData.builder()
|
||||
.id(c.getId())
|
||||
.productId(c.getProductId())
|
||||
.productSrNo(c.getProductSrNo())
|
||||
.productName(c.getProductName())
|
||||
.slabOrder(c.getSlabOrder())
|
||||
|
||||
+101
-4
@@ -10,21 +10,32 @@ package org.apache.fineract.portfolio.loanproduct.service;
|
||||
import com.google.gson.Gson;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.loanproduct.api.LoanRateRegisterSaveRequest;
|
||||
import org.apache.fineract.portfolio.loanproduct.api.LoanRateRegisterSaveRequest.CibilSlabRequest;
|
||||
import org.apache.fineract.portfolio.loanproduct.api.LoanRateRegisterSaveRequest.ProductRequest;
|
||||
import org.apache.fineract.portfolio.loanproduct.api.LoanRateRegisterSaveRequest.SlabRequest;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterCibilSlab;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterCibilSlabRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterEffective;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterEffectiveRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterProductRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterSlabHistory;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanRateRegisterSlabHistoryRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.serialization.LoanRateRegisterDataValidator;
|
||||
@@ -45,6 +56,8 @@ public class LoanRateRegisterWritePlatformServiceImpl implements LoanRateRegiste
|
||||
private final LoanRateRegisterEffectiveRepository effectiveRepository;
|
||||
private final LoanRateRegisterSlabHistoryRepository slabRepository;
|
||||
private final LoanRateRegisterCibilSlabRepository cibilRepository;
|
||||
private final LoanRateRegisterProductRepository productRepository;
|
||||
private final GLAccountRepositoryWrapper glAccountRepositoryWrapper;
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
@Override
|
||||
@@ -56,6 +69,10 @@ public class LoanRateRegisterWritePlatformServiceImpl implements LoanRateRegiste
|
||||
final LocalDate today = LocalDate.now(DateUtils.getDateTimeZoneOfTenant());
|
||||
final LocalDate effectiveFrom = parseDateOrToday(req.getEffectiveFromDate(), today);
|
||||
|
||||
// 0) Upsert the particulars master first, so a missing GL on a new product fails the whole
|
||||
// save before any version row is touched. Returns srNo -> master row for the cells below.
|
||||
final Map<Integer, LoanRateRegisterProduct> productsBySrNo = upsertProducts(req);
|
||||
|
||||
// 1) Deactivate the currently-active version (if any).
|
||||
final List<LoanRateRegisterEffective> active = this.effectiveRepository.findByIsActiveTrueOrderByEffectiveFromDateDesc();
|
||||
for (final LoanRateRegisterEffective prior : active) {
|
||||
@@ -65,17 +82,20 @@ public class LoanRateRegisterWritePlatformServiceImpl implements LoanRateRegiste
|
||||
this.effectiveRepository.saveAll(active);
|
||||
}
|
||||
|
||||
// 2) Insert the new effective row.
|
||||
// 2) Insert the new effective row. saveAndFlush ensures Hibernate executes the INSERT
|
||||
// immediately so the auto-generated id is populated before we use it as the FK on the
|
||||
// child slab/CIBIL rows (same pattern as FD side's FdBaseInterestRateReadPlatformServiceImpl).
|
||||
final LoanRateRegisterEffective fresh = LoanRateRegisterEffective.create(req.getName(), effectiveFrom,
|
||||
req.getCurrencyCode(), req.getFallbackNoCibilSlab(), req.getFallbackBelowMin(), req.getNote(), createdById);
|
||||
final LoanRateRegisterEffective saved = this.effectiveRepository.save(fresh);
|
||||
final LoanRateRegisterEffective saved = this.effectiveRepository.saveAndFlush(fresh);
|
||||
final Long versionId = saved.getId();
|
||||
|
||||
// 3) Insert all chart cells for this version.
|
||||
final List<LoanRateRegisterSlabHistory> cells = new ArrayList<>(req.getSlabs().size());
|
||||
for (final SlabRequest s : req.getSlabs()) {
|
||||
cells.add(LoanRateRegisterSlabHistory.create(versionId, s.getProductSrNo(), s.getProductName(), s.getSlabOrder(),
|
||||
s.getSlabLabel(), s.getAmountUpto(), s.getRateValue(), s.getRateFormula(),
|
||||
final Long productId = productsBySrNo.get(s.getProductSrNo()).getId();
|
||||
cells.add(LoanRateRegisterSlabHistory.create(versionId, productId, s.getProductSrNo(), s.getProductName(),
|
||||
s.getSlabOrder(), s.getSlabLabel(), s.getAmountUpto(), s.getRateValue(), s.getRateFormula(),
|
||||
Boolean.TRUE.equals(s.getIsFormula()), Boolean.TRUE.equals(s.getIsNa())));
|
||||
}
|
||||
this.slabRepository.saveAll(cells);
|
||||
@@ -94,6 +114,83 @@ public class LoanRateRegisterWritePlatformServiceImpl implements LoanRateRegiste
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure every particular used by the submitted chart exists in the master. A particular not
|
||||
* yet in the master is created and must carry a valid GL account; for an existing one a changed
|
||||
* GL or name is applied. A particular that still has no GL after this (seeded rows the bank has
|
||||
* not mapped yet) rejects the whole save.
|
||||
*/
|
||||
private Map<Integer, LoanRateRegisterProduct> upsertProducts(final LoanRateRegisterSaveRequest req) {
|
||||
final Map<Integer, LoanRateRegisterProduct> bySrNo = new HashMap<>();
|
||||
for (final LoanRateRegisterProduct p : this.productRepository.findAll()) {
|
||||
bySrNo.put(p.getSrNo(), p);
|
||||
}
|
||||
final Map<Integer, ProductRequest> requested = new HashMap<>();
|
||||
if (req.getProducts() != null) {
|
||||
for (final ProductRequest pr : req.getProducts()) {
|
||||
requested.put(pr.getSrNo(), pr);
|
||||
}
|
||||
}
|
||||
// Every srNo the chart uses, with a label taken from the first cell that mentions it.
|
||||
final Set<Integer> used = new LinkedHashSet<>();
|
||||
final Map<Integer, String> cellLabel = new HashMap<>();
|
||||
for (final SlabRequest s : req.getSlabs()) {
|
||||
used.add(s.getProductSrNo());
|
||||
cellLabel.putIfAbsent(s.getProductSrNo(), s.getProductName());
|
||||
}
|
||||
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final List<LoanRateRegisterProduct> toSave = new ArrayList<>();
|
||||
for (final Integer srNo : used) {
|
||||
final ProductRequest pr = requested.get(srNo);
|
||||
LoanRateRegisterProduct existing = bySrNo.get(srNo);
|
||||
final String name = pr != null && pr.getName() != null && !pr.getName().isBlank() ? pr.getName() : cellLabel.get(srNo);
|
||||
if (existing == null) {
|
||||
if (pr == null || pr.getGlAccountId() == null) {
|
||||
errors.add(ApiParameterError.parameterError("validation.loanRateRegister.product.gl.required",
|
||||
"New product '" + name + "' (Sr. " + srNo + ") needs a GL account", "products[" + srNo + "].glAccountId"));
|
||||
continue;
|
||||
}
|
||||
this.glAccountRepositoryWrapper.findOneWithNotFoundDetection(pr.getGlAccountId());
|
||||
existing = new LoanRateRegisterProduct();
|
||||
existing.setSrNo(srNo);
|
||||
existing.setName(name);
|
||||
existing.setGlAccountId(pr.getGlAccountId());
|
||||
existing.setIsActive(true);
|
||||
existing.setCreatedDate(LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant()));
|
||||
toSave.add(existing);
|
||||
} else if (pr != null) {
|
||||
boolean changed = false;
|
||||
if (pr.getGlAccountId() != null && !pr.getGlAccountId().equals(existing.getGlAccountId())) {
|
||||
this.glAccountRepositoryWrapper.findOneWithNotFoundDetection(pr.getGlAccountId());
|
||||
existing.setGlAccountId(pr.getGlAccountId());
|
||||
changed = true;
|
||||
}
|
||||
if (name != null && !name.equals(existing.getName())) {
|
||||
existing.setName(name);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
toSave.add(existing);
|
||||
}
|
||||
}
|
||||
// Every particular in a submitted chart must end up with a GL, including the seeded ones.
|
||||
if (existing.getGlAccountId() == null) {
|
||||
errors.add(ApiParameterError.parameterError("validation.loanRateRegister.product.gl.required",
|
||||
"Product '" + existing.getName() + "' (Sr. " + srNo + ") needs a GL account", "products[" + srNo + "].glAccountId"));
|
||||
continue;
|
||||
}
|
||||
bySrNo.put(srNo, existing);
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.loanRateRegister.failed", "Validation errors exist.", errors);
|
||||
}
|
||||
for (final LoanRateRegisterProduct p : this.productRepository.saveAllAndFlush(toSave)) {
|
||||
bySrNo.put(p.getSrNo(), p);
|
||||
}
|
||||
return bySrNo;
|
||||
}
|
||||
|
||||
private static LocalDate parseDateOrToday(final String iso, final LocalDate today) {
|
||||
if (iso == null || iso.isBlank()) {
|
||||
return today;
|
||||
|
||||
@@ -619,4 +619,6 @@
|
||||
<include file="parts/0572_add_loan_product_advance_ext_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0580_create_loan_rate_register_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0581_enable_loan_rate_register_maker_checker.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0594_loan_rate_register_product_master.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0595_seed_loan_rate_register_gl_accounts.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+46
-12
@@ -28,29 +28,63 @@
|
||||
Enable maker-checker on the Loan Interest Rate Register. Mirrors 0470 for the FD side.
|
||||
Two permissions: the maker's gated with can_maker_checker=true so submits land in
|
||||
m_portfolio_command_source as PENDING; the checker approves from the standard Checker Inbox
|
||||
via /makercheckers/{id}?command=approve, which fires CreateCbsLoanBaseInterestRateCommandHandler.
|
||||
via /makercheckers/{id}?command=approve, which fires CreateLoanBaseInterestRateCommandHandler.
|
||||
|
||||
Permission codes use the LOANBASEINTERESTRATE entity (no "CBS" prefix) to match the clean
|
||||
naming convention requested for the Loan module.
|
||||
-->
|
||||
|
||||
<changeSet id="0581a_loan_rate_register_maker_checker_permissions" author="cbsnexora" runOnChange="false">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<sql>
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'CREATE_CBSLOANBASEINTERESTRATE', 'CBSLOANBASEINTERESTRATE', 'CREATE', true
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_CBSLOANBASEINTERESTRATE');
|
||||
-- Rename any previously-seeded CBS-prefixed rows (safe no-op if a fresh DB).
|
||||
UPDATE m_permission
|
||||
SET code = 'CREATE_LOANBASEINTERESTRATE', entity_name = 'LOANBASEINTERESTRATE'
|
||||
WHERE code = 'CREATE_CBSLOANBASEINTERESTRATE';
|
||||
|
||||
UPDATE m_permission
|
||||
SET code = 'CREATE_LOANBASEINTERESTRATE_CHECKER', entity_name = 'LOANBASEINTERESTRATE'
|
||||
WHERE code = 'CREATE_CBSLOANBASEINTERESTRATE_CHECKER';
|
||||
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'CREATE_CBSLOANBASEINTERESTRATE_CHECKER', 'CBSLOANBASEINTERESTRATE', 'CREATE_CHECKER', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_CBSLOANBASEINTERESTRATE_CHECKER');
|
||||
SELECT 'portfolio', 'CREATE_LOANBASEINTERESTRATE', 'LOANBASEINTERESTRATE', 'CREATE', true
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_LOANBASEINTERESTRATE');
|
||||
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'CREATE_LOANBASEINTERESTRATE_CHECKER', 'LOANBASEINTERESTRATE', 'CREATE_CHECKER', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_LOANBASEINTERESTRATE_CHECKER');
|
||||
|
||||
UPDATE m_permission
|
||||
SET can_maker_checker = TRUE
|
||||
WHERE entity_name = 'CBSLOANBASEINTERESTRATE'
|
||||
WHERE entity_name = 'LOANBASEINTERESTRATE'
|
||||
AND action_name = 'CREATE';
|
||||
|
||||
UPDATE m_permission
|
||||
SET action_name = 'CREATE_CHECKER'
|
||||
WHERE entity_name = 'CBSLOANBASEINTERESTRATE'
|
||||
AND code = 'CREATE_CBSLOANBASEINTERESTRATE_CHECKER';
|
||||
WHERE entity_name = 'LOANBASEINTERESTRATE'
|
||||
AND code = 'CREATE_LOANBASEINTERESTRATE_CHECKER';
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<!-- Separate changeset to force the CBS→LOAN rename on DBs that already applied 0581a with the
|
||||
old names. Liquibase won't re-run an already-applied changeset, so the UPDATE statements
|
||||
inside 0581a don't fire a second time; this new id runs once per DB on next startup. -->
|
||||
<changeSet id="0581c_rename_cbs_prefix_to_loanbaseinterestrate" author="cbsnexora" runOnChange="false">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<sql>
|
||||
UPDATE m_permission
|
||||
SET code = 'CREATE_LOANBASEINTERESTRATE', entity_name = 'LOANBASEINTERESTRATE'
|
||||
WHERE code = 'CREATE_CBSLOANBASEINTERESTRATE';
|
||||
|
||||
UPDATE m_permission
|
||||
SET code = 'CREATE_LOANBASEINTERESTRATE_CHECKER', entity_name = 'LOANBASEINTERESTRATE'
|
||||
WHERE code = 'CREATE_CBSLOANBASEINTERESTRATE_CHECKER';
|
||||
|
||||
-- Rename any pending command-source rows still carrying the old entity string so
|
||||
-- the handler picks them up after the restart.
|
||||
UPDATE m_portfolio_command_source
|
||||
SET entity_name = 'LOANBASEINTERESTRATE'
|
||||
WHERE entity_name = 'CBSLOANBASEINTERESTRATE';
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
@@ -63,7 +97,7 @@
|
||||
FROM m_role r
|
||||
CROSS JOIN m_permission p
|
||||
WHERE r.name = 'Super user'
|
||||
AND p.code IN ('CREATE_CBSLOANBASEINTERESTRATE', 'CREATE_CBSLOANBASEINTERESTRATE_CHECKER')
|
||||
AND p.code IN ('CREATE_LOANBASEINTERESTRATE', 'CREATE_LOANBASEINTERESTRATE_CHECKER')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_role_permission rp
|
||||
WHERE rp.role_id = r.id AND rp.permission_id = p.id
|
||||
@@ -76,7 +110,7 @@
|
||||
JOIN m_appuser_role ur ON ur.appuser_id = u.id
|
||||
CROSS JOIN m_permission p
|
||||
WHERE u.username = 'bankadmin'
|
||||
AND p.code IN ('CREATE_CBSLOANBASEINTERESTRATE', 'CREATE_CBSLOANBASEINTERESTRATE_CHECKER')
|
||||
AND p.code IN ('CREATE_LOANBASEINTERESTRATE', 'CREATE_LOANBASEINTERESTRATE_CHECKER')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_role_permission rp
|
||||
WHERE rp.role_id = ur.role_id AND rp.permission_id = p.id
|
||||
@@ -88,7 +122,7 @@
|
||||
FROM m_role r
|
||||
CROSS JOIN m_permission p
|
||||
WHERE LOWER(r.name) = 'clerk'
|
||||
AND p.code = 'CREATE_CBSLOANBASEINTERESTRATE'
|
||||
AND p.code = 'CREATE_LOANBASEINTERESTRATE'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_role_permission rp
|
||||
WHERE rp.role_id = r.id AND rp.permission_id = p.id
|
||||
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
Master list of "particulars" (loan types) that appear as rows in the Loan Interest Rate
|
||||
Register chart. The GL account belongs to the particular, not to a version, so it is kept
|
||||
here and never has to be re-selected when a new rate version is created.
|
||||
|
||||
Seeded with the ~16 loan types that almost every bank offers. Bank-specific particulars are
|
||||
added later through Loan Interest Rate Register > Edit (new version) > Add product, which
|
||||
requires a GL. gl_account_id is NULL for the seeded rows until the bank maps them.
|
||||
-->
|
||||
|
||||
<changeSet id="0594a_create_loan_rate_register_product" author="cbsnexora">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_loan_rate_register_product"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_loan_rate_register_product">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="sr_no" type="INT">
|
||||
<constraints nullable="false" unique="true" uniqueConstraintName="uq_mlrrp_sr_no"/>
|
||||
</column>
|
||||
<column name="name" type="VARCHAR(200)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="gl_account_id" type="BIGINT"/>
|
||||
<column name="is_active" type="BOOLEAN" defaultValueBoolean="true">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_date" type="DATETIME"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_loan_rate_register_product"
|
||||
baseColumnNames="gl_account_id"
|
||||
constraintName="fk_mlrrp_gl_account"
|
||||
referencedTableName="acc_gl_account"
|
||||
referencedColumnNames="id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- Link chart cells to the particular by id. product_sr_no / product_name stay on the row as the
|
||||
denormalised label of that version; product_id is the stable link to the master (and its GL).
|
||||
Nullable so rows written before this changeset survive; backfilled by sr_no below. -->
|
||||
<changeSet id="0594c_loan_rate_register_slab_history_product_id" author="cbsnexora">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_loan_rate_register_slab_history" columnName="product_id"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_loan_rate_register_slab_history">
|
||||
<column name="product_id" type="BIGINT"/>
|
||||
</addColumn>
|
||||
<addForeignKeyConstraint baseTableName="m_loan_rate_register_slab_history"
|
||||
baseColumnNames="product_id"
|
||||
constraintName="fk_mlrrsh_product"
|
||||
referencedTableName="m_loan_rate_register_product"
|
||||
referencedColumnNames="id"/>
|
||||
<createIndex indexName="idx_mlrrsh_product" tableName="m_loan_rate_register_slab_history">
|
||||
<column name="product_id"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0594d_backfill_loan_rate_register_slab_history_product_id" author="cbsnexora">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<sql>
|
||||
UPDATE m_loan_rate_register_slab_history
|
||||
SET product_id = (SELECT p.id FROM m_loan_rate_register_product p
|
||||
WHERE p.sr_no = m_loan_rate_register_slab_history.product_sr_no)
|
||||
WHERE product_id IS NULL;
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0594b_seed_loan_rate_register_default_products" author="cbsnexora">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_loan_rate_register_product</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="1"/>
|
||||
<column name="name" value="Housing Loan (Purchase/Construction)"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="2"/>
|
||||
<column name="name" value="Home Repair/Renovation"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="3"/>
|
||||
<column name="name" value="Personal Loan (Unsecured)"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="4"/>
|
||||
<column name="name" value="Personal Loan (Secured/Property)"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="5"/>
|
||||
<column name="name" value="Two Wheeler Loan"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="6"/>
|
||||
<column name="name" value="Car Loan (New)"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="7"/>
|
||||
<column name="name" value="Used Car/Vehicle Loan"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="8"/>
|
||||
<column name="name" value="Commercial Vehicle Loan"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="9"/>
|
||||
<column name="name" value="Gold Loan"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="10"/>
|
||||
<column name="name" value="Business Term Loan"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="11"/>
|
||||
<column name="name" value="Machinery & Equipment Loan"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="12"/>
|
||||
<column name="name" value="Cash Credit"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="13"/>
|
||||
<column name="name" value="Overdraft"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="14"/>
|
||||
<column name="name" value="Agriculture Loan"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="15"/>
|
||||
<column name="name" value="Loan against FDR"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
<insert tableName="m_loan_rate_register_product">
|
||||
<column name="sr_no" valueNumeric="16"/>
|
||||
<column name="name" value="Loan against RD"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="created_date" valueComputed="NOW()"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
Loan portfolio (asset) GL accounts for the Loan Interest Rate Register particulars that had no
|
||||
GL in the standardized chart of accounts (0503). Existing GLs are reused for the rest:
|
||||
LOAN AGAINST DEPOSITE 100301, CASH CREDIT 100303, LOAN AGAINST PROPERTY 100401,
|
||||
PERSONAL LOAN 100402, HOUSE LOAN 100501.
|
||||
|
||||
Each row is inserted under its existing header (SHORT/MEDIUM/LONG TERM LOAN) and only when that
|
||||
header exists, so tenants with a different chart of accounts are left untouched. Codes follow the
|
||||
0503 scheme: classification(1) + group(2) + sequence(3). Idempotent on gl_code.
|
||||
-->
|
||||
<changeSet id="0595_seed_loan_rate_register_gl_accounts" author="cbsnexora">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<sql>
|
||||
-- SHORT TERM LOAN (100300)
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'OVERDRAFT', '100304', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100300' AND p.name = 'SHORT TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100304');
|
||||
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'GOLD LOAN', '100305', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100300' AND p.name = 'SHORT TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100305');
|
||||
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'AGRICULTURE LOAN', '100306', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100300' AND p.name = 'SHORT TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100306');
|
||||
|
||||
-- MEDIUM TERM LOAN (100400)
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'TWO WHEELER LOAN', '100403', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100400' AND p.name = 'MEDIUM TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100403');
|
||||
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'CAR LOAN (NEW)', '100404', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100400' AND p.name = 'MEDIUM TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100404');
|
||||
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'USED CAR / VEHICLE LOAN', '100405', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100400' AND p.name = 'MEDIUM TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100405');
|
||||
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'COMMERCIAL VEHICLE LOAN', '100406', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100400' AND p.name = 'MEDIUM TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100406');
|
||||
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'BUSINESS TERM LOAN', '100407', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100400' AND p.name = 'MEDIUM TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100407');
|
||||
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'MACHINERY & EQUIPMENT LOAN', '100408', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100400' AND p.name = 'MEDIUM TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100408');
|
||||
|
||||
-- LONG TERM LOAN (100500)
|
||||
INSERT INTO acc_gl_account (name, gl_code, disabled, manual_journal_entries_allowed, account_usage, classification_enum, parent_id)
|
||||
SELECT 'HOME REPAIR / RENOVATION LOAN', '100502', false, true, 1, 1, p.id FROM acc_gl_account p
|
||||
WHERE p.gl_code = '100500' AND p.name = 'LONG TERM LOAN'
|
||||
AND NOT EXISTS (SELECT 1 FROM acc_gl_account WHERE gl_code = '100502');
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user