diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index 4a5139885..84e079bf2 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -510,6 +510,7 @@ object ErrorMessages { val DeleteCounterpartyLimitError = "OBP-30265: Could not delete the counterparty limit." val CustomViewAlreadyExistsError = "OBP-30266: The custom view is already exists." val UserDoesNotHavePermission = "OBP-30267: The user does not have the permission:" + val CounterpartyLimitValidationError = "OBP-30268: Counterparty Limit Validation Error." val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. " val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. " 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 3b1feb758..8a50678b5 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -4267,6 +4267,46 @@ object NewStyle extends MdcLoggable{ i => (unboxFullOrFail(i._1, callContext, s"$GetCounterpartyLimitError Current BANK_ID($bankId), " + s"ACCOUNT_ID($accountId), VIEW_ID($viewId),COUNTERPARTY_ID($counterpartyId)"), i._2) } + + def getCountOfTransactionsFromAccountToCounterparty( + fromBankId: BankId, + fromAccountId: AccountId, + counterpartyId: CounterpartyId, + fromDate: Date, + toDate: Date, + callContext: Option[CallContext] + ): OBPReturnType[Int] = + Connector.connector.vend.getCountOfTransactionsFromAccountToCounterparty( + fromBankId: BankId, + fromAccountId: AccountId, + counterpartyId: CounterpartyId, + fromDate: Date, + toDate: Date, + callContext: Option[CallContext] + ) map { + i => + (unboxFullOrFail(i._1, callContext, s"$InvalidConnectorResponse ${nameOf(getCountOfTransactionsFromAccountToCounterparty _)}"), i._2) + } + + def getSumOfTransactionsFromAccountToCounterparty( + fromBankId: BankId, + fromAccountId: AccountId, + counterpartyId: CounterpartyId, + fromDate: Date, + toDate:Date, + callContext: Option[CallContext] + ):OBPReturnType[AmountOfMoney] = + Connector.connector.vend.getSumOfTransactionsFromAccountToCounterparty( + fromBankId: BankId, + fromAccountId: AccountId, + counterpartyId: CounterpartyId, + fromDate: Date, + toDate:Date, + callContext: Option[CallContext] + ) map { + i => + (unboxFullOrFail(i._1, callContext, s"$InvalidConnectorResponse ${nameOf(getCountOfTransactionsFromAccountToCounterparty _)}"), i._2) + } def deleteCounterpartyLimit( bankId: String, 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 84c69617c..9165dd9ed 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 @@ -40,6 +40,7 @@ import code.api.v2_1_0._ import code.api.v3_0_0.{CreateScopeJson, JSONFactory300} import code.api.v3_1_0._ import code.api.v4_0_0.JSONFactory400._ +import code.fx.{MappedFXRate, fx} import code.api.dynamic.endpoint.helper._ import code.api.dynamic.endpoint.helper.practise.PractiseEndpoint import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo} @@ -58,6 +59,7 @@ import code.dynamicMessageDoc.JsonDynamicMessageDoc import code.dynamicResourceDoc.JsonDynamicResourceDoc import code.endpointMapping.EndpointMappingCommons import code.entitlement.Entitlement +import code.fx.fx import code.loginattempts.LoginAttempt import code.metadata.counterparties.{Counterparties, MappedCounterparty} import code.metadata.tags.Tags @@ -103,6 +105,8 @@ import net.liftweb.util.Mailer.{From, PlainMailBodyType, Subject, To, XHTMLMailB import net.liftweb.util.{Helpers, Mailer, StringHelpers} import org.apache.commons.lang3.StringUtils +import java.time.{LocalDate, ZoneId, ZonedDateTime} +import java.util.Date import scala.collection.immutable.{List, Nil} import scala.collection.mutable.ArrayBuffer import scala.concurrent.Future @@ -12599,6 +12603,107 @@ object APIMethods400 extends RestHelper with APIMethods400 { } toCounterpartyId = transactionRequestBodyCounterparty.to.counterparty_id (toCounterparty, callContext) <- NewStyle.function.getCounterpartyByCounterpartyId(CounterpartyId(toCounterpartyId), callContext) + + (counterpartyLimit, callContext) <- NewStyle.function.getCounterpartyLimit( + bankId.value, + accountId.value, + viewId.value, + toCounterpartyId, + callContext + ) + + maxSingleAmount = counterpartyLimit.maxSingleAmount + maxMonthlyAmount = counterpartyLimit.maxMonthlyAmount + maxNumberOfMonthlyTransactions = counterpartyLimit.maxNumberOfMonthlyTransactions + maxYearlyAmount = counterpartyLimit.maxYearlyAmount + maxNumberOfYearlyTransactions = counterpartyLimit.maxNumberOfYearlyTransactions + + // Get the first day of the current month + firstDayOfMonth: LocalDate = LocalDate.now().withDayOfMonth(1) + + // Get the last day of the current month + lastDayOfMonth: LocalDate = LocalDate.now().withDayOfMonth( + LocalDate.now().lengthOfMonth() + ) + // Get the first day of the current year + firstDayOfYear: LocalDate = LocalDate.now().withDayOfYear(1) + + // Get the last day of the current year + lastDayOfYear: LocalDate = LocalDate.now().withDayOfYear( + LocalDate.now().lengthOfYear() + ) + + // Convert LocalDate to Date + zoneId: ZoneId = ZoneId.systemDefault() + firstCurrentMonthDate: Date = Date.from(firstDayOfMonth.atStartOfDay(zoneId).toInstant) + lastCurrentMonthDate: Date = Date.from(lastDayOfMonth.atStartOfDay(zoneId).toInstant) + + firstCurrentYearDate: Date = Date.from(firstDayOfYear.atStartOfDay(zoneId).toInstant) + lastCurrentYearDate: Date = Date.from(lastDayOfYear.atStartOfDay(zoneId).toInstant) + + (sumOfTransactionsFromAccountToCounterpartyMonthly, callContext) <- NewStyle.function.getSumOfTransactionsFromAccountToCounterparty( + fromAccount.bankId: BankId, + fromAccount.accountId: AccountId, + CounterpartyId(toCounterpartyId): CounterpartyId, + firstCurrentMonthDate: Date, + lastCurrentMonthDate: Date, + callContext: Option[CallContext] + ) + + (countOfTransactionsFromAccountToCounterpartyMonthly, callContext) <- NewStyle.function.getCountOfTransactionsFromAccountToCounterparty( + fromAccount.bankId: BankId, + fromAccount.accountId: AccountId, + CounterpartyId(toCounterpartyId): CounterpartyId, + firstCurrentMonthDate: Date, + lastCurrentMonthDate: Date, + callContext: Option[CallContext] + ) + + (sumOfTransactionsFromAccountToCounterpartyYearly, callContext) <- NewStyle.function.getSumOfTransactionsFromAccountToCounterparty( + fromAccount.bankId: BankId, + fromAccount.accountId: AccountId, + CounterpartyId(toCounterpartyId): CounterpartyId, + firstCurrentYearDate: Date, + lastCurrentYearDate: Date, + callContext: Option[CallContext] + ) + + (countOfTransactionsFromAccountToCounterpartyYearly, callContext) <- NewStyle.function.getCountOfTransactionsFromAccountToCounterparty( + fromAccount.bankId: BankId, + fromAccount.accountId: AccountId, + CounterpartyId(toCounterpartyId): CounterpartyId, + firstCurrentYearDate: Date, + lastCurrentYearDate: Date, + callContext: Option[CallContext] + ) + + + currentTransactionAmountWithFxApplied <- NewStyle.function.tryons(s"${InvalidJsonFormat}, it should be $COUNTERPARTY json format", 400, callContext) { + val fromAccountCurrency = fromAccount.currency //eg: if from account currency is EUR + val transferCurrency = transactionRequestBodyCounterparty.value.currency //eg: if the payment json body currency is GBP. + val transferAmount= BigDecimal(transactionRequestBodyCounterparty.value.amount) //eg: if the payment json body amount is 1. + val debitRate = fx.exchangeRate(transferCurrency, fromAccountCurrency, Some(fromAccount.bankId.value), callContext) //eg: the rate here is 1.16278. + fx.convert(transferAmount, debitRate) // 1.16278 Euro + } + + _ <- Helper.booleanToFuture(s"$CounterpartyLimitValidationError maxSingleAmount is $maxSingleAmount ${fromAccount.currency}, " + + s"but current transaction body amount is ${transactionRequestBodyCounterparty.value.amount} ${transactionRequestBodyCounterparty.value.currency}, " + + s"which is $currentTransactionAmountWithFxApplied ${fromAccount.currency}. ", cc=callContext) { + BigDecimal(maxSingleAmount) >= currentTransactionAmountWithFxApplied + } + _ <- Helper.booleanToFuture(s"$CounterpartyLimitValidationError maxMonthlyAmount is $maxSingleAmount, but current monthly amount is ${sumOfTransactionsFromAccountToCounterpartyMonthly.amount}", cc=callContext) { + BigDecimal(maxMonthlyAmount) >= BigDecimal(sumOfTransactionsFromAccountToCounterpartyMonthly.amount) + } + _ <- Helper.booleanToFuture(s"$CounterpartyLimitValidationError maxNumberOfMonthlyTransactions is $maxSingleAmount, but current count of monthly transactions is ${countOfTransactionsFromAccountToCounterpartyMonthly}", cc=callContext) { + maxNumberOfMonthlyTransactions >= countOfTransactionsFromAccountToCounterpartyMonthly + } + _ <- Helper.booleanToFuture(s"$CounterpartyLimitValidationError maxYearlyAmount is $maxYearlyAmount, but current yearly amount is ${sumOfTransactionsFromAccountToCounterpartyYearly.amount}", cc=callContext) { + BigDecimal(maxYearlyAmount) >= BigDecimal(sumOfTransactionsFromAccountToCounterpartyYearly.amount) + } + _ <- Helper.booleanToFuture(s"$CounterpartyLimitValidationError maxNumberOfYearlyTransactions is $maxNumberOfYearlyTransactions, but current count of yearly transaction is ${countOfTransactionsFromAccountToCounterpartyYearly}", cc=callContext) { + maxNumberOfYearlyTransactions >= countOfTransactionsFromAccountToCounterpartyYearly + } + toAccount <- NewStyle.function.getBankAccountFromCounterparty(toCounterparty, true, callContext) // Check we can send money to it. _ <- Helper.booleanToFuture(s"$CounterpartyBeneficiaryPermit", cc=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 fe8b246d9..2ae7d7eab 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 @@ -943,7 +943,7 @@ trait APIMethods500 { // TODO Add routing scheme as well. In case IBAN is provided this will not work. val fromBankIdAccountId = BankIdAccountId(BankId(postConsentRequestJsonV510.from_account.bank_routing.address), AccountId(postConsentRequestJsonV510.from_account.account_routing.address)) - val vrpViewId = s"_VRP-${UUID.randomUUID.toString}".dropRight(5)// to make sure the length of the viewId is 36. + val vrpViewId = s"_vrp-${UUID.randomUUID.toString}".dropRight(5)// to make sure the length of the viewId is 36. val targetPermissions = List(//may need getTransactionRequest .. so far only this payments. "can_add_transaction_request_to_beneficiary", "can_get_counterparty"