mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 11:06:49 +00:00
Merge pull request #2214 from hongwei1/develop
feature/OBPv510 added new atms endpoints
This commit is contained in:
commit
9ed2eac430
@ -36,7 +36,7 @@ import com.openbankproject.commons.util.{ApiVersion, FieldNameApiVersions, Refle
|
||||
import net.liftweb.json
|
||||
import java.net.URLEncoder
|
||||
|
||||
import code.api.v5_1_0.{AtmAttributeJsonV510, AtmAttributeResponseJsonV510, CertificateInfoJsonV510, CurrenciesJsonV510, CurrencyJsonV510}
|
||||
import code.api.v5_1_0.{AtmsJsonV510, _}
|
||||
import code.endpointMapping.EndpointMappingCommons
|
||||
|
||||
import scala.collection.immutable.List
|
||||
@ -4018,13 +4018,16 @@ object SwaggerDefinitionsJSON {
|
||||
val atmAttributeResponseJsonV510 = AtmAttributeResponseJsonV510(
|
||||
bank_id = bankIdExample.value,
|
||||
atm_id = atmIdExample.value,
|
||||
atm_attribute_id = "613c83ea-80f9-4560-8404-b9cd4ec42a7f",
|
||||
name = "OVERDRAFT_START_DATE",
|
||||
`type` = "DATE_WITH_DAY",
|
||||
value = "2012-04-23",
|
||||
is_active = Some(true)
|
||||
atm_attribute_id = atmAttributeIdExample.value,
|
||||
name = nameExample.value,
|
||||
`type` = typeExample.value,
|
||||
value = valueExample.value,
|
||||
is_active = Some(activeExample.value.toBoolean)
|
||||
)
|
||||
|
||||
val atmAttributesResponseJsonV510 = AtmAttributesResponseJsonV510(
|
||||
List(atmAttributeResponseJsonV510)
|
||||
)
|
||||
|
||||
|
||||
val accountAttributeJson = AccountAttributeJson(
|
||||
name = "OVERDRAFT_START_DATE",
|
||||
@ -5284,7 +5287,48 @@ object SwaggerDefinitionsJSON {
|
||||
total_duration = BigDecimal(durationExample.value),
|
||||
backend_messages= List(inboundStatusMessage),
|
||||
)
|
||||
|
||||
|
||||
val atmJsonV510 = AtmJsonV510(
|
||||
id = Some(atmIdExample.value),
|
||||
bank_id = bankIdExample.value,
|
||||
name = atmNameExample.value,
|
||||
address = addressJsonV300,
|
||||
location = locationJson,
|
||||
meta = metaJson,
|
||||
monday = openingTimesV300,
|
||||
tuesday = openingTimesV300,
|
||||
wednesday = openingTimesV300,
|
||||
thursday = openingTimesV300,
|
||||
friday = openingTimesV300,
|
||||
saturday = openingTimesV300,
|
||||
sunday = openingTimesV300,
|
||||
is_accessible = isAccessibleExample.value,
|
||||
located_at = locatedAtExample.value,
|
||||
more_info = moreInfoExample.value,
|
||||
has_deposit_capability = hasDepositCapabilityExample.value,
|
||||
supported_languages = supportedLanguagesJson.supported_languages,
|
||||
services = atmServicesJson.services,
|
||||
accessibility_features = accessibilityFeaturesJson.accessibility_features,
|
||||
supported_currencies = supportedCurrenciesJson.supported_currencies,
|
||||
notes = atmNotesJson.notes,
|
||||
location_categories = atmLocationCategoriesJsonV400.location_categories,
|
||||
minimum_withdrawal = atmMinimumWithdrawalExample.value,
|
||||
branch_identification = atmBranchIdentificationExample.value,
|
||||
site_identification = siteIdentification.value,
|
||||
site_name = atmSiteNameExample.value,
|
||||
cash_withdrawal_national_fee = cashWithdrawalNationalFeeExample.value,
|
||||
cash_withdrawal_international_fee = cashWithdrawalInternationalFeeExample.value,
|
||||
balance_inquiry_fee = balanceInquiryFeeExample.value,
|
||||
atm_type = atmTypeExample.value,
|
||||
phone = phoneExample.value,
|
||||
|
||||
attributes = Some(List(atmAttributeResponseJsonV510))
|
||||
)
|
||||
|
||||
val atmsJsonV510 = AtmsJsonV510(
|
||||
atms = List(atmJsonV510)
|
||||
)
|
||||
//The common error or success format.
|
||||
//Just some helper format to use in Json
|
||||
case class NotSupportedYet()
|
||||
|
||||
@ -858,6 +858,9 @@ object ExampleValue {
|
||||
|
||||
lazy val atmIdExample = ConnectorField("atme0352a-9a0f-4bfa-b30b-9003aa467f51","A string that MUST uniquely identify the ATM on this OBP instance.")
|
||||
glossaryItems += makeGlossaryItem("atm_id", atmIdExample)
|
||||
|
||||
lazy val atmAttributeIdExample = ConnectorField("xxaf2a-9a0f-4bfa-b30b-9003aa467f51","A string that MUST uniquely identify the ATM Attribute on this OBP instance.")
|
||||
glossaryItems += makeGlossaryItem("ATM.attribute_id", atmIdExample)
|
||||
|
||||
lazy val atmNameExample = ConnectorField("Atm by the Lake","The name of the ATM")
|
||||
glossaryItems += makeGlossaryItem("ATM.name", atmNameExample)
|
||||
@ -897,6 +900,9 @@ object ExampleValue {
|
||||
|
||||
lazy val balanceInquiryFeeExample = ConnectorField(NoExampleProvided, NoDescriptionProvided)
|
||||
glossaryItems += makeGlossaryItem("ATM.balance_inquiry_fee", balanceInquiryFeeExample)
|
||||
|
||||
lazy val atmTypeExample = ConnectorField(NoExampleProvided, NoDescriptionProvided)
|
||||
glossaryItems += makeGlossaryItem("ATM.atm_type", atmTypeExample)
|
||||
|
||||
lazy val accessibilityFeaturesExample = ConnectorField("""["ATAC","ATAD"]""", NoDescriptionProvided)
|
||||
glossaryItems += makeGlossaryItem("accessibility_features", accessibilityFeaturesExample)
|
||||
|
||||
@ -3904,6 +3904,26 @@ object NewStyle extends MdcLoggable{
|
||||
Connector.connector.vend.updateCustomerAccountLinkById(customerAccountLinkId: String, relationshipType: String, callContext: Option[CallContext]) map {
|
||||
i => (unboxFullOrFail(i._1, callContext, UpdateCustomerAccountLinkError), i._2)
|
||||
}
|
||||
|
||||
def getAtmsByBankId(bankId: BankId, offset: Box[String], limit: Box[String], callContext: Option[CallContext]): OBPReturnType[List[AtmT]] =
|
||||
Connector.connector.vend.getAtms(bankId, callContext) map {
|
||||
case Empty =>
|
||||
fullBoxOrException(Empty ?~! atmsNotFound)
|
||||
case Full((List(), callContext)) =>
|
||||
Full(List())
|
||||
case Full((list, _)) => Full(list)
|
||||
case Failure(msg, _, _) => fullBoxOrException(Empty ?~! msg)
|
||||
case ParamFailure(msg, _, _, _) => fullBoxOrException(Empty ?~! msg)
|
||||
} map {
|
||||
unboxFull(_)
|
||||
} map {
|
||||
atm =>
|
||||
// Before we slice we need to sort in order to keep consistent results
|
||||
(atm.sortWith(_.atmId.value < _.atmId.value)
|
||||
// Slice the result in next way: from=offset and until=offset + limit
|
||||
.slice(offset.getOrElse("0").toInt, offset.getOrElse("0").toInt + limit.getOrElse("100").toInt)
|
||||
, callContext)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11686,22 +11686,7 @@ trait APIMethods400 {
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
(atms, callContext) <- Connector.connector.vend.getAtms(bankId, callContext) map {
|
||||
case Empty =>
|
||||
fullBoxOrException(Empty ?~! atmsNotFound)
|
||||
case Full((List(), callContext)) =>
|
||||
Full(List())
|
||||
case Full((list, _)) =>Full(list)
|
||||
case Failure(msg, _, _) => fullBoxOrException(Empty ?~! msg)
|
||||
case ParamFailure(msg,_,_,_) => fullBoxOrException(Empty ?~! msg)
|
||||
} map { unboxFull(_) } map {
|
||||
branch =>
|
||||
// Before we slice we need to sort in order to keep consistent results
|
||||
(branch.sortWith(_.atmId.value < _.atmId.value)
|
||||
// Slice the result in next way: from=offset and until=offset + limit
|
||||
.slice(offset.getOrElse("0").toInt, offset.getOrElse("0").toInt + limit.getOrElse("100").toInt)
|
||||
,callContext)
|
||||
}
|
||||
(atms, callContext) <- NewStyle.function.getAtmsByBankId(bankId, offset, limit, cc.callContext)
|
||||
} yield {
|
||||
(JSONFactory400.createAtmsJsonV400(atms), HttpCode.`200`(callContext))
|
||||
}
|
||||
|
||||
@ -9,10 +9,12 @@ import code.api.util.ApiTag._
|
||||
import code.api.util.ErrorMessages.{$UserNotLoggedIn, BankNotFound, ConsentNotFound, InvalidJsonFormat, UnknownError, UserNotFoundByUserId, UserNotLoggedIn, _}
|
||||
import code.api.util.{APIUtil, ApiRole, CallContext, CurrencyUtil, NewStyle, X509}
|
||||
import code.api.util.NewStyle.HttpCode
|
||||
import code.api.v3_0_0.JSONFactory300
|
||||
import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson
|
||||
import code.api.v3_1_0.ConsentJsonV310
|
||||
import code.api.v3_1_0.JSONFactory310.createBadLoginStatusJson
|
||||
import code.api.v4_0_0.{JSONFactory400, PostApiCollectionJson400}
|
||||
import code.atmattribute.AtmAttribute
|
||||
import code.consent.Consents
|
||||
import code.loginattempts.LoginAttempt
|
||||
import code.metrics.APIMetrics
|
||||
@ -24,10 +26,11 @@ import code.util.Helper
|
||||
import code.views.system.{AccountAccess, ViewDefinition}
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model.{AtmId, BankId}
|
||||
import com.openbankproject.commons.model.{AtmId, AtmT, BankId}
|
||||
import com.openbankproject.commons.model.enums.AtmAttributeType
|
||||
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion}
|
||||
import net.liftweb.common.Full
|
||||
import net.liftweb.http.S
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
@ -373,7 +376,7 @@ trait APIMethods510 {
|
||||
|
|
||||
|""",
|
||||
EmptyBody,
|
||||
transactionAttributesResponseJson,
|
||||
atmAttributesResponseJsonV510,
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
$BankNotFound,
|
||||
@ -947,6 +950,182 @@ trait APIMethods510 {
|
||||
}
|
||||
}
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
createAtm,
|
||||
implementedInApiVersion,
|
||||
nameOf(createAtm),
|
||||
"POST",
|
||||
"/banks/BANK_ID/atms",
|
||||
"Create ATM",
|
||||
s"""Create ATM.""",
|
||||
atmJsonV510,
|
||||
atmJsonV510,
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagATM, apiTagNewStyle),
|
||||
Some(List(canCreateAtm, canCreateAtmAtAnyBank))
|
||||
)
|
||||
lazy val createAtm: OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) :: "atms" :: Nil JsonPost json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
atmJsonV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) {
|
||||
val atm = json.extract[AtmJsonV510]
|
||||
//Make sure the Create contains proper ATM ID
|
||||
atm.id.get
|
||||
atm
|
||||
}
|
||||
_ <- Helper.booleanToFuture(s"$InvalidJsonValue BANK_ID has to be the same in the URL and Body", 400, cc.callContext) {
|
||||
atmJsonV510.bank_id == bankId.value
|
||||
}
|
||||
atm <- NewStyle.function.tryons(CouldNotTransformJsonToInternalModel + " Atm", 400, cc.callContext) {
|
||||
JSONFactory510.transformToAtmFromV510(atmJsonV510)
|
||||
}
|
||||
(atm, callContext) <- NewStyle.function.createOrUpdateAtm(atm, cc.callContext)
|
||||
(atmAttributes, callContext) <- NewStyle.function.getAtmAttributesByAtm(bankId, atm.atmId, callContext)
|
||||
} yield {
|
||||
(JSONFactory510.createAtmJsonV510(atm, atmAttributes), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
updateAtm,
|
||||
implementedInApiVersion,
|
||||
nameOf(updateAtm),
|
||||
"PUT",
|
||||
"/banks/BANK_ID/atms/ATM_ID",
|
||||
"UPDATE ATM",
|
||||
s"""Update ATM.""",
|
||||
atmJsonV510.copy(id = None, attributes = None),
|
||||
atmJsonV510,
|
||||
List(
|
||||
$UserNotLoggedIn,
|
||||
InvalidJsonFormat,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagATM, apiTagNewStyle),
|
||||
Some(List(canUpdateAtm, canUpdateAtmAtAnyBank))
|
||||
)
|
||||
lazy val updateAtm: OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) :: "atms" :: AtmId(atmId) :: Nil JsonPut json -> _ => {
|
||||
cc =>
|
||||
for {
|
||||
(atm, callContext) <- NewStyle.function.getAtm(bankId, atmId, cc.callContext)
|
||||
atmJsonV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, callContext) {
|
||||
json.extract[AtmJsonV510]
|
||||
}
|
||||
_ <- Helper.booleanToFuture(s"$InvalidJsonValue BANK_ID has to be the same in the URL and Body", 400, callContext) {
|
||||
atmJsonV510.bank_id == bankId.value
|
||||
}
|
||||
atm <- NewStyle.function.tryons(CouldNotTransformJsonToInternalModel + " Atm", 400, callContext) {
|
||||
JSONFactory510.transformToAtmFromV510(atmJsonV510.copy(id = Some(atmId.value)))
|
||||
}
|
||||
(atm, callContext) <- NewStyle.function.createOrUpdateAtm(atm, callContext)
|
||||
(atmAttributes, callContext) <- NewStyle.function.getAtmAttributesByAtm(bankId, atm.atmId, callContext)
|
||||
} yield {
|
||||
(JSONFactory510.createAtmJsonV510(atm, atmAttributes), HttpCode.`201`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
getAtms,
|
||||
implementedInApiVersion,
|
||||
nameOf(getAtms),
|
||||
"GET",
|
||||
"/banks/BANK_ID/atms",
|
||||
"Get Bank ATMS",
|
||||
s"""Get Bank ATMS.""",
|
||||
EmptyBody,
|
||||
atmsJsonV510,
|
||||
List(
|
||||
$BankNotFound,
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagATM, apiTagNewStyle)
|
||||
)
|
||||
lazy val getAtms: OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) :: "atms" :: Nil JsonGet _ => {
|
||||
cc =>
|
||||
val limit = S.param("limit")
|
||||
val offset = S.param("offset")
|
||||
for {
|
||||
(_, callContext) <- getAtmsIsPublic match {
|
||||
case false => authenticatedAccess(cc)
|
||||
case true => anonymousAccess(cc)
|
||||
}
|
||||
_ <- Helper.booleanToFuture(failMsg = s"${InvalidNumber} limit:${limit.getOrElse("")}", cc = callContext) {
|
||||
limit match {
|
||||
case Full(i) => i.toList.forall(c => Character.isDigit(c) == true)
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
_ <- Helper.booleanToFuture(failMsg = maximumLimitExceeded, cc = callContext) {
|
||||
limit match {
|
||||
case Full(i) if i.toInt > 10000 => false
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
(atms, callContext) <- NewStyle.function.getAtmsByBankId(bankId, offset, limit, callContext)
|
||||
|
||||
atmAndAttributesTupleList: List[(AtmT, List[AtmAttribute])] <- Future.sequence(atms.map(
|
||||
atm => NewStyle.function.getAtmAttributesByAtm(bankId, atm.atmId, callContext).map(_._1).map(
|
||||
attributes =>{
|
||||
(atm-> attributes)
|
||||
}
|
||||
)))
|
||||
|
||||
} yield {
|
||||
(JSONFactory510.createAtmsJsonV510(atmAndAttributesTupleList), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
getAtm,
|
||||
implementedInApiVersion,
|
||||
nameOf(getAtm),
|
||||
"GET",
|
||||
"/banks/BANK_ID/atms/ATM_ID",
|
||||
"Get Bank ATM",
|
||||
s"""Returns information about ATM for a single bank specified by BANK_ID and ATM_ID including:
|
||||
|
|
||||
|* Address
|
||||
|* Geo Location
|
||||
|* License the data under this endpoint is released under
|
||||
|* ATM Attributes
|
||||
|
|
||||
|
|
||||
|
|
||||
|${authenticationRequiredMessage(!getAtmsIsPublic)}""".stripMargin,
|
||||
EmptyBody,
|
||||
atmJsonV510,
|
||||
List(UserNotLoggedIn, BankNotFound, AtmNotFoundByAtmId, UnknownError),
|
||||
List(apiTagATM, apiTagNewStyle)
|
||||
)
|
||||
lazy val getAtm: OBPEndpoint = {
|
||||
case "banks" :: BankId(bankId) :: "atms" :: AtmId(atmId) :: Nil JsonGet req => {
|
||||
cc =>
|
||||
for {
|
||||
(_, callContext) <- getAtmsIsPublic match {
|
||||
case false => authenticatedAccess(cc)
|
||||
case true => anonymousAccess(cc)
|
||||
}
|
||||
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
|
||||
(atm, callContext) <- NewStyle.function.getAtm(bankId, atmId, callContext)
|
||||
(atmAttributes, callContext) <- NewStyle.function.getAtmAttributesByAtm(bankId, atmId, callContext)
|
||||
} yield {
|
||||
(JSONFactory510.createAtmJsonV510(atm, atmAttributes), HttpCode.`200`(callContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,12 +29,18 @@ package code.api.v5_1_0
|
||||
import code.api.Constant
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.APIUtil.gitCommit
|
||||
import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, transformToLocationFromV140, transformToMetaFromV140}
|
||||
import code.api.v3_0_0.JSONFactory300.{createLocationJson, createMetaJson, transformToAddressFromV300}
|
||||
import code.api.v3_0_0.{AddressJsonV300, OpeningTimesV300}
|
||||
import code.api.v4_0_0.{EnergySource400, HostedAt400, HostedBy400}
|
||||
import code.atmattribute.AtmAttribute
|
||||
import code.atms.Atms.Atm
|
||||
import code.views.system.{AccountAccess, ViewDefinition}
|
||||
import com.openbankproject.commons.model.{Address, AtmId, AtmT, BankId, Location, Meta}
|
||||
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion}
|
||||
|
||||
import scala.collection.immutable.List
|
||||
import scala.util.Try
|
||||
|
||||
|
||||
case class APIInfoJsonV510(
|
||||
@ -67,7 +73,46 @@ case class CheckSystemIntegrityJsonV510(
|
||||
case class CurrencyJsonV510(alphanumeric_code: String)
|
||||
case class CurrenciesJsonV510(currencies: List[CurrencyJsonV510])
|
||||
|
||||
case class AtmJsonV510 (
|
||||
id : Option[String],
|
||||
bank_id : String,
|
||||
name : String,
|
||||
address: AddressJsonV300,
|
||||
location: LocationJsonV140,
|
||||
meta: MetaJsonV140,
|
||||
|
||||
monday: OpeningTimesV300,
|
||||
tuesday: OpeningTimesV300,
|
||||
wednesday: OpeningTimesV300,
|
||||
thursday: OpeningTimesV300,
|
||||
friday: OpeningTimesV300,
|
||||
saturday: OpeningTimesV300,
|
||||
sunday: OpeningTimesV300,
|
||||
|
||||
is_accessible : String,
|
||||
located_at : String,
|
||||
more_info : String,
|
||||
has_deposit_capability : String,
|
||||
|
||||
supported_languages: List[String],
|
||||
services: List[String],
|
||||
accessibility_features: List[String],
|
||||
supported_currencies: List[String],
|
||||
notes: List[String],
|
||||
location_categories: List[String],
|
||||
minimum_withdrawal: String,
|
||||
branch_identification: String,
|
||||
site_identification: String,
|
||||
site_name: String,
|
||||
cash_withdrawal_national_fee: String,
|
||||
cash_withdrawal_international_fee: String,
|
||||
balance_inquiry_fee: String,
|
||||
atm_type: String,
|
||||
phone: String,
|
||||
attributes: Option[List[AtmAttributeResponseJsonV510]]
|
||||
)
|
||||
|
||||
case class AtmsJsonV510(atms : List[AtmJsonV510])
|
||||
|
||||
case class ProductAttributeJsonV510(
|
||||
name: String,
|
||||
@ -109,13 +154,134 @@ case class AtmAttributeResponseJsonV510(
|
||||
is_active: Option[Boolean]
|
||||
)
|
||||
case class AtmAttributesResponseJsonV510(atm_attributes: List[AtmAttributeResponseJsonV510])
|
||||
case class AtmAttributeBankResponseJsonV510(name: String,
|
||||
value: String)
|
||||
case class AtmAttributesResponseJson(list: List[AtmAttributeBankResponseJsonV510])
|
||||
|
||||
|
||||
|
||||
object JSONFactory510 {
|
||||
|
||||
def createAtmsJsonV510(atmAndAttributesTupleList: List[(AtmT, List[AtmAttribute])] ): AtmsJsonV510 = {
|
||||
AtmsJsonV510(atmAndAttributesTupleList.map(
|
||||
atmAndAttributesTuple =>
|
||||
createAtmJsonV510(atmAndAttributesTuple._1,atmAndAttributesTuple._2)
|
||||
))
|
||||
}
|
||||
|
||||
def createAtmJsonV510(atm: AtmT, atmAttributes:List[AtmAttribute]): AtmJsonV510 = {
|
||||
AtmJsonV510(
|
||||
id = Some(atm.atmId.value),
|
||||
bank_id = atm.bankId.value,
|
||||
name = atm.name,
|
||||
AddressJsonV300(atm.address.line1,
|
||||
atm.address.line2,
|
||||
atm.address.line3,
|
||||
atm.address.city,
|
||||
atm.address.county.getOrElse(""),
|
||||
atm.address.state,
|
||||
atm.address.postCode,
|
||||
atm.address.countryCode),
|
||||
createLocationJson(atm.location),
|
||||
createMetaJson(atm.meta),
|
||||
monday = OpeningTimesV300(
|
||||
opening_time = atm.OpeningTimeOnMonday.getOrElse(""),
|
||||
closing_time = atm.ClosingTimeOnMonday.getOrElse("")),
|
||||
tuesday = OpeningTimesV300(
|
||||
opening_time = atm.OpeningTimeOnTuesday.getOrElse(""),
|
||||
closing_time = atm.ClosingTimeOnTuesday.getOrElse("")),
|
||||
wednesday = OpeningTimesV300(
|
||||
opening_time = atm.OpeningTimeOnWednesday.getOrElse(""),
|
||||
closing_time = atm.ClosingTimeOnWednesday.getOrElse("")),
|
||||
thursday = OpeningTimesV300(
|
||||
opening_time = atm.OpeningTimeOnThursday.getOrElse(""),
|
||||
closing_time = atm.ClosingTimeOnThursday.getOrElse("")),
|
||||
friday = OpeningTimesV300(
|
||||
opening_time = atm.OpeningTimeOnFriday.getOrElse(""),
|
||||
closing_time = atm.ClosingTimeOnFriday.getOrElse("")),
|
||||
saturday = OpeningTimesV300(
|
||||
opening_time = atm.OpeningTimeOnSaturday.getOrElse(""),
|
||||
closing_time = atm.ClosingTimeOnSaturday.getOrElse("")),
|
||||
sunday = OpeningTimesV300(
|
||||
opening_time = atm.OpeningTimeOnSunday.getOrElse(""),
|
||||
closing_time = atm.ClosingTimeOnSunday.getOrElse("")),
|
||||
is_accessible = atm.isAccessible.map(_.toString).getOrElse(""),
|
||||
located_at = atm.locatedAt.getOrElse(""),
|
||||
more_info = atm.moreInfo.getOrElse(""),
|
||||
has_deposit_capability = atm.hasDepositCapability.map(_.toString).getOrElse(""),
|
||||
supported_languages = atm.supportedLanguages.getOrElse(Nil),
|
||||
services = atm.services.getOrElse(Nil),
|
||||
accessibility_features = atm.accessibilityFeatures.getOrElse(Nil),
|
||||
supported_currencies = atm.supportedCurrencies.getOrElse(Nil),
|
||||
notes = atm.notes.getOrElse(Nil),
|
||||
location_categories = atm.locationCategories.getOrElse(Nil),
|
||||
minimum_withdrawal = atm.minimumWithdrawal.getOrElse(""),
|
||||
branch_identification = atm.branchIdentification.getOrElse(""),
|
||||
site_identification = atm.siteIdentification.getOrElse(""),
|
||||
site_name = atm.siteName.getOrElse(""),
|
||||
cash_withdrawal_national_fee = atm.cashWithdrawalNationalFee.getOrElse(""),
|
||||
cash_withdrawal_international_fee = atm.cashWithdrawalInternationalFee.getOrElse(""),
|
||||
balance_inquiry_fee = atm.balanceInquiryFee.getOrElse(""),
|
||||
atm_type = atm.atmType.getOrElse(""),
|
||||
phone = atm.phone.getOrElse(""),
|
||||
attributes = Some(atmAttributes.map(createAtmAttributeJson))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def transformToAtmFromV510(atmJsonV510: AtmJsonV510): Atm = {
|
||||
val address: Address = transformToAddressFromV300(atmJsonV510.address) // Note the address in V220 is V140
|
||||
val location: Location = transformToLocationFromV140(atmJsonV510.location) // Note the location is V140
|
||||
val meta: Meta = transformToMetaFromV140(atmJsonV510.meta) // Note the meta is V140
|
||||
val isAccessible: Boolean = Try(atmJsonV510.is_accessible.toBoolean).getOrElse(false)
|
||||
val hdc: Boolean = Try(atmJsonV510.has_deposit_capability.toBoolean).getOrElse(false)
|
||||
|
||||
Atm(
|
||||
atmId = AtmId(atmJsonV510.id.getOrElse("")),
|
||||
bankId = BankId(atmJsonV510.bank_id),
|
||||
name = atmJsonV510.name,
|
||||
address = address,
|
||||
location = location,
|
||||
meta = meta,
|
||||
OpeningTimeOnMonday = Some(atmJsonV510.monday.opening_time),
|
||||
ClosingTimeOnMonday = Some(atmJsonV510.monday.closing_time),
|
||||
|
||||
OpeningTimeOnTuesday = Some(atmJsonV510.tuesday.opening_time),
|
||||
ClosingTimeOnTuesday = Some(atmJsonV510.tuesday.closing_time),
|
||||
|
||||
OpeningTimeOnWednesday = Some(atmJsonV510.wednesday.opening_time),
|
||||
ClosingTimeOnWednesday = Some(atmJsonV510.wednesday.closing_time),
|
||||
|
||||
OpeningTimeOnThursday = Some(atmJsonV510.thursday.opening_time),
|
||||
ClosingTimeOnThursday = Some(atmJsonV510.thursday.closing_time),
|
||||
|
||||
OpeningTimeOnFriday = Some(atmJsonV510.friday.opening_time),
|
||||
ClosingTimeOnFriday = Some(atmJsonV510.friday.closing_time),
|
||||
|
||||
OpeningTimeOnSaturday = Some(atmJsonV510.saturday.opening_time),
|
||||
ClosingTimeOnSaturday = Some(atmJsonV510.saturday.closing_time),
|
||||
|
||||
OpeningTimeOnSunday = Some(atmJsonV510.sunday.opening_time),
|
||||
ClosingTimeOnSunday = Some(atmJsonV510.sunday.closing_time),
|
||||
// Easy access for people who use wheelchairs etc. true or false ""=Unknown
|
||||
isAccessible = Some(isAccessible),
|
||||
locatedAt = Some(atmJsonV510.located_at),
|
||||
moreInfo = Some(atmJsonV510.more_info),
|
||||
hasDepositCapability = Some(hdc),
|
||||
|
||||
supportedLanguages = Some(atmJsonV510.supported_languages),
|
||||
services = Some(atmJsonV510.services),
|
||||
accessibilityFeatures = Some(atmJsonV510.accessibility_features),
|
||||
supportedCurrencies = Some(atmJsonV510.supported_currencies),
|
||||
notes = Some(atmJsonV510.notes),
|
||||
minimumWithdrawal = Some(atmJsonV510.minimum_withdrawal),
|
||||
branchIdentification = Some(atmJsonV510.branch_identification),
|
||||
locationCategories = Some(atmJsonV510.location_categories),
|
||||
siteIdentification = Some(atmJsonV510.site_identification),
|
||||
siteName = Some(atmJsonV510.site_name),
|
||||
cashWithdrawalNationalFee = Some(atmJsonV510.cash_withdrawal_national_fee),
|
||||
cashWithdrawalInternationalFee = Some(atmJsonV510.cash_withdrawal_international_fee),
|
||||
balanceInquiryFee = Some(atmJsonV510.balance_inquiry_fee),
|
||||
atmType = Some(atmJsonV510.atm_type),
|
||||
phone = Some(atmJsonV510.phone)
|
||||
)
|
||||
}
|
||||
|
||||
def getCustomViewNamesCheck(views: List[ViewDefinition]): CheckSystemIntegrityJsonV510 = {
|
||||
val success = views.size == 0
|
||||
@ -195,8 +361,8 @@ object JSONFactory510 {
|
||||
is_active = atmAttribute.isActive
|
||||
)
|
||||
|
||||
def createAtmAttributesJson(bankAttributes: List[AtmAttribute]): AtmAttributesResponseJsonV510 =
|
||||
AtmAttributesResponseJsonV510(bankAttributes.map(createAtmAttributeJson))
|
||||
def createAtmAttributesJson(atmAttributes: List[AtmAttribute]): AtmAttributesResponseJsonV510 =
|
||||
AtmAttributesResponseJsonV510(atmAttributes.map(createAtmAttributeJson))
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -60,7 +60,9 @@ object Atms extends SimpleInjector {
|
||||
cashWithdrawalNationalFee: Option[String] = None,
|
||||
cashWithdrawalInternationalFee: Option[String] = None,
|
||||
balanceInquiryFee: Option[String] = None,
|
||||
|
||||
atmType: Option[String] = None,
|
||||
phone: Option[String] = None,
|
||||
|
||||
) extends AtmT
|
||||
|
||||
val atmsProvider = new Inject(buildOne _) {}
|
||||
|
||||
@ -92,6 +92,8 @@ object MappedAtmsProvider extends AtmsProvider {
|
||||
.mCashWithdrawalNationalFee(atm.cashWithdrawalNationalFee.orNull)
|
||||
.mCashWithdrawalInternationalFee(atm.cashWithdrawalInternationalFee.orNull)
|
||||
.mBalanceInquiryFee(atm.balanceInquiryFee.orNull)
|
||||
.mAtmType(atm.atmType.orNull)
|
||||
.mPhone(atm.phone.orNull)
|
||||
.saveMe()
|
||||
}
|
||||
case _ =>
|
||||
@ -149,6 +151,9 @@ object MappedAtmsProvider extends AtmsProvider {
|
||||
.mCashWithdrawalNationalFee(atm.cashWithdrawalNationalFee.orNull)
|
||||
.mCashWithdrawalInternationalFee(atm.cashWithdrawalInternationalFee.orNull)
|
||||
.mBalanceInquiryFee(atm.balanceInquiryFee.orNull)
|
||||
|
||||
.mAtmType(atm.atmType.orNull)
|
||||
.mPhone(atm.phone.orNull)
|
||||
.saveMe()
|
||||
}
|
||||
}
|
||||
@ -231,6 +236,8 @@ class MappedAtm extends AtmT with LongKeyedMapper[MappedAtm] with IdPK {
|
||||
object mCashWithdrawalNationalFee extends MappedString(this, 255)
|
||||
object mCashWithdrawalInternationalFee extends MappedString(this, 255)
|
||||
object mBalanceInquiryFee extends MappedString(this, 255)
|
||||
object mAtmType extends MappedString(this, 255)
|
||||
object mPhone extends MappedString(this, 255)
|
||||
|
||||
|
||||
override def atmId: AtmId = AtmId(mAtmId.get)
|
||||
@ -360,6 +367,16 @@ class MappedAtm extends AtmT with LongKeyedMapper[MappedAtm] with IdPK {
|
||||
case _ => None
|
||||
}
|
||||
|
||||
override def atmType: Option[String] = mAtmType.get match {
|
||||
case value: String => Some(value)
|
||||
case _ => None
|
||||
}
|
||||
|
||||
override def phone: Option[String] = mPhone.get match {
|
||||
case value: String => Some(value)
|
||||
case _ => None
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@ -366,6 +366,8 @@ case class InboundAtmSept2018(
|
||||
cashWithdrawalNationalFee: Option[String] = None,
|
||||
cashWithdrawalInternationalFee: Option[String] = None,
|
||||
balanceInquiryFee: Option[String] = None,
|
||||
atmType: Option[String] = None,
|
||||
phone: Option[String] = None,
|
||||
) extends AtmT
|
||||
|
||||
case class InternalTransaction_vSept2018(
|
||||
|
||||
@ -62,6 +62,8 @@ class AtmsTest extends V140ServerSetup with DefaultUsers {
|
||||
cashWithdrawalNationalFee: Option[String] = None,
|
||||
cashWithdrawalInternationalFee: Option[String] = None,
|
||||
balanceInquiryFee: Option[String] = None,
|
||||
atmType: Option[String] = None,
|
||||
phone: Option[String] = None,
|
||||
) extends AtmT
|
||||
|
||||
case class AddressImpl(line1 : String, line2 : String, line3 : String, city : String, county : Option[String],
|
||||
|
||||
193
obp-api/src/test/scala/code/api/v5_1_0/AtmTest.scala
Normal file
193
obp-api/src/test/scala/code/api/v5_1_0/AtmTest.scala
Normal file
@ -0,0 +1,193 @@
|
||||
package code.api.v5_1_0
|
||||
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.ApiRole._
|
||||
import code.api.util.ErrorMessages.{AtmNotFoundByAtmId, UserHasMissingRoles}
|
||||
import code.api.util.ExampleValue.atmTypeExample
|
||||
import code.api.util.{ApiRole, ErrorMessages}
|
||||
import code.api.v5_1_0.APIMethods510.Implementations5_1_0
|
||||
import code.entitlement.Entitlement
|
||||
import code.setup.DefaultUsers
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.model.ErrorMessage
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb.json.Serialization.write
|
||||
import org.scalatest.Tag
|
||||
|
||||
class AtmTest extends V510ServerSetup with DefaultUsers {
|
||||
|
||||
override def beforeAll() {
|
||||
super.beforeAll()
|
||||
}
|
||||
|
||||
override def afterAll() {
|
||||
super.afterAll()
|
||||
}
|
||||
|
||||
/**
|
||||
* Test tags
|
||||
* Example: To run tests with tag "getPermissions":
|
||||
* mvn test -D tagsToInclude
|
||||
*
|
||||
* This is made possible by the scalatest maven plugin
|
||||
*/
|
||||
object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString)
|
||||
object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.createAtm))
|
||||
object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.updateAtm))
|
||||
object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.getAtms))
|
||||
object ApiEndpoint4 extends Tag(nameOf(Implementations5_1_0.getAtm))
|
||||
|
||||
lazy val bankId = randomBankId
|
||||
|
||||
feature(s"Test$ApiEndpoint1 test the error cases - $VersionOfApi") {
|
||||
scenario(s"We try to consume endpoint $ApiEndpoint1 - Anonymous access", ApiEndpoint1, VersionOfApi) {
|
||||
When("We make the request")
|
||||
val requestGet = (v5_1_0_Request / "banks" / bankId / "atms").POST
|
||||
val responseGet = makePostRequest(requestGet, write(atmJsonV510))
|
||||
Then("We should get a 401")
|
||||
And("We should get a message: " + ErrorMessages.UserNotLoggedIn)
|
||||
responseGet.code should equal(401)
|
||||
responseGet.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn)
|
||||
}
|
||||
|
||||
scenario(s"We try to consume endpoint $ApiEndpoint1 without proper role - Authorized access", ApiEndpoint1, VersionOfApi) {
|
||||
When("We make the request")
|
||||
val requestGet = (v5_1_0_Request / "banks" / bankId / "atms").POST <@ (user1)
|
||||
val responseGet = makePostRequest(requestGet, write(atmJsonV510))
|
||||
Then("We should get a 403")
|
||||
And("We should get a message: " + s"$canCreateAtmAtAnyBank or $canCreateAtm entitlement required")
|
||||
responseGet.code should equal(403)
|
||||
responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles)
|
||||
responseGet.body.extract[ErrorMessage].message contains (canCreateAtmAtAnyBank.toString()) shouldBe (true)
|
||||
responseGet.body.extract[ErrorMessage].message contains (canCreateAtm.toString()) shouldBe (true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
feature(s"Test$ApiEndpoint2 test the error cases - $VersionOfApi") {
|
||||
scenario(s"We try to consume endpoint $ApiEndpoint2 - Anonymous access", ApiEndpoint2, VersionOfApi) {
|
||||
When("We make the request")
|
||||
val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId" ).PUT
|
||||
val responseGet = makePutRequest(requestGet, write(atmJsonV510))
|
||||
Then("We should get a 401")
|
||||
And("We should get a message: " + ErrorMessages.UserNotLoggedIn)
|
||||
responseGet.code should equal(401)
|
||||
responseGet.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn)
|
||||
}
|
||||
scenario(s"We try to consume endpoint $ApiEndpoint2 without proper role - Authorized access", ApiEndpoint2, VersionOfApi) {
|
||||
When("We make the request")
|
||||
val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId" ).PUT <@ (user1)
|
||||
val responseGet = makePutRequest(requestGet, write(atmJsonV510))
|
||||
Then("We should get a 403")
|
||||
And("We should get a message: " + s"$canCreateAtmAtAnyBank or $canCreateAtm entitlement required")
|
||||
responseGet.code should equal(403)
|
||||
responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles)
|
||||
responseGet.body.extract[ErrorMessage].message contains (canUpdateAtmAtAnyBank.toString()) shouldBe (true)
|
||||
responseGet.body.extract[ErrorMessage].message contains (canUpdateAtm.toString()) shouldBe (true)
|
||||
}
|
||||
scenario(s"We try to consume endpoint $ApiEndpoint2 with proper role but invalid ATM - Authorized access", ApiEndpoint2, VersionOfApi) {
|
||||
When("We make the request")
|
||||
val entitlement = Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateAtmAtAnyBank.toString)
|
||||
val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId-invalid" ).PUT <@ (user1)
|
||||
val responseGet = makePutRequest(requestGet, write(atmJsonV510))
|
||||
Then("We should get a 404")
|
||||
And("We should get a message: " + s"$AtmNotFoundByAtmId")
|
||||
responseGet.code should equal(404)
|
||||
responseGet.body.extract[ErrorMessage].message should startWith(AtmNotFoundByAtmId)
|
||||
responseGet.body.extract[ErrorMessage].message contains (AtmNotFoundByAtmId) shouldBe (true)
|
||||
Entitlement.entitlement.vend.deleteEntitlement(entitlement)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"Test$ApiEndpoint3 test the error cases - $VersionOfApi") {
|
||||
scenario(s"We try to consume endpoint $ApiEndpoint3 - Anonymous access", ApiEndpoint3, VersionOfApi) {
|
||||
When("We make the request")
|
||||
val request = (v5_1_0_Request / "banks" / bankId / "atms").GET
|
||||
val response = makeGetRequest(request)
|
||||
Then("We should get a 200")
|
||||
response.code should equal(200)
|
||||
}
|
||||
}
|
||||
|
||||
feature(s"Test$ApiEndpoint1 $ApiEndpoint2 $ApiEndpoint3 $ApiEndpoint4 - $VersionOfApi") {
|
||||
scenario(s"Test the CUR methods", ApiEndpoint1, VersionOfApi) {
|
||||
When("We make the CREATE ATMs")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanCreateAtmAtAnyBank.toString)
|
||||
val requestCreate = (v5_1_0_Request / "banks" / bankId / "atms").POST <@ (user1)
|
||||
val responseCreate = makePostRequest(requestCreate, write(atmJsonV510.copy(
|
||||
bank_id = bankId,
|
||||
atm_type = "atm_type1",
|
||||
phone = "12345")))
|
||||
Then("We should get a 201")
|
||||
responseCreate.code should equal(201)
|
||||
responseCreate.body.extract[AtmJsonV510].atm_type shouldBe("atm_type1")
|
||||
responseCreate.body.extract[AtmJsonV510].phone shouldBe("12345")
|
||||
val atmId = responseCreate.body.extract[AtmJsonV510].id.getOrElse("")
|
||||
|
||||
Then("We create three ATM attributes")
|
||||
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, ApiRole.CanCreateAtmAttribute.toString)
|
||||
val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes").POST <@ (user1)
|
||||
makePostRequest(requestGet, write(atmAttributeJsonV510.copy(name = "1")))
|
||||
makePostRequest(requestGet, write(atmAttributeJsonV510.copy(name = "2")))
|
||||
makePostRequest(requestGet, write(atmAttributeJsonV510.copy(name = "3")))
|
||||
|
||||
|
||||
Then("We Update the ATMs")
|
||||
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateAtmAtAnyBank.toString)
|
||||
val requestUpdate = (v5_1_0_Request / "banks" / bankId / "atms" / atmId).PUT <@ (user1)
|
||||
val responseUpdate = makePutRequest(requestUpdate, write(atmJsonV510.copy(
|
||||
bank_id = bankId,
|
||||
atm_type = "atm_type_111",
|
||||
phone = "123456")))
|
||||
Then("We should get a 201")
|
||||
responseUpdate.code should equal(201)
|
||||
responseUpdate.body.extract[AtmJsonV510].atm_type shouldBe ("atm_type_111")
|
||||
responseUpdate.body.extract[AtmJsonV510].phone shouldBe ("123456")
|
||||
|
||||
Then("We create 2 more ATMs")
|
||||
makePostRequest(requestCreate, write(atmJsonV510.copy(
|
||||
bank_id = bankId,
|
||||
id = Some("id2"),
|
||||
atm_type = "atm_type2",
|
||||
phone = "12345-2")))
|
||||
makePostRequest(requestCreate, write(atmJsonV510.copy(
|
||||
bank_id = bankId,
|
||||
id = Some("id3"),
|
||||
atm_type = "atm_type3",
|
||||
phone = "12345-3")))
|
||||
|
||||
Then("We Get the ATMs")
|
||||
val request = (v5_1_0_Request / "banks" / bankId / "atms").GET
|
||||
Then("We should get a 200")
|
||||
val responseGet = makeGetRequest(request)
|
||||
responseGet.code should equal(200)
|
||||
val atms = responseGet.body.extract[AtmsJsonV510].atms
|
||||
atms.length should be (3)
|
||||
atms(0).atm_type equals ("atm_type_111")
|
||||
atms(1).atm_type equals ("atm_type2")
|
||||
atms(2).atm_type equals ("atm_type3")
|
||||
val attibutes = atms.find(_.id == Some(atmId)).get.attributes.get
|
||||
attibutes.length shouldBe(3)
|
||||
attibutes(0).name equals ("1")
|
||||
attibutes(1).name equals ("2")
|
||||
attibutes(2).name equals ("3")
|
||||
|
||||
|
||||
Then("We Get the ATM")
|
||||
val requestOne = (v5_1_0_Request / "banks" / bankId / "atms" /atmId ).GET
|
||||
Then("We should get a 200")
|
||||
val responseOne = makeGetRequest(requestOne)
|
||||
|
||||
responseOne.code should equal(200)
|
||||
val atm = responseOne.body.extract[AtmJsonV510]
|
||||
atm.atm_type equals ("atm_type_111")
|
||||
val atmAttributes = atm.attributes.get
|
||||
atmAttributes.length shouldBe(3)
|
||||
atmAttributes(0).name equals ("1")
|
||||
atmAttributes(1).name equals ("2")
|
||||
atmAttributes(2).name equals ("3")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -269,6 +269,8 @@ case class AtmTCommons(
|
||||
cashWithdrawalNationalFee: Option[String] = None,
|
||||
cashWithdrawalInternationalFee: Option[String] = None,
|
||||
balanceInquiryFee: Option[String] = None,
|
||||
atmType: Option[String] = None,
|
||||
phone: Option[String] = None,
|
||||
) extends AtmT
|
||||
|
||||
object AtmTCommons extends Converter[AtmT, AtmTCommons]
|
||||
|
||||
@ -182,6 +182,8 @@ trait AtmT {
|
||||
def cashWithdrawalNationalFee: Option[String]
|
||||
def cashWithdrawalInternationalFee: Option[String]
|
||||
def balanceInquiryFee: Option[String]
|
||||
def atmType: Option[String]
|
||||
def phone: Option[String]
|
||||
}
|
||||
|
||||
// MappedBranch will implement this.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user