From 983d66d1fdb69ff46e26ff4763d0dfef88571692 Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 9 Aug 2024 12:55:53 +0200 Subject: [PATCH 1/7] refactor/added the comments --- obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index 5b34388171..d04d5f4374 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala @@ -972,6 +972,8 @@ trait APIMethods500 { permissionsFromSource = permission.views.map(view =>APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition]).toList).flatten.toSet permissionsFromTarget = targetCreateCustomViewJson.allowed_permissions + //eg: permissionsFromTarget=List(1,2), permissionsFromSource = List(1,3,4) => userMissingPermissions = List(2) + //Here would find the missing permissions and show them in the error messages userMissingPermissions = permissionsFromTarget.toSet diff permissionsFromSource failMsg = s"${ErrorMessages.UserDoesNotHavePermission} ${userMissingPermissions.toString}" From 366e4274bdc17c0f3931d4454a551cb45b77df74 Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 14 Aug 2024 11:10:00 +0200 Subject: [PATCH 2/7] feature/BGv1.3 added periodic_payments - step1 --- .../v1_3/PaymentInitiationServicePISApi.scala | 25 ++++++++++++++++++- .../MappedTransactionRequestProvider.scala | 16 +++++++++++- .../commons/model/CommonModel.scala | 21 +++++++++++++--- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala index 84b95f4d89..ebc268ef7e 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala @@ -45,7 +45,7 @@ object APIMethods_PaymentInitiationServicePISApi extends RestHelper { def checkPaymentServerError(paymentService: String) = { val ccc = "" - s"${InvalidTransactionRequestType.replaceAll("TRANSACTION_REQUEST_TYPE", "PAYMENT_SERVICE in the URL.")}: '${paymentService}'.It should be `payments` for now, will support (bulk-payments, periodic-payments) soon" + s"${InvalidTransactionRequestType.replaceAll("TRANSACTION_REQUEST_TYPE", "PAYMENT_SERVICE in the URL.")}: '${paymentService}'.It should be `payments` or `periodic-payments` for now, will support `bulk-payments` soon" } def checkPaymentProductError(paymentProduct: String) = s"${InvalidTransactionRequestType.replaceAll("TRANSACTION_REQUEST_TYPE", "PAYMENT_PRODUCT in the URL.")}: '${paymentProduct}'.It should be `sepa-credit-transfers`for now, will support (instant-sepa-credit-transfers, target-2-payments, cross-border-credit-transfers) soon." @@ -529,9 +529,15 @@ Check the transaction status of a payment initiation.""", for { (Full(u), callContext) <- authenticatedAccess(cc) _ <- passesPsd2Pisp(callContext) + _ <- NewStyle.function.tryons(checkPaymentServerError(paymentService), 400, callContext) { PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")) } + _ <- Helper.booleanToFuture(failMsg= checkPaymentServerError(paymentService), cc=callContext) { + PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")).equals(PaymentServiceTypes.payments) || + PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")).equals(PaymentServiceTypes.periodic_payments) + } + transactionRequestTypes <- NewStyle.function.tryons(checkPaymentProductError(paymentProduct), 400, callContext) { TransactionRequestTypes.withName(paymentProduct.replaceAll("-", "_").toUpperCase) } @@ -539,6 +545,23 @@ Check the transaction status of a payment initiation.""", transDetailsJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $SepaCreditTransfersBerlinGroupV13 ", 400, callContext) { json.extract[SepaCreditTransfersBerlinGroupV13] } + //If it is periodic_payments, we need to make sure, we have more fileds. + + _ <- Helper.booleanToFuture(s"$InvalidJsonFormat startDate field is missing. ", 400, callContext) { + if(PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")).equals(PaymentServiceTypes.periodic_payments)) { + transDetailsJson.startDate.isDefined + } else { + true + } + } + + _ <- Helper.booleanToFuture(s"$InvalidJsonFormat frequency field is missing. ", 400, callContext) { + if(PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")).equals(PaymentServiceTypes.periodic_payments)) { + transDetailsJson.frequency.isDefined + } else { + true + } + } transDetailsSerialized <- NewStyle.function.tryons(s"$UnknownError Can not serialize in request Json ", 400, callContext) { write(transDetailsJson)(Serialization.formats(NoTypeHints)) diff --git a/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala b/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala index c10aebc46c..517d056b96 100644 --- a/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala +++ b/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala @@ -136,7 +136,14 @@ object MappedTransactionRequestProvider extends TransactionRequestProvider { .mBody_Value_Amount(transactionRequestCommonBody.value.amount) .mBody_Description(transactionRequestCommonBody.description) .mDetails(details) // This is the details / body of the request (contains all fields in the body) + + .mDetails(details) // This is the details / body of the request (contains all fields in the body) + .mPaymentStartDate(now) + .mPaymentEndDate(now) + .mPaymentExecutionRule("") + .mPaymentFrequency("") + .mPaymentDayOfExecution("") .saveMe Full(mappedTransactionRequest).flatMap(_.toTransactionRequest) @@ -235,7 +242,14 @@ class MappedTransactionRequest extends LongKeyedMapper[MappedTransactionRequest] object mOtherBankRoutingScheme extends MappedString(this, 32) object mOtherBankRoutingAddress extends MappedString(this, 64) object mIsBeneficiary extends MappedBoolean(this) - + + //Here are for Berlin Group V1.3 + object mPaymentStartDate extends MappedDate(this) //BGv1.3 Open API Document example value: "startDate":"2024-08-12" + object mPaymentEndDate extends MappedDate(this) //BGv1.3 Open API Document example value: "startDate":"2025-08-01" + object mPaymentExecutionRule extends MappedString(this, 64) //BGv1.3 Open API Document example value: "executionRule":"preceding" + object mPaymentFrequency extends MappedString(this, 64) //BGv1.3 Open API Document example value: "frequency":"Monthly", + object mPaymentDayOfExecution extends MappedString(this, 64)//BGv1.3 Open API Document example value: "dayOfExecution":"01" + def updateStatus(newStatus: String) = { mStatus.set(newStatus) } diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala index b66518b83f..b723b1f423 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala @@ -652,7 +652,7 @@ case class TransactionRequestTransferToAtm( to: ToAccountTransferToAtm ) extends TransactionRequestCommonBodyJSON -//For COUNTERPATY, it need the counterparty_id to find the toCounterpaty--> toBankAccount +//For COUNTERPARTY, it needs the counterparty_id to find the toCounterparty--> toBankAccount case class TransactionRequestCounterpartyId (counterparty_id : String) case class TransactionRequestSimple ( @@ -712,7 +712,12 @@ case class SepaCreditTransfersBerlinGroupV13( //This is from berlinGroup remittanceInformationStructured: Option[String] = None, remittanceInformationStructuredArray: Option[String] = None, requestedExecutionDate: Option[String] = None, - requestedExecutionTime: Option[String] = None + requestedExecutionTime: Option[String] = None, + startDate: Option[String] = None,//this is mandatory for periodic-payments, we have guard in api level + executionRule: Option[String] = None, + endDate: Option[String] = None, + frequency: Option[String] = None, //this is mandatory for periodic-payments, we have guard in api level + dayOfExecution: Option[String] = None, ) case class TransactionRequestBodyAllTypes ( @@ -781,7 +786,17 @@ case class TransactionRequest ( @optional is_beneficiary :Boolean, @optional - future_date :Option[String] = None + future_date :Option[String] = None, + @optional + payment_start_date :Option[Date] = None, + @optional + payment_end_date :Option[Date] = None, + @optional + payment_execution_Rule :Option[String] = None, + @optional + payment_frequency :Option[String] = None, + @optional + payment_day_of_execution :Option[String] = None, ) case class TransactionRequestBody ( val to: TransactionRequestAccount, From 756ea2e662209c6ac541951ee0107fbe3d9e5a2a Mon Sep 17 00:00:00 2001 From: Hongwei Date: Fri, 16 Aug 2024 15:26:24 +0200 Subject: [PATCH 3/7] refactor/tweaked the authenticationRequiredMessage to true for getAdapterInfo endpoint --- obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala | 2 +- obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala | 2 +- obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala index c7a63c729c..76b1bd651f 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala @@ -1064,7 +1064,7 @@ trait APIMethods300 { "Get Adapter Info for a bank", s"""Get basic information about the Adapter listening on behalf of this bank. | - |${authenticationRequiredMessage(false)} + |${authenticationRequiredMessage(true)} | """.stripMargin, EmptyBody, diff --git a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala index 251bff491a..ae7ace2235 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -1011,7 +1011,7 @@ trait APIMethods310 { "Get Adapter Info", s"""Get basic information about the Adapter. | - |${authenticationRequiredMessage(false)} + |${authenticationRequiredMessage(true)} | """.stripMargin, EmptyBody, diff --git a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index d04d5f4374..6b0946b6e3 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala @@ -2361,7 +2361,7 @@ trait APIMethods500 { "Get Adapter Info", s"""Get basic information about the Adapter. | - |${authenticationRequiredMessage(false)} + |${authenticationRequiredMessage(true)} | """.stripMargin, EmptyBody, From d4aa006dacc213e2669ab17c6465590287f967a5 Mon Sep 17 00:00:00 2001 From: Hongwei Date: Tue, 20 Aug 2024 08:28:42 +0200 Subject: [PATCH 4/7] feature/BGv1.3 added periodic_payments - step2 --- .../v1_3/PaymentInitiationServicePISApi.scala | 30 ++--- .../scala/code/bankconnectors/Connector.scala | 3 +- .../bankconnectors/LocalMappedConnector.scala | 6 +- .../code/remotedata/RemotedataActors.scala | 1 - .../RemotedataTransactionRequests.scala | 94 --------------- .../RemotedataTransactionRequestsActor.scala | 110 ------------------ .../MappedTransactionRequestProvider.scala | 19 ++- .../TransactionRequests.scala | 38 +----- 8 files changed, 37 insertions(+), 264 deletions(-) delete mode 100644 obp-api/src/main/scala/code/remotedata/RemotedataTransactionRequests.scala delete mode 100644 obp-api/src/main/scala/code/remotedata/RemotedataTransactionRequestsActor.scala diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala index ebc268ef7e..f0d6499083 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala @@ -542,33 +542,33 @@ Check the transaction status of a payment initiation.""", TransactionRequestTypes.withName(paymentProduct.replaceAll("-", "_").toUpperCase) } - transDetailsJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $SepaCreditTransfersBerlinGroupV13 ", 400, callContext) { + sepaCreditTransfersBerlinGroupV13 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $SepaCreditTransfersBerlinGroupV13 ", 400, callContext) { json.extract[SepaCreditTransfersBerlinGroupV13] } //If it is periodic_payments, we need to make sure, we have more fileds. _ <- Helper.booleanToFuture(s"$InvalidJsonFormat startDate field is missing. ", 400, callContext) { if(PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")).equals(PaymentServiceTypes.periodic_payments)) { - transDetailsJson.startDate.isDefined + sepaCreditTransfersBerlinGroupV13.startDate.isDefined } else { true } } - + _ <- Helper.booleanToFuture(s"$InvalidJsonFormat frequency field is missing. ", 400, callContext) { if(PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")).equals(PaymentServiceTypes.periodic_payments)) { - transDetailsJson.frequency.isDefined + sepaCreditTransfersBerlinGroupV13.frequency.isDefined } else { true } } transDetailsSerialized <- NewStyle.function.tryons(s"$UnknownError Can not serialize in request Json ", 400, callContext) { - write(transDetailsJson)(Serialization.formats(NoTypeHints)) + write(sepaCreditTransfersBerlinGroupV13)(Serialization.formats(NoTypeHints)) } - isValidAmountNumber <- NewStyle.function.tryons(s"$InvalidNumber Current input is ${transDetailsJson.instructedAmount.amount} ", 400, callContext) { - BigDecimal(transDetailsJson.instructedAmount.amount) + isValidAmountNumber <- NewStyle.function.tryons(s"$InvalidNumber Current input is ${sepaCreditTransfersBerlinGroupV13.instructedAmount.amount} ", 400, callContext) { + BigDecimal(sepaCreditTransfersBerlinGroupV13.instructedAmount.amount) } _ <- Helper.booleanToFuture(s"${NotPositiveAmount} Current input is: '${isValidAmountNumber}'", cc = callContext) { @@ -576,13 +576,13 @@ Check the transaction status of a payment initiation.""", } // Prevent default value for transaction request type (at least). - _ <- Helper.booleanToFuture(s"${InvalidISOCurrencyCode} Current input is: '${transDetailsJson.instructedAmount.currency}'", cc = callContext) { - isValidCurrencyISOCode(transDetailsJson.instructedAmount.currency) + _ <- Helper.booleanToFuture(s"${InvalidISOCurrencyCode} Current input is: '${sepaCreditTransfersBerlinGroupV13.instructedAmount.currency}'", cc = callContext) { + isValidCurrencyISOCode(sepaCreditTransfersBerlinGroupV13.instructedAmount.currency) } _ <- NewStyle.function.isEnabledTransactionRequests(callContext) - fromAccountIban = transDetailsJson.debtorAccount.iban - toAccountIban = transDetailsJson.creditorAccount.iban + fromAccountIban = sepaCreditTransfersBerlinGroupV13.debtorAccount.iban + toAccountIban = sepaCreditTransfersBerlinGroupV13.creditorAccount.iban (fromAccount, callContext) <- NewStyle.function.getBankAccountByIban(fromAccountIban, callContext) (ibanChecker, callContext) <- NewStyle.function.validateAndCheckIbanNumber(toAccountIban, callContext) @@ -601,11 +601,11 @@ Check the transaction status of a payment initiation.""", NewStyle.function.hasEntitlement(fromAccount.bankId.value, u.userId, ApiRole.canCreateAnyTransactionRequest, callContext, InsufficientAuthorisationToCreateTransactionRequest) // Prevent default value for transaction request type (at least). - _ <- Helper.booleanToFuture(s"From Account Currency is ${fromAccount.currency}, but Requested Transaction Currency is: ${transDetailsJson.instructedAmount.currency}", cc = callContext) { - transDetailsJson.instructedAmount.currency == fromAccount.currency + _ <- Helper.booleanToFuture(s"From Account Currency is ${fromAccount.currency}, but Requested Transaction Currency is: ${sepaCreditTransfersBerlinGroupV13.instructedAmount.currency}", cc = callContext) { + sepaCreditTransfersBerlinGroupV13.instructedAmount.currency == fromAccount.currency } - amountOfMoneyJSON = transDetailsJson.instructedAmount + amountOfMoneyJSON = sepaCreditTransfersBerlinGroupV13.instructedAmount (createdTransactionRequest, callContext) <- transactionRequestTypes match { case TransactionRequestTypes.SEPA_CREDIT_TRANSFERS => { @@ -625,7 +625,7 @@ Check the transaction status of a payment initiation.""", Some(BERLIN_GROUP_PAYMENT_CHALLENGE), None, None, - Some(transDetailsJson), + Some(sepaCreditTransfersBerlinGroupV13), callContext ) //in SANDBOX_TAN, ChargePolicy set default "SHARED" } yield (createdTransactionRequest, callContext) diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index a4c3ebdc5d..126c8e597c 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -1191,7 +1191,8 @@ trait Connector extends MdcLoggable { details: String, status: String, charge: TransactionRequestCharge, - chargePolicy: String): Box[TransactionRequest] = Failure(setUnimplementedError) + chargePolicy: String, + berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]): Box[TransactionRequest] = Failure(setUnimplementedError) def notifyTransactionRequest(fromAccount: BankAccount, toAccount: BankAccount, transactionRequest: TransactionRequest, callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequestStatusValue]] = Future{(Failure(setUnimplementedError), callContext)} diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index b6ea170f10..3fed007cf3 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -2234,7 +2234,8 @@ object LocalMappedConnector extends Connector with MdcLoggable { details: String, status: String, charge: TransactionRequestCharge, - chargePolicy: String): Box[TransactionRequest] = { + chargePolicy: String, + berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]): Box[TransactionRequest] = { TransactionRequests.transactionRequestProvider.vend.createTransactionRequestImpl210(transactionRequestId, transactionRequestType, @@ -2244,7 +2245,8 @@ object LocalMappedConnector extends Connector with MdcLoggable { details, status, charge, - chargePolicy) + chargePolicy, + berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]) } override def saveTransactionRequestTransactionImpl(transactionRequestId: TransactionRequestId, transactionId: TransactionId): Box[Boolean] = { diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataActors.scala b/obp-api/src/main/scala/code/remotedata/RemotedataActors.scala index 47c86c697e..43dc2bcb50 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataActors.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataActors.scala @@ -39,7 +39,6 @@ object RemotedataActors extends MdcLoggable { ActorProps[RemotedataCustomerDependantsActor] -> RemotedataCustomerDependants.actorName, ActorProps[RemotedataUserCustomerLinksActor] -> RemotedataUserCustomerLinks.actorName, ActorProps[RemotedataConsumersActor] -> RemotedataConsumers.actorName, - ActorProps[RemotedataTransactionRequestsActor] -> RemotedataTransactionRequests.actorName, ActorProps[RemotedataMetricsActor] -> RemotedataMetrics.actorName, ActorProps[RemotedataTokensActor] -> RemotedataTokens.actorName, ActorProps[RemotedataNoncesActor] -> RemotedataNonces.actorName, diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataTransactionRequests.scala b/obp-api/src/main/scala/code/remotedata/RemotedataTransactionRequests.scala deleted file mode 100644 index 76b9329d43..0000000000 --- a/obp-api/src/main/scala/code/remotedata/RemotedataTransactionRequests.scala +++ /dev/null @@ -1,94 +0,0 @@ -package code.remotedata - -import akka.pattern.ask -import code.actorsystem.ObpActorInit - -import com.openbankproject.commons.model.{TransactionRequest, TransactionRequestChallenge, TransactionRequestCharge, _} -import code.transactionrequests.{MappedTransactionRequest, RemotedataTransactionRequestsCaseClasses, TransactionRequestProvider} - -import net.liftweb.common.Box - -object RemotedataTransactionRequests extends ObpActorInit with TransactionRequestProvider { - - val cc = RemotedataTransactionRequestsCaseClasses - - def getMappedTransactionRequest(transactionRequestId: TransactionRequestId): Box[MappedTransactionRequest] = getValueFromFuture( - (actor ? cc.getMappedTransactionRequest(transactionRequestId)).mapTo[Box[MappedTransactionRequest]] - ) - - def getTransactionRequestsFromProvider(bankId: BankId, accountId: AccountId): Box[List[TransactionRequest]] = getValueFromFuture( - (actor ? cc.getTransactionRequestsFromProvider(bankId, accountId)).mapTo[Box[List[TransactionRequest]]] - ) - - def getTransactionRequestFromProvider(transactionRequestId: TransactionRequestId): Box[TransactionRequest] = getValueFromFuture( - (actor ? cc.getTransactionRequestFromProvider(transactionRequestId)).mapTo[Box[TransactionRequest]] - ) - - def updateAllPendingTransactionRequests(): Box[Option[Unit]] = getValueFromFuture( - (actor ? cc.updateAllPendingTransactionRequests()).mapTo[ Box[Option[Unit]]] - ) - - def createTransactionRequestImpl(transactionRequestId: TransactionRequestId, - transactionRequestType: TransactionRequestType, - account: BankAccount, - counterparty: BankAccount, - body: TransactionRequestBody, - status: String, - charge: TransactionRequestCharge): Box[TransactionRequest] = getValueFromFuture( - (actor ? cc.createTransactionRequestImpl( - transactionRequestId, - transactionRequestType, - account, - counterparty, - body, - status, - charge)).mapTo[Box[TransactionRequest]] - ) - - def createTransactionRequestImpl210(transactionRequestId: TransactionRequestId, - transactionRequestType: TransactionRequestType, - fromAccount: BankAccount, - toAccount: BankAccount, - transactionRequestCommonBody: TransactionRequestCommonBodyJSON, - details: String, - status: String, - charge: TransactionRequestCharge, - chargePolicy: String): Box[TransactionRequest] = getValueFromFuture( - (actor ? cc.createTransactionRequestImpl210( - transactionRequestId, - transactionRequestType, - fromAccount, - toAccount, - transactionRequestCommonBody, - details, - status, - charge, - chargePolicy)).mapTo[Box[TransactionRequest]] - ) - - def saveTransactionRequestTransactionImpl(transactionRequestId: TransactionRequestId, transactionId: TransactionId): Box[Boolean] = getValueFromFuture( - (actor ? cc.saveTransactionRequestTransactionImpl(transactionRequestId, transactionId)).mapTo[Box[Boolean]] - ) - - def saveTransactionRequestChallengeImpl(transactionRequestId: TransactionRequestId, challenge: TransactionRequestChallenge): Box[Boolean] = getValueFromFuture( - (actor ? cc.saveTransactionRequestChallengeImpl(transactionRequestId, challenge)).mapTo[Box[Boolean]] - ) - - def saveTransactionRequestStatusImpl(transactionRequestId: TransactionRequestId, status: String): Box[Boolean] = getValueFromFuture( - (actor ? cc.saveTransactionRequestStatusImpl(transactionRequestId, status)).mapTo[Box[Boolean]] - ) - - def saveTransactionRequestDescriptionImpl(transactionRequestId: TransactionRequestId, description: String): Box[Boolean] = getValueFromFuture( - (actor ? cc.saveTransactionRequestDescriptionImpl(transactionRequestId, description)).mapTo[Box[Boolean]] - ) - - def bulkDeleteTransactionRequests(): Boolean = getValueFromFuture( - (actor ? cc.bulkDeleteTransactionRequests()).mapTo[Boolean] - ) - - override def bulkDeleteTransactionRequestsByTransactionId(transactionId: TransactionId): Boolean = getValueFromFuture( - (actor ? cc.bulkDeleteTransactionRequestsByTransactionId(transactionId)).mapTo[Boolean] - ) - - -} diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataTransactionRequestsActor.scala b/obp-api/src/main/scala/code/remotedata/RemotedataTransactionRequestsActor.scala deleted file mode 100644 index ee6d25f4ec..0000000000 --- a/obp-api/src/main/scala/code/remotedata/RemotedataTransactionRequestsActor.scala +++ /dev/null @@ -1,110 +0,0 @@ -package code.remotedata - -import akka.actor.Actor -import code.actorsystem.ObpActorHelper -import code.transactionrequests.{MappedTransactionRequestProvider, RemotedataTransactionRequestsCaseClasses} -import code.util.Helper.MdcLoggable -import com.openbankproject.commons.model._ - - -class RemotedataTransactionRequestsActor extends Actor with ObpActorHelper with MdcLoggable { - - val mapper = MappedTransactionRequestProvider - val cc = RemotedataTransactionRequestsCaseClasses - - def receive = { - - case cc.getMappedTransactionRequest(transactionRequestId: TransactionRequestId) => - logger.debug("getMappedTransactionRequest(" + transactionRequestId + ")") - sender ! (mapper.getMappedTransactionRequest(transactionRequestId)) - - case cc.getTransactionRequestsFromProvider(bankId: BankId, accountId: AccountId) => - logger.debug("getTransactionRequestsFromProvider(" + bankId + ", " + accountId + ", " + ")") - sender ! (mapper.getTransactionRequestsFromProvider(bankId, accountId)) - - case cc.getTransactionRequestFromProvider(transactionRequestId: TransactionRequestId) => - logger.debug("getTransactionRequestFromProvider(" + transactionRequestId + ")") - sender ! (mapper.getTransactionRequestFromProvider(transactionRequestId)) - - case cc.updateAllPendingTransactionRequests() => - logger.debug("updateAllPendingTransactionRequests()") - sender ! (mapper.updateAllPendingTransactionRequests) - - case cc.createTransactionRequestImpl(transactionRequestId: TransactionRequestId, - transactionRequestType: TransactionRequestType, - account: BankAccount, - counterparty: BankAccount, - body: TransactionRequestBody, - status: String, - charge: TransactionRequestCharge) => - logger.debug("createTransactionRequestImpl(" + transactionRequestId + ", " + - transactionRequestType + ", " + - account + ", " + - counterparty + ", " + - body + ", " + - status + ", " + - charge + ", " + - ")") - sender ! (mapper.createTransactionRequestImpl(transactionRequestId, - transactionRequestType, - account, - counterparty, - body, - status, - charge)) - - case cc.createTransactionRequestImpl210(transactionRequestId: TransactionRequestId, - transactionRequestType: TransactionRequestType, - fromAccount: BankAccount, - toAccount: BankAccount, - transactionRequestCommonBody: TransactionRequestCommonBodyJSON, - details: String, - status: String, - charge: TransactionRequestCharge, - chargePolicy: String) => - logger.debug("createTransactionRequestImpl210(" + transactionRequestId + ", " + - transactionRequestType + ", " + - fromAccount + ", " + - toAccount + ", " + - transactionRequestCommonBody + ", " + - details + ", " + - status + ", " + - charge + ", " + - chargePolicy + ", " + - ")") - sender ! (mapper.createTransactionRequestImpl210(transactionRequestId, - transactionRequestType, - fromAccount, - toAccount, - transactionRequestCommonBody, - details, - status, - charge, - chargePolicy)) - - case cc.saveTransactionRequestTransactionImpl(transactionRequestId: TransactionRequestId, transactionId: TransactionId) => - logger.debug("saveTransactionRequestTransactionImpl(" + transactionRequestId + ", " + transactionId + ")") - sender ! (mapper.saveTransactionRequestTransactionImpl(transactionRequestId, transactionId)) - - case cc.saveTransactionRequestChallengeImpl(transactionRequestId: TransactionRequestId, challenge: TransactionRequestChallenge) => - logger.debug("saveTransactionRequestChallengeImpl(" + transactionRequestId + ", " + challenge + ")") - sender ! (mapper.saveTransactionRequestChallengeImpl(transactionRequestId, challenge)) - - case cc.saveTransactionRequestStatusImpl(transactionRequestId: TransactionRequestId, status: String) => - logger.debug("saveTransactionRequestStatusImpl(" + transactionRequestId + ", " + status + ")") - sender ! (mapper.saveTransactionRequestStatusImpl(transactionRequestId, status)) - - case cc.bulkDeleteTransactionRequests() => - logger.debug("bulkDeleteTransactionRequests()") - sender ! (mapper.bulkDeleteTransactionRequests()) - - case cc.bulkDeleteTransactionRequestsByTransactionId(transactionId: TransactionId) => - logger.debug(s"bulkDeleteTransactionRequestsByTransactionId($transactionId)") - sender ! (mapper.bulkDeleteTransactionRequestsByTransactionId(transactionId)) - - case message => logger.warn("[AKKA ACTOR ERROR - REQUEST NOT RECOGNIZED] " + message) - - } - -} - diff --git a/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala b/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala index 517d056b96..2bfdaf630d 100644 --- a/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala +++ b/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala @@ -1,5 +1,6 @@ package code.transactionrequests +import code.api.util.APIUtil.DateWithMsFormat import code.api.util.CustomJsonFormats import code.api.util.ErrorMessages._ import code.bankconnectors.Connector @@ -14,6 +15,8 @@ import net.liftweb.json.JsonAST.{JField, JObject, JString} import net.liftweb.mapper._ import net.liftweb.util.Helpers._ +import java.text.SimpleDateFormat + object MappedTransactionRequestProvider extends TransactionRequestProvider { private val logger = Logger(classOf[TransactionRequestProvider]) @@ -84,7 +87,8 @@ object MappedTransactionRequestProvider extends TransactionRequestProvider { details: String, status: String, charge: TransactionRequestCharge, - chargePolicy: String): Box[TransactionRequest] = { + chargePolicy: String, + berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]): Box[TransactionRequest] = { val toAccountRouting = transactionRequestType.value match { case "SEPA" => @@ -93,6 +97,9 @@ object MappedTransactionRequestProvider extends TransactionRequestProvider { case _ => toAccount.accountRoutings.headOption } + val paymentStartDate = berlinGroupPayments.flatMap(_.startDate).map(DateWithMsFormat.parse) + val paymentEndDate = berlinGroupPayments.flatMap(_.endDate).map(DateWithMsFormat.parse) + // Note: We don't save transaction_ids, status and challenge here. val mappedTransactionRequest = MappedTransactionRequest.create @@ -139,11 +146,11 @@ object MappedTransactionRequestProvider extends TransactionRequestProvider { .mDetails(details) // This is the details / body of the request (contains all fields in the body) - .mPaymentStartDate(now) - .mPaymentEndDate(now) - .mPaymentExecutionRule("") - .mPaymentFrequency("") - .mPaymentDayOfExecution("") + .mPaymentStartDate(paymentStartDate.orNull) + .mPaymentEndDate(paymentEndDate.orNull) + .mPaymentExecutionRule(berlinGroupPayments.flatMap(_.executionRule).orNull) + .mPaymentFrequency(berlinGroupPayments.flatMap(_.frequency).orNull) + .mPaymentDayOfExecution(berlinGroupPayments.flatMap(_.dayOfExecution).orNull) .saveMe Full(mappedTransactionRequest).flatMap(_.toTransactionRequest) diff --git a/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala b/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala index 662dc1fab8..30d4ebada6 100644 --- a/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala +++ b/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala @@ -2,7 +2,6 @@ package code.transactionrequests import code.api.util.APIUtil -import code.remotedata.RemotedataTransactionRequests import com.openbankproject.commons.model.{TransactionRequest, TransactionRequestChallenge, TransactionRequestCharge, _} import net.liftweb.common.{Box, Logger} import net.liftweb.util.SimpleInjector @@ -29,10 +28,7 @@ object TransactionRequests extends SimpleInjector { def buildOne: TransactionRequestProvider = APIUtil.getPropsValue("transactionRequests_connector", "mapped") match { - case "mapped" => APIUtil.getPropsAsBoolValue("use_akka", false) match { - case false => MappedTransactionRequestProvider - case true => RemotedataTransactionRequests // We will use Akka as a middleware - } + case "mapped" => MappedTransactionRequestProvider case tc: String => throw new IllegalArgumentException("No such connector for Transaction Requests: " + tc) } @@ -78,7 +74,8 @@ trait TransactionRequestProvider { details: String, status: String, charge: TransactionRequestCharge, - chargePolicy: String): Box[TransactionRequest] + chargePolicy: String, + berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]): Box[TransactionRequest] def saveTransactionRequestTransactionImpl(transactionRequestId: TransactionRequestId, transactionId: TransactionId): Box[Boolean] def saveTransactionRequestChallengeImpl(transactionRequestId: TransactionRequestId, challenge: TransactionRequestChallenge): Box[Boolean] def saveTransactionRequestStatusImpl(transactionRequestId: TransactionRequestId, status: String): Box[Boolean] @@ -87,33 +84,4 @@ trait TransactionRequestProvider { def bulkDeleteTransactionRequests(): Boolean } -class RemotedataTransactionRequestsCaseClasses { - case class getMappedTransactionRequest(transactionRequestId: TransactionRequestId) - case class getTransactionRequestsFromProvider(bankId : BankId, accountId: AccountId) - case class getTransactionRequestFromProvider(transactionRequestId : TransactionRequestId) - case class updateAllPendingTransactionRequests() - case class createTransactionRequestImpl(transactionRequestId: TransactionRequestId, - transactionRequestType: TransactionRequestType, - account : BankAccount, - counterparty : BankAccount, - body: TransactionRequestBody, - status: String, - charge: TransactionRequestCharge) - case class createTransactionRequestImpl210(transactionRequestId: TransactionRequestId, - transactionRequestType: TransactionRequestType, - fromAccount: BankAccount, - toAccount: BankAccount, - transactionRequestCommonBody: TransactionRequestCommonBodyJSON, - details: String, - status: String, - charge: TransactionRequestCharge, - chargePolicy: String) - case class saveTransactionRequestTransactionImpl(transactionRequestId: TransactionRequestId, transactionId: TransactionId) - case class saveTransactionRequestChallengeImpl(transactionRequestId: TransactionRequestId, challenge: TransactionRequestChallenge) - case class saveTransactionRequestStatusImpl(transactionRequestId: TransactionRequestId, status: String) - case class saveTransactionRequestDescriptionImpl(transactionRequestId: TransactionRequestId, description: String) - case class bulkDeleteTransactionRequestsByTransactionId(transactionId: TransactionId) - case class bulkDeleteTransactionRequests() -} -object RemotedataTransactionRequestsCaseClasses extends RemotedataTransactionRequestsCaseClasses \ No newline at end of file From ea3edbe85b7a378f45bfd6f6d5a449b221698e64 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 20 Aug 2024 08:44:51 +0200 Subject: [PATCH 5/7] feature/BGv1.3 added periodic_payments - step3 --- .../scala/code/bankconnectors/Connector.scala | 42 ++++++----------- .../bankconnectors/ConnectorBuilderUtil.scala | 1 - .../bankconnectors/LocalMappedConnector.scala | 47 +++++++++---------- .../TransactionRequests.scala | 17 ++++++- .../commons/dto/JsonsTransfer.scala | 5 -- 5 files changed, 53 insertions(+), 59 deletions(-) diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 126c8e597c..fc832dd842 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -1076,7 +1076,19 @@ trait Connector extends MdcLoggable { chargeValue <- getChargeValue(chargeLevelAmount,transactionRequestCommonBodyAmount) charge = TransactionRequestCharge("Total charges for completed transaction", AmountOfMoney(transactionRequestCommonBody.value.currency, chargeValue)) // Always create a new Transaction Request - transactionRequest <- Future{ createTransactionRequestImpl210(TransactionRequestId(generateUUID()), transactionRequestType, fromAccount, toAccount, transactionRequestCommonBody, detailsPlain, status.toString, charge, chargePolicy)} map { + transactionRequest <- Future{ + TransactionRequests.transactionRequestProvider.vend.createTransactionRequestImpl210( + TransactionRequestId(generateUUID()), + transactionRequestType, + fromAccount, + toAccount, + transactionRequestCommonBody, + detailsPlain, + status.toString, + charge, + chargePolicy, + ) + } map { unboxFullOrFail(_, callContext, s"$InvalidConnectorResponseForCreateTransactionRequestImpl210") } @@ -1100,7 +1112,7 @@ trait Connector extends MdcLoggable { //save transaction_id into database _ <- Future {saveTransactionRequestTransaction(transactionRequest.id, createdTransactionId)} - //update transaction_id field for varibale 'transactionRequest' + //update transaction_id field for variable 'transactionRequest' transactionRequest <- Future(transactionRequest.copy(transaction_ids = createdTransactionId.value)) } yield { @@ -1165,35 +1177,11 @@ trait Connector extends MdcLoggable { berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = Future{(Failure(setUnimplementedError), callContext)} - //place holder for various connector methods that overwrite methods like these, does the actual data access + //placeholder for various connector methods that overwrite methods like these, does the actual data access protected def createTransactionRequestImpl(transactionRequestId: TransactionRequestId, transactionRequestType: TransactionRequestType, fromAccount : BankAccount, counterparty : BankAccount, body: TransactionRequestBody, status: String, charge: TransactionRequestCharge) : Box[TransactionRequest] = Failure(setUnimplementedError) - /** - * - * @param transactionRequestId - * @param transactionRequestType Support Types: SANDBOX_TAN, FREE_FORM, SEPA and COUNTERPARTY - * @param fromAccount - * @param toAccount - * @param transactionRequestCommonBody Body from http request: should have common fields: - * @param details This is the details / body of the request (contains all fields in the body) - * @param status "INITIATED" "PENDING" "FAILED" "COMPLETED" - * @param charge - * @param chargePolicy SHARED, SENDER, RECEIVER - * @return Always create a new Transaction Request in mapper, and return all the fields - */ - protected def createTransactionRequestImpl210(transactionRequestId: TransactionRequestId, - transactionRequestType: TransactionRequestType, - fromAccount: BankAccount, - toAccount: BankAccount, - transactionRequestCommonBody: TransactionRequestCommonBodyJSON, - details: String, - status: String, - charge: TransactionRequestCharge, - chargePolicy: String, - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]): Box[TransactionRequest] = Failure(setUnimplementedError) - def notifyTransactionRequest(fromAccount: BankAccount, toAccount: BankAccount, transactionRequest: TransactionRequest, callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequestStatusValue]] = Future{(Failure(setUnimplementedError), callContext)} diff --git a/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala b/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala index 937fa0e9c3..a5abd178fc 100644 --- a/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala +++ b/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala @@ -434,7 +434,6 @@ object ConnectorBuilderUtil { val protectedMethods = List( "makePaymentImpl", "createTransactionRequestImpl", - "createTransactionRequestImpl210", "saveTransactionRequestTransactionImpl", "saveTransactionRequestChallengeImpl", "saveTransactionRequestStatusImpl", diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index 3fed007cf3..23d5514150 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -2226,29 +2226,6 @@ object LocalMappedConnector extends Connector with MdcLoggable { charge) } - override def createTransactionRequestImpl210(transactionRequestId: TransactionRequestId, - transactionRequestType: TransactionRequestType, - fromAccount: BankAccount, - toAccount: BankAccount, - transactionRequestCommonBody: TransactionRequestCommonBodyJSON, - details: String, - status: String, - charge: TransactionRequestCharge, - chargePolicy: String, - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]): Box[TransactionRequest] = { - - TransactionRequests.transactionRequestProvider.vend.createTransactionRequestImpl210(transactionRequestId, - transactionRequestType, - fromAccount, - toAccount, - transactionRequestCommonBody, - details, - status, - charge, - chargePolicy, - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]) - } - override def saveTransactionRequestTransactionImpl(transactionRequestId: TransactionRequestId, transactionId: TransactionId): Box[Boolean] = { TransactionRequests.transactionRequestProvider.vend.saveTransactionRequestTransactionImpl(transactionRequestId, transactionId) } @@ -5026,7 +5003,17 @@ object LocalMappedConnector extends Connector with MdcLoggable { charge = TransactionRequestCharge("Total charges for completed transaction", AmountOfMoney(transactionRequestCommonBody.value.currency, chargeValue)) // Always create a new Transaction Request transactionRequest <- Future { - createTransactionRequestImpl210(TransactionRequestId(generateUUID()), transactionRequestType, fromAccount, toAccount, transactionRequestCommonBody, detailsPlain, status.toString, charge, chargePolicy) + TransactionRequests.transactionRequestProvider.vend.createTransactionRequestImpl210( + TransactionRequestId(generateUUID()), + transactionRequestType, + fromAccount, + toAccount, + transactionRequestCommonBody, + detailsPlain, + status.toString, + charge, + chargePolicy, + ) } map { unboxFullOrFail(_, callContext, s"$InvalidConnectorResponseForCreateTransactionRequestImpl210") } @@ -5171,7 +5158,17 @@ object LocalMappedConnector extends Connector with MdcLoggable { charge = TransactionRequestCharge("Total charges for completed transaction", AmountOfMoney(transactionRequestCommonBody.value.currency, chargeValue)) // Always create a new Transaction Request transactionRequest <- Future { - val transactionRequest = createTransactionRequestImpl210(TransactionRequestId(generateUUID()), transactionRequestType, fromAccount, toAccount, transactionRequestCommonBody, detailsPlain, status.toString, charge, chargePolicy) + val transactionRequest = TransactionRequests.transactionRequestProvider.vend.createTransactionRequestImpl210( + TransactionRequestId(generateUUID()), + transactionRequestType, + fromAccount, + toAccount, + transactionRequestCommonBody, + detailsPlain, + status.toString, + charge, + chargePolicy, + ) saveTransactionRequestReasons(reasons, transactionRequest) transactionRequest } map { diff --git a/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala b/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala index 30d4ebada6..74a3393ebb 100644 --- a/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala +++ b/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala @@ -66,6 +66,20 @@ trait TransactionRequestProvider { body: TransactionRequestBody, status: String, charge: TransactionRequestCharge) : Box[TransactionRequest] + + /** + * + * @param transactionRequestId + * @param transactionRequestType Support Types: SANDBOX_TAN, FREE_FORM, SEPA and COUNTERPARTY + * @param fromAccount + * @param toAccount + * @param transactionRequestCommonBody Body from http request: should have common fields: + * @param details This is the details / body of the request (contains all fields in the body) + * @param status "INITIATED" "PENDING" "FAILED" "COMPLETED" + * @param charge + * @param chargePolicy SHARED, SENDER, RECEIVER + * @return Always create a new Transaction Request in mapper, and return all the fields + */ def createTransactionRequestImpl210(transactionRequestId: TransactionRequestId, transactionRequestType: TransactionRequestType, fromAccount: BankAccount, @@ -75,7 +89,8 @@ trait TransactionRequestProvider { status: String, charge: TransactionRequestCharge, chargePolicy: String, - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]): Box[TransactionRequest] + berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13] = None): Box[TransactionRequest] + def saveTransactionRequestTransactionImpl(transactionRequestId: TransactionRequestId, transactionId: TransactionId): Box[Boolean] def saveTransactionRequestChallengeImpl(transactionRequestId: TransactionRequestId, challenge: TransactionRequestChallenge): Box[Boolean] def saveTransactionRequestStatusImpl(transactionRequestId: TransactionRequestId, status: String): Box[Boolean] diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala index 916be6c834..2acbf89b05 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala @@ -1099,11 +1099,6 @@ case class InBoundCreateTransactionRequestImpl(status: Status, data: Transaction override val inboundAdapterCallContext: InboundAdapterCallContext = InboundAdapterCallContext() } -case class OutBoundCreateTransactionRequestImpl210(transactionRequestId: TransactionRequestId, transactionRequestType: TransactionRequestType, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, details: String, status: String, charge: TransactionRequestCharge, chargePolicy: String) extends TopicTrait -case class InBoundCreateTransactionRequestImpl210(status: Status, data: TransactionRequest) extends InBoundTrait[TransactionRequest] { - override val inboundAdapterCallContext: InboundAdapterCallContext = InboundAdapterCallContext() -} - case class OutBoundSaveTransactionRequestTransaction(transactionRequestId: TransactionRequestId, transactionId: TransactionId) extends TopicTrait case class InBoundSaveTransactionRequestTransaction(status: Status, data: Boolean) extends InBoundTrait[Boolean] { override val inboundAdapterCallContext: InboundAdapterCallContext = InboundAdapterCallContext() From 2cd1ccac79d796e05ffe652ec6f309da6593a2aa Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 22 Aug 2024 10:24:05 +0200 Subject: [PATCH 6/7] feature/BGv1.3 added periodic_payments - step4 --- .../v1_3/PaymentInitiationServicePISApi.scala | 24 ++--- .../main/scala/code/api/util/NewStyle.scala | 4 +- .../scala/code/bankconnectors/Connector.scala | 3 +- .../bankconnectors/LocalMappedConnector.scala | 4 +- .../akka/AkkaConnector_vDec2018.scala | 2 +- .../rest/RestConnector_vMar2019.scala | 2 +- .../StoredProcedureConnector_vDec2019.scala | 2 +- .../MappedTransactionRequestProvider.scala | 29 ++++-- .../TransactionRequests.scala | 2 +- .../commons/dto/JsonsTransfer.scala | 2 +- .../commons/model/CommonModel.scala | 95 ++++++++++++------- .../commons/model/CommonModelTrait.scala | 29 ++++++ 12 files changed, 131 insertions(+), 67 deletions(-) diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala index f0d6499083..59e54314e8 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala @@ -542,26 +542,16 @@ Check the transaction status of a payment initiation.""", TransactionRequestTypes.withName(paymentProduct.replaceAll("-", "_").toUpperCase) } - sepaCreditTransfersBerlinGroupV13 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $SepaCreditTransfersBerlinGroupV13 ", 400, callContext) { - json.extract[SepaCreditTransfersBerlinGroupV13] - } - //If it is periodic_payments, we need to make sure, we have more fileds. - - _ <- Helper.booleanToFuture(s"$InvalidJsonFormat startDate field is missing. ", 400, callContext) { - if(PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")).equals(PaymentServiceTypes.periodic_payments)) { - sepaCreditTransfersBerlinGroupV13.startDate.isDefined - } else { - true + sepaCreditTransfersBerlinGroupV13 <- if(PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")).equals(PaymentServiceTypes.payments)){ + NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $SepaCreditTransfersBerlinGroupV13 ", 400, callContext) { + json.extract[SepaCreditTransfersBerlinGroupV13] } - } - - _ <- Helper.booleanToFuture(s"$InvalidJsonFormat frequency field is missing. ", 400, callContext) { - if(PaymentServiceTypes.withName(paymentService.replaceAll("-", "_")).equals(PaymentServiceTypes.periodic_payments)) { - sepaCreditTransfersBerlinGroupV13.frequency.isDefined - } else { - true + } else{ + NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $PeriodicSepaCreditTransfersBerlinGroupV13 ", 400, callContext) { + json.extract[PeriodicSepaCreditTransfersBerlinGroupV13] } } + //If it is periodic_payments, we need to make sure, we have more fileds. transDetailsSerialized <- NewStyle.function.tryons(s"$UnknownError Can not serialize in request Json ", 400, callContext) { write(sepaCreditTransfersBerlinGroupV13)(Serialization.formats(NoTypeHints)) diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index d4cb55eeb9..98932bbc92 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -1129,7 +1129,7 @@ object NewStyle extends MdcLoggable{ challengeType: Option[ChallengeType.Value], scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[TransactionRequest] = { Connector.connector.vend.createTransactionRequestv400( @@ -1144,7 +1144,7 @@ object NewStyle extends MdcLoggable{ challengeType = challengeType.map(_.toString), scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext] ) map { i => (unboxFullOrFail(i._1, callContext, s"$InvalidConnectorResponseForGetTransactionRequests210", 400), i._2) diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index fc832dd842..e94f296621 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -1087,6 +1087,7 @@ trait Connector extends MdcLoggable { status.toString, charge, chargePolicy, + None, ) } map { unboxFullOrFail(_, callContext, s"$InvalidConnectorResponseForCreateTransactionRequestImpl210") @@ -1174,7 +1175,7 @@ trait Connector extends MdcLoggable { challengeType: Option[String], scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = Future{(Failure(setUnimplementedError), callContext)} //placeholder for various connector methods that overwrite methods like these, does the actual data access diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index 23d5514150..1b4be83d77 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -5013,6 +5013,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { status.toString, charge, chargePolicy, + None, //berlinGroupPayments this is only from BerlinGroup ) } map { unboxFullOrFail(_, callContext, s"$InvalidConnectorResponseForCreateTransactionRequestImpl210") @@ -5102,7 +5103,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { challengeType: Option[String], scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { for { @@ -5168,6 +5169,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { status.toString, charge, chargePolicy, + berlinGroupPayments ) saveTransactionRequestReasons(reasons, transactionRequest) transactionRequest diff --git a/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala b/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala index c9712ced55..986744bac4 100644 --- a/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala +++ b/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala @@ -2305,7 +2305,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { + override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { import com.openbankproject.commons.dto.{InBoundCreateTransactionRequestv400 => InBound, OutBoundCreateTransactionRequestv400 => OutBound} val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, initiator, viewId, fromAccount, toAccount, transactionRequestType, transactionRequestCommonBody, detailsPlain, chargePolicy, challengeType, scaMethod, reasons, berlinGroupPayments) val response: Future[Box[InBound]] = (southSideActor ? req).mapTo[InBound].recoverWith(recoverFunction).map(Box !! _) diff --git a/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala b/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala index 137023df80..cb85fecf05 100644 --- a/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala +++ b/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala @@ -2446,7 +2446,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { + override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { import com.openbankproject.commons.dto.{InBoundCreateTransactionRequestv400 => InBound, OutBoundCreateTransactionRequestv400 => OutBound} val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, initiator, viewId, fromAccount, toAccount, transactionRequestType, transactionRequestCommonBody, detailsPlain, chargePolicy, challengeType, scaMethod, reasons, berlinGroupPayments) val response: Future[Box[InBound]] = sendRequest[InBound](getUrl(callContext, "createTransactionRequestv400"), HttpMethods.POST, req, callContext) diff --git a/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala b/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala index e0954ea6e9..a3a2d51549 100644 --- a/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala +++ b/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala @@ -2613,7 +2613,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { + override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { import com.openbankproject.commons.dto.{InBoundCreateTransactionRequestv400 => InBound, OutBoundCreateTransactionRequestv400 => OutBound} val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, initiator, viewId, fromAccount, toAccount, transactionRequestType, transactionRequestCommonBody, detailsPlain, chargePolicy, challengeType, scaMethod, reasons, berlinGroupPayments) val response: Future[Box[InBound]] = sendRequest[InBound]("obp_create_transaction_requestv400", req, callContext) diff --git a/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala b/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala index 2bfdaf630d..abb5529876 100644 --- a/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala +++ b/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala @@ -88,7 +88,7 @@ object MappedTransactionRequestProvider extends TransactionRequestProvider { status: String, charge: TransactionRequestCharge, chargePolicy: String, - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]): Box[TransactionRequest] = { + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson]): Box[TransactionRequest] = { val toAccountRouting = transactionRequestType.value match { case "SEPA" => @@ -97,8 +97,21 @@ object MappedTransactionRequestProvider extends TransactionRequestProvider { case _ => toAccount.accountRoutings.headOption } - val paymentStartDate = berlinGroupPayments.flatMap(_.startDate).map(DateWithMsFormat.parse) - val paymentEndDate = berlinGroupPayments.flatMap(_.endDate).map(DateWithMsFormat.parse) + val (paymentStartDate, paymentEndDate, executionRule, frequency, dayOfExecution) = if(transactionRequestType == TransactionRequestType("")){ //TODO, here need to add the paymentService + val paymentFields = berlinGroupPayments.asInstanceOf[Option[PeriodicSepaCreditTransfersBerlinGroupV13]] + + val paymentStartDate = paymentFields.map(_.startDate).map(DateWithMsFormat.parse).orNull + val paymentEndDate = paymentFields.flatMap(_.endDate).map(DateWithMsFormat.parse).orNull + + val executionRule = paymentFields.flatMap(_.executionRule).orNull + val frequency = paymentFields.map(_.frequency).orNull + val dayOfExecution = paymentFields.flatMap(_.dayOfExecution).orNull + + (paymentStartDate, paymentEndDate, executionRule, frequency, dayOfExecution) + } else{ + (null, null, null, null, null) + } + // Note: We don't save transaction_ids, status and challenge here. val mappedTransactionRequest = MappedTransactionRequest.create @@ -146,11 +159,11 @@ object MappedTransactionRequestProvider extends TransactionRequestProvider { .mDetails(details) // This is the details / body of the request (contains all fields in the body) - .mPaymentStartDate(paymentStartDate.orNull) - .mPaymentEndDate(paymentEndDate.orNull) - .mPaymentExecutionRule(berlinGroupPayments.flatMap(_.executionRule).orNull) - .mPaymentFrequency(berlinGroupPayments.flatMap(_.frequency).orNull) - .mPaymentDayOfExecution(berlinGroupPayments.flatMap(_.dayOfExecution).orNull) + .mPaymentStartDate(paymentStartDate) + .mPaymentEndDate(paymentEndDate) + .mPaymentExecutionRule(executionRule) + .mPaymentFrequency(frequency) + .mPaymentDayOfExecution(dayOfExecution) .saveMe Full(mappedTransactionRequest).flatMap(_.toTransactionRequest) diff --git a/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala b/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala index e4869bbb11..6ed093a7c6 100644 --- a/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala +++ b/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala @@ -89,7 +89,7 @@ trait TransactionRequestProvider { status: String, charge: TransactionRequestCharge, chargePolicy: String, - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13] = None): Box[TransactionRequest] + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson]): Box[TransactionRequest] def saveTransactionRequestTransactionImpl(transactionRequestId: TransactionRequestId, transactionId: TransactionId): Box[Boolean] def saveTransactionRequestChallengeImpl(transactionRequestId: TransactionRequestId, challenge: TransactionRequestChallenge): Box[Boolean] diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala index 2acbf89b05..f71ed90171 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala @@ -1091,7 +1091,7 @@ case class InBoundGetChargeValue(status: Status, data: String) extends InBoundTr override val inboundAdapterCallContext: InboundAdapterCallContext = InboundAdapterCallContext() } -case class OutBoundCreateTransactionRequestv400(outboundAdapterCallContext: OutboundAdapterCallContext, initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13]) extends TopicTrait +case class OutBoundCreateTransactionRequestv400(outboundAdapterCallContext: OutboundAdapterCallContext, initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson]) extends TopicTrait case class InBoundCreateTransactionRequestv400(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: TransactionRequest) extends InBoundTrait[TransactionRequest] case class OutBoundCreateTransactionRequestImpl(transactionRequestId: TransactionRequestId, transactionRequestType: TransactionRequestType, fromAccount: BankAccount, counterparty: BankAccount, body: TransactionRequestBody, status: String, charge: TransactionRequestCharge) extends TopicTrait diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala index b723b1f423..1bd331dc7a 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala @@ -686,39 +686,68 @@ case class SepaCreditTransfers( //This is from berlinGroup creditorName: String ) -case class SepaCreditTransfersBerlinGroupV13( //This is from berlinGroup - endToEndIdentification: Option[String] = None, - instructionIdentification: Option[String] = None, - debtorName: Option[String] = None, - debtorAccount: PaymentAccount, - debtorId: Option[String] = None, - ultimateDebtor: Option[String] = None, - instructedAmount: AmountOfMoneyJsonV121, - currencyOfTransfer: Option[String] = None, - exchangeRateInformation: Option[String] = None, - creditorAccount: PaymentAccount, - creditorAgent: Option[String] = None, - creditorAgentName: Option[String] = None, - creditorName: String, - creditorId: Option[String] = None, - creditorAddress: Option[String] = None, - creditorNameAndAddress: Option[String] = None, - ultimateCreditor: Option[String] = None, - purposeCode: Option[String] = None, - chargeBearer: Option[String] = None, - serviceLevel: Option[String] = None, - remittanceInformationUnstructured: Option[String] = None, - remittanceInformationUnstructuredArray: Option[String] = None, - remittanceInformationStructured: Option[String] = None, - remittanceInformationStructuredArray: Option[String] = None, - requestedExecutionDate: Option[String] = None, - requestedExecutionTime: Option[String] = None, - startDate: Option[String] = None,//this is mandatory for periodic-payments, we have guard in api level - executionRule: Option[String] = None, - endDate: Option[String] = None, - frequency: Option[String] = None, //this is mandatory for periodic-payments, we have guard in api level - dayOfExecution: Option[String] = None, - ) +case class SepaCreditTransfersBerlinGroupV13( + endToEndIdentification: Option[String] = None, + instructionIdentification: Option[String] = None, + debtorName: Option[String] = None, + debtorAccount: PaymentAccount, + debtorId: Option[String] = None, + ultimateDebtor: Option[String] = None, + instructedAmount: AmountOfMoneyJsonV121, + currencyOfTransfer: Option[String] = None, + exchangeRateInformation: Option[String] = None, + creditorAccount: PaymentAccount, + creditorAgent: Option[String] = None, + creditorAgentName: Option[String] = None, + creditorName: String, + creditorId: Option[String] = None, + creditorAddress: Option[String] = None, + creditorNameAndAddress: Option[String] = None, + ultimateCreditor: Option[String] = None, + purposeCode: Option[String] = None, + chargeBearer: Option[String] = None, + serviceLevel: Option[String] = None, + remittanceInformationUnstructured: Option[String] = None, + remittanceInformationUnstructuredArray: Option[String] = None, + remittanceInformationStructured: Option[String] = None, + remittanceInformationStructuredArray: Option[String] = None, + requestedExecutionDate: Option[String] = None, + requestedExecutionTime: Option[String] = None +) extends BerlinGroupTransactionRequestCommonBodyJson + +case class PeriodicSepaCreditTransfersBerlinGroupV13( + endToEndIdentification: Option[String] = None, + instructionIdentification: Option[String] = None, + debtorName: Option[String] = None, + debtorAccount: PaymentAccount, + debtorId: Option[String] = None, + ultimateDebtor: Option[String] = None, + instructedAmount: AmountOfMoneyJsonV121, + currencyOfTransfer: Option[String] = None, + exchangeRateInformation: Option[String] = None, + creditorAccount: PaymentAccount, + creditorAgent: Option[String] = None, + creditorAgentName: Option[String] = None, + creditorName: String, + creditorId: Option[String] = None, + creditorAddress: Option[String] = None, + creditorNameAndAddress: Option[String] = None, + ultimateCreditor: Option[String] = None, + purposeCode: Option[String] = None, + chargeBearer: Option[String] = None, + serviceLevel: Option[String] = None, + remittanceInformationUnstructured: Option[String] = None, + remittanceInformationUnstructuredArray: Option[String] = None, + remittanceInformationStructured: Option[String] = None, + remittanceInformationStructuredArray: Option[String] = None, + requestedExecutionDate: Option[String] = None, + requestedExecutionTime: Option[String] = None, + startDate: String, + executionRule: Option[String] = None, + endDate: Option[String] = None, + frequency: String, + dayOfExecution: Option[String] = None, +) extends BerlinGroupTransactionRequestCommonBodyJson case class TransactionRequestBodyAllTypes ( @optional diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala index 06161ca899..c596db9b32 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala @@ -434,6 +434,35 @@ trait TransactionRequestCommonBodyJSON { val description: String } +trait BerlinGroupTransactionRequestCommonBodyJson { + val endToEndIdentification: Option[String] + val instructionIdentification: Option[String] + val debtorName: Option[String] + val debtorAccount: PaymentAccount + val debtorId: Option[String] + val ultimateDebtor: Option[String] + val instructedAmount: AmountOfMoneyJsonV121 + val currencyOfTransfer: Option[String] + val exchangeRateInformation: Option[String] + val creditorAccount: PaymentAccount + val creditorAgent: Option[String] + val creditorAgentName: Option[String] + val creditorName: String + val creditorId: Option[String] + val creditorAddress: Option[String] + val creditorNameAndAddress: Option[String] + val ultimateCreditor: Option[String] + val purposeCode: Option[String] + val chargeBearer: Option[String] + val serviceLevel: Option[String] + val remittanceInformationUnstructured: Option[String] + val remittanceInformationUnstructuredArray: Option[String] + val remittanceInformationStructured: Option[String] + val remittanceInformationStructuredArray: Option[String] + val requestedExecutionDate: Option[String] + val requestedExecutionTime: Option[String] +} + trait Product { def code : ProductCode def parentProductCode : ProductCode From c7398e2ea44f371bd70fababe5b550b093ed5c44 Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 22 Aug 2024 12:40:55 +0200 Subject: [PATCH 7/7] feature/BGv1.3 added periodic_payments - step5 --- .../group/v1_3/PaymentInitiationServicePISApi.scala | 1 + obp-api/src/main/scala/code/api/util/ExampleValue.scala | 3 +++ obp-api/src/main/scala/code/api/util/NewStyle.scala | 2 ++ .../src/main/scala/code/api/v4_0_0/APIMethods400.scala | 8 ++++++++ .../src/main/scala/code/bankconnectors/Connector.scala | 1 + .../scala/code/bankconnectors/LocalMappedConnector.scala | 1 + .../code/bankconnectors/akka/AkkaConnector_vDec2018.scala | 5 +++-- .../code/bankconnectors/rest/RestConnector_vMar2019.scala | 5 +++-- .../StoredProcedureConnector_vDec2019.scala | 5 +++-- .../com/openbankproject/commons/dto/JsonsTransfer.scala | 2 +- 10 files changed, 26 insertions(+), 7 deletions(-) diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala index 59e54314e8..0c4b1722c4 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala @@ -615,6 +615,7 @@ Check the transaction status of a payment initiation.""", Some(BERLIN_GROUP_PAYMENT_CHALLENGE), None, None, + Some(paymentService), Some(sepaCreditTransfersBerlinGroupV13), callContext ) //in SANDBOX_TAN, ChargePolicy set default "SHARED" diff --git a/obp-api/src/main/scala/code/api/util/ExampleValue.scala b/obp-api/src/main/scala/code/api/util/ExampleValue.scala index 716bdc73cc..710535f4c4 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -2275,6 +2275,9 @@ object ExampleValue { lazy val descriptionExample = ConnectorField(s"This an optional field. Maximum length is ${ApiCollection.Description.maxLen}. It can be any characters here.","The human readable description here.") glossaryItems += makeGlossaryItem("description", descriptionExample) + lazy val paymentServiceExample = ConnectorField("payments", s"The berlin group payment services, eg: payments, periodic-payments and bulk-payments. ") + glossaryItems += makeGlossaryItem("paymentService", paymentServiceExample) + lazy val dynamicResourceDocDescriptionExample = ConnectorField("Create one User", "the description for this endpoint") glossaryItems += makeGlossaryItem("DynamicResourceDoc.description", dynamicResourceDocDescriptionExample) diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index 98932bbc92..ab5944ce17 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -1129,6 +1129,7 @@ object NewStyle extends MdcLoggable{ challengeType: Option[ChallengeType.Value], scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], + paymentService: Option[String], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[TransactionRequest] = { @@ -1144,6 +1145,7 @@ object NewStyle extends MdcLoggable{ challengeType = challengeType.map(_.toString), scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], + paymentService: Option[String], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext] ) map { i => diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index 56f25e11a9..61ee3cdd50 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -1096,6 +1096,7 @@ trait APIMethods400 extends MdcLoggable { getScaMethodAtInstance(transactionRequestType.value).toOption, None, None, + None, callContext) //in ACCOUNT, ChargePolicy set default "SHARED" _ <- NewStyle.function.createOrUpdateTransactionRequestAttribute( @@ -1152,6 +1153,7 @@ trait APIMethods400 extends MdcLoggable { getScaMethodAtInstance(transactionRequestType.value).toOption, None, None, + None, callContext) //in ACCOUNT, ChargePolicy set default "SHARED" } yield (createdTransactionRequest, callContext) } @@ -1181,6 +1183,7 @@ trait APIMethods400 extends MdcLoggable { getScaMethodAtInstance(transactionRequestType.value).toOption, None, None, + None, callContext) //in ACCOUNT, ChargePolicy set default "SHARED" } yield (createdTransactionRequest, callContext) } @@ -1216,6 +1219,7 @@ trait APIMethods400 extends MdcLoggable { getScaMethodAtInstance(transactionRequestType.value).toOption, None, None, + None, callContext) } yield (createdTransactionRequest, callContext) } @@ -1248,6 +1252,7 @@ trait APIMethods400 extends MdcLoggable { getScaMethodAtInstance(transactionRequestType.value).toOption, None, None, + None, callContext) } yield (createdTransactionRequest, callContext) @@ -1300,6 +1305,7 @@ trait APIMethods400 extends MdcLoggable { getScaMethodAtInstance(transactionRequestType.value).toOption, None, None, + None, callContext) } yield (createdTransactionRequest, callContext) @@ -1335,6 +1341,7 @@ trait APIMethods400 extends MdcLoggable { getScaMethodAtInstance(transactionRequestType.value).toOption, transDetailsSEPAJson.reasons.map(_.map(_.transform)), None, + None, callContext) } yield (createdTransactionRequest, callContext) } @@ -1360,6 +1367,7 @@ trait APIMethods400 extends MdcLoggable { getScaMethodAtInstance(transactionRequestType.value).toOption, None, None, + None, callContext) } yield (createdTransactionRequest, callContext) diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index e94f296621..5a9b29a8a2 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -1175,6 +1175,7 @@ trait Connector extends MdcLoggable { challengeType: Option[String], scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], + paymentService: Option[String], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = Future{(Failure(setUnimplementedError), callContext)} diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index 1b4be83d77..7633b913ab 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -5103,6 +5103,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { challengeType: Option[String], scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], + paymentService: Option[String], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { diff --git a/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala b/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala index 986744bac4..9f8db54123 100644 --- a/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala +++ b/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala @@ -2196,6 +2196,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { amount=Some(amountExample.value), currency=Some(currencyExample.value), description=Some(descriptionExample.value)))), + paymentService = Some(paymentServiceExample.value), berlinGroupPayments=Some( SepaCreditTransfersBerlinGroupV13(endToEndIdentification=Some("string"), instructionIdentification=Some("string"), debtorName=Some("string"), @@ -2305,9 +2306,9 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { + override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], paymentService: Option[String], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { import com.openbankproject.commons.dto.{InBoundCreateTransactionRequestv400 => InBound, OutBoundCreateTransactionRequestv400 => OutBound} - val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, initiator, viewId, fromAccount, toAccount, transactionRequestType, transactionRequestCommonBody, detailsPlain, chargePolicy, challengeType, scaMethod, reasons, berlinGroupPayments) + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, initiator, viewId, fromAccount, toAccount, transactionRequestType, transactionRequestCommonBody, detailsPlain, chargePolicy, challengeType, scaMethod, reasons, paymentService, berlinGroupPayments) val response: Future[Box[InBound]] = (southSideActor ? req).mapTo[InBound].recoverWith(recoverFunction).map(Box !! _) response.map(convertToTuple[TransactionRequest](callContext)) } diff --git a/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala b/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala index cb85fecf05..7bc7f5d4ce 100644 --- a/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala +++ b/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala @@ -2337,6 +2337,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable amount=Some(amountExample.value), currency=Some(currencyExample.value), description=Some(descriptionExample.value)))), + paymentService = Some(paymentServiceExample.value), berlinGroupPayments=Some( SepaCreditTransfersBerlinGroupV13(endToEndIdentification=Some("string"), instructionIdentification=Some("string"), debtorName=Some("string"), @@ -2446,9 +2447,9 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { + override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], paymentService: Option[String], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { import com.openbankproject.commons.dto.{InBoundCreateTransactionRequestv400 => InBound, OutBoundCreateTransactionRequestv400 => OutBound} - val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, initiator, viewId, fromAccount, toAccount, transactionRequestType, transactionRequestCommonBody, detailsPlain, chargePolicy, challengeType, scaMethod, reasons, berlinGroupPayments) + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, initiator, viewId, fromAccount, toAccount, transactionRequestType, transactionRequestCommonBody, detailsPlain, chargePolicy, challengeType, scaMethod, reasons, paymentService, berlinGroupPayments) val response: Future[Box[InBound]] = sendRequest[InBound](getUrl(callContext, "createTransactionRequestv400"), HttpMethods.POST, req, callContext) response.map(convertToTuple[TransactionRequest](callContext)) } diff --git a/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala b/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala index a3a2d51549..91d807b31a 100644 --- a/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala +++ b/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala @@ -2504,6 +2504,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { amount=Some(amountExample.value), currency=Some(currencyExample.value), description=Some(descriptionExample.value)))), + paymentService = Some(paymentServiceExample.value), berlinGroupPayments=Some( SepaCreditTransfersBerlinGroupV13(endToEndIdentification=Some("string"), instructionIdentification=Some("string"), debtorName=Some("string"), @@ -2613,9 +2614,9 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { + override def createTransactionRequestv400(initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], paymentService: Option[String], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { import com.openbankproject.commons.dto.{InBoundCreateTransactionRequestv400 => InBound, OutBoundCreateTransactionRequestv400 => OutBound} - val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, initiator, viewId, fromAccount, toAccount, transactionRequestType, transactionRequestCommonBody, detailsPlain, chargePolicy, challengeType, scaMethod, reasons, berlinGroupPayments) + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, initiator, viewId, fromAccount, toAccount, transactionRequestType, transactionRequestCommonBody, detailsPlain, chargePolicy, challengeType, scaMethod, reasons, paymentService, berlinGroupPayments) val response: Future[Box[InBound]] = sendRequest[InBound]("obp_create_transaction_requestv400", req, callContext) response.map(convertToTuple[TransactionRequest](callContext)) } diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala index f71ed90171..f8d566a1e4 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala @@ -1091,7 +1091,7 @@ case class InBoundGetChargeValue(status: Status, data: String) extends InBoundTr override val inboundAdapterCallContext: InboundAdapterCallContext = InboundAdapterCallContext() } -case class OutBoundCreateTransactionRequestv400(outboundAdapterCallContext: OutboundAdapterCallContext, initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson]) extends TopicTrait +case class OutBoundCreateTransactionRequestv400(outboundAdapterCallContext: OutboundAdapterCallContext, initiator: User, viewId: ViewId, fromAccount: BankAccount, toAccount: BankAccount, transactionRequestType: TransactionRequestType, transactionRequestCommonBody: TransactionRequestCommonBodyJSON, detailsPlain: String, chargePolicy: String, challengeType: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], reasons: Option[List[TransactionRequestReason]], paymentService: Option[String], berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson]) extends TopicTrait case class InBoundCreateTransactionRequestv400(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: TransactionRequest) extends InBoundTrait[TransactionRequest] case class OutBoundCreateTransactionRequestImpl(transactionRequestId: TransactionRequestId, transactionRequestType: TransactionRequestType, fromAccount: BankAccount, counterparty: BankAccount, body: TransactionRequestBody, status: String, charge: TransactionRequestCharge) extends TopicTrait