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 a953825243..74d28a466e 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,23 +529,36 @@ 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) } - transDetailsJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $SepaCreditTransfersBerlinGroupV13 ", 400, callContext) { - json.extract[SepaCreditTransfersBerlinGroupV13] + 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] + } + } 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(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) { @@ -553,13 +566,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) @@ -575,11 +588,11 @@ Check the transaction status of a payment initiation.""", view.canAddTransactionRequestToAnyAccount } // 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 => { @@ -599,7 +612,8 @@ Check the transaction status of a payment initiation.""", Some(BERLIN_GROUP_PAYMENT_CHALLENGE), None, None, - Some(transDetailsJson), + Some(paymentService), + Some(sepaCreditTransfersBerlinGroupV13), callContext ) //in SANDBOX_TAN, ChargePolicy set default "SHARED" } yield (createdTransactionRequest, callContext) 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 d4cb55eeb9..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,7 +1129,8 @@ object NewStyle extends MdcLoggable{ challengeType: Option[ChallengeType.Value], scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], + paymentService: Option[String], + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[TransactionRequest] = { Connector.connector.vend.createTransactionRequestv400( @@ -1144,7 +1145,8 @@ object NewStyle extends MdcLoggable{ challengeType = challengeType.map(_.toString), scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], + paymentService: Option[String], + 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/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/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index fcf804f28d..88841a027f 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/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index 8d973c2d8b..5c80f49743 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 @@ -975,6 +975,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}" @@ -2362,7 +2364,7 @@ trait APIMethods500 { "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/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index a4c3ebdc5d..5a9b29a8a2 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -1076,7 +1076,20 @@ 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, + None, + ) + } map { unboxFullOrFail(_, callContext, s"$InvalidConnectorResponseForCreateTransactionRequestImpl210") } @@ -1100,7 +1113,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 { @@ -1162,37 +1175,15 @@ trait Connector extends MdcLoggable { challengeType: Option[String], scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], + paymentService: Option[String], + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], 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): 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 b35a61f648..cb5b00d681 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -2236,27 +2236,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): Box[TransactionRequest] = { - - TransactionRequests.transactionRequestProvider.vend.createTransactionRequestImpl210(transactionRequestId, - transactionRequestType, - fromAccount, - toAccount, - transactionRequestCommonBody, - details, - status, - charge, - chargePolicy) - } - override def saveTransactionRequestTransactionImpl(transactionRequestId: TransactionRequestId, transactionId: TransactionId): Box[Boolean] = { TransactionRequests.transactionRequestProvider.vend.saveTransactionRequestTransactionImpl(transactionRequestId, transactionId) } @@ -5034,7 +5013,18 @@ 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, + None, //berlinGroupPayments this is only from BerlinGroup + ) } map { unboxFullOrFail(_, callContext, s"$InvalidConnectorResponseForCreateTransactionRequestImpl210") } @@ -5123,7 +5113,8 @@ object LocalMappedConnector extends Connector with MdcLoggable { challengeType: Option[String], scaMethod: Option[SCA], reasons: Option[List[TransactionRequestReason]], - berlinGroupPayments: Option[SepaCreditTransfersBerlinGroupV13], + paymentService: Option[String], + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson], callContext: Option[CallContext]): OBPReturnType[Box[TransactionRequest]] = { for { @@ -5179,7 +5170,18 @@ 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, + berlinGroupPayments + ) saveTransactionRequestReasons(reasons, transactionRequest) transactionRequest } map { 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..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[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]], 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 137023df80..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[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]], 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 e0954ea6e9..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[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]], 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-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala b/obp-api/src/main/scala/code/transactionrequests/MappedTransactionRequestProvider.scala index c10aebc46c..abb5529876 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[BerlinGroupTransactionRequestCommonBodyJson]): Box[TransactionRequest] = { val toAccountRouting = transactionRequestType.value match { case "SEPA" => @@ -93,6 +97,22 @@ object MappedTransactionRequestProvider extends TransactionRequestProvider { case _ => toAccount.accountRoutings.headOption } + 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 @@ -136,7 +156,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(paymentStartDate) + .mPaymentEndDate(paymentEndDate) + .mPaymentExecutionRule(executionRule) + .mPaymentFrequency(frequency) + .mPaymentDayOfExecution(dayOfExecution) .saveMe Full(mappedTransactionRequest).flatMap(_.toTransactionRequest) @@ -235,7 +262,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-api/src/main/scala/code/transactionrequests/TransactionRequests.scala b/obp-api/src/main/scala/code/transactionrequests/TransactionRequests.scala index 449746ee56..6ed093a7c6 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, @@ -74,11 +88,15 @@ trait TransactionRequestProvider { details: String, status: String, charge: TransactionRequestCharge, - chargePolicy: String): Box[TransactionRequest] + chargePolicy: String, + berlinGroupPayments: Option[BerlinGroupTransactionRequestCommonBodyJson]): 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] def saveTransactionRequestDescriptionImpl(transactionRequestId: TransactionRequestId, description: String): Box[Boolean] def bulkDeleteTransactionRequestsByTransactionId(transactionId: TransactionId): Boolean def bulkDeleteTransactionRequests(): Boolean -} \ No newline at end of file +} + + 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..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[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]], 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 @@ -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() 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..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 @@ -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 ( @@ -686,34 +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 - ) +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 @@ -781,7 +815,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, 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