4646import org .apache .fineract .portfolio .loanaccount .service .LoanJournalEntryPoster ;
4747import org .springframework .context .annotation .Conditional ;
4848import org .springframework .data .domain .Sort ;
49+ import org .springframework .lang .Nullable ;
4950import org .springframework .stereotype .Component ;
5051
5152@ Component
@@ -173,21 +174,20 @@ private ExternalAssetOwnerTransfer sellAsset(final Loan loan, final LocalDate se
173174 ExternalAssetOwner previousOwner = determinePreviousOwnerAndCleanupIfNeeded (loan , settlementDate , externalAssetOwnerTransfer );
174175 ExternalTransferStatus activeStatus = determineActiveStatus (externalAssetOwnerTransfer );
175176
176- ExternalAssetOwnerTransfer newTransfer = activatePendingEntry (settlementDate , externalAssetOwnerTransfer , activeStatus );
177+ ExternalAssetOwnerTransfer newTransfer = activatePendingEntry (settlementDate , externalAssetOwnerTransfer , activeStatus ,
178+ previousOwner );
179+
177180 loanJournalEntryPoster .postJournalEntriesForExternalOwnerTransfer (loan , newTransfer , previousOwner );
178181 return newTransfer ;
179182 }
180183
181184 private ExternalAssetOwner determinePreviousOwnerAndCleanupIfNeeded (final Loan loan , final LocalDate settlementDate ,
182185 final ExternalAssetOwnerTransfer externalAssetOwnerTransfer ) {
183- if (!delayedSettlementAttributeService .isEnabled (loan .getLoanProduct ().getId ())) {
184- // When delayed settlement is disabled, asset is directly sold to investor, and we are the previous owner.
185- return null ;
186- }
187-
188- if (ExternalTransferStatus .PENDING_INTERMEDIATE == externalAssetOwnerTransfer .getStatus ()) {
189- // When delayed settlement is enabled and asset is sold to intermediate, we are the previous owner.
190- return null ;
186+ if (!delayedSettlementAttributeService .isEnabled (loan .getLoanProduct ().getId ())
187+ || ExternalTransferStatus .PENDING_INTERMEDIATE == externalAssetOwnerTransfer .getStatus ()) {
188+ // Use the loan mapping as the source of truth for the current owner.
189+ // If a mapping exists, this is an owner-to-owner transfer — expire the current active and clean up.
190+ return expireCurrentOwnerIfPresent (loan , settlementDate );
191191 }
192192
193193 // When delayed settlement is enabled and asset is sold from intermediate to investor, the intermediate is the
@@ -199,6 +199,19 @@ private ExternalAssetOwner determinePreviousOwnerAndCleanupIfNeeded(final Loan l
199199 return activeIntermediateTransfer .getOwner ();
200200 }
201201
202+ @ Nullable
203+ private ExternalAssetOwner expireCurrentOwnerIfPresent (final Loan loan , final LocalDate settlementDate ) {
204+ Optional <ExternalAssetOwnerTransfer > activeTransfer = externalAssetOwnerTransferRepository .findActiveByLoanId (loan .getId ());
205+ if (activeTransfer .isPresent ()) {
206+ ExternalAssetOwnerTransfer currentActiveTransfer = activeTransfer .get ();
207+ expireTransfer (settlementDate , currentActiveTransfer );
208+ externalAssetOwnerTransferLoanMappingRepository .deleteByLoanIdAndOwnerTransfer (loan .getId (), currentActiveTransfer );
209+ return currentActiveTransfer .getOwner ();
210+ }
211+ // Internal-to-external transfer: no previous external owner
212+ return null ;
213+ }
214+
202215 private ExternalTransferStatus determineActiveStatus (final ExternalAssetOwnerTransfer externalAssetOwnerTransfer ) {
203216 if (ExternalTransferStatus .PENDING_INTERMEDIATE == externalAssetOwnerTransfer .getStatus ()) {
204217 return ExternalTransferStatus .ACTIVE_INTERMEDIATE ;
@@ -208,13 +221,16 @@ private ExternalTransferStatus determineActiveStatus(final ExternalAssetOwnerTra
208221 }
209222
210223 private ExternalAssetOwnerTransfer getActiveIntermediateOrThrow (final Loan loan ) {
211- Optional <ExternalAssetOwnerTransfer > optionalActiveIntermediateTransfer = externalAssetOwnerTransferRepository
224+ Optional <ExternalAssetOwnerTransfer > optionalActiveIntermediateTransfer = findActiveIntermediateTransfer (loan );
225+ return optionalActiveIntermediateTransfer
226+ .orElseThrow (() -> new IllegalStateException ("Expected a effective transfer of ACTIVE_INTERMEDIATE status to be present." ));
227+ }
228+
229+ private Optional <ExternalAssetOwnerTransfer > findActiveIntermediateTransfer (final Loan loan ) {
230+ return externalAssetOwnerTransferRepository
212231 .findOne ((root , query , criteriaBuilder ) -> criteriaBuilder .and (criteriaBuilder .equal (root .get ("loanId" ), loan .getId ()),
213232 criteriaBuilder .equal (root .get ("status" ), ExternalTransferStatus .ACTIVE_INTERMEDIATE ),
214233 criteriaBuilder .equal (root .get ("effectiveDateTo" ), FUTURE_DATE_9999_12_31 )));
215-
216- return optionalActiveIntermediateTransfer
217- .orElseThrow (() -> new IllegalStateException ("Expected a effective transfer of ACTIVE_INTERMEDIATE status to be present." ));
218234 }
219235
220236 private ExternalAssetOwnerTransferDetails createAssetOwnerTransferDetails (Loan loan ,
@@ -253,9 +269,11 @@ private ExternalAssetOwnerTransfer cancelTransfer(final LocalDate settlementDate
253269 }
254270
255271 private ExternalAssetOwnerTransfer activatePendingEntry (final LocalDate settlementDate ,
256- final ExternalAssetOwnerTransfer pendingTransfer , final ExternalTransferStatus activeStatus ) {
272+ final ExternalAssetOwnerTransfer pendingTransfer , final ExternalTransferStatus activeStatus ,
273+ final ExternalAssetOwner previousOwner ) {
257274 LocalDate effectiveFrom = settlementDate .plusDays (1 );
258- return createNewEntryAndExpireOldEntry (settlementDate , pendingTransfer , activeStatus , null , effectiveFrom , FUTURE_DATE_9999_12_31 );
275+ return createNewEntryAndExpireOldEntry (settlementDate , pendingTransfer , activeStatus , null , effectiveFrom , FUTURE_DATE_9999_12_31 ,
276+ previousOwner );
259277 }
260278
261279 private ExternalAssetOwnerTransfer declinePendingEntry (final Loan loan , final LocalDate settlementDate ,
@@ -267,6 +285,14 @@ private ExternalAssetOwnerTransfer declinePendingEntry(final Loan loan, final Lo
267285 private ExternalAssetOwnerTransfer createNewEntryAndExpireOldEntry (final LocalDate settlementDate ,
268286 final ExternalAssetOwnerTransfer externalAssetOwnerTransfer , final ExternalTransferStatus status ,
269287 final ExternalTransferSubStatus subStatus , final LocalDate effectiveDateFrom , final LocalDate effectiveDateTo ) {
288+ return createNewEntryAndExpireOldEntry (settlementDate , externalAssetOwnerTransfer , status , subStatus , effectiveDateFrom ,
289+ effectiveDateTo , null );
290+ }
291+
292+ private ExternalAssetOwnerTransfer createNewEntryAndExpireOldEntry (final LocalDate settlementDate ,
293+ final ExternalAssetOwnerTransfer externalAssetOwnerTransfer , final ExternalTransferStatus status ,
294+ final ExternalTransferSubStatus subStatus , final LocalDate effectiveDateFrom , final LocalDate effectiveDateTo ,
295+ final ExternalAssetOwner previousOwner ) {
270296 ExternalAssetOwnerTransfer newExternalAssetOwnerTransfer = new ExternalAssetOwnerTransfer ();
271297 newExternalAssetOwnerTransfer .setOwner (externalAssetOwnerTransfer .getOwner ());
272298 newExternalAssetOwnerTransfer .setExternalId (externalAssetOwnerTransfer .getExternalId ());
@@ -279,7 +305,8 @@ private ExternalAssetOwnerTransfer createNewEntryAndExpireOldEntry(final LocalDa
279305 newExternalAssetOwnerTransfer .setPurchasePriceRatio (externalAssetOwnerTransfer .getPurchasePriceRatio ());
280306 newExternalAssetOwnerTransfer .setEffectiveDateFrom (effectiveDateFrom );
281307 newExternalAssetOwnerTransfer .setEffectiveDateTo (effectiveDateTo );
282- newExternalAssetOwnerTransfer .setPreviousOwner (externalAssetOwnerTransfer .getPreviousOwner ());
308+ newExternalAssetOwnerTransfer
309+ .setPreviousOwner (previousOwner != null ? previousOwner : externalAssetOwnerTransfer .getPreviousOwner ());
283310
284311 expireTransfer (settlementDate , externalAssetOwnerTransfer );
285312
0 commit comments