Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
3c899d2
FINERACT-2391: check for AppUser type before casting Authentication P…
nick9822 Oct 9, 2025
f80ee9f
FINERACT-2380: create feign client
budaidev Oct 3, 2025
ffa1a97
FINERACT-2389: Fix related installment query
adamsaghy Oct 16, 2025
834b653
FINERACT-2389: added e2e test to validate loan rescheduling on the fi…
ruzeynalov Oct 17, 2025
e4a8a8c
improve release process kickoff email template
meonkeys Oct 6, 2025
ca5b820
improve release branch email template
meonkeys Oct 7, 2025
8e4ac2c
clean during artifact build release step 6
meonkeys Oct 9, 2025
aaee438
doc: remove errant config but keep :sectlinks:
meonkeys Oct 9, 2025
29d02e0
improve staging doc and commands in release step 8
meonkeys Oct 9, 2025
6e327f7
release step 6: fix sanity check example
meonkeys Oct 10, 2025
a061787
further improve release steps 6 and 9
meonkeys Oct 11, 2025
e24aa27
cucumber.adoc: fix list continuations
meonkeys Oct 11, 2025
78fee12
fix tarball explosion in release step 9 🧯
meonkeys Oct 12, 2025
013bfad
improve step 9 artifact verification gpg tips
meonkeys Oct 13, 2025
f469b2c
fix typos in step 9 re: running the binary
meonkeys Oct 13, 2025
e936490
update link to rc verification instructions
meonkeys Oct 13, 2025
bea0c82
shorten/simplify gpg key sending commands
meonkeys Oct 13, 2025
1503037
save 1.13.0 vote result
meonkeys Oct 19, 2025
b18e850
don't include actual email addresses
meonkeys Oct 20, 2025
e3096c7
FINERACT-2396: Fix retry issue during persisting CommandSource
adamsaghy Oct 21, 2025
54bc740
FINERACT-2380: feign related test fixes
budaidev Oct 21, 2025
40a044e
FINERACT-2326: fix delinquent days & delinquency date after delinquen…
budaidev Oct 13, 2025
4c17c1d
FINERACT-2326: fix delinquency date calculation after pause - E2E test
peter-kovacs-dpc Oct 21, 2025
c2b669d
FINERACT-2397: update release manager docs post-1.13.0 release (#5116)
meonkeys Oct 22, 2025
c181490
FINERACT-2389: Automatically close loans at the end of test executions
adamsaghy Oct 22, 2025
7c9c00e
FINERACT-2389: Improve E2E test data initialization resilience
adamsaghy Oct 22, 2025
57e1701
FINERACT-2382: Repayment schedule for Flat-Cumulative-Multi Disbursement
Sep 26, 2025
e5f5a6a
FINERACT-2382: e2e test scenarios for flat cumulative multidisbursal …
MarianaDmytrivBinariks Oct 24, 2025
cf0a262
FINERACT-2385: Zero amount reage transaction should not be allowed
oleksii-novikov-onix Oct 15, 2025
2b9102f
FINERACT-2385: e2e test scenarios for re-aging disallowed with zero a…
MarianaDmytrivBinariks Oct 24, 2025
0b575dd
Bump actions/upload-artifact from 4.6.2 to 5.0.0
dependabot[bot] Oct 27, 2025
4a1cf9f
FINERACT-2390: e2e test scenarios for infinite loop with MIR trn issue
MarianaDmytrivBinariks Oct 20, 2025
a06b6d8
FINERACT-2390: Fix `infinite loop` issue
adamsaghy Oct 27, 2025
cda6fa6
FINERACT-2389: Fix the handling of nullable field overrides
oleksii-novikov-onix Oct 21, 2025
4bef564
FINERACT-2389: Fix the handling of nullable field overrides - E2E tests
peter-kovacs-dpc Oct 28, 2025
499f4db
FINERACT-2389: Upgrade OpenPDF to v3
andreasrosdal Oct 28, 2025
b001d61
FINERACT-2404: Improved extensibility and cleanup for 1.12.1 maintenance
Oct 2, 2025
77d04f8
FINERACT-2404: merged release/1.13.1
Nov 3, 2025
0d7ac14
FINERACT-2404: fixed formatting after merge
Nov 3, 2025
d445b50
FINERACT-2404: merged develop
Nov 3, 2025
ceb8fbb
FINERACT-2404: fixed style checks
Nov 3, 2025
df9748a
FINERACT-2404: fixed license check for JournalEntryReadPlatformServic…
Nov 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,9 @@ configure(project.fineractJavaProjects) {

if (project.hasProperty('excludeTests')) {
filter {
excludeTestsMatching project.property('excludeTests')
project.property('excludeTests').split(",").each {
excludeTestsMatching "*${it.trim()}*"
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,22 @@
*/
@Component
@RequiredArgsConstructor
public final class JournalEntryCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<JournalEntryCommand> {
public class JournalEntryCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<JournalEntryCommand> {

private final FromJsonHelper fromApiJsonHelper;

protected Set<String> getSupportedParameters() {
return JournalEntryJsonInputParams.getAllValues();
}

@Override
public JournalEntryCommand commandFromApiJson(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}

final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = JournalEntryJsonInputParams.getAllValues();
final Set<String> supportedParameters = this.getSupportedParameters();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);

final JsonElement element = this.fromApiJsonHelper.parse(json);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ public void put(String key, String val) {
public void remove(String key) {
MDC.remove(key);
}

public String get(String key) {
return MDC.get(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public RepeatStatus execute(StepContribution contribution, ChunkContext chunkCon
return RepeatStatus.FINISHED;
}

private boolean isDownstreamChannelEnabled() {
protected boolean isDownstreamChannelEnabled() {
return fineractProperties.getEvents().getExternal().getProducer().getJms().isEnabled()
|| fineractProperties.getEvents().getExternal().getProducer().getKafka().isEnabled();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
*/
@Getter
@JsonLocalDateArrayFormat
public final class SavingsAccountTransactionData implements Serializable {
public class SavingsAccountTransactionData implements Serializable {

private Long id;
private final SavingsAccountTransactionEnumData transactionType;
Expand Down Expand Up @@ -110,7 +110,7 @@ public final class SavingsAccountTransactionData implements Serializable {
private Long accountCredit;
private Long accountDebit;

private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
protected SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate transactionDate,
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public List<EventMessage<?>> getReceivedEvents() {
return receivedEvents;
}

void receive(byte[] message) throws Exception {
public void receive(byte[] message) throws Exception {
MessageV1 msgObject = MessageV1.fromByteBuffer(ByteBuffer.wrap(message));
String type = msgObject.getType();
String idempotencyKey = msgObject.getIdempotencyKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@

import java.time.LocalDate;
import java.util.Set;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;

public interface LoanAccrualActivityRepository extends Repository<Loan, Long> {
public interface LoanAccrualActivityRepository extends JpaRepository<Loan, Long> {

@Query("select loan.id from Loan loan left join LoanTransaction lt on lt.loan = loan and lt.typeOf = :loanType and lt.reversed = false and lt.dateOf = :currentDate inner join LoanRepaymentScheduleInstallment rs on rs.loan = loan and rs.isDownPayment = false and rs.additional = false and rs.dueDate = :currentDate where loan.loanRepaymentScheduleDetail.enableAccrualActivityPosting = true and loan.loanStatus = :loanStatus and lt.id is null ")
Set<Long> fetchLoanIdsForAccrualActivityPosting(@Param("currentDate") LocalDate currentDate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
private final PaginationHelper paginationHelper;
private final DatabaseSpecificSQLGenerator sqlGenerator;

private static final class GLJournalEntryMapper implements RowMapper<JournalEntryData> {
protected static class GLJournalEntryMapper implements RowMapper<JournalEntryData> {

private final JournalEntryAssociationParametersData associationParametersData;

GLJournalEntryMapper(final JournalEntryAssociationParametersData associationParametersData) {
protected GLJournalEntryMapper(final JournalEntryAssociationParametersData associationParametersData) {
this.associationParametersData = Objects.requireNonNullElseGet(associationParametersData,
JournalEntryAssociationParametersData::new);
}
Expand Down Expand Up @@ -240,8 +240,7 @@ public Page<JournalEntryData> retrieveAll(final SearchParameters searchParameter
final Boolean onlyManualEntries, final LocalDate fromDate, final LocalDate toDate, final LocalDate submittedOnDateFrom,
final LocalDate submittedOnDateTo, final String transactionId, final Integer entityType,
final JournalEntryAssociationParametersData associationParametersData) {

GLJournalEntryMapper rm = new GLJournalEntryMapper(associationParametersData);
GLJournalEntryMapper rm = getGlJournalEntryMapper(associationParametersData);
final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select ").append(sqlGenerator.calcFoundRows()).append(" ");
sqlBuilder.append(rm.schema());
Expand Down Expand Up @@ -383,12 +382,16 @@ public Page<JournalEntryData> retrieveAll(final SearchParameters searchParameter
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, rm);
}

protected GLJournalEntryMapper getGlJournalEntryMapper(JournalEntryAssociationParametersData associationParametersData) {
return new GLJournalEntryMapper(associationParametersData);
}

@Override
public JournalEntryData retrieveGLJournalEntryById(final long glJournalEntryId,
JournalEntryAssociationParametersData associationParametersData) {
try {

final GLJournalEntryMapper rm = new GLJournalEntryMapper(associationParametersData);
final GLJournalEntryMapper rm = getGlJournalEntryMapper(associationParametersData);
// Programmatic query, disable sonar issue
final String sql = "select " + rm.schema() + " where journalEntry.id = ?";

Expand Down Expand Up @@ -526,7 +529,7 @@ private Page<JournalEntryData> retrieveContraTransactions(final Long officeId, f
public Page<JournalEntryData> retrieveJournalEntriesByEntityId(String transactionId, Long entityId, Integer entityType) {
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(true, true);
try {
final GLJournalEntryMapper rm = new GLJournalEntryMapper(associationParametersData);
final GLJournalEntryMapper rm = getGlJournalEntryMapper(associationParametersData);
final String sql = "select " + rm.schema()
+ " where journalEntry.transaction_id = ? and journalEntry.entity_id = ? and journalEntry.entity_type_enum = ?";
Object[] data = { transactionId, entityId, entityType };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@

@Component("loanTransactionValidator")
@AllArgsConstructor
public final class LoanTransactionValidatorImpl implements LoanTransactionValidator {
public class LoanTransactionValidatorImpl implements LoanTransactionValidator {

private final FromJsonHelper fromApiJsonHelper;
private final LoanApplicationValidator fromApiJsonDeserializer;
Expand Down Expand Up @@ -245,7 +245,7 @@ public void validateDisbursement(JsonCommand command, boolean isAccountTransfer,
});
}

private void validateDisbursementWithPostDatedChecks(final String json, final Long loanId) {
public void validateDisbursementWithPostDatedChecks(final String json, final Long loanId) {
final JsonElement jsonElement = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.disbursement");
Expand Down Expand Up @@ -291,7 +291,7 @@ private void validateDisbursementWithPostDatedChecks(final String json, final Lo
}
}

private void validateDisbursementDateWithMeetingDate(final LocalDate actualDisbursementDate, final CalendarInstance calendarInstance,
public void validateDisbursementDateWithMeetingDate(final LocalDate actualDisbursementDate, final CalendarInstance calendarInstance,
Boolean isSkipRepaymentOnFirstMonth, Integer numberOfDays) {
if (null != calendarInstance) {
final Calendar calendar = calendarInstance.getCalendar();
Expand Down Expand Up @@ -334,9 +334,7 @@ public void validateTransaction(final String json) {
throw new InvalidJsonException();
}

final Set<String> transactionParameters = new HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note",
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME));
final Set<String> transactionParameters = getTransactionParametersForEdit();

final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
Expand All @@ -363,6 +361,12 @@ public void validateTransaction(final String json) {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}

protected HashSet<String> getTransactionParametersForEdit() {
return new HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note", "locale", "dateFormat",
"paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME));
}

@Override
public void validateChargebackTransaction(final String json) {

Expand Down Expand Up @@ -405,8 +409,7 @@ public void validateTransactionWithNoAmount(final String json) {
throw new InvalidJsonException();
}

final Set<String> disbursementParameters = new HashSet<>(
Arrays.asList("transactionDate", "note", "locale", "dateFormat", "writeoffReasonId", "externalId"));
final Set<String> disbursementParameters = getNoAmountTransactionParameters();

final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
Expand All @@ -427,6 +430,10 @@ public void validateTransactionWithNoAmount(final String json) {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}

protected Set<String> getNoAmountTransactionParameters() {
return new HashSet<>(Arrays.asList("transactionDate", "note", "locale", "dateFormat", "writeoffReasonId", "externalId"));
}

@Override
public void validateChargeOffTransaction(final String json) {
if (StringUtils.isBlank(json)) {
Expand Down Expand Up @@ -659,7 +666,7 @@ public void validateLoanGroupIsActive(final Loan loan) {
}
}

private void validateLoanHasNoLaterChargeRefundTransactionToReverseOrCreateATransaction(Loan loan, LocalDate transactionDate,
public void validateLoanHasNoLaterChargeRefundTransactionToReverseOrCreateATransaction(Loan loan, LocalDate transactionDate,
String reversedOrCreated) {
for (LoanTransaction txn : loan.getLoanTransactions()) {
if (txn.isChargeRefund() && DateUtils.isBefore(transactionDate, txn.getTransactionDate())) {
Expand All @@ -671,7 +678,7 @@ private void validateLoanHasNoLaterChargeRefundTransactionToReverseOrCreateATran
}
}

private void validateLoanDisbursementIsBeforeTransactionDate(final Loan loan, final LocalDate transactionDate) {
public void validateLoanDisbursementIsBeforeTransactionDate(final Loan loan, final LocalDate transactionDate) {
if (DateUtils.isBefore(transactionDate, loan.getDisbursementDate())) {
final String errorMessage = "The transaction date cannot be before the loan disbursement date: "
+ loan.getDisbursementDate().toString();
Expand All @@ -680,22 +687,22 @@ private void validateLoanDisbursementIsBeforeTransactionDate(final Loan loan, fi
}
}

private void validateTransactionShouldNotBeInTheFuture(final LocalDate transactionDate) {
public void validateTransactionShouldNotBeInTheFuture(final LocalDate transactionDate) {
if (DateUtils.isDateInTheFuture(transactionDate)) {
final String errorMessage = "The transaction date cannot be in the future.";
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.a.future.date", errorMessage, transactionDate);
}
}

private void validateLoanHasCurrency(final Loan loan) {
public void validateLoanHasCurrency(final Loan loan) {
MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency defaultApplicationCurrency = this.applicationCurrencyRepository.findOneByCode(currency.getCode());
if (defaultApplicationCurrency == null) {
throw new CurrencyNotFoundException(currency.getCode());
}
}

private void validateClientOfficeJoiningDateIsBeforeTransactionDate(Loan loan, LocalDate transactionDate) {
public void validateClientOfficeJoiningDateIsBeforeTransactionDate(Loan loan, LocalDate transactionDate) {
if (loan.getClient() != null && loan.getClient().getOfficeJoiningDate() != null) {
final LocalDate clientOfficeJoiningDate = loan.getClient().getOfficeJoiningDate();
if (DateUtils.isBefore(transactionDate, clientOfficeJoiningDate)) {
Expand Down Expand Up @@ -759,7 +766,7 @@ public void validateRepaymentDateIsOnHoliday(final LocalDate repaymentDate, fina
}
}

private void validateTransactionAmountNotExceedThresholdForMultiDisburseLoan(Loan loan) {
public void validateTransactionAmountNotExceedThresholdForMultiDisburseLoan(Loan loan) {
if (loan.getLoanProduct().isMultiDisburseLoan()) {
BigDecimal totalDisbursed = loan.getDisbursedAmount();
BigDecimal totalPrincipalAdjusted = loan.getSummary().getTotalPrincipalAdjustments();
Expand Down Expand Up @@ -830,7 +837,7 @@ public void validateRefund(final Loan loan, LoanTransactionType loanTransactionT
validateTransactionAmountNotExceedThresholdForMultiDisburseLoan(loan);
}

private void validateRepaymentTypeTransactionNotBeforeAChargeRefund(final Loan loan, final LoanTransactionType loanTransactionType,
public void validateRepaymentTypeTransactionNotBeforeAChargeRefund(final Loan loan, final LoanTransactionType loanTransactionType,
final LocalDate transactionDate) {
if (loanTransactionType.isRepaymentType() && !loanTransactionType.isChargeRefund()) {
for (LoanTransaction txn : loan.getLoanTransactions()) {
Expand Down Expand Up @@ -931,7 +938,7 @@ public void validateActivityNotBeforeClientOrGroupTransferDate(final Loan loan,
return totalCollateral;
}

private static @NonNull Set<String> getDisbursementParameters(boolean isAccountTransfer) {
protected @NonNull Set<String> getDisbursementParameters(boolean isAccountTransfer) {
Set<String> disbursementParameters;

if (isAccountTransfer) {
Expand All @@ -952,9 +959,7 @@ private void validatePaymentTransaction(String json) {
throw new InvalidJsonException();
}

final Set<String> transactionParameters = new HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note",
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
"loanId", "numberOfRepayments", "interestRefundCalculation"));
final Set<String> transactionParameters = getRepaymentParameters();

final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
Expand All @@ -976,6 +981,12 @@ private void validatePaymentTransaction(String json) {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}

protected Set<String> getRepaymentParameters() {
return new HashSet<>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note",
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
"loanId", "numberOfRepayments", "interestRefundCalculation"));
}

@Override
public void validatePaymentDetails(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
// Validate all string payment detail fields for max length
Expand Down
Loading