From 7c72e651e48845b2664c9368bf8e2e0a60157329 Mon Sep 17 00:00:00 2001 From: hongwei Date: Mon, 6 Oct 2025 00:11:48 +0200 Subject: [PATCH] feature/ add endpoint to get holding account by parent account using account attribute PARENT_ACCOUNT_ID --- .../scala/code/api/v6_0_0/APIMethods600.scala | 54 +++++++++++++++++++ .../LocalMappedConnectorInternal.scala | 1 + .../src/main/scala/code/users/LiftUsers.scala | 5 +- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala index f1b27003c..6f18b84dc 100644 --- a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala +++ b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala @@ -1,5 +1,6 @@ package code.api.v6_0_0 +import code.accountattribute.AccountAttributeX import code.api.ObpApiFailure import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.util.APIUtil._ @@ -9,6 +10,7 @@ import code.api.util.ErrorMessages.{$UserNotLoggedIn, InvalidDateFormat, Invalid import code.api.util.FutureUtil.EndpointContext import code.api.util.NewStyle.HttpCode import code.api.util.{NewStyle, RateLimitingUtil} +import code.api.v3_0_0.JSONFactory300 import code.api.v6_0_0.JSONFactory600.{createActiveCallLimitsJsonV600, createCallLimitJsonV600, createCurrentUsageJson} import code.bankconnectors.LocalMappedConnectorInternal import code.bankconnectors.LocalMappedConnectorInternal._ @@ -83,6 +85,58 @@ trait APIMethods600 { val transactionRequestType = TransactionRequestType("HOLD") LocalMappedConnectorInternal.createTransactionRequest(bankId, accountId, viewId , transactionRequestType, json) } + + // --- GET Holding Account by Parent --- + staticResourceDocs += ResourceDoc( + getHoldingAccountByParent, + implementedInApiVersion, + nameOf(getHoldingAccountByParent), + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/holding-account", + "Get Holding Account By Parent", + s""" + |Return the Holding Account linked to the given parent account via account attribute `PARENT_ACCOUNT_ID`. + |If multiple holding accounts exist, the first one will be returned. + | + """.stripMargin, + EmptyBody, + moderatedCoreAccountJsonV300, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + UnknownError + ), + List(apiTagAccount) + ) + + lazy val getHoldingAccountByParent: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "holding-account" :: Nil JsonGet _ => + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (user @Full(u), _, _, view, callContext) <- SS.userBankAccountView + // Find accounts by attribute PARENT_ACCOUNT_ID + (accountIdsBox, callContext) <- AccountAttributeX.accountAttributeProvider.vend.getAccountIdsByParams(bankId, Map("PARENT_ACCOUNT_ID" -> List(accountId.value))) map { ids => (ids, callContext) } + accountIds = accountIdsBox.getOrElse(Nil) + // load the first holding account + holdingOpt <- { + def firstHolding(ids: List[String]): Future[Option[BankAccount]] = ids match { + case Nil => Future.successful(None) + case id :: tail => + NewStyle.function.getBankAccount(bankId, AccountId(id), callContext).flatMap { case (acc, cc) => + if (acc.accountType == "HOLDING") Future.successful(Some(acc)) else firstHolding(tail) + } + } + firstHolding(accountIds) + } + holding <- NewStyle.function.tryons($BankAccountNotFound, 404, callContext) { holdingOpt.get } + moderatedAccount <- NewStyle.function.moderatedBankAccountCore(holding, view, user, callContext) + } yield { + val core = JSONFactory300.createCoreBankAccountJSON(moderatedAccount) + (core, HttpCode.`200`(callContext)) + } + } staticResourceDocs += ResourceDoc( getCurrentCallsLimit, diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnectorInternal.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnectorInternal.scala index c706b9a9c..87d3ae300 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnectorInternal.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnectorInternal.scala @@ -1637,6 +1637,7 @@ object LocalMappedConnectorInternal extends MdcLoggable { accountRoutings= Nil, callContext = callContext ) + _ <- code.model.dataAccess.BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, newAccountId, cc1.get.user.head, callContext) // Link attributes on holding account _ <- NewStyle.function.createOrUpdateAccountAttribute( bankId, holding.accountId, ProductCode("HOLDING"), diff --git a/obp-api/src/main/scala/code/users/LiftUsers.scala b/obp-api/src/main/scala/code/users/LiftUsers.scala index 39ae2f690..b47b71ccf 100644 --- a/obp-api/src/main/scala/code/users/LiftUsers.scala +++ b/obp-api/src/main/scala/code/users/LiftUsers.scala @@ -1,8 +1,5 @@ package code.users -import code.api.util.Consent.logger - -import java.util.Date import code.api.util._ import code.entitlement.Entitlement import code.loginattempts.LoginAttempt.maxBadLoginAttempts @@ -15,8 +12,8 @@ import net.liftweb.common.{Box, Empty, Full} import net.liftweb.mapper._ import net.liftweb.util.Helpers +import java.util.Date import scala.collection.immutable -import scala.collection.immutable.List import scala.concurrent.Future object LiftUsers extends Users with MdcLoggable{