From 6046134b515281e7dc6a3bb80c84172e8bdd147a Mon Sep 17 00:00:00 2001 From: Nitesh Yadav Date: Fri, 18 Sep 2026 13:56:49 +0530 Subject: [PATCH] fix(shares): drop stale client-mismatch check, fix stale client_id on settle - Remove the client-mismatch validation in the Additional Shares tranche submit path (ShareAccountDataSerializer) for parity with the same decision already made on the first-application Apply Collection flow -- a voucher a counter was shown and allowed to pick should not then be rejected for the same reason at the very last step - Rewrite m_share_advance_collection.client_id to the settling account's own client at the moment a voucher is actually applied (both ShareAdvanceCollectionApiResource.apply() and AdditionalShareTrancheFundingService.settle()) -- a voucher can now be found and applied from any member's screen, so whoever last searched it is not necessarily who its money became share capital for, and the stale value was misnaming the receipt in its own detail popup after settlement Co-Authored-By: Claude Sonnet 5 --- .../api/ShareAdvanceCollectionApiResource.java | 15 +++++++++++---- .../serialization/ShareAccountDataSerializer.java | 12 +++++------- .../AdditionalShareTrancheFundingService.java | 10 ++++++++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/cashtransfer/api/ShareAdvanceCollectionApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/cashtransfer/api/ShareAdvanceCollectionApiResource.java index d2a8a5be9..4874057f9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/cashtransfer/api/ShareAdvanceCollectionApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/cashtransfer/api/ShareAdvanceCollectionApiResource.java @@ -712,17 +712,24 @@ public class ShareAdvanceCollectionApiResource { // Every receipt in this settlement is marked applied, not just the one in the path — they // were all consumed by it, and an unmarked one would still show as money awaiting use. The fee // receipt is marked applied too, even though it posted no GL entry of its own. + // + // client_id is rewritten to this account's own client here too -- a voucher can now be found + // and applied from any member's screen, so whoever last searched it (the value it carried + // going in) is not necessarily who its money actually became share capital for. Leaving the + // old value in place would make the receipt's own record misname whose it is the moment it + // settles, which is exactly the figure a receipt-detail popup reads back afterwards. + final Object settledClientId = acct.get("client_id"); for (final Long id : collectionIds) { jdbcTemplate.update( "UPDATE m_share_advance_collection SET status = 'APPLIED', applied_share_account_id = ?, " - + " applied_on_date = ?, applied_gl_transaction_id = ? WHERE id = ?", - shareAccountId, java.sql.Date.valueOf(entryDate), txnId, id); + + " applied_on_date = ?, applied_gl_transaction_id = ?, client_id = ? WHERE id = ?", + shareAccountId, java.sql.Date.valueOf(entryDate), txnId, settledClientId, id); } if (chargeCollectionId != null) { jdbcTemplate.update( "UPDATE m_share_advance_collection SET status = 'APPLIED', applied_share_account_id = ?, " - + " applied_on_date = ?, applied_gl_transaction_id = ? WHERE id = ?", - shareAccountId, java.sql.Date.valueOf(entryDate), txnId, chargeCollectionId); + + " applied_on_date = ?, applied_gl_transaction_id = ?, client_id = ? WHERE id = ?", + shareAccountId, java.sql.Date.valueOf(entryDate), txnId, settledClientId, chargeCollectionId); } // Reveals the purchase transaction in the passbook and transactions views. diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java index c3f1fc7a8..29bbb45d2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java @@ -894,13 +894,11 @@ public class ShareAccountDataSerializer { // already used" does not say which one to go and look at. final Object label = collection.get("voucher_no") != null ? collection.get("voucher_no") : collection.get("receipt_no"); - final Number collectionClientId = (Number) collection.get("client_id"); - if (collectionClientId == null || collectionClientId.longValue() != account.getClientId().longValue()) { - baseDataValidator.reset().parameter(ShareAccountApiConstants.advancecollectionid_paramname).value(advanceCollectionId) - .failWithCodeAndMessage("share.advance.collection.client.mismatch", - "Voucher " + label + " belongs to a different member."); - } - + // No client-match check here, matching the same product decision already applied to the + // first-application Apply Collection flow: a voucher is reachable and attachable from any + // account's screen (see ShareAdvanceCollectionApiResource), so rejecting it again here, + // at the very last step, would only fail a submission the counter was told was valid. + // // Only money still sitting in Sundry can fund a tranche. APPLIED has already become // share capital somewhere, and REFUNDED went back to the member. final String status = (String) collection.get("status"); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/AdditionalShareTrancheFundingService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/AdditionalShareTrancheFundingService.java index fcf1b502a..4598e3211 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/AdditionalShareTrancheFundingService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/AdditionalShareTrancheFundingService.java @@ -146,12 +146,18 @@ public class AdditionalShareTrancheFundingService { } // Every receipt behind the tranche, not just the lead one: a member's later instalments have // become share capital too, and one left UNAPPLIED could be spent a second time. + // + // client_id is rewritten to this account's own client here too -- a voucher can now be found + // and applied from any member's screen, so whoever last searched it (the value it carried + // going in) is not necessarily who its money actually became share capital for. Leaving the + // old value in place would make the receipt's own record misname whose it is the moment it + // settles, which is exactly the figure a receipt-detail popup reads back afterwards. for (final Long collectionId : transaction.getAllAdvanceCollectionIds()) { this.jdbcTemplate.update(""" UPDATE m_share_advance_collection - SET status = 'APPLIED', applied_share_account_id = ?, applied_on_date = ? + SET status = 'APPLIED', applied_share_account_id = ?, applied_on_date = ?, client_id = ? WHERE id = ? AND status = 'UNAPPLIED' - """, account.getId(), java.sql.Date.valueOf(DateUtils.getBusinessLocalDate()), collectionId); + """, account.getId(), java.sql.Date.valueOf(DateUtils.getBusinessLocalDate()), account.getClientId(), collectionId); } }