Skip to content

Commit

Permalink
Merge pull request #45 from hmrc/feature/yta-3215
Browse files Browse the repository at this point in the history
YTA-3215: Added endpoint for Payment History with Allocations.
  • Loading branch information
flashingpumpkin authored Mar 8, 2018
2 parents 8fd8034 + 3a490aa commit 5befc9d
Show file tree
Hide file tree
Showing 23 changed files with 1,359 additions and 6 deletions.
6 changes: 6 additions & 0 deletions app/uk/gov/hmrc/epayeapi/connectors/EpayeConnector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,11 @@ case class EpayeConnector @Inject() (

get[EpayePaymentHistory](url, headers)
}

def getPaymentHistoryWithAllocations(empRef: EmpRef, taxYear: TaxYear, headers: HeaderCarrier): Future[EpayeResponse[EpayePaymentHistoryWithAllocations]] = {
val url = s"${config.epayeBaseUrl}/epaye/${empRef.encodedValue}/api/v1/payment-history-with-allocations/${taxYear.asString}"

get[EpayePaymentHistoryWithAllocations](url, headers)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2018 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.epayeapi.controllers

import javax.inject.{Inject, Singleton}

import akka.stream.Materializer
import play.api.libs.json.Json
import play.api.mvc.{Action, EssentialAction, Result}
import uk.gov.hmrc.auth.core.AuthConnector
import uk.gov.hmrc.domain.EmpRef
import uk.gov.hmrc.epayeapi.connectors.{EpayeApiConfig, EpayeConnector}
import uk.gov.hmrc.epayeapi.models.Formats.paymentHistoryWithAllocationsJsonWrites
import uk.gov.hmrc.epayeapi.models.TaxYear
import uk.gov.hmrc.epayeapi.models.in.{EpayePaymentHistory, EpayePaymentHistoryWithAllocations, EpayeResponse, EpayeSuccess}
import uk.gov.hmrc.epayeapi.models.out.PaymentHistoryWithAllocationsJson

import scala.concurrent.ExecutionContext

@Singleton
case class GetPaymentHistoryWithAllocationsController @Inject() (
config: EpayeApiConfig,
authConnector: AuthConnector,
epayeConnector: EpayeConnector,
implicit val ec: ExecutionContext,
implicit val mat: Materializer
)
extends ApiController
with EpayeErrorHandler {

def getPaymentHistoryWithAllocations(empRef: EmpRef, taxYear: TaxYear): EssentialAction = {
EmpRefAction(empRef) {
Action.async { implicit request =>
val epayePaymentHistory = epayeConnector.getPaymentHistoryWithAllocations(empRef, taxYear, hc)
epayePaymentHistory.map {
successHandler(empRef, taxYear) orElse errorHandler
}
}
}
}

private def successHandler(empRef: EmpRef, taxYear: TaxYear): PartialFunction[EpayeResponse[EpayePaymentHistoryWithAllocations], Result] = {
case EpayeSuccess(epayePaymentHistory) =>
val paymentHistory = PaymentHistoryWithAllocationsJson.transform(config.apiBaseUrl, empRef, taxYear, epayePaymentHistory)

Ok(Json.toJson(paymentHistory))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2018 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.epayeapi.models.in

import org.joda.time.LocalDate
import uk.gov.hmrc.epayeapi.models.TaxYear

case class EpayePaymentHistoryWithAllocations(
taxYear: TaxYear,
payments: Seq[EpayePaymentHistoryWithAllocationsPayment]
)

case class EpayePaymentHistoryWithAllocationsPayment(
paymentDate: Option[LocalDate],
method: Option[String],
amount: BigDecimal,
allocatedAmount: BigDecimal,
allocations: Seq[EpayePaymentAllocation]
)

trait EpayePaymentAllocation {
def period: EpayeTaxPeriod
def amount: BigDecimal
def code: Option[EpayeCode]

lazy val taxYear: TaxYear = TaxYear(period.startingTaxYear)
lazy val taxMonth: EpayeTaxMonth = EpayeTaxMonth.fromLocalDate(period.taxFrom)
}

case class EpayeRtiPaymentAllocation(
period: EpayeTaxPeriod,
amount: BigDecimal
) extends EpayePaymentAllocation {
lazy val code: Option[EpayeCode] = None
}

case class EpayeNonRtiPaymentAllocation(
period: EpayeTaxPeriod,
amount: BigDecimal,
code: Option[EpayeCode]
) extends EpayePaymentAllocation
14 changes: 14 additions & 0 deletions app/uk/gov/hmrc/epayeapi/models/in/EpayeReads.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import uk.gov.hmrc.epayeapi.models.TaxYear
trait EpayeReads {
implicit lazy val taxYearReads: Reads[TaxYear] = reads[TaxYear]
implicit lazy val epayeTaxMonthReads: Reads[EpayeTaxMonth] = reads[EpayeTaxMonth]
implicit lazy val epayeTaxPeriodReads: Reads[EpayeTaxPeriod] = reads[EpayeTaxPeriod]
implicit lazy val epayeCodeReads: Reads[EpayeCode] = new Reads[EpayeCode]() {
override def reads(json: JsValue): JsResult[EpayeCode] =
json match {
Expand Down Expand Up @@ -57,5 +58,18 @@ trait EpayeReads {
implicit lazy val epayePaymentHistoryReads: Reads[EpayePaymentHistory] = reads[EpayePaymentHistory]
implicit lazy val epayePaymentHistoryPaymentReads: Reads[EpayePaymentHistoryPayment] = reads[EpayePaymentHistoryPayment]

implicit lazy val epayePaymentHistoryWithAllocationsReads: Reads[EpayePaymentHistoryWithAllocations] = reads[EpayePaymentHistoryWithAllocations]
implicit lazy val epayePaymentHistoryWithAllocationsPaymentReads: Reads[EpayePaymentHistoryWithAllocationsPayment] = reads[EpayePaymentHistoryWithAllocationsPayment]
implicit lazy val epayeRtiPaymentAllocationReads: Reads[EpayeRtiPaymentAllocation] = reads[EpayeRtiPaymentAllocation]
implicit lazy val epayeNonRtiPaymentAllocationReads: Reads[EpayeNonRtiPaymentAllocation] = reads[EpayeNonRtiPaymentAllocation]
implicit lazy val epayePaymentAllocationReads: Reads[EpayePaymentAllocation] = new Reads[EpayePaymentAllocation]() {
override def reads(json: JsValue): JsResult[EpayePaymentAllocation] = {
json \ "code" match {
case JsDefined(_) => epayeNonRtiPaymentAllocationReads.reads(json)
case _ => epayeRtiPaymentAllocationReads.reads(json)
}
}
}

implicit lazy val epayeEmpRefsResponse: Format[EpayeEmpRefsResponse] = Json.format[EpayeEmpRefsResponse]
}
14 changes: 14 additions & 0 deletions app/uk/gov/hmrc/epayeapi/models/in/EpayeTaxMonth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,18 @@

package uk.gov.hmrc.epayeapi.models.in

import org.joda.time.LocalDate

case class EpayeTaxMonth(month: Int)

object EpayeTaxMonth {
def fromLocalDate(date: LocalDate): EpayeTaxMonth = {
val month = if (date.getDayOfMonth < 6) {
date.minusMonths(1).getMonthOfYear
} else {
date.getMonthOfYear
}

EpayeTaxMonth(((month + 8) % 12) + 1)
}
}
58 changes: 58 additions & 0 deletions app/uk/gov/hmrc/epayeapi/models/in/EpayeTaxPeriod.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2018 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.epayeapi.models.in

import org.joda.time.LocalDate

case class EpayeTaxPeriod(taxFrom: LocalDate, taxTo: LocalDate) {
/**
* Gets the TaxMonth from a tax period. This requires the period's start date to be on the 6th of the given month
* and the end date to be on the 5th of the next month otherwise it is considered invalid
*
* Note: For quirky reasons which will go away soon, the start date is also valid if it is on the 4th...
*
*/
def taxMonth: Option[Int] = {
implicit val dateAscendingOrdering = uk.gov.hmrc.epayeapi.models.ImplicitOrderings.localDateDescendingOrdering.reverse

val Seq(lowerDate, upperDate) = Seq(taxFrom, taxTo).sorted
val expectedTo1 = lowerDate.plusMonths(1).minusDays(1)
val isTaxToCorrect1 = expectedTo1 == upperDate
(lowerDate.getMonthOfYear, lowerDate.getDayOfMonth) match {
case (m, 6) if isTaxToCorrect1 => Some(((m + 8) % 12) + 1)
case _ => None
}
}

def taxYear: Option[Int] =
if (taxFrom == startOfTaxYear && taxTo == endOfTaxYear) {
Some(taxFrom.getYear)
} else {
None
}

def startingTaxYear: Int =
if (taxFrom.isBefore(startOfTaxYear)) {
taxFrom.getYear - 1
} else {
taxFrom.getYear
}

private def startOfTaxYear: LocalDate = new LocalDate(taxFrom.getYear, 4, 6)

private def endOfTaxYear: LocalDate = startOfTaxYear.plusYears(1).minusDays(1)
}
5 changes: 5 additions & 0 deletions app/uk/gov/hmrc/epayeapi/models/out/JsonWrites.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,9 @@ trait JsonWrites {

implicit lazy val paymentHistoryJsonWrites: Writes[PaymentHistoryJson] = writes[PaymentHistoryJson]
implicit lazy val paymentHistoryLinksJsonWrites: Writes[PaymentHistoryLinks] = writes[PaymentHistoryLinks]

implicit lazy val paymentHistoryWithAllocationsJsonWrites: Writes[PaymentHistoryWithAllocationsJson] = writes[PaymentHistoryWithAllocationsJson]
implicit lazy val paymentWithAllocationsJsonWrites: Writes[PaymentWithAllocations] = writes[PaymentWithAllocations]
implicit lazy val paymentAllocationJsonWrites: Writes[PaymentAllocation] = writes[PaymentAllocation]
implicit lazy val paymentHistoryWithAllocationsLinksJsonWrites: Writes[PaymentHistoryWithAllocationsLinks] = writes[PaymentHistoryWithAllocationsLinks]
}
3 changes: 3 additions & 0 deletions app/uk/gov/hmrc/epayeapi/models/out/Link.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ object Link {

def paymentHistoryLink(empRef: EmpRef, taxYear: TaxYear): Link =
Link(s"$prefix/${empRef.taxOfficeNumber}/${empRef.taxOfficeReference}/payment-history/${taxYear.asString}")

def paymentHistoryWithAllocationsLink(empRef: EmpRef, taxYear: TaxYear): Link =
Link(s"$prefix/${empRef.taxOfficeNumber}/${empRef.taxOfficeReference}/payment-history/${taxYear.asString}/allocations")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright 2018 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.epayeapi.models.out

import org.joda.time.LocalDate
import play.api.Logger
import uk.gov.hmrc.domain.EmpRef
import uk.gov.hmrc.epayeapi.models.{TaxMonth, TaxYear}
import uk.gov.hmrc.epayeapi.models.in.{EpayePaymentAllocation, EpayePaymentHistoryWithAllocations, EpayePaymentHistoryWithAllocationsPayment, EpayeRtiPaymentAllocation}

case class PaymentHistoryWithAllocationsJson (
taxOfficeNumber: String,
taxOfficeReference: String,
taxYear: TaxYear,
payments: Seq[PaymentWithAllocations],
_links: PaymentHistoryWithAllocationsLinks
)

case class PaymentWithAllocations(
paymentDate: LocalDate,
method: Option[String],
amount: BigDecimal,
allocatedAmount: BigDecimal,
allocations: Seq[PaymentAllocation]
)

case class PaymentAllocation(
taxYear: TaxYear,
taxMonth: TaxMonth,
amount: BigDecimal,
code: Option[String]
)

object PaymentAllocation{
def transform(epayePaymentAllocation: EpayePaymentAllocation): PaymentAllocation = {
PaymentAllocation(
epayePaymentAllocation.taxYear,
TaxMonth(epayePaymentAllocation.taxYear, epayePaymentAllocation.taxMonth.month),
epayePaymentAllocation.amount,
epayePaymentAllocation.code.map{_.name}
)
}
}

case class PaymentHistoryWithAllocationsLinks (
empRefs: Link,
summary: Link,
statements: Link,
payments: Link,
self: Link,
next: Link,
previous: Link
)

object PaymentWithAllocations {
import uk.gov.hmrc.epayeapi.models.ImplicitOrderings.localDateDescendingOrdering

implicit val paymentDescendingOrdering = Ordering.by[PaymentWithAllocations, (LocalDate, BigDecimal)](payment => (payment.paymentDate, -payment.amount))

private lazy val paymentMethods: Map[String, String] = Map(
"TPS RECEIPTS BY DEBIT CARD" -> "Debit Card",
"PAYMENTS MADE BY CHEQUE" -> "Cheque",
"CHEQUE RECEIPTS" -> "Cheque",
"BACS RECEIPTS" -> "BACS",
"CHAPS" -> "CHAPS",
"TPS RECEIPTS BY CREDIT CARD" -> "Credit Card",
"NATIONAL DIRECT DEBIT RECEIPTS" -> "Direct Debit",
"BILLPAY/OLPG/GIROBANK" -> "Online Payment",
"BANK LODGEMENT PAYMENT" -> "Bank Lodgement",
"BANK GIRO RECEIPTS" -> "Giro Receipts",
"BANK GIRO IN CREDITS" -> "Giro Credits",
"FPS RECEIPTS" -> "FPS Receipts",
"CREDIT FOR INTERNET RECEIPTS" -> "Internet Receipts",
"GIROBANK RECEIPTS" -> "Girobank",
"GIROBANK/ POST OFFICE" -> "Post Office",
"NIL DECLARATIONS" -> "Nil Declarations",
"PAYMASTER" -> "Paymaster",
"VOLUNTARY DIRECT PAYMENTS" -> "Voluntary Payments"
) withDefault { unknown =>
Logger.warn(s"Invalid payment method: [${unknown}].")
"UNKNOWN"
}

def transform(paymentMethodMaybe: Option[String]): Option[String] = {
paymentMethodMaybe.map { paymentMethods }
}
}

object PaymentHistoryWithAllocationsJson {
def transform(
apiBaseUrl: String,
empRef: EmpRef,
taxYear: TaxYear,
epayePaymentHistory: EpayePaymentHistoryWithAllocations): PaymentHistoryWithAllocationsJson = {

val payments: Seq[PaymentWithAllocations] = for {
epayePayment <- epayePaymentHistory.payments
epayePaymentDate <- epayePayment.paymentDate
} yield toPaymentWithAllocations(epayePayment, epayePaymentDate)

PaymentHistoryWithAllocationsJson(
empRef.taxOfficeNumber,
empRef.taxOfficeReference,
taxYear,
payments.sorted(PaymentWithAllocations.paymentDescendingOrdering),
PaymentHistoryWithAllocationsLinks(
empRefs = Link.empRefsLink,
summary = Link.summaryLink(empRef),
statements = Link.statementsLink(empRef),
payments = Link.paymentHistoryLink(empRef, taxYear),
self = Link.paymentHistoryWithAllocationsLink(empRef, taxYear),
next = Link.paymentHistoryWithAllocationsLink(empRef, taxYear.next),
previous = Link.paymentHistoryWithAllocationsLink(empRef, taxYear.previous)
)
)
}

private def toPaymentWithAllocations(
epayePaymentWithAllocations: EpayePaymentHistoryWithAllocationsPayment,
epayePaymentDate: LocalDate): PaymentWithAllocations = {

PaymentWithAllocations(
epayePaymentDate,
PaymentWithAllocations.transform(epayePaymentWithAllocations.method),
epayePaymentWithAllocations.amount,
epayePaymentWithAllocations.allocatedAmount,
epayePaymentWithAllocations.allocations.map{ PaymentAllocation.transform })
}
}
Loading

0 comments on commit 5befc9d

Please sign in to comment.