From c1d005d00e7f4eaccf2c19d3c0b8359b53894ec3 Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Mon, 6 Oct 2025 15:23:34 -0700 Subject: [PATCH 01/18] improve release process kickoff email template this is shorter & sweeter --- .../main/resources/email/release.step01.headsup.message.ftl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/buildSrc/src/main/resources/email/release.step01.headsup.message.ftl b/buildSrc/src/main/resources/email/release.step01.headsup.message.ftl index 379f06d74..dbebd931f 100644 --- a/buildSrc/src/main/resources/email/release.step01.headsup.message.ftl +++ b/buildSrc/src/main/resources/email/release.step01.headsup.message.ftl @@ -20,9 +20,7 @@ --> Hello everyone, -... based on our "How to Release Apache Fineract" process (https://cwiki.apache.org/confluence/x/DRwIB) as well as the "Releases" chapter in the docs (https://fineract.apache.org/docs/current/#_releases), - -I will create a ${project['fineract.release.version']} branch off develop in our git repository at https://github.com/apache/fineract on ${project['fineract.releaseBranch.date']}. +... based on our release process (https://fineract.apache.org/docs/current/#_releases), I will create a release/${project['fineract.release.version']} branch off develop in our git repository at https://github.com/apache/fineract on ${project['fineract.releaseBranch.date']}. The release tracking umbrella issue for tracking all activity in JIRA is FINERACT-${project['fineract.release.issue']!'0000'} (https://issues.apache.org/jira/browse/FINERACT-${project['fineract.release.issue']!'0000'}). From 288319b874c5dee10c97c453c461757504087a5c Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Tue, 7 Oct 2025 11:15:51 -0700 Subject: [PATCH 02/18] improve release branch email template * fix typos * simplify * adapt to how I use this today, including explicit branch name --- .../resources/email/release.step03.branch.message.ftl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/resources/email/release.step03.branch.message.ftl b/buildSrc/src/main/resources/email/release.step03.branch.message.ftl index a6f3667b2..bcc15a15f 100644 --- a/buildSrc/src/main/resources/email/release.step03.branch.message.ftl +++ b/buildSrc/src/main/resources/email/release.step03.branch.message.ftl @@ -20,13 +20,13 @@ --> Hello everyone, -... as previously announced, I've just created the release branch for our upcoming ${project['fineract.release.version']} release. +... as previously announced, I've created the branch for our upcoming ${project['fineract.release.version']} release. The branch name is release/${project['fineract.release.version']}. -You can continue working and merging PRs to the develop branch for future releases, as always. +You can continue working and merging PRs into the develop branch for future releases, as always. -The DRAFT release notes are on https://cwiki.apache.org/confluence/display/FINERACT/${project['fineract.release.version']}+-+Apache+Fineract . Does anyone see anything missing? +I started the DRAFT release notes at https://cwiki.apache.org/confluence/display/FINERACT/${project['fineract.release.version']}+-+Apache+Fineract . Please help me by filling in "Summary of changes". Does anyone see anything else missing? -Does anyone have any last minutes changes they would like to see cherry-picked to branch ${project['fineract.release.version']}, or are we good go and actually cut the release based on this branch as it is? +Does anyone have any last minuteĀ changes for the release branch, or are we good to go and actually cut the release based on this branch as it is? I'll initiate the final stage of actually creating the release on ${project['fineract.release.date']} if nobody objects. From a950e4e36a37f352885a8e88f99814d32824e5f6 Mon Sep 17 00:00:00 2001 From: Arnold Galovics Date: Tue, 7 Oct 2025 20:12:12 +0200 Subject: [PATCH 03/18] FINERACT-2326: Loan point in time API now properly handles future dates --- .../service/LoanPointInTimeServiceImpl.java | 17 ++- .../BaseLoanIntegrationTest.java | 2 +- .../loan/pointintime/LoanPointInTimeTest.java | 112 +++++++++++++++++- 3 files changed, 124 insertions(+), 7 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanPointInTimeServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanPointInTimeServiceImpl.java index 278c2a9a7..da5c8db76 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanPointInTimeServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanPointInTimeServiceImpl.java @@ -65,11 +65,24 @@ public class LoanPointInTimeServiceImpl implements LoanPointInTimeService { ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, date))); Loan loan = loanAssembler.assembleFrom(loanId); + + int txCount = loan.getLoanTransactions().size(); + int chargeCount = loan.getCharges().size(); removeAfterDateTransactions(loan, date); removeAfterDateCharges(loan, date); + int afterRemovalTxCount = loan.getLoanTransactions().size(); + int afterRemovalChargeCount = loan.getCharges().size(); - ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null, null); - loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO); + // In case the loan is cumulative and is being prepaid by the latest repayment tx, we need the + // recalculateFrom and recalculateTill + // set to the same date which is the prepaying transaction's date + // currently this is not implemented and opens up buggy edge cases + // we work this around only for cases when the loan is already closed or the requested date doesn't change + // the loan's state + if (txCount != afterRemovalTxCount || chargeCount != afterRemovalChargeCount) { + ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null, null); + loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO); + } LoanArrearsData arrearsData = arrearsAgingService.calculateArrearsForLoan(loan); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java index df8cc9d84..ba07af2d6 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java @@ -992,7 +992,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest { } } - protected void verifyArreals(LoanPointInTimeData pointInTimeData, boolean isOverDue, String overdueSince) { + protected void verifyArrears(LoanPointInTimeData pointInTimeData, boolean isOverDue, String overdueSince) { assertThat(Objects.requireNonNull(pointInTimeData.getArrears()).getOverdue()).isEqualTo(isOverDue); if (isOverDue) { assertThat(Objects.requireNonNull(pointInTimeData.getArrears().getOverDueSince()).toString()).isEqualTo(overdueSince); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/pointintime/LoanPointInTimeTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/pointintime/LoanPointInTimeTest.java index 969a87032..81705b52d 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/pointintime/LoanPointInTimeTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/pointintime/LoanPointInTimeTest.java @@ -658,7 +658,7 @@ public class LoanPointInTimeTest extends BaseLoanIntegrationTest { } @Test - public void test_LoanPointInTimeDataWorks_ForArrealDataCalculation() { + public void test_LoanPointInTimeDataWorks_ForArrearsDataCalculation() { AtomicReference aLoanId = new AtomicReference<>(); runAt("01 January 2023", () -> { @@ -683,7 +683,6 @@ public class LoanPointInTimeTest extends BaseLoanIntegrationTest { .interestType(InterestType.DECLINING_BALANCE)// .interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)// .interestRecalculationCompoundingMethod(InterestRecalculationCompoundingMethod.NONE)// - .rescheduleStrategyMethod(RescheduleStrategyMethod.ADJUST_LAST_UNPAID_PERIOD)// .isInterestRecalculationEnabled(true)// .recalculationRestFrequencyInterval(1)// .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY)// @@ -737,14 +736,14 @@ public class LoanPointInTimeTest extends BaseLoanIntegrationTest { LoanPointInTimeData pointInTimeData = getPointInTimeData(loanId, "10 February 2023"); verifyOutstanding(pointInTimeData, outstanding(5000.0, 0.0, 0.0, 0.0, 5000.0)); - verifyArreals(pointInTimeData, true, "2023-02-01"); + verifyArrears(pointInTimeData, true, "2023-02-01"); // repay 500 addRepaymentForLoan(loanId, 2500.0, "01 February 2023"); LoanPointInTimeData pointInTimeDataAfterRepay = getPointInTimeData(loanId, "10 February 2023"); verifyOutstanding(pointInTimeDataAfterRepay, outstanding(2500.0, 0.0, 0.0, 0.0, 2500.0)); - verifyArreals(pointInTimeDataAfterRepay, false, null); + verifyArrears(pointInTimeDataAfterRepay, false, null); // verify transactions verifyTransactions(loanId, // @@ -754,4 +753,109 @@ public class LoanPointInTimeTest extends BaseLoanIntegrationTest { ); }); } + + @Test + public void test_LoanPointInTimeDataWorks_ForArrearsDataCalculation_ForFutureDate_WithInterest() { + AtomicReference aLoanId = new AtomicReference<>(); + + runAt("01 January 2023", () -> { + // Create Client + Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + + int numberOfRepayments = 3; + int repaymentEvery = 1; + + // Create charges + double charge1Amount = 1.0; + double charge2Amount = 1.5; + Long charge1Id = createDisbursementPercentageCharge(charge1Amount); + Long charge2Id = createDisbursementPercentageCharge(charge2Amount); + + // Create Loan Product + double interestRatePerPeriod = 10.0; + PostLoanProductsRequest product = createOnePeriod30DaysPeriodicAccrualProduct(interestRatePerPeriod) // + .numberOfRepayments(numberOfRepayments) // + .repaymentEvery(repaymentEvery) // + .installmentAmountInMultiplesOf(null) // + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS.longValue()) // + .interestType(InterestType.DECLINING_BALANCE)// + .interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)// + .interestRecalculationCompoundingMethod(InterestRecalculationCompoundingMethod.NONE)// + .isInterestRecalculationEnabled(true)// + .recalculationRestFrequencyInterval(1)// + .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY)// + .rescheduleStrategyMethod(RescheduleStrategyMethod.REDUCE_EMI_AMOUNT)// + .allowPartialPeriodInterestCalcualtion(false)// + .disallowExpectedDisbursements(false)// + .allowApprovedDisbursedAmountsOverApplied(false)// + .overAppliedNumber(null)// + .overAppliedCalculationType(null)// + .multiDisburseLoan(null)// + .charges(List.of(new LoanProductChargeData().id(charge1Id), new LoanProductChargeData().id(charge2Id)));// + + PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product); + Long loanProductId = loanProductResponse.getResourceId(); + + // Apply and Approve Loan + double amount = 5000.0; + + PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductId, "01 January 2023", amount, numberOfRepayments)// + .repaymentEvery(repaymentEvery)// + .interestRatePerPeriod(BigDecimal.valueOf(interestRatePerPeriod)).loanTermFrequency(numberOfRepayments)// + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS)// + .loanTermFrequencyType(RepaymentFrequencyType.MONTHS)// + .interestType(InterestType.DECLINING_BALANCE)// + .interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)// + .charges(List.of(// + new PostLoansRequestChargeData().chargeId(charge1Id).amount(BigDecimal.valueOf(charge1Amount)), // + new PostLoansRequestChargeData().chargeId(charge2Id).amount(BigDecimal.valueOf(charge2Amount))// + ));// + + PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applicationRequest); + + PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), + approveLoanRequest(amount, "01 January 2023")); + + aLoanId.getAndSet(approvedLoanResult.getLoanId()); + Long loanId = aLoanId.get(); + + // disburse Loan + disburseLoan(loanId, BigDecimal.valueOf(5000.0), "01 January 2023"); + + // verify transactions + verifyTransactions(loanId, // + transaction(5000.0, "Disbursement", "01 January 2023"), // + transaction(125.0, "Repayment (at time of disbursement)", "01 January 2023") // + ); + }); + + runAt("05 March 2023", () -> { + Long loanId = aLoanId.get(); + + // repay + addRepaymentForLoan(loanId, 5897.89, "05 March 2023"); + + // verify transactions + verifyTransactions(loanId, // + transaction(5000.0, "Disbursement", "01 January 2023"), // + transaction(125.0, "Repayment (at time of disbursement)", "01 January 2023"), // + transaction(5897.89, "Repayment", "05 March 2023"), // + transaction(897.89, "Accrual", "05 March 2023") // + ); + }); + + runAt("05 June 2023", () -> { + Long loanId = aLoanId.get(); + + LoanPointInTimeData pointInTimeData = getPointInTimeData(loanId, "05 June 2023"); + + verifyOutstanding(pointInTimeData, outstanding(0.0, 0.0, 0.0, 0.0, 0.0)); + verifyArrears(pointInTimeData, false, null); + assertThat(pointInTimeData.getArrears().getPrincipalOverdue()).isZero(); + assertThat(pointInTimeData.getArrears().getFeeOverdue()).isZero(); + assertThat(pointInTimeData.getArrears().getInterestOverdue()).isZero(); + assertThat(pointInTimeData.getArrears().getPenaltyOverdue()).isZero(); + assertThat(pointInTimeData.getArrears().getTotalOverdue()).isZero(); + }); + } } From 6066c0d97f20d46144e59092150ff2361e2899c3 Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Wed, 8 Oct 2025 10:15:58 +0200 Subject: [PATCH 04/18] FINERACT-2326: Fix `Trial Balance Summary with asset owner` report --- ...ummary_with_asset_owner_journal_entry_aggregation.xml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0202_trial_balance_summary_with_asset_owner_journal_entry_aggregation.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0202_trial_balance_summary_with_asset_owner_journal_entry_aggregation.xml index d66a8b2ea..b0e126f15 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0202_trial_balance_summary_with_asset_owner_journal_entry_aggregation.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0202_trial_balance_summary_with_asset_owner_journal_entry_aggregation.xml @@ -23,7 +23,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd"> - + { + final ArrayList interestRefundTypes = new ArrayList(); + interestRefundTypes.add("PAYOUT_REFUND"); + interestRefundTypes.add("MERCHANT_ISSUED_REFUND"); + Long clientId = ClientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation() + .interestRatePerPeriod(interestRatePerPeriod.doubleValue()).interestRateFrequencyType(YEARS)// + .daysInMonthType(DaysInMonthType.DAYS_30)// + .daysInYearType(DaysInYearType.DAYS_360)// + .numberOfRepayments(6)// + .repaymentEvery(1)// + .repaymentFrequencyType(2L)// + .chargeOffBehaviour("ZERO_INTEREST")// + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS.longValue())// + .repaymentStartDateType(LoanProduct.RepaymentStartDateTypeEnum.SUBMITTED_ON_DATE.ordinal())// + .enableDownPayment(false)// + .enableAccrualActivityPosting(true)// + .allowPartialPeriodInterestCalcualtion(null)// + .enableAutoRepaymentForDownPayment(null)// + .isInterestRecalculationEnabled(true)// + .delinquencyBucketId(delinquencyBucketId.longValue())// + .enableInstallmentLevelDelinquency(true)// + .interestRecalculationCompoundingMethod(0)// + .interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)// + .installmentAmountInMultiplesOf(null)// + .supportedInterestRefundTypes(interestRefundTypes) // + .rescheduleStrategyMethod(LoanRescheduleStrategyMethod.ADJUST_LAST_UNPAID_PERIOD.getValue())// + .recalculationRestFrequencyType(2)// + .recalculationRestFrequencyInterval(1)// + .enableAccrualActivityPosting(true); + PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product); + PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), operationDate, + principalAmount.doubleValue(), 6).interestCalculationPeriodType(DAYS)// + .transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)// + .interestRatePerPeriod(interestRatePerPeriod)// + .repaymentEvery(1)// + .repaymentFrequencyType(MONTHS)// + .loanTermFrequency(6)// + .loanTermFrequencyType(MONTHS); + + PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest); + createdLoanId.set(loanResponse.getLoanId()); + + loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().approvedLoanAmount(principalAmount) + .dateFormat(DATETIME_PATTERN).approvedOnDate(operationDate).locale("en")); + + loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().actualDisbursementDate(operationDate) + .dateFormat(DATETIME_PATTERN).locale("en").transactionAmount(principalAmount)); + + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get()); + assertTrue(loanDetails.getStatus().getActive()); + }); + + runAt("22 October 2025", () -> { + + executeInlineCOB(createdLoanId.get()); + verifyTransactions(createdLoanId.get(), // + transaction(135.94, "Disbursement", "13 September 2025", 135.94, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), + transaction(1.28, "Accrual Activity", "13 October 2025", 0.0, 0.0, 1.28, 0.0, 0.0, 0.0, 0.0), + transaction(1.61, "Accrual", "21 October 2025", 0.0, 0.0, 1.61, 0.0, 0.0, 0.0, 0.0)); + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get()); + loanDetails.getTransactions().stream().filter(t -> "loanTransactionType.accrualActivity".equals(t.getType().getCode())) + .findFirst().ifPresent(t -> { + accrualActivityId[0] = t; + }); + assertNotNull(accrualActivityId[0]); + assertNotNull(accrualActivityId[0].getExternalId()); + + loanTransactionHelper.makeLoanRepayment(createdLoanId.get(), new PostLoansLoanIdTransactionsRequest() // + .transactionDate("13 September 2025") // + .transactionAmount(135.94) // + .locale("en") // + .dateFormat(DATETIME_PATTERN)); // + + GetLoansLoanIdTransactionsTransactionIdResponse loanTransactionDetails = loanTransactionHelper + .getLoanTransactionDetails(createdLoanId.get(), accrualActivityId[0].getId()); + assertNotNull(loanTransactionDetails.getExternalId()); + assertEquals(LocalDate.of(2025, 10, 22), loanTransactionDetails.getReversedOnDate()); + }); + } + private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId, Integer numberOfRepayments, String loanDisbursementDate, double amount) { LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------"); From 80c84af3a45b943e200d8c640e206864e3f284aa Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Thu, 9 Oct 2025 09:57:40 -0700 Subject: [PATCH 06/18] clean during artifact build release step 6 I think the `clean` task is important here, Felix and I were seeing issues with jar filenames with incorrect version numbers. His work on FINERACT-2341 may have fixed whatever was causing it, but let's also/still do the `clean` before building release artifacts / candidates just to be safe. --- .../src/docs/en/chapters/release/process-step06.adoc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/fineract-doc/src/docs/en/chapters/release/process-step06.adoc b/fineract-doc/src/docs/en/chapters/release/process-step06.adoc index c480f78e3..2e669f722 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step06.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step06.adoc @@ -4,17 +4,15 @@ Create source and binary tarballs. -// FIXME - clean this up? focus on what commands should actually be run - [source,bash,subs="attributes+"] ---- -./gradlew --rerun-tasks srcDistTar binaryDistTar <1> +./gradlew clean +./gradlew srcDistTar binaryDistTar ---- -<1> The source tarball might not be created if `--rerun-tasks` is omitted. Look in `fineract-war/build/distributions/` for the tarballs. -Make sure to do some sanity checks. The source tarball and the code in the release branch (at the commit with the release tag) should match. +Do some sanity checks. The source tarball and the code in the release branch (at the commit with the release tag) should match. [source,bash,subs="attributes+"] ---- @@ -27,8 +25,6 @@ cd .. diff -r fineract apache-fineract-src-{revnumber} ---- -// FIXME - add output example - Make sure the code compiles and tests pass on the uncompressed source. Do as much testing as you can and share what you did. Here's the bare minimum check: [source,bash,subs="attributes+"] From 45404bafba96eda959154f7240794f66fa0dda6a Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Thu, 9 Oct 2025 10:25:47 -0700 Subject: [PATCH 07/18] doc: remove errant config but keep :sectlinks: See 89e9cfcd7dd30662e31afd63dac2b4b6e39912bc I think this was a typo. It's presumably changing the highlighter midstream and causing section numbering to start at the "Fineract Development Environment" chapter. But I really like `:sectlinks:` and I want to keep that! It's handy for sharing deep / sorta permanent links to specific sections (just click on any heading and copy the URL). --- .../en/chapters/features/journal-entry-aggregation.adoc | 7 ------- fineract-doc/src/docs/en/config.adoc | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/fineract-doc/src/docs/en/chapters/features/journal-entry-aggregation.adoc b/fineract-doc/src/docs/en/chapters/features/journal-entry-aggregation.adoc index 256ab5d77..f44fbaf7f 100644 --- a/fineract-doc/src/docs/en/chapters/features/journal-entry-aggregation.adoc +++ b/fineract-doc/src/docs/en/chapters/features/journal-entry-aggregation.adoc @@ -1,11 +1,4 @@ = Journal Entry Aggregation -:experimental: -:source-highlighter: highlightjs -:toc: left -:toclevels: 3 -:icons: font -:sectlinks: -:sectnums: == Overview The Journal Entry Aggregation Job is a Spring Batch-based solution designed to efficiently aggregate journal entries in the Fineract system. This job processes journal entries in configurable chunks, improving performance and resource utilization when dealing with large volumes of financial transactions. diff --git a/fineract-doc/src/docs/en/config.adoc b/fineract-doc/src/docs/en/config.adoc index 1bc515b5f..b6543f8dc 100644 --- a/fineract-doc/src/docs/en/config.adoc +++ b/fineract-doc/src/docs/en/config.adoc @@ -20,6 +20,7 @@ :checkedbox: pass:normal[{startsb}✔{endsb}] :table-stripes: even :hardbreaks: +:sectlinks: // ifeval::["{draft}"=="true"] // :title-page-background-image: image:{commondir}/images/draft.svg[position=top] From 6f09c9425d92f47832cf1cb204999b6203ca4c1c Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Thu, 9 Oct 2025 10:58:08 -0700 Subject: [PATCH 08/18] improve staging doc and commands in release step 8 --- .../src/docs/en/chapters/release/process-step08.adoc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fineract-doc/src/docs/en/chapters/release/process-step08.adoc b/fineract-doc/src/docs/en/chapters/release/process-step08.adoc index 05a73770b..f97ba4fec 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step08.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step08.adoc @@ -2,7 +2,7 @@ == Description -Finally create a directory with release name ({revnumber} in this example) in https://dist.apache.org/repos/dist/dev/fineract and add the following files in this new directory: +Next we'll stage the release candidate. Create a new folder and add these files: * apache-fineract-bin-{revnumber}.tar.gz * apache-fineract-bin-{revnumber}.tar.gz.sha512 @@ -11,16 +11,18 @@ Finally create a directory with release name ({revnumber} in this example) in ht * apache-fineract-src-{revnumber}.tar.gz.sha512 * apache-fineract-src-{revnumber}.tar.gz.asc -These files (or "artifacts") make up the release candidate. - -Upload these files to ASF's distribution dev (staging) area: +These files (or "artifacts") comprise the release candidate. Upload these files to https://www.apache.org/legal/release-policy.html#stage[ASF's distribution dev/staging area] like so: [source,bash,subs="attributes+"] ---- +# this is a remote operation svn mkdir https://dist.apache.org/repos/dist/dev/fineract/{revnumber} +# create local svn-tracked folder "{revnumber}" svn checkout https://dist.apache.org/repos/dist/dev/fineract/{revnumber} -cp path/to/files/* {revnumber}/ +# prepare to upload +cp path/to/new/folder/* {revnumber}/ cd {revnumber}/ +# actual upload occurs here svn add * && svn commit ---- From 6052ae60b414640ea0974693ade0641cadf91e8b Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Fri, 10 Oct 2025 14:33:47 -0700 Subject: [PATCH 09/18] release step 6: fix sanity check example --- fineract-doc/src/docs/en/chapters/release/process-step06.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fineract-doc/src/docs/en/chapters/release/process-step06.adoc b/fineract-doc/src/docs/en/chapters/release/process-step06.adoc index 2e669f722..a32093eeb 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step06.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step06.adoc @@ -17,7 +17,7 @@ Do some sanity checks. The source tarball and the code in the release branch (at [source,bash,subs="attributes+"] ---- cd /fineract-release-preparations -tar -xvf path/to/apache-fineract-src-{revnumber}.tar.gz +tar -xvzf path/to/apache-fineract-src-{revnumber}.tar.gz git clone git@github.com:apache/fineract.git cd fineract/ git checkout tags/{revnumber} From cce46de7672b7869ff2568918584f98a3cf4617d Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Sat, 11 Oct 2025 13:35:16 -0700 Subject: [PATCH 10/18] further improve release steps 6 and 9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally I thought I was causing issues by running `./gradlew clean` separate from `./gradlew srcDistTar binaryDistTar`. If I did (or so I thought), a `fineract-provider/build/classes/java/main/git.properties` file was _sometimes_ not generated (the `generateGitProperties` task failing, misbehaving, or not even running? was the file getting deleted? no idea). Unsure of the cause, at least I understand that file is supposed to end up at `BOOT-INF/classes/git.properties` in `fineract-provider-VERSION.jar` in the binary release tarball. Its contents are displayed at the `/fineract-provider/actuator/info` endpoint. I think that's more or less how it works, but I did not spend enough time on this to be certain. Now I think it wasn't at all related to how I was running our Gradle tasks, but rather some nasty behavior in gradle-git-properties: * https://github.com/n0mer/gradle-git-properties/issues/233 * https://github.com/gradle/gradle/issues/34177 Yuck. 🤢 --- .../en/chapters/release/process-step06.adoc | 14 +++----- .../en/chapters/release/process-step09.adoc | 35 +++++++++++++------ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/fineract-doc/src/docs/en/chapters/release/process-step06.adoc b/fineract-doc/src/docs/en/chapters/release/process-step06.adoc index a32093eeb..ed970fd5b 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step06.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step06.adoc @@ -6,10 +6,11 @@ Create source and binary tarballs. [source,bash,subs="attributes+"] ---- -./gradlew clean -./gradlew srcDistTar binaryDistTar +./gradlew clean srcDistTar binaryDistTar ---- +Check that `fineract-provider/build/classes/java/main/git.properties` exists. If so, continue. If not, you're likely encountering https://github.com/n0mer/gradle-git-properties/issues/233[this bug], and you need to re-run the command above to create proper source and binary tarballs. That `git.properties` file is supposed to end up at `BOOT-INF/classes/git.properties` in `fineract-provider-{revnumber}.jar` in the binary release tarball. Its contents are displayed at the `/fineract-provider/actuator/info` endpoint. It may be possible to fix this heisenbug entirely by https://github.com/gradle/gradle/issues/34177#issuecomment-3051970053[modifying our git properties gradle plugin config] in `fineract-provider/build.gradle`, perhaps by changing where `git.properties` is written. + Look in `fineract-war/build/distributions/` for the tarballs. Do some sanity checks. The source tarball and the code in the release branch (at the commit with the release tag) should match. @@ -25,17 +26,10 @@ cd .. diff -r fineract apache-fineract-src-{revnumber} ---- -Make sure the code compiles and tests pass on the uncompressed source. Do as much testing as you can and share what you did. Here's the bare minimum check: - -[source,bash,subs="attributes+"] ----- -./gradlew build -x test -x doc ----- +Make sure the code compiles and tests pass on the uncompressed source. You should at the very least do exactly what you will ask the community to do in <>. Ideally you'd build code and docs and run every possible test and check, but https://github.com/apache/fineract/actions[running everything has complex dependencies, caches, and takes many hours]. It is rarely done in practice offline / local / on developer machines. But please, go ahead and run the test and doc tasks, and more! Grab a cup of coffee and run everything you can. See the various builds in `.github/workflows/` and try the same things on your own. We should all hammer on a release candidate as much as we can to see if it breaks and fix it if so. All that of course improves our final release. -Finally, inspect `apache-fineract-bin-{revnumber}.tar.gz`. Make sure the `fineract-provider-{revnumber}.jar` can be run directly, and the `fineract-provider.war` can be run with Tomcat. - NOTE: We don't release any artifacts to Apache's Maven repository. == Gradle Task diff --git a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc index e532f3ad7..4c3948513 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc @@ -21,8 +21,7 @@ Make sure release artifacts are hosted at https://dist.apache.org/repos/dist/dev ---- # source tarball signature and checksum verification steps # we'll check the source tarball first -version={revnumber} -src=apache-fineract-src-$version.tar.gz +src=apache-fineract-src-{revnumber}.tar.gz # upon success: prints "Good signature" and returns successful exit code # upon failure: prints "BAD signature" and returns error exit code @@ -33,7 +32,7 @@ gpg --verify $src.asc gpg --print-md SHA512 $src | diff - $src.sha512 # binary tarball signature and checksum verification steps and outputs are similar -bin=apache-fineract-bin-$version.tar.gz +bin=apache-fineract-bin-{revnumber}.tar.gz gpg --verify $bin.asc gpg --print-md SHA512 $bin | diff - $bin.sha512 ---- @@ -48,28 +47,44 @@ TIP: Consider also https://en.wikipedia.org/wiki/Key_signing_party[signing] and === Build from source -[source,bash] +[source,bash,subs="attributes+"] ---- tar -xzf $src -cd apache-fineract-src-$version -gradle build -x test -x doc +cd apache-fineract-src-{revnumber} +./gradlew build -x test -x doc cd .. ---- === Run from binary -Before running this you must first start a database server and ensure the `fineract_default` and `fineract_tenant` databases exist. Then: +Before running Fineract you must first start a <> and ensure the `fineract_default` and `fineract_tenants` databases exist. Detailed steps for database preparation are left as an exercise for the reader. You can find ideas on how to do prepare your database in the `build-mariadb.yml`, `build-mysql.yml`, and `build-postgresql.yml` files in source control. -[source,bash] +Finally, start your Fineract server: + +[source,bash,subs="attributes+"] +---- +tar -xvzf apache-fineract-bin-{revnumber} +cd apache-fineract-bin-{revnumber} +export FINERACT_SERVER_SSL_ENABLED=false +export FINERACT_SERVER_PORT=8080 +export BACKEND_PROTOCOL=http +export BACKEND_PORT=$FINERACT_SERVER_PORT +# assumes reachable, healthy mariadb with default username, password, and port +java -jar fineract-provider-{revnumber}.jar +---- + +Alternatively, you can run it in Tomcat: + +[source,bash,subs="attributes+"] ---- -tar -xzf $bin -cd apache-fineract-bin-$version cat << 'EndOfRcenv' >> rcenv FINERACT_SERVER_SSL_ENABLED=false FINERACT_SERVER_PORT=8080 BACKEND_PROTOCOL=http BACKEND_PORT=$FINERACT_SERVER_PORT EndOfRcenv +source rcenv +# assumes reachable, healthy mariadb with default username, password, and port docker run --rm -it -v "$(pwd):/usr/local/tomcat/webapps" \ --net=host --env-file=rcenv tomcat:jre21 ---- From ee55b4ef753a8a15d421fdaeed8ca85414bf18cd Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Sat, 11 Oct 2025 14:08:30 -0700 Subject: [PATCH 11/18] cucumber.adoc: fix list continuations Fixes: ``` > Task :fineract-doc:asciidoctorPdf chapters/testing/cucumber.adoc: line 409: list item index: expected 1, got 3 chapters/testing/cucumber.adoc: line 411: list item index: expected 2, got 4 chapters/testing/cucumber.adoc: line 418: list item index: expected 1, got 5 ``` See https://docs.asciidoctor.org/asciidoc/latest/lists/continuation/#list-continuation --- fineract-doc/src/docs/en/chapters/testing/cucumber.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc b/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc index 40b63cf90..e9e876525 100644 --- a/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc +++ b/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc @@ -393,6 +393,7 @@ When writing new Cucumber tests: 1. *Create Feature File*: Add new `.feature` file in `fineract-e2e-tests-runner/src/test/resources/features/` 2. *Use Gherkin Syntax*: ++ [source,gherkin] ---- Feature: Loan Disbursement @@ -409,6 +410,7 @@ Feature: Loan Disbursement 3. *Implement Step Definitions*: Add corresponding step definitions in `fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/` 4. *Add Tags*: Tag scenarios appropriately: ++ [source,gherkin] ---- @TestRailId:C1234 @Smoke @@ -416,6 +418,7 @@ Scenario: Critical loan test ---- 5. *Verify with Gradle*: ++ [source,bash] ---- cd fineract-e2e-tests-runner From 513fbe1833de21d4d3be548eaaec1bed5a489d65 Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Sat, 11 Oct 2025 17:00:19 -0700 Subject: [PATCH 12/18] =?UTF-8?q?fix=20tarball=20explosion=20in=20release?= =?UTF-8?q?=20step=209=20=F0=9F=A7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fineract-doc/src/docs/en/chapters/release/process-step09.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc index 4c3948513..fa0080245 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc @@ -63,7 +63,7 @@ Finally, start your Fineract server: [source,bash,subs="attributes+"] ---- -tar -xvzf apache-fineract-bin-{revnumber} +tar -xvzf apache-fineract-bin-{revnumber}.tar.gz cd apache-fineract-bin-{revnumber} export FINERACT_SERVER_SSL_ENABLED=false export FINERACT_SERVER_PORT=8080 From 5d478f5fbb60ff5eef1dabbef21d2e0a99d58572 Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Mon, 13 Oct 2025 09:05:07 -0700 Subject: [PATCH 13/18] improve step 9 artifact verification gpg tips --- .../en/chapters/release/process-step09.adoc | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc index fa0080245..85a63088a 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc @@ -37,13 +37,30 @@ gpg --verify $bin.asc gpg --print-md SHA512 $bin | diff - $bin.sha512 ---- -For folks new to https://www.gnupg.org/[GnuPG], there are a couple things to note. First, if it says the source or binary tarball detached signature is correct, that's great! That's the most important part. +Look for `Good signature` in the `gpg` output: -Second, if you've imported `KEYS` but gpg warns you the key used for signing is not trusted, you can tell gpg you trust the key to squelch the warning. Ideally you meet the alleged key owner in person and check their ID first. Once you trust their identity matches, you then indicate your trust for their key. +[source,text,subs="attributes+"] +---- +$ gpg --verify $src.asc +gpg: assuming signed data in 'apache-fineract-bin-{revnumber}.tar.gz' +gpg: Signature made Sat 11 Oct 2025 05:46:42 PM PDT +gpg: using EDDSA key 250775BDB5FE7D53E4AF95C00E895A1A7A090CFC +gpg: Good signature from "Adam Monsen " [unknown] +---- -Start with `gpg --edit-key KEYID`, substituting the signing key id for `KEYID`. At the `gpg>` prompt, run the `trust` command and choose `4` (I trust fully). You could also choose `3` (marginal), but do _not_ choose `5` (ultimate). +That's the most important part. -TIP: Consider also https://en.wikipedia.org/wiki/Key_signing_party[signing] and https://en.wikipedia.org/wiki/Web_of_trust[uploading] each other's keys. +You may see this warning: + +[source,text] +---- +gpg: WARNING: This key is not certified with a trusted signature! +gpg: There is no indication that the signature belongs to the owner. +---- + +You may choose to ignore it. To squelch this warning, you must extend your https://en.wikipedia.org/wiki/Web_of_trust[web of trust], by, for example, https://en.wikipedia.org/wiki/Key_signing_party[signing the release manager's key]. + +Now it's time to build and run the release candidate. === Build from source From e8673d5ff38b5c5270e130ffb72bc99f53877fc3 Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Mon, 13 Oct 2025 09:15:53 -0700 Subject: [PATCH 14/18] fix typos in step 9 re: running the binary --- .../src/docs/en/chapters/release/process-step09.adoc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc index 85a63088a..77fe3e956 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc @@ -41,7 +41,7 @@ Look for `Good signature` in the `gpg` output: [source,text,subs="attributes+"] ---- -$ gpg --verify $src.asc +$ gpg --verify $bin.asc gpg: assuming signed data in 'apache-fineract-bin-{revnumber}.tar.gz' gpg: Signature made Sat 11 Oct 2025 05:46:42 PM PDT gpg: using EDDSA key 250775BDB5FE7D53E4AF95C00E895A1A7A090CFC @@ -74,7 +74,7 @@ cd .. === Run from binary -Before running Fineract you must first start a <> and ensure the `fineract_default` and `fineract_tenants` databases exist. Detailed steps for database preparation are left as an exercise for the reader. You can find ideas on how to do prepare your database in the `build-mariadb.yml`, `build-mysql.yml`, and `build-postgresql.yml` files in source control. +Before running Fineract you must first start a <> and ensure the `fineract_default` and `fineract_tenants` databases exist. Detailed steps for database preparation are left as an exercise for the reader. You can find ideas on how to prepare your database in the `build-mariadb.yml`, `build-mysql.yml`, and `build-postgresql.yml` files in source control. Finally, start your Fineract server: @@ -100,7 +100,6 @@ FINERACT_SERVER_PORT=8080 BACKEND_PROTOCOL=http BACKEND_PORT=$FINERACT_SERVER_PORT EndOfRcenv -source rcenv # assumes reachable, healthy mariadb with default username, password, and port docker run --rm -it -v "$(pwd):/usr/local/tomcat/webapps" \ --net=host --env-file=rcenv tomcat:jre21 From 1005b56d0f57eb6142eeae5ebd03c7715ebe544e Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Mon, 13 Oct 2025 09:23:35 -0700 Subject: [PATCH 15/18] update link to rc verification instructions --- .../src/main/resources/email/release.step10.vote.message.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/resources/email/release.step10.vote.message.ftl b/buildSrc/src/main/resources/email/release.step10.vote.message.ftl index 8be7ff5d0..96d3fd38d 100644 --- a/buildSrc/src/main/resources/email/release.step10.vote.message.ftl +++ b/buildSrc/src/main/resources/email/release.step10.vote.message.ftl @@ -42,7 +42,7 @@ Please indicate if you are a binding vote (member of the PMC). Note: PMC members Please also indicate with "Tested: YES/NO/PARTIAL" if you have locally built and/or tested these artifacts and/or a clone of the code checked out to the release commit, following the form: -Tested: YES ... Verified integrity and signatures of release artifacts locally, built from source, ran jar/war: Did everything mentioned in the current release candidate verification guidance ( https://lists.apache.org/thread/hym94pdy3nk9gjspkz4qonv2v15n5dpo ). If you did more than that, please specify. +Tested: YES ... Verified integrity and signatures of release artifacts locally, built from source, ran jar/war: Did everything mentioned in the current release candidate verification guidance ( https://fineract.apache.org/docs/rc/#_artifact_verification ). If you did more than that, please specify. Tested: NO ... No testing performed on release candidate, e.g. relying on testing performed by other contributors and/or output of GitHub Actions, while exercising my right to vote. From 95e6cea50493393833d15c175ebddd807af023c3 Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Mon, 13 Oct 2025 09:37:30 -0700 Subject: [PATCH 16/18] shorten/simplify gpg key sending commands also: change <3> to match text above, omitting otherwise helpful 0x hex designation prefix --- .../src/docs/en/chapters/release/configuration-gpg.adoc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/fineract-doc/src/docs/en/chapters/release/configuration-gpg.adoc b/fineract-doc/src/docs/en/chapters/release/configuration-gpg.adoc index 2678766d3..4774cce9a 100644 --- a/fineract-doc/src/docs/en/chapters/release/configuration-gpg.adoc +++ b/fineract-doc/src/docs/en/chapters/release/configuration-gpg.adoc @@ -151,7 +151,7 @@ sub cv25519/4FGHIJ56 2022-04-16 [E] [expires: 2024-04-16] <6> + <2> GPG created a revocation certificate and its directory. If your private key is compromised, you need to use your revocation certificate to revoke your key. + -<3> The public key uses the Ed25519 ECC (Elliptic Curve Cryptography) algorithm and shows the expiration date of 16 Apr 2024. The public key ID `0x7890ABCD` matches the last 8 characters of key fingerprint. The `[SC]` indicates this key is used to sign (prove authorship) and certify (issue subkeys for encryption, signature and authentication operations). +<3> The public key uses the Ed25519 ECC (Elliptic Curve Cryptography) algorithm and shows the expiration date of 16 Apr 2024. The public key ID `7890ABCD` matches the last 8 characters of key fingerprint. The `[SC]` indicates this key is used to sign (prove authorship) and certify (issue subkeys for encryption, signature and authentication operations). <4> The key fingerprint (`ABCD EFGH IJKL MNOP QRST UVWX YZ12 3456 7890 ABCD`) is a hash of your public key. + <5> Your name and your email address are shown with information about the subkey. @@ -186,7 +186,7 @@ IMPORTANT: Please contact a PMC member to add your GPG public key in Fineract's + [source,bash] ---- -gpg --send-keys ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCD +gpg --send-keys 0xYZ1234567890ABCD ---- + Before doing this, make sure that your default keyserver is hkp://keyserver.ubuntu.com/. You can do this by changing the default keyserver in ~/.gnupg/dirmngr.conf: @@ -200,9 +200,7 @@ Alternatively you can provide the keyserver with the send command: + [source,bash] ---- -gpg \ - --keyserver 'hkp://keyserver.ubuntu.com:11371' \ - --send-keys ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCD +gpg --keyserver keyserver.ubuntu.com --send-keys 0xYZ1234567890ABCD ---- + Another option to publish your key is to submit an armored public key directly at https://keyserver.ubuntu.com/. You can create the necessary data with this command by providing the email address that you used when you created your key pair: From ccfce92a3b21fc2201d791c67362de8ad175fe80 Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Sun, 19 Oct 2025 09:50:06 -0700 Subject: [PATCH 17/18] save 1.13.0 vote result --- .../main/resources/vote/result.1.13.0.json | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 buildSrc/src/main/resources/vote/result.1.13.0.json diff --git a/buildSrc/src/main/resources/vote/result.1.13.0.json b/buildSrc/src/main/resources/vote/result.1.13.0.json new file mode 100644 index 000000000..91650ad02 --- /dev/null +++ b/buildSrc/src/main/resources/vote/result.1.13.0.json @@ -0,0 +1,56 @@ +{ + "approve": { + "binding": [ + { + "name": "James Dailey", + "email": "jdailey@apache.org" + }, + { + "name": "Victor Manuel Romero Rodriguez", + "email": "victor.romero@fintecheando.mx" + }, + { + "name": "Terence Monteiro", + "email": "terence.placker@gmail.com" + } + ], + "nonBinding": [ + { + "name": "Adam Monsen", + "email": "amonsen@mifos.org" + }, + { + "name": "Bharath Gowda", + "email": "bgowda@mifos.org" + }, + { + "name": "Aleksandar Vidakovic", + "email": "cheetah@monkeysintown.com" + }, + { + "name": "Ed Cable", + "email": "edcable@mifos.org" + }, + { + "name": "Felix van Hove", + "email": "fvanhove@gmx.de.invalid" + }, + { + "name": "Ahmed adel", + "email": "a7med3del1973@gmail.com" + } + ] + }, + "disapprove": { + "binding": [ + ], + "nonBinding": [ + ] + }, + "noOpinion": { + "binding": [ + ], + "nonBinding": [ + ] + } +} From 826608f6ccc7520d601bbbd53ee93c8eb97e07c6 Mon Sep 17 00:00:00 2001 From: Adam Monsen Date: Mon, 20 Oct 2025 08:05:08 -0700 Subject: [PATCH 18/18] don't include actual email addresses might help voters avoid a bit of spam --- .../resources/email/release.step11.vote.message.ftl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/resources/email/release.step11.vote.message.ftl b/buildSrc/src/main/resources/email/release.step11.vote.message.ftl index 232dd67f0..227d956d6 100644 --- a/buildSrc/src/main/resources/email/release.step11.vote.message.ftl +++ b/buildSrc/src/main/resources/email/release.step11.vote.message.ftl @@ -38,7 +38,7 @@ Here are the detailed results: <#list project['fineract.vote'].approve.binding> Binding +1s: <#items as item> -- ${item.name} (${item.email}) +- ${item.name} @@ -46,7 +46,7 @@ Binding +1s: <#list project['fineract.vote'].approve.nonBinding> Non binding +1s: <#items as item> -- ${item.name} (${item.email}) +- ${item.name} @@ -54,14 +54,14 @@ Non binding +1s: <#list project['fineract.vote'].disapprove.binding> Binding -1s: <#items as item> -- ${item.name} (${item.email}) +- ${item.name} <#list project['fineract.vote'].disapprove.nonBinding> Non binding -1s: <#items as item> -- ${item.name} (${item.email}) +- ${item.name} @@ -69,14 +69,14 @@ Non binding -1s: <#list project['fineract.vote'].noOpinion.binding> Binding +0s: <#items as item> -- ${item.name} (${item.email}) +- ${item.name} <#list project['fineract.vote'].noOpinion.nonBinding> Non binding +0s: <#items as item> -- ${item.name} (${item.email}) +- ${item.name}