Skip to content

Commit

Permalink
Merge branch 'fiter/sumas/dev' into SU-320
Browse files Browse the repository at this point in the history
  • Loading branch information
faheem205 committed Nov 20, 2024
2 parents e16ee73 + 9d03d18 commit f408dd1
Show file tree
Hide file tree
Showing 19 changed files with 637 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.fineract.infrastructure.configuration.domain;

import java.time.LocalDate;
import java.util.List;
import org.apache.fineract.infrastructure.cache.domain.CacheType;

public interface ConfigurationDomainService {
Expand Down Expand Up @@ -149,4 +150,10 @@ public interface ConfigurationDomainService {

Long retriveMinimumDaysOfArrearsToWriteOff();

Long retrieveInvoiceResolutionExpiryDays();

Long retrieveInvoiceThreshold();

List<String> retrieveInvoiceJobNotificationEmails();

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ public enum JobName {
"Compensation"), COMPENSATION_ALERT_EMAIL("Compensation Alert Email"), DAILY_LOAN_ACCRUAL("Devengo de Interés diario"), //
INSURANCE_CHARGE_CANCELLATION_DUE_TO_DEFAULT("Cancel Default Insurance Charges"), INSTALLMENT_LOAN_CHARGE_ACCRUAL(
"Devengo de seguro"), ARCHIVE_LOAN_HISTORY("Archivo de cartera"), //
INSURANCE_CHARGE_SUSPENSION_DUE_TO_DEFAULT("Suspension temporal por mora") // ;
INSURANCE_CHARGE_SUSPENSION_DUE_TO_DEFAULT("Suspension temporal por mora") //
, INVOICE_NUMBERING_LIMIT("Control de Límite de Numeración de Facturación Electrónica"), INVOICE_EXPIRY_RESOLUTION(
"Control de Vencimiento de Resolución de Facturación Electrónica")//
;

private final String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4251,7 +4251,8 @@ private Money calculateTotalOverpayment() {

/*
* // This code was added initially under SU-320 to handle overpayments but later on it is no longer needed
* based on the way // overpayment is now handled if (overpaid.isZero()) { Money totalPrincipalPaid =
* based on the way overpayment is now handled
* if (overpaid.isZero()) { Money totalPrincipalPaid =
* Money.zero(this.getCurrency()); for (final LoanRepaymentScheduleInstallment scheduledRepayment :
* installments) { totalPrincipalPaid = totalPrincipalPaid.add(
* scheduledRepayment.getPrincipalCompleted(this.getCurrency()).plus(scheduledRepayment.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,28 +92,32 @@ public void populate(Workbook workbook, String dateFormat) {
writeString(CURRENCY_CODE_COL, row, currencies.getCode());
}
int channelRowIndex = 1;
for (ChannelData channelData : channelOptions) {
Row row;
if (channelRowIndex < currencyCodeRowIndex || currencyCodeRowIndex < paymentTypeRowIndex
|| currencyCodeRowIndex < fundRowIndex) {
row = extrasSheet.getRow(channelRowIndex++);
} else {
row = extrasSheet.createRow(channelRowIndex++);
if (channelOptions != null) {
for (ChannelData channelData : channelOptions) {
Row row;
if (channelRowIndex < currencyCodeRowIndex || currencyCodeRowIndex < paymentTypeRowIndex
|| currencyCodeRowIndex < fundRowIndex) {
row = extrasSheet.getRow(channelRowIndex++);
} else {
row = extrasSheet.createRow(channelRowIndex++);
}
writeLong(CHANNEL_ID_COL, row, channelData.getId());
writeString(CHANNEL_NAME_COL, row, channelData.getName().trim().replaceAll("[ )(]", "_"));
}
writeLong(CHANNEL_ID_COL, row, channelData.getId());
writeString(CHANNEL_NAME_COL, row, channelData.getName().trim().replaceAll("[ )(]", "_"));
}
int bankRowIndex = 1;
for (CodeValueData bankCodeValueData : bankOptions) {
Row row;
if (bankRowIndex < channelRowIndex || channelRowIndex < currencyCodeRowIndex || currencyCodeRowIndex < paymentTypeRowIndex
|| currencyCodeRowIndex < fundRowIndex) {
row = extrasSheet.getRow(bankRowIndex++);
} else {
row = extrasSheet.createRow(bankRowIndex++);
if (bankOptions != null) {
for (CodeValueData bankCodeValueData : bankOptions) {
Row row;
if (bankRowIndex < channelRowIndex || channelRowIndex < currencyCodeRowIndex || currencyCodeRowIndex < paymentTypeRowIndex
|| currencyCodeRowIndex < fundRowIndex) {
row = extrasSheet.getRow(bankRowIndex++);
} else {
row = extrasSheet.createRow(bankRowIndex++);
}
writeLong(BANK_ID_COL, row, bankCodeValueData.getId());
writeString(BANK_NAME_COL, row, bankCodeValueData.getName().trim().replaceAll("[ )(]", "_"));
}
writeLong(BANK_ID_COL, row, bankCodeValueData.getId());
writeString(BANK_NAME_COL, row, bankCodeValueData.getName().trim().replaceAll("[ )(]", "_"));
}
extrasSheet.protectSheet("");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@ public final class EmailMessageWithAttachmentData {
private String text;
private String subject;
private List<File> attachments;
private List<String> recipients;

public static EmailMessageWithAttachmentData createNew(final String to, final String text, final String subject,
final List<File> attachments) {
return new EmailMessageWithAttachmentData().setTo(to).setText(text).setSubject(subject).setAttachments(attachments);
}

public static EmailMessageWithAttachmentData createNew(final String text, final String subject, final List<File> attachments,
final List<String> recipients) {
return new EmailMessageWithAttachmentData().setText(text).setSubject(subject).setAttachments(attachments).setRecipients(recipients)
.setRecipients(recipients);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ public void sendEmailWithAttachment(EmailMessageWithAttachmentData emailMessageW
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);

mimeMessageHelper.setFrom(smtpCredentialsData.getFromEmail());
mimeMessageHelper.setTo(emailMessageWithAttachmentData.getTo());
// if recipient is a list of recipients use the multiple to
if (emailMessageWithAttachmentData.getRecipients() != null && !emailMessageWithAttachmentData.getRecipients().isEmpty()) {
mimeMessageHelper.setTo(emailMessageWithAttachmentData.getRecipients().toArray(new String[0]));
} else {
mimeMessageHelper.setTo(emailMessageWithAttachmentData.getTo());
}
mimeMessageHelper.setText(emailMessageWithAttachmentData.getText(), true);
mimeMessageHelper.setSubject(emailMessageWithAttachmentData.getSubject());
final List<File> attachments = emailMessageWithAttachmentData.getAttachments();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -54,6 +55,9 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService

public static final String CHARGE_ACCRUAL_DATE_CRITERIA = "charge-accrual-date";
public static final String NEXT_PAYMENT_DUE_DATE = "next-payment-due-date";
public static final String INVOICE_RESOLUTION_EXPIRY = "Días previos para notificar vencimiento de la resolución de facturas";
public static final String REMAINING_INVOICES_THRESHOLD = "Cantidad previa al límte de la numeración de la facturación para notificar";
public static final String INVOICE_NOTIFICATION_EMAILS = "Correo/s para enviar alerta por factura electrónica por vencer o agotarse";

private final PermissionRepository permissionRepository;
private final GlobalConfigurationRepositoryWrapper globalConfigurationRepository;
Expand Down Expand Up @@ -548,4 +552,32 @@ public Long retriveMinimumDaysOfArrearsToWriteOff() {
return property.getValue();
}

@Override
public Long retrieveInvoiceResolutionExpiryDays() {
final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(INVOICE_RESOLUTION_EXPIRY);
if (property.isEnabled()) {
return property.getValue();
}

return null;
}

@Override
public Long retrieveInvoiceThreshold() {
final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(REMAINING_INVOICES_THRESHOLD);
if (property.isEnabled()) {
return property.getValue();
}
return null;
}

@Override
public List<String> retrieveInvoiceJobNotificationEmails() {
final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(INVOICE_NOTIFICATION_EMAILS);
if (property.isEnabled()) {
return List.of(property.getStringValue().split(","));
}
return List.of();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ public void validateChargeOffTransaction(final String json) {
}

final Set<String> chargeOffParameters = new HashSet<>(
Arrays.asList("transactionDate", "note", "locale", "dateFormat", "chargeOffReasonId", "externalId"));
Arrays.asList("transactionDate", "note", "locale", "dateFormat", "chargeOffReasonId", "externalId", "incidentTypeId"));

final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, chargeOffParameters);
Expand All @@ -456,6 +456,9 @@ public void validateChargeOffTransaction(final String json) {
final Long chargeOffReasonId = fromApiJsonHelper.extractLongNamed("chargeOffReasonId", element);
baseDataValidator.reset().parameter("chargeOffReasonId").value(chargeOffReasonId).ignoreIfNull().integerGreaterThanZero();

final Long incidentTypeId = fromApiJsonHelper.extractLongNamed("incidentTypeId", element);
baseDataValidator.reset().parameter("incidentTypeId").value(incidentTypeId).ignoreIfNull().integerGreaterThanZero();

throwExceptionIfValidationWarningsExist(dataValidationErrors);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3612,18 +3612,37 @@ public CommandProcessingResult chargeOff(JsonCommand command) {

checkIfProductAllowsCancelationOrReversal(loan);

businessEventNotifierService.notifyPreBusinessEvent(new LoanChargeOffPreBusinessEvent(loan));
loan.markAsChargedOff(transactionDate, currentUser, null);

if (command.hasParameter(LoanApiConstants.chargeOffReasonIdParamName)) {
Long chargeOffReasonId = command.longValueOfParameterNamed(LoanApiConstants.chargeOffReasonIdParamName);
CodeValue chargeOffReason = this.codeValueRepository
.findOneByCodeNameAndIdWithNotFoundDetection(LoanApiConstants.CHARGE_OFF_REASONS, chargeOffReasonId);
changes.put(LoanApiConstants.chargeOffReasonIdParamName, chargeOffReasonId);
loan.markAsChargedOff(transactionDate, currentUser, chargeOffReason);
} else {
loan.markAsChargedOff(transactionDate, currentUser, null);
InsuranceIncidentType incidentType = InsuranceIncidentType.DEATH_CANCELLATION;
if (command.hasParameter("incidentTypeId")) {
Integer incidentTypeId = command.integerValueOfParameterNamed("incidentTypeId");
incidentType = InsuranceIncidentType.fromInt(incidentTypeId);
}

this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, null);
List<DefaultOrCancelInsuranceInstallmentData> cancelInsuranceInstallmentIds = this.loanReadPlatformService
.getLoanDataWithDefaultOrCancelInsurance(loanId, null, transactionDate);
InsuranceIncident incident = this.insuranceIncidentRepository.findByIncidentType(incidentType);
if (incident == null) {
throw new InsuranceIncidentNotFoundException(InsuranceIncidentType.DEATH_CANCELLATION.name());
}
for (final DefaultOrCancelInsuranceInstallmentData data : cancelInsuranceInstallmentIds) {
LoanCharge loanCharge = null;
Optional<LoanCharge> loanChargeOptional = loan.getLoanCharges().stream()
.filter(lc -> Objects.equals(lc.getId(), data.loanChargeId())).findFirst();
if (loanChargeOptional.isPresent()) {
loanCharge = loanChargeOptional.get();
}
BigDecimal cumulative = BigDecimal.ZERO;
cumulative = processInsuranceChargeCancellation(cumulative, loan, loanCharge, data, true);
InsuranceIncidentNoveltyNews insuranceIncidentNoveltyNews = InsuranceIncidentNoveltyNews.instance(loan, loanCharge,
data.installment(), incident, transactionDate, cumulative);
this.insuranceIncidentNoveltyNewsRepository.saveAndFlush(insuranceIncidentNoveltyNews);
}

businessEventNotifierService.notifyPreBusinessEvent(new LoanChargeOffPreBusinessEvent(loan));

final List<Long> existingTransactionIds = loan.findExistingTransactionIds();
final List<Long> existingReversedTransactionIds = loan.findExistingReversedTransactionIds();
loan.getLoanCustomizationDetail().recordActivity();
Expand All @@ -3640,6 +3659,11 @@ public CommandProcessingResult chargeOff(JsonCommand command) {
this.noteRepository.save(note);
}

this.loanAccountDomainService.foreCloseLoan(loan, transactionDate, noteText, txnExternalId, changes);
final BlockingReasonSetting blockingReasonSetting = loanBlockingReasonRepository.getSingleBlockingReasonSettingByReason(
BlockingReasonSettingEnum.CREDIT_CANCELADO.getDatabaseString(), BlockLevel.CREDIT.toString());
loanBlockWritePlatformService.blockLoan(loan.getId(), blockingReasonSetting, "CANCELADO", DateUtils.getLocalDateOfTenant());

postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
businessEventNotifierService.notifyPostBusinessEvent(new LoanChargeOffPostBusinessEvent(chargeOffTransaction));
return new CommandProcessingResultBuilder() //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductType;
import org.apache.fineract.portfolio.loanproductparameterization.data.LoanProductParameterizationData;

Expand Down Expand Up @@ -142,4 +143,36 @@ private static LoanProductParameterization extractParameters(JsonCommand command
return new LoanProductParameterization(productType, billingPrefix, billingResolutionNumber, generationDate, expirationDate,
rangeStartNumber, rangeEndNumber, lastInvoiceNumber, lastCreditNoteNumber, lastDebitNoteNumber);
}

public boolean isInvoiceResolutionExpiring(Long daysPrior) {
LocalDate currentDate = DateUtils.getLocalDateOfTenant();
LocalDate warningDate = currentDate.plusDays(daysPrior);
// Implement logic to check if the invoice resolution is expiring within the specified days
// Return true if the condition is met, otherwise false

// Check if the expiration date is within the specified days
return expirationDate != null && (warningDate.isAfter(expirationDate) || warningDate.isEqual(expirationDate));
}

public boolean isInvoiceNumberingLimitReached(Long threshold) {
// Implement logic to check if the invoice numbering limit is reached within the specified quantity
// Return true if the condition is met, otherwise false
long maximumInvoiceNumber = rangeEndNumber;
long usedInvoiceNumber = lastInvoiceNumber;
long remainingInvoiceNumber = maximumInvoiceNumber - usedInvoiceNumber;

// Check if the last invoice number is within the specified quantity
return remainingInvoiceNumber <= threshold;
}

public Long getInvoiceNumberingRemaining() {
long maximumInvoiceNumber = rangeEndNumber;
long usedInvoiceNumber = lastInvoiceNumber;
return maximumInvoiceNumber - usedInvoiceNumber;
}

public Long getInvoiceResolutionExpiryDays() {
LocalDate currentDate = DateUtils.getLocalDateOfTenant();
return DateUtils.getDifferenceInDays(currentDate, expirationDate);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* 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.loanproductparameterization.jobs.invoiceexpiryresolution;

import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
@RequiredArgsConstructor
public class InvoiceResolutionExpiryConfig {

private final JobRepository jobRepository;

private final PlatformTransactionManager transactionManager;
private final String jobName = JobName.INVOICE_EXPIRY_RESOLUTION.name();

@Bean
protected Step runInvoiceResolutionExpiryStep(InvoiceResolutionExpiryTasklet invoiceResolutionExpiryTasklet) {
return new StepBuilder(jobName, jobRepository).tasklet(invoiceResolutionExpiryTasklet, transactionManager).build();
}

@Bean
public Job runInvoiceResolutionExpiryJob(InvoiceResolutionExpiryTasklet runInvoiceResolutionExpiryStep) {
return new JobBuilder(jobName, jobRepository).start(runInvoiceResolutionExpiryStep(runInvoiceResolutionExpiryStep))
.incrementer(new RunIdIncrementer()).build();
}
}
Loading

0 comments on commit f408dd1

Please sign in to comment.