FINERACT-2348: Calculate and store Capitalized income / Buydown fee amortization allocation schedule

This commit is contained in:
mariiaKraievska
2025-09-08 14:00:06 +02:00
committed by Adam Saghy
parent 9790f59e4a
commit cd0ecfcda0
34 changed files with 2094 additions and 91 deletions
@@ -0,0 +1,79 @@
/**
* 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.loanaccount.api;
import io.swagger.v3.oas.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
* Swagger documentation for Loan Amortization Allocation API
*/
final class LoanAmortizationAllocationApiResourceSwagger {
private LoanAmortizationAllocationApiResourceSwagger() {}
/**
* Common response class for all loan amortization allocation APIs Used for both Capitalized Income and Buydown Fee
* endpoints
*/
@Schema(description = "LoanAmortizationAllocationResponse")
public static final class LoanAmortizationAllocationResponse {
private LoanAmortizationAllocationResponse() {}
@Schema(example = "1")
public Long loanId;
@Schema(example = "loan-ext-123")
public String loanExternalId;
@Schema(example = "123")
public Long baseLoanTransactionId;
@Schema(example = "2024-01-15")
public LocalDate baseLoanTransactionDate;
@Schema(example = "1000.00")
public BigDecimal baseLoanTransactionAmount;
@Schema(example = "50.00")
public BigDecimal unrecognizedAmount;
@Schema(example = "0.00")
public BigDecimal chargedOffAmount;
@Schema(example = "25.00")
public BigDecimal adjustmentAmount;
public List<AmortizationMappingData> amortizationMappings;
/**
* Data transfer object for amortization mapping details
*/
static final class AmortizationMappingData {
private AmortizationMappingData() {}
@Schema(example = "789")
public Long amortizationLoanTransactionId;
@Schema(example = "amort-txn-ext-789")
public String amortizationLoanTransactionExternalId;
@Schema(example = "2024-01-15")
public LocalDate date;
@Schema(example = "AM", description = "AM for amortization, AM_ADJ for amortization adjustment")
public String type;
@Schema(example = "10.00")
public BigDecimal amount;
}
}
}
@@ -0,0 +1,42 @@
/**
* 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.loanaccount.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
/**
* DTO for base transaction information for AmortizationAllocationMapping
*/
@Getter
@AllArgsConstructor
public class AmortizationAllocationBaseTransactionDTO {
private Long loanId;
private ExternalId loanExternalId;
private Long baseLoanTransactionId;
private LocalDate baseLoanTransactionDate;
private BigDecimal baseLoanTransactionAmount;
private BigDecimal unrecognizedAmount;
private BigDecimal chargedOffAmount;
private BigDecimal adjustmentAmount;
}
@@ -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.loanaccount.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
/**
* DTO for amortization allocation mapping data
*/
@Getter
@AllArgsConstructor
public class AmortizationAllocationMappingDTO {
private Long amortizationLoanTransactionId;
private ExternalId amortizationLoanTransactionExternalId;
private LocalDate amortizationDate;
private AmortizationType amortizationType;
private BigDecimal amount;
}
@@ -0,0 +1,62 @@
/**
* 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.loanaccount.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
/**
* Data transfer object for loan amortization allocation information
*/
@Getter
@Builder
@AllArgsConstructor
public class LoanAmortizationAllocationData {
private Long loanId;
private ExternalId loanExternalId;
private Long baseLoanTransactionId;
private LocalDate baseLoanTransactionDate;
private BigDecimal baseLoanTransactionAmount;
private BigDecimal unrecognizedAmount;
private BigDecimal chargedOffAmount;
private BigDecimal adjustmentAmount;
private List<AmortizationMappingData> amortizationMappings;
/**
* Data transfer object for amortization mapping details
*/
@Getter
@Builder
@AllArgsConstructor
public static class AmortizationMappingData {
private Long amortizationLoanTransactionId;
private ExternalId amortizationLoanTransactionExternalId;
private LocalDate date;
private AmortizationType type; // AM or AM_ADJ
private BigDecimal amount;
}
}
@@ -0,0 +1,34 @@
/**
* 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.loanaccount.domain;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.api.ApiFacingEnum;
@Getter
@RequiredArgsConstructor
public enum AmortizationType implements ApiFacingEnum<AmortizationType> {
AM("amortizationTransaction.type.amortization", "Amortization"), //
AM_ADJ("amortizationTransaction.type.amortizationAdjustment", "Amortization Adjustment"); //
private final String code;
private final String humanReadableName;
}
@@ -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.loanaccount.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
/**
* Entity to store the mapping between base loan transactions (Capitalized Income or Buy Down Fee) and their
* amortization allocation schedule. This mapping table stores which base loan transaction amortization occurs, on which
* date and what was the calculated amount.
*/
@Entity
@Table(name = "m_loan_amortization_allocation_mapping")
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class LoanAmortizationAllocationMapping extends AbstractAuditableWithUTCDateTimeCustom<Long> {
@Column(name = "loan_id", nullable = false)
private Long loanId;
@Column(name = "base_loan_transaction_id", nullable = false)
private Long baseLoanTransactionId;
@Column(name = "date", nullable = false)
private LocalDate date;
@Column(name = "amortization_loan_transaction_id", nullable = false)
private Long amortizationLoanTransactionId;
/**
* Type of the amortization transaction (AM - amortization, AM_ADJ - amortization adjustment)
*/
@Enumerated(EnumType.STRING)
@Column(name = "amortization_type", nullable = false)
private AmortizationType amortizationType;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
}
@@ -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.loanaccount.domain;
import java.util.List;
import org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationMappingDTO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
/**
* Repository for LoanAmortizationAllocationMapping entity
*/
@Repository
public interface LoanAmortizationAllocationMappingRepository
extends JpaRepository<LoanAmortizationAllocationMapping, Long>, JpaSpecificationExecutor<LoanAmortizationAllocationMapping> {
@Query(value = "SELECT new org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationMappingDTO("
+ "laam.amortizationLoanTransactionId, at.externalId, laam.date, laam.amortizationType, laam.amount) "
+ "FROM LoanAmortizationAllocationMapping laam " + "JOIN LoanTransaction at ON laam.amortizationLoanTransactionId = at.id "
+ "WHERE laam.baseLoanTransactionId = :baseLoanTransactionId AND laam.loanId = :loanId "
+ "ORDER BY laam.date, laam.amortizationLoanTransactionId")
List<AmortizationAllocationMappingDTO> findAmortizationMappingsByBaseTransactionAndLoan(
@Param("baseLoanTransactionId") Long baseLoanTransactionId, @Param("loanId") Long loanId);
}
@@ -0,0 +1,44 @@
/**
* 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.loanaccount.service;
import java.math.BigDecimal;
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
public interface LoanAmortizationAllocationService {
/**
* Retrieve amortization allocation data for a specific buy down fee loan transaction
*/
LoanAmortizationAllocationData retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(Long loanTransactionId, Long loanId);
/**
* Retrieve amortization allocation data for a specific capitalized income loan transaction
*/
LoanAmortizationAllocationData retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(Long loanTransactionId, Long loanId);
LoanAmortizationAllocationMapping createAmortizationAllocationMappingWithBaseLoanTransaction(LoanTransaction loanTransaction,
BigDecimal amount, AmortizationType amortizationType);
void setAmortizationTransactionDataAndSaveAmortizationAllocationMapping(LoanAmortizationAllocationMapping amortizationAllocationMapping,
LoanTransaction amortizationTransaction);
}
@@ -163,4 +163,6 @@ public interface LoanReadPlatformService {
Long getResolvedLoanId(ExternalId loanExternalId);
Long getResolvedLoanTransactionId(Long transactionId, ExternalId externalTransactionId);
}