From 6c7c0f84451a8c5aa2c6e5c6e12f2e410b53491f Mon Sep 17 00:00:00 2001 From: Nitesh Yadav Date: Tue, 6 Oct 2026 12:42:12 +0530 Subject: [PATCH 1/2] build: freeze the default (non-release/maintenance branch) jar version git-versioning's rev{} fallback previously derived the SNAPSHOT version from the nearest reachable git tag (major.(minor+1).0-SNAPSHOT), so the built jar's name drifted as tags moved. Pins it to a fixed 1.15.0-SNAPSHOT instead so the jar name stays predictable across builds on dev-latest/feature branches. release/*.*.* and maintenance/*.* branches still version off their own tags, unchanged. Co-Authored-By: Claude Sonnet 5 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fc2ca9941..c2dc2e63f 100644 --- a/build.gradle +++ b/build.gradle @@ -154,7 +154,7 @@ gitVersioning.apply { } } rev { - version = '${describe.tag.version.major}.${describe.tag.version.minor.next}.0-SNAPSHOT' + version = '1.15.0-SNAPSHOT' } } From 643427bd1abc5791b174c2e4d1764a132f760134 Mon Sep 17 00:00:00 2001 From: Nitesh Yadav Date: Tue, 6 Oct 2026 19:03:51 +0530 Subject: [PATCH 2/2] fix(shares): correct share transaction lookup and validate NONE settlements redemptionValueOf() read from "m_share_account_transaction" with a "share_account_id" column. The table is m_share_account_transactions and the column is account_id, so every share withdrawal approval failed with a BadSqlGrammarException on the Share Sundry balance guard, before any posting was attempted. The lookup now also filters on is_active, matching ShareAdvanceCollectionApiResource.shareValueOf(), so a reversed purchase cannot price a redemption. validate() branched on "GL" versus everything else, but approve() handles a third settlement mode: submit() downgrades a request to NONE when the clerk names the product's own Share Sundry GL as the destination, and that mode posts Dr Share Capital / Cr Share Sundry and stops. Validating it as a savings settlement reported a missing fund source mapping that no posting in this mode ever reads, so a correct request showed a failed check. NONE is now reported as not applicable for both the linked-savings and the fund-source checks. Co-Authored-By: Claude Opus 5 (1M context) --- ...lerkShareWithdrawalRequestApiResource.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/clerkwithdrawal/api/ClerkShareWithdrawalRequestApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/clerkwithdrawal/api/ClerkShareWithdrawalRequestApiResource.java index da1a79dc7..e7dedf034 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/clerkwithdrawal/api/ClerkShareWithdrawalRequestApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/clerkwithdrawal/api/ClerkShareWithdrawalRequestApiResource.java @@ -451,8 +451,18 @@ public class ClerkShareWithdrawalRequestApiResource { // not-applicable rather than failed for a GL request — a share account with no linked // savings is a perfectly valid GL-settlement case. final boolean glSettlementMode = "GL".equals(record.get("settlement_mode")); - checks.add(glSettlementMode - ? checkResult("Linked savings account is Active", true, "Not applicable — settlement is a GL book-transfer") + // 'NONE' is the third mode approve() handles, and submit() produces it on its own: a clerk who + // names the product's own Share Sundry GL as the destination is saying "stop here", so the + // request is downgraded from GL to NONE and approve() posts Dr Share Capital / Cr Share Sundry + // and nothing further. Like a GL settlement it never reaches a savings account, so the + // savings-side checks below do not apply to it either — running them reports a missing fund + // source mapping that no posting in this mode is ever going to read. + final boolean sundryOnlySettlement = "NONE".equals(record.get("settlement_mode")); + final boolean noSavingsLeg = glSettlementMode || sundryOnlySettlement; + checks.add(noSavingsLeg + ? checkResult("Linked savings account is Active", true, sundryOnlySettlement + ? "Not applicable — the redemption rests in Share Sundry" + : "Not applicable — settlement is a GL book-transfer") : checkResult("Linked savings account is Active", savingsAccountId != null && savingsStatus != null && savingsStatus == 300, savingsAccountId == null ? "No linked savings account configured" : "Account #" + savingsAccountId + " — " + savingsStatusLabel(savingsStatus))); @@ -541,6 +551,9 @@ public class ClerkShareWithdrawalRequestApiResource { final boolean settlementGlPresent = glAccountIdObj != null; checks.add(checkResult("Settlement GL Account recorded", settlementGlPresent, settlementGlPresent ? glCodeOf(((Number) glAccountIdObj).longValue()) : "Missing")); + } else if (sundryOnlySettlement) { + checks.add(checkResult("Settlement destination recorded", true, + "Share Sundry — the redemption stops there, so no fund source mapping is read")); } else { boolean glMappingOk = false; String glMappingDetail = "Linked savings account or settlement payment type missing — cannot check"; @@ -992,8 +1005,8 @@ public class ClerkShareWithdrawalRequestApiResource { // The purchase that issued the holding - type 500. Its unit price is what the member paid. final BigDecimal unitPrice = jdbcTemplate .queryForList(""" - SELECT unit_price FROM m_share_account_transaction - WHERE share_account_id = ? AND type_enum = 500 AND unit_price IS NOT NULL + SELECT unit_price FROM m_share_account_transactions + WHERE account_id = ? AND is_active = true AND type_enum = 500 AND unit_price IS NOT NULL ORDER BY id DESC LIMIT 1 """, BigDecimal.class, shareAccountId) .stream().findFirst().orElse(null);