FINERACT-2178: Ability to retrieve a loan's balance at a certain point in time in the past
This commit is contained in:
committed by
Arnold Gálovics
parent
986dceea1c
commit
0f1be3c913
+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.loanaccount.api.pointintime;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.api.DateParam;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.pointintime.data.RetrieveLoansPointInTimeExternalIdsRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.pointintime.data.RetrieveLoansPointInTimeRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanPointInTimeData;
|
||||
|
||||
public interface LoansPointInTimeApi {
|
||||
|
||||
LoanPointInTimeData retrieveLoanPointInTime(Long loanId, DateParam dateParam, String dateFormat, String locale);
|
||||
|
||||
LoanPointInTimeData retrieveLoanPointInTimeByExternalId(String loanExternalId, DateParam dateParam, String dateFormat, String locale);
|
||||
|
||||
List<LoanPointInTimeData> retrieveLoansPointInTime(RetrieveLoansPointInTimeRequest request);
|
||||
|
||||
List<LoanPointInTimeData> retrieveLoansPointInTimeByExternalIds(RetrieveLoansPointInTimeExternalIdsRequest request);
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* 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.pointintime;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.DateParam;
|
||||
import org.apache.fineract.infrastructure.core.data.DateFormat;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.pointintime.data.RetrieveLoansPointInTimeExternalIdsRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.pointintime.data.RetrieveLoansPointInTimeRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanPointInTimeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanPointInTimeService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LoansPointInTimeApiDelegate implements LoansPointInTimeApi {
|
||||
|
||||
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
|
||||
|
||||
private final LoanPointInTimeService loanPointInTimeService;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
|
||||
@Override
|
||||
public LoanPointInTimeData retrieveLoanPointInTime(Long loanId, DateParam dateParam, String dateFormat, String locale) {
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
return getLoanPointInTime(loanId, dateParam, dateFormat, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanPointInTimeData retrieveLoanPointInTimeByExternalId(String loanExternalIdStr, DateParam dateParam, String dateFormat,
|
||||
String locale) {
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
ExternalId loanExternalId = ExternalIdFactory.produce(loanExternalIdStr);
|
||||
Long loanId = resolveExternalId(loanExternalId);
|
||||
|
||||
return getLoanPointInTime(loanId, dateParam, dateFormat, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoanPointInTimeData> retrieveLoansPointInTime(RetrieveLoansPointInTimeRequest request) {
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
List<Long> loanIds = request.getLoanIds();
|
||||
DateParam dateParam = request.getDate();
|
||||
String dateFormat = request.getDateFormat();
|
||||
String locale = request.getLocale();
|
||||
|
||||
return getLoansPointInTime(loanIds, dateParam, dateFormat, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoanPointInTimeData> retrieveLoansPointInTimeByExternalIds(RetrieveLoansPointInTimeExternalIdsRequest request) {
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
List<String> loanExternalIds = request.getExternalIds();
|
||||
DateParam dateParam = request.getDate();
|
||||
String dateFormat = request.getDateFormat();
|
||||
String locale = request.getLocale();
|
||||
|
||||
List<ExternalId> externalIds = ExternalIdFactory.produce(loanExternalIds);
|
||||
List<Long> loanIds = resolveExternalIds(externalIds);
|
||||
|
||||
return getLoansPointInTime(loanIds, dateParam, dateFormat, locale);
|
||||
}
|
||||
|
||||
private List<LoanPointInTimeData> getLoansPointInTime(List<Long> loanIds, DateParam dateParam, String dateFormat, String locale) {
|
||||
DateFormat df = StringUtils.isBlank(dateFormat) ? null : new DateFormat(dateFormat);
|
||||
LocalDate date = dateParam.getDate("date", df, locale);
|
||||
return loanPointInTimeService.retrieveAt(loanIds, date);
|
||||
}
|
||||
|
||||
private LoanPointInTimeData getLoanPointInTime(Long loanId, DateParam dateParam, String dateFormat, String locale) {
|
||||
DateFormat df = StringUtils.isBlank(dateFormat) ? null : new DateFormat(dateFormat);
|
||||
LocalDate date = dateParam.getDate("date", df, locale);
|
||||
return loanPointInTimeService.retrieveAt(loanId, date);
|
||||
}
|
||||
|
||||
private List<Long> resolveExternalIds(List<ExternalId> loanExternalIds) {
|
||||
loanExternalIds.forEach(ExternalId::throwExceptionIfEmpty);
|
||||
return loanReadPlatformService.retrieveLoanIdsByExternalIds(loanExternalIds);
|
||||
}
|
||||
|
||||
private Long resolveExternalId(ExternalId loanExternalId) {
|
||||
loanExternalId.throwExceptionIfEmpty();
|
||||
Long resolvedLoanId = loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId);
|
||||
if (resolvedLoanId == null) {
|
||||
throw new LoanNotFoundException(loanExternalId);
|
||||
}
|
||||
return resolvedLoanId;
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.pointintime;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.core.MediaType;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.DateParam;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.pointintime.data.RetrieveLoansPointInTimeExternalIdsRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.pointintime.data.RetrieveLoansPointInTimeRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanPointInTimeData;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/loans/at-date")
|
||||
@Component
|
||||
@Tag(name = "LoansPointInTime", description = "API to enable clients to retrieve Loan states in a specific point in time")
|
||||
@RequiredArgsConstructor
|
||||
public class LoansPointInTimeApiResource implements LoansPointInTimeApi {
|
||||
|
||||
private final LoansPointInTimeApiDelegate delegate;
|
||||
|
||||
@GET
|
||||
@Path("{loanId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Override
|
||||
public LoanPointInTimeData retrieveLoanPointInTime(@PathParam("loanId") @Parameter(description = "loanId", required = true) Long loanId,
|
||||
@QueryParam("date") @Parameter(description = "date", required = true) DateParam dateParam,
|
||||
@QueryParam("dateFormat") @Parameter(description = "dateFormat") final String dateFormat,
|
||||
@QueryParam("locale") @Parameter(description = "locale") final String locale) {
|
||||
return delegate.retrieveLoanPointInTime(loanId, dateParam, dateFormat, locale);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("external-id/{loanExternalId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Override
|
||||
public LoanPointInTimeData retrieveLoanPointInTimeByExternalId(
|
||||
@PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) String loanExternalIdStr,
|
||||
@QueryParam("date") @Parameter(description = "date", required = true) DateParam dateParam,
|
||||
@QueryParam("dateFormat") @Parameter(description = "dateFormat") final String dateFormat,
|
||||
@QueryParam("locale") @Parameter(description = "locale") final String locale) {
|
||||
return delegate.retrieveLoanPointInTimeByExternalId(loanExternalIdStr, dateParam, dateFormat, locale);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("search")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Override
|
||||
public List<LoanPointInTimeData> retrieveLoansPointInTime(@Parameter RetrieveLoansPointInTimeRequest request) {
|
||||
return delegate.retrieveLoansPointInTime(request);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("search/external-id")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Override
|
||||
public List<LoanPointInTimeData> retrieveLoansPointInTimeByExternalIds(@Parameter RetrieveLoansPointInTimeExternalIdsRequest request) {
|
||||
return delegate.retrieveLoansPointInTimeByExternalIds(request);
|
||||
}
|
||||
|
||||
}
|
||||
+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.loanaccount.api.pointintime.data;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import org.apache.fineract.infrastructure.core.api.DateParam;
|
||||
|
||||
@Data
|
||||
public class RetrieveLoansPointInTimeExternalIdsRequest {
|
||||
|
||||
private List<String> externalIds;
|
||||
private DateParam date;
|
||||
private String dateFormat;
|
||||
private String locale;
|
||||
}
|
||||
+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.loanaccount.api.pointintime.data;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import org.apache.fineract.infrastructure.core.api.DateParam;
|
||||
|
||||
@Data
|
||||
public class RetrieveLoansPointInTimeRequest {
|
||||
|
||||
private List<Long> loanIds;
|
||||
private DateParam date;
|
||||
private String dateFormat;
|
||||
private String locale;
|
||||
}
|
||||
+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.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Data
|
||||
public class LoanFeeData {
|
||||
|
||||
private final BigDecimal feeChargesCharged;
|
||||
private final BigDecimal feeAdjustments;
|
||||
private final BigDecimal feeChargesDueAtDisbursementCharged;
|
||||
private final BigDecimal feeChargesPaid;
|
||||
private final BigDecimal feeChargesWaived;
|
||||
private final BigDecimal feeChargesWrittenOff;
|
||||
private final BigDecimal feeChargesOutstanding;
|
||||
|
||||
@org.mapstruct.Mapper(config = MapstructMapperConfig.class)
|
||||
public interface Mapper {
|
||||
|
||||
@Mapping(source = "totalFeeChargesCharged", target = "feeChargesCharged")
|
||||
@Mapping(source = "totalFeeChargesDueAtDisbursement", target = "feeChargesDueAtDisbursementCharged")
|
||||
@Mapping(source = "totalFeeAdjustments", target = "feeAdjustments")
|
||||
@Mapping(source = "totalFeeChargesRepaid", target = "feeChargesPaid")
|
||||
@Mapping(source = "totalFeeChargesWaived", target = "feeChargesWaived")
|
||||
@Mapping(source = "totalFeeChargesWrittenOff", target = "feeChargesWrittenOff")
|
||||
@Mapping(source = "totalFeeChargesOutstanding", target = "feeChargesOutstanding")
|
||||
LoanFeeData map(LoanSummary source);
|
||||
}
|
||||
}
|
||||
+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.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Data
|
||||
public class LoanInterestData {
|
||||
|
||||
private final BigDecimal interestCharged;
|
||||
private final BigDecimal interestPaid;
|
||||
private final BigDecimal interestWaived;
|
||||
private final BigDecimal interestWrittenOff;
|
||||
private final BigDecimal interestOutstanding;
|
||||
|
||||
@org.mapstruct.Mapper(config = MapstructMapperConfig.class)
|
||||
public interface Mapper {
|
||||
|
||||
@Mapping(source = "totalInterestCharged", target = "interestCharged")
|
||||
@Mapping(source = "totalInterestRepaid", target = "interestPaid")
|
||||
@Mapping(source = "totalInterestWaived", target = "interestWaived")
|
||||
@Mapping(source = "totalInterestWrittenOff", target = "interestWrittenOff")
|
||||
@Mapping(source = "totalInterestOutstanding", target = "interestOutstanding")
|
||||
LoanInterestData map(LoanSummary source);
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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 lombok.Data;
|
||||
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Data
|
||||
public class LoanPenaltyData {
|
||||
|
||||
private final BigDecimal penaltyChargesCharged;
|
||||
private final BigDecimal penaltyAdjustments;
|
||||
private final BigDecimal penaltyChargesPaid;
|
||||
private final BigDecimal penaltyChargesWaived;
|
||||
private final BigDecimal penaltyChargesWrittenOff;
|
||||
private final BigDecimal penaltyChargesOutstanding;
|
||||
|
||||
@org.mapstruct.Mapper(config = MapstructMapperConfig.class)
|
||||
public interface Mapper {
|
||||
|
||||
@Mapping(source = "totalPenaltyChargesCharged", target = "penaltyChargesCharged")
|
||||
@Mapping(source = "totalPenaltyAdjustments", target = "penaltyAdjustments")
|
||||
@Mapping(source = "totalPenaltyChargesRepaid", target = "penaltyChargesPaid")
|
||||
@Mapping(source = "totalPenaltyChargesWaived", target = "penaltyChargesWaived")
|
||||
@Mapping(source = "totalPenaltyChargesWrittenOff", target = "penaltyChargesWrittenOff")
|
||||
@Mapping(source = "totalPenaltyChargesOutstanding", target = "penaltyChargesOutstanding")
|
||||
LoanPenaltyData map(LoanSummary source);
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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 lombok.Data;
|
||||
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Data
|
||||
public class LoanPointInTimeData {
|
||||
|
||||
// Loan attributes
|
||||
private Long id;
|
||||
private String accountNo;
|
||||
private LoanStatusEnumData status;
|
||||
private String externalId;
|
||||
private CurrencyData currency;
|
||||
private LoanPrincipalData principal;
|
||||
private LoanInterestData interest;
|
||||
private LoanFeeData fee;
|
||||
private LoanPenaltyData penalty;
|
||||
private LoanTotalAmountData total;
|
||||
|
||||
// Client attributes
|
||||
private Long clientId;
|
||||
private String clientAccountNo;
|
||||
private String clientExternalId;
|
||||
private String clientDisplayName;
|
||||
private Long clientOfficeId;
|
||||
|
||||
// Loan product attributes
|
||||
private Long loanProductId;
|
||||
private String loanProductName;
|
||||
|
||||
@org.mapstruct.Mapper(config = MapstructMapperConfig.class, uses = { LoanStatusEnumData.Mapper.class, CurrencyData.Mapper.class,
|
||||
LoanPrincipalData.Mapper.class, LoanInterestData.Mapper.class, LoanFeeData.Mapper.class, LoanPenaltyData.Mapper.class,
|
||||
LoanTotalAmountData.Mapper.class })
|
||||
public interface Mapper {
|
||||
|
||||
@Mapping(source = "accountNumber", target = "accountNo")
|
||||
@Mapping(source = "source", target = "status")
|
||||
@Mapping(source = "client.id", target = "clientId")
|
||||
@Mapping(source = "client.accountNumber", target = "clientAccountNo")
|
||||
@Mapping(source = "client.externalId", target = "clientExternalId")
|
||||
@Mapping(source = "client.displayName", target = "clientDisplayName")
|
||||
@Mapping(source = "client.office.id", target = "clientOfficeId")
|
||||
@Mapping(source = "summary", target = "principal")
|
||||
@Mapping(source = "summary", target = "interest")
|
||||
@Mapping(source = "summary", target = "fee")
|
||||
@Mapping(source = "summary", target = "penalty")
|
||||
@Mapping(source = "summary", target = "total")
|
||||
@Mapping(source = "loanProduct.id", target = "loanProductId")
|
||||
@Mapping(source = "loanProduct.name", target = "loanProductName")
|
||||
LoanPointInTimeData map(Loan source);
|
||||
}
|
||||
}
|
||||
+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.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Data
|
||||
public class LoanPrincipalData {
|
||||
|
||||
private final BigDecimal principalDisbursed;
|
||||
private final BigDecimal principalAdjustments;
|
||||
private final BigDecimal principalPaid;
|
||||
private final BigDecimal principalWrittenOff;
|
||||
private final BigDecimal principalOutstanding;
|
||||
|
||||
@org.mapstruct.Mapper(config = MapstructMapperConfig.class)
|
||||
public interface Mapper {
|
||||
|
||||
@Mapping(source = "totalPrincipalDisbursed", target = "principalDisbursed")
|
||||
@Mapping(source = "totalPrincipalAdjustments", target = "principalAdjustments")
|
||||
@Mapping(source = "totalPrincipalRepaid", target = "principalPaid")
|
||||
@Mapping(source = "totalPrincipalWrittenOff", target = "principalWrittenOff")
|
||||
@Mapping(source = "totalPrincipalOutstanding", target = "principalOutstanding")
|
||||
LoanPrincipalData map(LoanSummary source);
|
||||
}
|
||||
}
|
||||
+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.loanaccount.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Data
|
||||
public class LoanTotalAmountData {
|
||||
|
||||
private final BigDecimal totalExpectedRepayment;
|
||||
private final BigDecimal totalRepayment;
|
||||
private final BigDecimal totalExpectedCostOfLoan;
|
||||
private final BigDecimal totalCostOfLoan;
|
||||
private final BigDecimal totalWaived;
|
||||
private final BigDecimal totalWrittenOff;
|
||||
private final BigDecimal totalOutstanding;
|
||||
|
||||
@org.mapstruct.Mapper(config = MapstructMapperConfig.class)
|
||||
public interface Mapper {
|
||||
|
||||
@Mapping(source = "totalExpectedRepayment", target = "totalExpectedRepayment")
|
||||
@Mapping(source = "totalRepayment", target = "totalRepayment")
|
||||
@Mapping(source = "totalExpectedCostOfLoan", target = "totalExpectedCostOfLoan")
|
||||
@Mapping(source = "totalCostOfLoan", target = "totalCostOfLoan")
|
||||
@Mapping(source = "totalWaived", target = "totalWaived")
|
||||
@Mapping(source = "totalWrittenOff", target = "totalWrittenOff")
|
||||
@Mapping(source = "totalOutstanding", target = "totalOutstanding")
|
||||
LoanTotalAmountData map(LoanSummary source);
|
||||
}
|
||||
}
|
||||
+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.loanaccount.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanPointInTimeData;
|
||||
|
||||
public interface LoanPointInTimeService {
|
||||
|
||||
LoanPointInTimeData retrieveAt(Long loanId, LocalDate date);
|
||||
|
||||
List<LoanPointInTimeData> retrieveAt(List<Long> loanIds, LocalDate date);
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanPointInTimeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class LoanPointInTimeServiceImpl implements LoanPointInTimeService {
|
||||
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanPointInTimeData.Mapper dataMapper;
|
||||
|
||||
@Override
|
||||
public LoanPointInTimeData retrieveAt(Long loanId, LocalDate date) {
|
||||
validateSingularRetrieval(loanId, date);
|
||||
|
||||
// Note: since everything is running in a readOnly transaction
|
||||
// whatever we modify on the loan is not going to be propagated to the DB
|
||||
// Note2: Interest is always calculated against the current date of the system so we need to roll time back
|
||||
HashMap<BusinessDateType, LocalDate> originalBDs = ThreadLocalContextUtil.getBusinessDates();
|
||||
try {
|
||||
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, date)));
|
||||
|
||||
Loan loan = loanAssembler.assembleFrom(loanId);
|
||||
removeAfterDateTransactions(loan, date);
|
||||
removeAfterDateCharges(loan, date);
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||
loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO);
|
||||
|
||||
return dataMapper.map(loan);
|
||||
} finally {
|
||||
ThreadLocalContextUtil.setBusinessDates(originalBDs);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeAfterDateCharges(Loan loan, LocalDate date) {
|
||||
loan.removeCharges(c -> DateUtils.isAfter(c.getEffectiveDueDate(), date));
|
||||
}
|
||||
|
||||
private void removeAfterDateTransactions(Loan loan, LocalDate date) {
|
||||
loan.removeLoanTransactions(tx -> DateUtils.isAfter(tx.getTransactionDate(), date));
|
||||
}
|
||||
|
||||
private void validateSingularRetrieval(Long loanId, LocalDate date) {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
baseDataValidator.reset().parameter("loanId").value(loanId).notNull();
|
||||
baseDataValidator.reset().parameter("date").value(date).notNull().notBlank();
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoanPointInTimeData> retrieveAt(List<Long> loanIds, LocalDate date) {
|
||||
validateBulkRetrieval(loanIds, date);
|
||||
return loanIds.stream().map(loanId -> retrieveAt(loanId, date)).toList();
|
||||
}
|
||||
|
||||
private void validateBulkRetrieval(List<Long> loanIds, LocalDate date) {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
baseDataValidator.reset().parameter("loanIds").value(loanIds).notNull().listNotEmpty();
|
||||
baseDataValidator.reset().parameter("date").value(date).notNull().notBlank();
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -141,4 +141,6 @@ public interface LoanReadPlatformService {
|
||||
Long retrieveLoanTransactionIdByExternalId(ExternalId externalId);
|
||||
|
||||
Long retrieveLoanIdByExternalId(ExternalId externalId);
|
||||
|
||||
List<Long> retrieveLoanIdsByExternalIds(List<ExternalId> externalIds);
|
||||
}
|
||||
|
||||
+5
@@ -2172,4 +2172,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
return loanRepositoryWrapper.findIdByExternalId(externalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> retrieveLoanIdsByExternalIds(List<ExternalId> externalIds) {
|
||||
return loanRepositoryWrapper.findIdByExternalIds(externalIds);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -3816,7 +3816,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
&& DateUtils.isEqual(loanTermVariations.getTermApplicableFrom(), lastTransactionDate))
|
||||
|| DateUtils.isAfter(loanTermVariations.getTermApplicableFrom(), lastTransactionDate));
|
||||
reverseExistingTransactionsTillLastDisbursal(loan, lastDisbursalTransaction);
|
||||
loanScheduleService.recalculateScheduleFromLastTransaction(loan, scheduleGeneratorDTO);
|
||||
loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO);
|
||||
actualChanges.put("undolastdisbursal", "true");
|
||||
actualChanges.put("disbursedAmount", loan.getDisbursedAmount());
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
|
||||
+2
-1
@@ -1088,7 +1088,8 @@ public final class LoanProductDataValidator {
|
||||
&& !LoanRescheduleStrategyMethod.ADJUST_LAST_UNPAID_PERIOD.equals(loanRescheduleStrategyMethod)) {
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.rescheduleStrategyMethodParameterName).failWithCode(
|
||||
"reschedule.strategy.method.not.supported.for.loan.schedule.type.progressive",
|
||||
loanScheduleType.toString() + " reschedule strategy type is not supported for Progressive loan schedule type");
|
||||
loanRescheduleStrategyMethod.toString()
|
||||
+ " reschedule strategy type is not supported for Progressive loan schedule type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user