diff --git a/app/v1/controllers/PaymentsController.scala b/app/v1/controllers/PaymentsController.scala index 6eec6868..69c37238 100644 --- a/app/v1/controllers/PaymentsController.scala +++ b/app/v1/controllers/PaymentsController.scala @@ -89,6 +89,7 @@ extends AuthorisedController(cc) with BaseController with Logging { private def errorResult(errorWrapper: ErrorWrapper): Result = { (errorWrapper.error: @unchecked) match { + case RuleDuplicatePaymentError => Conflict(Json.toJson(errorWrapper)) // Error case to Handle duplicate payments case VrnFormatError | VrnFormatErrorDes | FinancialDataInvalidDateFromError | InvalidDateFromErrorDes | FinancialDataInvalidDateToError | InvalidDateToErrorDes | diff --git a/app/v1/models/errors/mtdErrors.scala b/app/v1/models/errors/mtdErrors.scala index 1fa460b6..4973677d 100644 --- a/app/v1/models/errors/mtdErrors.scala +++ b/app/v1/models/errors/mtdErrors.scala @@ -158,4 +158,7 @@ object DuplicateVatSubmission extends MtdError( ) )) -object RuleIncorrectGovTestScenarioError extends MtdError(code = "RULE_INCORRECT_GOV_TEST_SCENARIO", message = "The Gov-Test-Scenario was not found") \ No newline at end of file +object RuleIncorrectGovTestScenarioError extends MtdError(code = "RULE_INCORRECT_GOV_TEST_SCENARIO", message = "The Gov-Test-Scenario was not found") +object RuleDuplicatePaymentError extends MtdError( + "RULE_DUPLICATE_PAYMENT", + "Duplicate payment found: paymentLot, and paymentLotItem must be unique") \ No newline at end of file diff --git a/app/v1/models/response/payments/PaymentItem.scala b/app/v1/models/response/payments/PaymentItem.scala index 09cd6a81..dc41906d 100644 --- a/app/v1/models/response/payments/PaymentItem.scala +++ b/app/v1/models/response/payments/PaymentItem.scala @@ -17,19 +17,25 @@ package v1.models.response.payments import play.api.libs.functional.syntax._ -import play.api.libs.json.{JsPath, Json, OWrites, Reads} +import play.api.libs.json.{JsNull, JsPath, Json, OWrites, Reads, __} case class PaymentItem(amount: Option[BigDecimal], - received: Option[String]) + received: Option[String], + paymentLot: Option[String], + paymentLotItem: Option[String]) object PaymentItem { - - val empty: PaymentItem = PaymentItem(None, None) - - implicit val writes: OWrites[PaymentItem] = Json.writes[PaymentItem] - + val empty: PaymentItem = PaymentItem(None, None, None, None) + implicit val writes: OWrites[PaymentItem] = OWrites[PaymentItem] { paymentItem => + Json.obj( + "amount" -> paymentItem.amount, + "received" -> paymentItem.received + ).fields.filterNot(_._2 == JsNull).foldLeft(Json.obj())(_ + _) + } implicit val reads: Reads[PaymentItem] = ( (JsPath \ "paymentAmount").readNullable[BigDecimal] and - (JsPath \ "clearingDate").readNullable[String] + (JsPath \ "clearingDate").readNullable[String] and + (JsPath \ "paymentLot").readNullable[String] and + (JsPath \ "paymentLotItem").readNullable[String] ) (PaymentItem.apply _) } diff --git a/app/v1/services/PaymentsService.scala b/app/v1/services/PaymentsService.scala index f334efe9..d907b1b9 100644 --- a/app/v1/services/PaymentsService.scala +++ b/app/v1/services/PaymentsService.scala @@ -42,11 +42,31 @@ class PaymentsService @Inject()(connector: PaymentsConnector) extends DesRespons val result = for { desResponseWrapper <- EitherT(connector.retrievePayments(request)).leftMap(mapDesErrors(desErrorMap)) mtdResponseWrapper <- EitherT.fromEither[Future](validatePaymentsSuccessResponse(desResponseWrapper)) - } yield mtdResponseWrapper - + } yield { + val deduplicatedPayments = ensureUniquePayments(mtdResponseWrapper.responseData) + mtdResponseWrapper.copy(responseData = deduplicatedPayments) + } result.value } - + private def ensureUniquePayments(paymentsResponse: PaymentsResponse): PaymentsResponse = { + val seen = scala.collection.mutable.Set[(BigDecimal, String, String)]() + val uniquePayments = paymentsResponse.payments.map { payment => + payment.paymentItems match { + case Some(items) => + val uniqueItems = items.filter { item => + val key = ( + item.amount.getOrElse(BigDecimal(0)), + item.paymentLot.getOrElse("").trim, + item.paymentLotItem.getOrElse("").trim + ) + !seen.contains(key) && seen.add(key) + } + payment.copy(paymentItems = Some(uniqueItems)) + case None => payment + } + } + paymentsResponse.copy(payments = uniquePayments) + } private val desErrorMap: Map[String, MtdError] = Map( "INVALID_IDTYPE" -> DownstreamError, diff --git a/func/test/v1/fixtures/PaymentsFixture.scala b/func/test/v1/fixtures/PaymentsFixture.scala index 216115bc..c206aba7 100644 --- a/func/test/v1/fixtures/PaymentsFixture.scala +++ b/func/test/v1/fixtures/PaymentsFixture.scala @@ -266,24 +266,24 @@ trait PaymentsFixture { taxPeriod = Some(TaxPeriod(from = "2017-02-01", to = "2017-02-28")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(15.0), received = Some("2017-02-11")) + PaymentItem(amount = Some(15.0), received = Some("2017-02-11"), paymentLot = Some("081203010024"), paymentLotItem = Some ("000001")) )) ), Payment( taxPeriod = Some(TaxPeriod(from = "2017-03-01", to = "2017-03-25")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(40.00), received = Some("2017-03-11")), - PaymentItem(amount = Some(1001.00), received = Some("2017-03-12")) + PaymentItem(amount = Some(40.00), received = Some("2017-03-11"), paymentLot = Some("081203010024"), paymentLotItem = Some ("000001")), + PaymentItem(amount = Some(1001.00), received = Some("2017-03-12"), paymentLot = Some("081203010024"), paymentLotItem = Some ("000001")) )) ), Payment( taxPeriod = Some(TaxPeriod(from = "2017-08-01", to = "2017-12-20")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(Some(322.00), Some("2017-08-05")), - PaymentItem(Some(90.00), None), - PaymentItem(Some(6.00), Some("2017-09-12")) + PaymentItem(Some(322.00), Some("2017-08-05"), paymentLot = Some("081203010024"), paymentLotItem = Some ("000001")), + PaymentItem(Some(90.00), None, paymentLot = Some("081203010024"), paymentLotItem = Some ("000002")), + PaymentItem(Some(6.00), Some("2017-09-12"), paymentLot = Some("081203010024"), paymentLotItem = Some ("000003")) )) ) ) diff --git a/test/v1/connectors/PaymentsConnectorSpec.scala b/test/v1/connectors/PaymentsConnectorSpec.scala index 11a58d70..065a9c00 100644 --- a/test/v1/connectors/PaymentsConnectorSpec.scala +++ b/test/v1/connectors/PaymentsConnectorSpec.scala @@ -42,7 +42,7 @@ class PaymentsConnectorSpec extends ConnectorSpec { taxPeriod = Some(TaxPeriod(from = "2017-1-1", to = "2017-12-31")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(200.00), received = Some("2017-03-12")) + PaymentItem(amount = Some(200.00), received = Some("2017-03-12"), paymentLot = Some("01234"), paymentLotItem = Some("0001")) )) ) ) @@ -55,14 +55,14 @@ class PaymentsConnectorSpec extends ConnectorSpec { taxPeriod = Some(TaxPeriod(from = "2017-1-1", to = "2017-12-31")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(200.00), received = Some("2017-03-12")) + PaymentItem(amount = Some(200.00), received = Some("2017-03-12"), paymentLot = Some("01234"), paymentLotItem = Some("0001")) )) ), Payment( taxPeriod = Some(TaxPeriod(from = "2018-1-1", to = "2018-12-31")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(2375.23), received = Some("2018-03-12")) + PaymentItem(amount = Some(2375.23), received = Some("2018-03-12"), paymentLot = Some("56789"), paymentLotItem = Some("0002")) )) ) ) diff --git a/test/v1/controllers/PaymentsControllerSpec.scala b/test/v1/controllers/PaymentsControllerSpec.scala index 5f9a722c..1fa1feb3 100644 --- a/test/v1/controllers/PaymentsControllerSpec.scala +++ b/test/v1/controllers/PaymentsControllerSpec.scala @@ -79,24 +79,24 @@ class PaymentsControllerSpec taxPeriod = Some(TaxPeriod(from = "2017-02-01", to = "2017-02-28")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(15.0), received = Some("2017-02-11")) + PaymentItem(amount = Some(15.0), received = Some("2017-02-11"), paymentLot = Some("01234"), paymentLotItem = Some("0001")) )) ), Payment( taxPeriod = Some(TaxPeriod(from = "2017-03-01", to = "2017-03-25")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(40.00), received = Some("2017-03-11")), - PaymentItem(amount = Some(1001.00), received = Some("2017-03-12")) + PaymentItem(amount = Some(40.00), received = Some("2017-03-11"), paymentLot = Some("01234"), paymentLotItem = Some("0001")), + PaymentItem(amount = Some(1001.00), received = Some("2017-03-12"), paymentLot = Some("56789"), paymentLotItem = Some("0002")) )) ), Payment( taxPeriod = Some(TaxPeriod(from = "2017-08-01", to = "2017-12-20")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(Some(322.00), Some("2017-08-05")), - PaymentItem(Some(90.00), None), - PaymentItem(Some(6.00), Some("2017-09-12")) + PaymentItem(Some(322.00), Some("2017-08-05"), paymentLot = Some("01234"), paymentLotItem = Some("0001")), + PaymentItem(Some(90.00), None, paymentLot = Some("56789"), paymentLotItem = Some("0002")), + PaymentItem(Some(6.00), Some("2017-09-12"), paymentLot = Some("12345"), paymentLotItem = Some("0003")) )) ) ) diff --git a/test/v1/models/response/payments/PaymentItemSpec.scala b/test/v1/models/response/payments/PaymentItemSpec.scala index cb10e123..e9cb5c13 100644 --- a/test/v1/models/response/payments/PaymentItemSpec.scala +++ b/test/v1/models/response/payments/PaymentItemSpec.scala @@ -25,7 +25,9 @@ class PaymentItemSpec extends UnitSpec { """ |{ | "paymentAmount" : 100.2, - | "clearingDate" : "2017-01-01" + | "clearingDate" : "2017-01-01", + | "paymentLot":"01234", + | "paymentLotItem":"0001" |} """.stripMargin ) @@ -34,7 +36,9 @@ class PaymentItemSpec extends UnitSpec { """ |{ | "paymentAmount" : 100.2, - | "clearingDate" : false + | "clearingDate" : false, + | "paymentLot":"01234", + | "paymentLotItem":"0001" |} """.stripMargin ) @@ -49,7 +53,7 @@ class PaymentItemSpec extends UnitSpec { ) val paymentItemModel: PaymentItem = - PaymentItem(amount = Some(100.2), received = Some("2017-01-01")) + PaymentItem(amount = Some(100.2), received = Some("2017-01-01"), paymentLot = Some("01234"), paymentLotItem = Some("0001")) "PaymentItem" when { "read from valid JSON" should { @@ -74,8 +78,7 @@ class PaymentItemSpec extends UnitSpec { "not write empty fields" in { val emptyPaymentItemModel: PaymentItem = - PaymentItem(amount = None, received = None) - + PaymentItem(amount = None, received = None, paymentLot = None, paymentLotItem = None) Json.toJson(emptyPaymentItemModel) shouldBe JsObject.empty } } diff --git a/test/v1/models/response/payments/PaymentSpec.scala b/test/v1/models/response/payments/PaymentSpec.scala index a4ef009e..27ca9d5f 100644 --- a/test/v1/models/response/payments/PaymentSpec.scala +++ b/test/v1/models/response/payments/PaymentSpec.scala @@ -144,7 +144,7 @@ class PaymentSpec extends UnitSpec { taxPeriod = Some(TaxPeriod(from = "2017-02-01", to = "2017-02-28")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(5.00), received = Some("2017-02-11")) + PaymentItem(amount = Some(5.00), received = Some("2017-02-11"), paymentLot = Some("081203010024"), paymentLotItem = Some("000001")), )) ) @@ -153,8 +153,8 @@ class PaymentSpec extends UnitSpec { taxPeriod = Some(TaxPeriod("2017-03-01", "2017-03-25")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(50.00), received = Some("2017-03-11")), - PaymentItem(amount = Some(1000.00), received = Some("2017-03-12")) + PaymentItem(amount = Some(50.00), received = Some("2017-03-11"), paymentLot = Some("081203010024"), paymentLotItem = Some("000001")), + PaymentItem(amount = Some(1000.00), received = Some("2017-03-12"), paymentLot = Some("081203010024"), paymentLotItem = Some("000001")) )) ) @@ -205,15 +205,21 @@ class PaymentSpec extends UnitSpec { | "items": [ | { | "clearingDate":"2017-02-11", - | "paymentAmount":5.0 + | "paymentAmount":5.0, + | "paymentLot":"081203010024", + | "paymentLotItem":"000001" | }, | { - | "paymentAmount":-15.2 + | "paymentAmount":-15.2, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | }, | { | "subItem":"001", | "dueDate":"2017-03-02", - | "amount":1001.00 + | "amount":1001.00, + | "paymentLot":"6789", + | "paymentLotItem":"0002" | } | | ] @@ -226,8 +232,8 @@ class PaymentSpec extends UnitSpec { taxPeriod = Some(TaxPeriod("2017-02-01", "2017-02-28")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(5.00), received = Some("2017-02-11")), - PaymentItem(amount = Some(-15.2), received = None) + PaymentItem(amount = Some(5.00), received = Some("2017-02-11"), paymentLot = Some("081203010024"), paymentLotItem = Some("000001")), + PaymentItem(amount = Some(-15.2), received = None, paymentLot = Some("01234"), paymentLotItem = Some("0001")) )) ) diff --git a/test/v1/models/response/payments/PaymentsResponseSpec.scala b/test/v1/models/response/payments/PaymentsResponseSpec.scala index bc848ae9..11d1b913 100644 --- a/test/v1/models/response/payments/PaymentsResponseSpec.scala +++ b/test/v1/models/response/payments/PaymentsResponseSpec.scala @@ -42,11 +42,15 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-02-11", - | "paymentAmount":5.0 + | "paymentAmount":5.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | }, | { | "clearingDate":"2017-04-11", - | "paymentAmount":10.0 + | "paymentAmount":10.0, + | "paymentLot":"56789", + | "paymentLotItem":"0002" | } | ] | }, @@ -58,7 +62,9 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-03-19", - | "paymentAmount":-25.0 + | "paymentAmount":-25.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | } | ] | } @@ -95,15 +101,15 @@ class PaymentsResponseSpec extends UnitSpec { taxPeriod = Some(TaxPeriod(from = "2017-02-01", to = "2017-02-28")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(5.00), received = Some("2017-02-11")), - PaymentItem(amount = Some(10.00), received = Some("2017-04-11")) + PaymentItem(amount = Some(5.00), received = Some("2017-02-11"), paymentLot = Some("01234"), paymentLotItem = Some("0001")), + PaymentItem(amount = Some(10.00), received = Some("2017-04-11"), paymentLot = Some("56789"), paymentLotItem = Some("0002")) )) ), Payment( taxPeriod = None, `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(-25.00), received = Some("2017-03-19")) + PaymentItem(amount = Some(-25.00), received = Some("2017-03-19"), paymentLot = Some("01234"), paymentLotItem = Some("0001")) )) ) ) @@ -125,7 +131,7 @@ class PaymentsResponseSpec extends UnitSpec { taxPeriod = Some(TaxPeriod(from = "2017-04-20", to = "2017-09-28")), `type` = "VAT Return Debit Charge", paymentItems = Some(Seq( - PaymentItem(amount = Some(-25.00), received = Some("2017-03-19")) + PaymentItem(amount = Some(-25.00), received = Some("2017-03-19"), paymentLot = Some("01234"), paymentLotItem = Some("0001")) )) ) ) @@ -147,11 +153,15 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-02-11", - | "paymentAmount":5.0 + | "paymentAmount":5.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | }, | { | "clearingDate":"2017-04-11", - | "paymentAmount":10.0 + | "paymentAmount":10.0, + | "paymentLot":"56789", + | "paymentLotItem":"0002" | } | ] | }, @@ -165,7 +175,9 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-03-19", - | "paymentAmount":-25.0 + | "paymentAmount":-25.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | } | ] | } @@ -193,11 +205,15 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-02-11", - | "paymentAmount":5.0 + | "paymentAmount":5.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | }, | { | "clearingDate":"2017-04-11", - | "paymentAmount":10.0 + | "paymentAmount":10.0, + | "paymentLot":"56789", + | "paymentLotItem":"0002" | } | ] | }, @@ -211,7 +227,9 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-03-19", - | "paymentAmount":-25.0 + | "paymentAmount":-25.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | } | ] | } @@ -247,10 +265,14 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-03-19", - | "paymentAmount":-25.0 + | "paymentAmount":-25.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | }, | { - | "clearingDate":"2017-04-19" + | "clearingDate":"2017-04-19", + | "paymentLot":"01234", + | "paymentLotItem":"0001" | } | ] | } @@ -286,7 +308,9 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-03-19", - | "paymentAmount":-25.0 + | "paymentAmount":-25.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | } | ] | } @@ -314,11 +338,15 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-02-11", - | "paymentAmount":5.0 + | "paymentAmount":5.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | }, | { | "clearingDate":"2017-04-11", - | "paymentAmount":10.0 + | "paymentAmount":10.0, + | "paymentLot":"56789", + | "paymentLotItem":"0002" | } | ] | }, @@ -329,11 +357,15 @@ class PaymentsResponseSpec extends UnitSpec { | "items":[ | { | "clearingDate":"2017-02-11", - | "paymentAmount":5.0 + | "paymentAmount":5.0, + | "paymentLot":"01234", + | "paymentLotItem":"0001" | }, | { | "clearingDate":"2017-04-11", - | "paymentAmount":10.0 + | "paymentAmount":10.0, + | "paymentLot":"56789", + | "paymentLotItem":"0002" | } | ] | }, diff --git a/test/v1/services/PaymentsServiceSpec.scala b/test/v1/services/PaymentsServiceSpec.scala index c0e417fa..cf29c905 100644 --- a/test/v1/services/PaymentsServiceSpec.scala +++ b/test/v1/services/PaymentsServiceSpec.scala @@ -49,7 +49,7 @@ class PaymentsServiceSpec extends ServiceSpec { `type` = "VAT Return Debit Charge", paymentItems = Some( Seq( - PaymentItem(amount = Some(200.00), received = Some("2017-03-12")) + PaymentItem(amount = Some(200.00), received = Some("2017-03-12"), paymentLot = Some("01234"), paymentLotItem = Some("0001")) )) ) )