From 1592221ea9184b6247c5b3fd6c4f7a7cbb38f3f9 Mon Sep 17 00:00:00 2001 From: Maxim Khailo Date: Wed, 7 Dec 2016 10:28:54 -0800 Subject: [PATCH] Fix gift card capture and store credit 1. To capture gift card you must auth first. 2. capture changes state of auth intead of creating new adjustment 3. To partial capture a gift card requires re-auth like stripe. 4. You cancel auth now, a capture requires refund or manual adjustment. --- .../app/failures/GiftCardFailures.scala | 15 ++- .../app/failures/StoreCreditFailures.scala | 5 + .../models/payment/giftcard/GiftCard.scala | 33 ++++--- .../payment/giftcard/GiftCardAdjustment.scala | 3 + .../payment/storecredit/StoreCredit.scala | 26 +++-- .../storecredit/StoreCreditAdjustment.scala | 3 + phoenix-scala/app/services/Capture.scala | 2 +- .../services/orders/OrderStateUpdater.scala | 78 +++++++-------- .../app/utils/seeds/GiftCardSeeds.scala | 2 - .../seeds/generators/OrderGenerator.scala | 3 +- ...rd_and_store_credit_balance_adjustment.sql | 87 ++++++++++++++++ .../integration/GiftCardIntegrationTest.scala | 2 +- .../StoreCreditIntegrationTest.scala | 2 +- .../GiftCardAdjustmentIntegrationTest.scala | 98 ++++++------------- .../models/GiftCardIntegrationTest.scala | 16 +-- ...StoreCreditAdjustmentIntegrationTest.scala | 47 ++++----- .../models/StoreCreditIntegrationTest.scala | 8 +- .../services/CartValidatorTest.scala | 4 +- 18 files changed, 245 insertions(+), 189 deletions(-) create mode 100644 phoenix-scala/sql/V4.072__fix_gift_card_and_store_credit_balance_adjustment.sql diff --git a/phoenix-scala/app/failures/GiftCardFailures.scala b/phoenix-scala/app/failures/GiftCardFailures.scala index fc89759669..1efabb90d9 100644 --- a/phoenix-scala/app/failures/GiftCardFailures.scala +++ b/phoenix-scala/app/failures/GiftCardFailures.scala @@ -18,24 +18,29 @@ object GiftCardFailures { case class GiftCardPaymentAlreadyAdded(refNum: String, code: String) extends Failure { override def description = - s"giftCard with code=$code already added as payment method to order with refNum=$refNum" + s"Gift Card with code=$code already added as payment method to order with refNum=$refNum" } case class GiftCardPaymentNotFound(refNum: String, code: String) extends Failure { override def description = - s"giftCard with code=$code is not added as payment method to order with refNum=$refNum" + s"Gift Card with code=$code is not added as payment method to order with refNum=$refNum" + } + + case class GiftCardAuthAdjustmentNotFound(orderPaymentId: Int) extends Failure { + override def description = + s"Cannot capture a Gift Card using order payment $orderPaymentId because no adjustment in auth" } case class GiftCardNotEnoughBalance(gc: GiftCard, requestedAmount: Int) extends Failure { override def description = - s"giftCard with code=${gc.code} has availableBalance=${gc.availableBalance} less than requestedAmount=$requestedAmount" + s"Gift Card with code=${gc.code} has availableBalance=${gc.availableBalance} less than requestedAmount=$requestedAmount" } case class GiftCardIsInactive(gc: GiftCard) extends Failure { - override def description = s"giftCard with id=${gc.id} is inactive" + override def description = s"Gift Card with id=${gc.id} is inactive" } case object CreditCardMustHaveAddress extends Failure { - override def description = "cannot create creditCard without an address" + override def description = "cannot create Credit Card without an address" } } diff --git a/phoenix-scala/app/failures/StoreCreditFailures.scala b/phoenix-scala/app/failures/StoreCreditFailures.scala index f0b688011a..ebe20231cd 100644 --- a/phoenix-scala/app/failures/StoreCreditFailures.scala +++ b/phoenix-scala/app/failures/StoreCreditFailures.scala @@ -16,4 +16,9 @@ object StoreCreditFailures { override def description = s"customer with id=$id has storeCredit=$has less than requestedAmount=$want" } + + case class StoreCreditAuthAdjustmentNotFound(orderPaymentId: Int) extends Failure { + override def description = + s"Cannot capture a Store Credit using order payment $orderPaymentId because no adjustment in auth" + } } diff --git a/phoenix-scala/app/models/payment/giftcard/GiftCard.scala b/phoenix-scala/app/models/payment/giftcard/GiftCard.scala index 9c3760a81c..f31b7de800 100644 --- a/phoenix-scala/app/models/payment/giftcard/GiftCard.scala +++ b/phoenix-scala/app/models/payment/giftcard/GiftCard.scala @@ -265,25 +265,31 @@ object GiftCards import GiftCard._ - def auth(giftCard: GiftCard, orderPaymentId: Option[Int], debit: Int = 0, credit: Int = 0)( + def auth(giftCard: GiftCard, orderPaymentId: Int, debit: Int = 0)( implicit ec: EC): DbResultT[GiftCardAdjustment] = - adjust(giftCard, orderPaymentId, debit = debit, credit = credit, state = Adj.Auth) + adjust(giftCard, orderPaymentId.some, debit = debit, state = Adj.Auth) def authOrderPayment( giftCard: GiftCard, pmt: OrderPayment, maxPaymentAmount: Option[Int] = None)(implicit ec: EC): DbResultT[GiftCardAdjustment] = - auth(giftCard = giftCard, - orderPaymentId = pmt.id.some, - debit = pmt.getAmount(maxPaymentAmount)) + auth(giftCard = giftCard, orderPaymentId = pmt.id, debit = pmt.getAmount(maxPaymentAmount)) def captureOrderPayment(giftCard: GiftCard, pmt: OrderPayment, maxAmount: Option[Int] = None)( implicit ec: EC): DbResultT[GiftCardAdjustment] = - capture(giftCard, pmt.id.some, debit = pmt.getAmount(maxAmount)) + capture(giftCard, pmt.id, debit = pmt.getAmount(maxAmount)) - def capture(giftCard: GiftCard, orderPaymentId: Option[Int], debit: Int, credit: Int = 0)( + def capture(giftCard: GiftCard, orderPaymentId: Int, debit: Int)( implicit ec: EC): DbResultT[GiftCardAdjustment] = - adjust(giftCard, orderPaymentId, debit = debit, credit = credit, state = Adj.Capture) + for { + auth ← * <~ GiftCardAdjustments + .authorizedOrderPayment(orderPaymentId) + .mustFindOneOr(GiftCardAuthAdjustmentNotFound(orderPaymentId)) + _ ← * <~ ( + require(debit <= auth.debit) + ) + cap ← * <~ GiftCardAdjustments.update(auth, auth.copy(debit = debit, state = Adj.Capture)) + } yield cap def cancelByCsr(giftCard: GiftCard, storeAdmin: User)( implicit ec: EC): DbResultT[GiftCardAdjustment] = { @@ -321,11 +327,12 @@ object GiftCards def findActive(): QuerySeq = filter(_.state === (GiftCard.Active: GiftCard.State)) - private def adjust(giftCard: GiftCard, - orderPaymentId: Option[Int], - debit: Int = 0, - credit: Int = 0, - state: GiftCardAdjustment.State = Adj.Auth)( + //Public because tests use it to validate DB constraints + def adjust(giftCard: GiftCard, + orderPaymentId: Option[Int], + debit: Int = 0, + credit: Int = 0, + state: GiftCardAdjustment.State = Adj.Auth)( implicit ec: EC): DbResultT[GiftCardAdjustment] = { val balance = giftCard.availableBalance - debit + credit val adjustment = Adj(giftCardId = giftCard.id, diff --git a/phoenix-scala/app/models/payment/giftcard/GiftCardAdjustment.scala b/phoenix-scala/app/models/payment/giftcard/GiftCardAdjustment.scala index 8044fea19f..d28d173e85 100644 --- a/phoenix-scala/app/models/payment/giftcard/GiftCardAdjustment.scala +++ b/phoenix-scala/app/models/payment/giftcard/GiftCardAdjustment.scala @@ -105,6 +105,9 @@ object GiftCardAdjustments def authorizedOrderPayments(orderPaymentIds: Seq[Int]): QuerySeq = filter(adj ⇒ adj.orderPaymentId.inSet(orderPaymentIds) && adj.state === (Auth: State)) + def authorizedOrderPayment(orderPaymentId: Int): QuerySeq = + filter(adj ⇒ adj.orderPaymentId === orderPaymentId && adj.state === (Auth: State)) + object scope { implicit class GCAQuerySeqAdditions(query: QuerySeq) { diff --git a/phoenix-scala/app/models/payment/storecredit/StoreCredit.scala b/phoenix-scala/app/models/payment/storecredit/StoreCredit.scala index 47095246f0..7a6f8c0793 100644 --- a/phoenix-scala/app/models/payment/storecredit/StoreCredit.scala +++ b/phoenix-scala/app/models/payment/storecredit/StoreCredit.scala @@ -6,7 +6,7 @@ import cats.data.Validated._ import cats.data.{ValidatedNel, Xor} import cats.implicits._ import com.pellucid.sealerate -import failures.{Failure, Failures, GeneralFailure} +import failures.{Failure, Failures, GeneralFailure, StoreCreditFailures} import models.account._ import models.cord.OrderPayment import models.payment.PaymentMethod @@ -202,10 +202,10 @@ class StoreCredits(tag: Tag) extends FoxTable[StoreCredit](tag, "store_credits") object StoreCredits extends FoxTableQuery[StoreCredit, StoreCredits](new StoreCredits(_)) { - def auth(storeCredit: StoreCredit, orderPaymentId: Option[Int], amount: Int = 0)( + def auth(storeCredit: StoreCredit, orderPaymentId: Int, amount: Int = 0)( implicit ec: EC): DbResultT[StoreCreditAdjustment] = debit(storeCredit = storeCredit, - orderPaymentId = orderPaymentId, + orderPaymentId = orderPaymentId.some, amount = amount, state = Adj.Auth) @@ -214,7 +214,7 @@ object StoreCredits extends FoxTableQuery[StoreCredit, StoreCredits](new StoreCr pmt: OrderPayment, maxPaymentAmount: Option[Int] = None)(implicit ec: EC): DbResultT[StoreCreditAdjustment] = auth(storeCredit = storeCredit, - orderPaymentId = pmt.id.some, + orderPaymentId = pmt.id, amount = pmt.getAmount(maxPaymentAmount)) def captureOrderPayment( @@ -222,15 +222,21 @@ object StoreCredits extends FoxTableQuery[StoreCredit, StoreCredits](new StoreCr pmt: OrderPayment, maxPaymentAmount: Option[Int] = None)(implicit ec: EC): DbResultT[StoreCreditAdjustment] = capture(storeCredit = storeCredit, - orderPaymentId = pmt.id.some, + orderPaymentId = pmt.id, amount = pmt.getAmount(maxPaymentAmount)) - def capture(storeCredit: StoreCredit, orderPaymentId: Option[Int], amount: Int = 0)( + def capture(storeCredit: StoreCredit, orderPaymentId: Int, amount: Int)( implicit ec: EC): DbResultT[StoreCreditAdjustment] = - debit(storeCredit = storeCredit, - orderPaymentId = orderPaymentId, - amount = amount, - state = Adj.Capture) + for { + auth ← * <~ StoreCreditAdjustments + .authorizedOrderPayment(orderPaymentId) + .mustFindOneOr(StoreCreditFailures.StoreCreditAuthAdjustmentNotFound(orderPaymentId)) + _ ← * <~ ( + require(amount <= auth.debit) + ) + cap ← * <~ StoreCreditAdjustments.update(auth, + auth.copy(debit = amount, state = Adj.Capture)) + } yield cap def cancelByCsr(storeCredit: StoreCredit, storeAdmin: User)( implicit ec: EC): DbResultT[StoreCreditAdjustment] = { diff --git a/phoenix-scala/app/models/payment/storecredit/StoreCreditAdjustment.scala b/phoenix-scala/app/models/payment/storecredit/StoreCreditAdjustment.scala index 10959b046b..103ab05ac6 100644 --- a/phoenix-scala/app/models/payment/storecredit/StoreCreditAdjustment.scala +++ b/phoenix-scala/app/models/payment/storecredit/StoreCreditAdjustment.scala @@ -90,6 +90,9 @@ object StoreCreditAdjustments def authorizedOrderPayments(orderPaymentIds: Seq[Int]): QuerySeq = filter(adj ⇒ adj.orderPaymentId.inSet(orderPaymentIds) && adj.state === (Auth: State)) + def authorizedOrderPayment(orderPaymentId: Int): QuerySeq = + filter(adj ⇒ adj.orderPaymentId === orderPaymentId && adj.state === (Auth: State)) + object scope { implicit class SCAQuerySeqAdditions(query: QuerySeq) { diff --git a/phoenix-scala/app/services/Capture.scala b/phoenix-scala/app/services/Capture.scala index fd2bd2b498..5b5d2262cc 100644 --- a/phoenix-scala/app/services/Capture.scala +++ b/phoenix-scala/app/services/Capture.scala @@ -110,8 +110,8 @@ case class Capture(payload: CapturePayloads.Capture)(implicit ec: EC, db: DB, ap scPayments, order.currency) internalCaptureTotal = total - externalCaptureTotal - _ ← * <~ externalCapture(externalCaptureTotal, order) _ ← * <~ internalCapture(internalCaptureTotal, order, customer, gcPayments, scPayments) + _ ← * <~ externalCapture(externalCaptureTotal, order) resp = CaptureResponse(order = order.refNum, captured = total, diff --git a/phoenix-scala/app/services/orders/OrderStateUpdater.scala b/phoenix-scala/app/services/orders/OrderStateUpdater.scala index 93bd800aa1..72c3355ac3 100644 --- a/phoenix-scala/app/services/orders/OrderStateUpdater.scala +++ b/phoenix-scala/app/services/orders/OrderStateUpdater.scala @@ -50,7 +50,7 @@ object OrderStateUpdater { admin: User, refNumbers: Seq[String], newState: Order.State, - skipActivity: Boolean = false)(implicit ec: EC, ac: AC): DbResultT[BatchMetadata] = { + skipActivity: Boolean = false)(implicit ec: EC, ac: AC, db: DB): DbResultT[BatchMetadata] = { val query = Orders.filter(_.referenceNumber.inSet(refNumbers)).result appendForUpdate(query).dbresult.flatMap { orders ⇒ @@ -60,67 +60,65 @@ object OrderStateUpdater { val possibleRefNums = validTransitions.map(_.referenceNumber) val skipActivityMod = skipActivity || possibleRefNums.isEmpty - updateQueriesWrapper(admin, possibleRefNums, newState, skipActivityMod).dbresult.flatMap { - _ ⇒ - // Failure handling - val invalid = invalidTransitions.map(o ⇒ - (o.refNum, StateTransitionNotAllowed(o.state, newState, o.refNum).description)) - val notFound = refNumbers - .filterNot(refNum ⇒ orders.map(_.referenceNumber).contains(refNum)) - .map(refNum ⇒ (refNum, NotFoundFailure400(Order, refNum).description)) - - val batchFailures = (invalid ++ notFound).toMap - DbResultT.good(BatchMetadata(BatchMetadataSource(Order, possibleRefNums, batchFailures))) + updateQueriesWrapper(admin, possibleRefNums, newState, skipActivityMod).flatMap { _ ⇒ + // Failure handling + val invalid = invalidTransitions.map(o ⇒ + (o.refNum, StateTransitionNotAllowed(o.state, newState, o.refNum).description)) + val notFound = refNumbers + .filterNot(refNum ⇒ orders.map(_.referenceNumber).contains(refNum)) + .map(refNum ⇒ (refNum, NotFoundFailure400(Order, refNum).description)) + + val batchFailures = (invalid ++ notFound).toMap + DbResultT.good(BatchMetadata(BatchMetadataSource(Order, possibleRefNums, batchFailures))) } } } - private def updateQueriesWrapper(admin: User, - cordRefs: Seq[String], - newState: State, - skipActivity: Boolean = false)(implicit ec: EC, ac: AC) = { + private def updateQueriesWrapper( + admin: User, + cordRefs: Seq[String], + newState: State, + skipActivity: Boolean = false)(implicit ec: EC, ac: AC, db: DB) = { if (skipActivity) updateQueries(admin, cordRefs, newState) else - orderBulkStateChanged(newState, cordRefs, admin.some).value >> - updateQueries(admin, cordRefs, newState) + for { + _ ← * <~ orderBulkStateChanged(newState, cordRefs, admin.some).value + _ ← * <~ updateQueries(admin, cordRefs, newState) + } yield () } - private def updateQueries(admin: User, cordRefs: Seq[String], newState: State)(implicit ec: EC) = + private def updateQueries(admin: User, cordRefs: Seq[String], newState: State)(implicit ec: EC, + db: DB) = newState match { case Canceled ⇒ cancelOrders(cordRefs) case _ ⇒ - Orders.filter(_.referenceNumber.inSet(cordRefs)).map(_.state).update(newState) + for { + _ ← * <~ Orders.filter(_.referenceNumber.inSet(cordRefs)).map(_.state).update(newState) + } yield () } - private def cancelOrders(cordRefs: Seq[String])(implicit ec: EC) = { - val updateLineItems = OrderLineItems - .filter(_.cordRef.inSetBind(cordRefs)) - .map(_.state) - .update(OrderLineItem.Canceled) - - val cancelPayments = for { - orderPayments ← OrderPayments.filter(_.cordRef.inSetBind(cordRefs)).result - _ ← cancelGiftCards(orderPayments) - _ ← cancelStoreCredits(orderPayments) - // TODO: add credit card charge return + private def cancelOrders(cordRefs: Seq[String])(implicit ec: EC, db: DB) = + for { + updateLineItems ← * <~ OrderLineItems + .filter(_.cordRef.inSetBind(cordRefs)) + .map(_.state) + .update(OrderLineItem.Canceled) + + orderPayments ← * <~ OrderPayments.filter(_.cordRef.inSetBind(cordRefs)).result + _ ← * <~ cancelGiftCards(orderPayments) + _ ← * <~ cancelStoreCredits(orderPayments) + _ ← * <~ Orders.filter(_.referenceNumber.inSetBind(cordRefs)).map(_.state).update(Canceled) } yield () - val updateOrder = - Orders.filter(_.referenceNumber.inSetBind(cordRefs)).map(_.state).update(Canceled) - - // (updateLineItems >> updateOrderPayments >> updateOrder).transactionally - (updateLineItems >> updateOrder >> cancelPayments).transactionally - } - - private def cancelGiftCards(orderPayments: Seq[OrderPayment]) = { + private def cancelGiftCards(orderPayments: Seq[OrderPayment])(implicit ec: EC, db: DB) = { val paymentIds = orderPayments.map(_.id) GiftCardAdjustments.filter(_.orderPaymentId.inSetBind(paymentIds)).cancel() } - private def cancelStoreCredits(orderPayments: Seq[OrderPayment]) = { + private def cancelStoreCredits(orderPayments: Seq[OrderPayment])(implicit ec: EC, db: DB) = { val paymentIds = orderPayments.map(_.id) StoreCreditAdjustments.filter(_.orderPaymentId.inSetBind(paymentIds)).cancel() } diff --git a/phoenix-scala/app/utils/seeds/GiftCardSeeds.scala b/phoenix-scala/app/utils/seeds/GiftCardSeeds.scala index 9e48359551..da019c9794 100644 --- a/phoenix-scala/app/utils/seeds/GiftCardSeeds.scala +++ b/phoenix-scala/app/utils/seeds/GiftCardSeeds.scala @@ -23,8 +23,6 @@ trait GiftCardSeeds { _ ← * <~ GiftCardSubtypes.createAll(giftCardSubTypes) origin ← * <~ GiftCardManuals.create(GiftCardManual(adminId = 1, reasonId = 1)) gc1 ← * <~ GiftCards.create(giftCard.copy(originId = origin.id)) - _ ← * <~ GiftCards.capture(gc1, debit = 1000, orderPaymentId = None) - gc2 ← * <~ GiftCards.create( build(payload(balance = 10000, reasonId = 1), originId = origin.id)) _ ← * <~ Notes.createAll(giftCardNotes.map(_.copy(referenceId = gc1.id))) diff --git a/phoenix-scala/app/utils/seeds/generators/OrderGenerator.scala b/phoenix-scala/app/utils/seeds/generators/OrderGenerator.scala index 3aa0558aec..c6bf2b57c9 100644 --- a/phoenix-scala/app/utils/seeds/generators/OrderGenerator.scala +++ b/phoenix-scala/app/utils/seeds/generators/OrderGenerator.scala @@ -111,7 +111,8 @@ trait OrderGenerator extends ShipmentSeeds { StoreCredit(originId = origin.id, accountId = accountId, originalBalance = totals)) op ← * <~ OrderPayments.create( OrderPayment.build(sc).copy(cordRef = cart.refNum, amount = totals.some)) - _ ← * <~ StoreCredits.capture(sc, op.id.some, totals) + _ ← * <~ StoreCredits.auth(sc, op.id, totals) + _ ← * <~ StoreCredits.capture(sc, op.id, totals) addr ← * <~ getDefaultAddress(accountId) shipMethodIds ← * <~ ShippingMethods.map(_.id).result shipMethod ← * <~ getShipMethod(1 + Random.nextInt(shipMethodIds.length)) diff --git a/phoenix-scala/sql/V4.072__fix_gift_card_and_store_credit_balance_adjustment.sql b/phoenix-scala/sql/V4.072__fix_gift_card_and_store_credit_balance_adjustment.sql new file mode 100644 index 0000000000..241fdf0fd2 --- /dev/null +++ b/phoenix-scala/sql/V4.072__fix_gift_card_and_store_credit_balance_adjustment.sql @@ -0,0 +1,87 @@ +create or replace function update_gift_card_current_balance() returns trigger as $$ +declare + adjustment integer default 0; + new_available_balance integer default 0; + refund integer default 0; +begin + if new.debit > 0 then + adjustment := new.debit; + elsif new.credit > 0 then + adjustment := -new.credit; + end if; + + if new.state = 'auth' then + update gift_cards + set available_balance = available_balance - adjustment + where id = new.gift_card_id + returning available_balance into new_available_balance; + new.available_balance = new_available_balance; + elsif new.state = 'canceled' then + update gift_cards + set available_balance = available_balance + adjustment + where id = new.gift_card_id + returning available_balance into new_available_balance; + new.available_balance = new_available_balance; + elsif new.state = 'capture' then + if tg_op = 'INSERT' then + refund := -adjustment; + elsif old.state = 'auth' then + refund := old.debit - new.debit; + end if; + update gift_cards + set current_balance = current_balance - adjustment, + available_balance = available_balance + refund + where id = new.gift_card_id; + elsif new.state = 'cancellationCapture' then + update gift_cards + set current_balance = current_balance + adjustment, + available_balance = available_balance + adjustment + where id = new.gift_card_id; + else + raise exception 'Failed to update gift card balance with id %, the state % is unknown', new.gift_card_id, new.state; + end if; + return new; +end; +$$ language plpgsql; + +create or replace function update_store_credit_current_balance() returns trigger as $$ +declare + adjustment integer default 0; + new_available_balance integer default 0; + refund integer default 0; +begin + adjustment := new.debit; + + if new.state = 'auth' then + update store_credits + set available_balance = available_balance - adjustment + where id = new.store_credit_id + returning available_balance into new_available_balance; + new.available_balance = new_available_balance; + elsif new.state = 'canceled' then + update store_credits + set available_balance = available_balance + adjustment + where id = new.store_credit_id + returning available_balance into new_available_balance; + new.available_balance = new_available_balance; + elsif new.state = 'capture' then + if tg_op = 'INSERT' then + refund := -adjustment; + elsif old.state = 'auth' then + refund := old.debit - new.debit; + end if; + update store_credits + set current_balance = current_balance - adjustment, + available_balance = available_balance + refund + where id = new.store_credit_id; + elsif new.state = 'cancellationCapture' then + update store_credits + set current_balance = current_balance + adjustment, + available_balance = available_balance + adjustment + where id = new.store_credit_id; + else + raise exception 'Failed to update store credit balance with id %, the state % is unknown', new.store_credit_id, new.state; + end if; + return new; +end; +$$ language plpgsql; diff --git a/phoenix-scala/test/integration/GiftCardIntegrationTest.scala b/phoenix-scala/test/integration/GiftCardIntegrationTest.scala index 9a646e57ec..f0d4143ca5 100644 --- a/phoenix-scala/test/integration/GiftCardIntegrationTest.scala +++ b/phoenix-scala/test/integration/GiftCardIntegrationTest.scala @@ -341,6 +341,6 @@ class GiftCardIntegrationTest val (cart, customer, payment) = (setup.cart, setup.customer, setup.orderPayments.head) // TODO: replace with checkout? - val adjustment1 = GiftCards.auth(giftCard1, Some(payment.id), 10).gimme + val adjustment1 = GiftCards.auth(giftCard1, payment.id, 10).gimme } } diff --git a/phoenix-scala/test/integration/StoreCreditIntegrationTest.scala b/phoenix-scala/test/integration/StoreCreditIntegrationTest.scala index 83ad273f6c..4e50117c30 100644 --- a/phoenix-scala/test/integration/StoreCreditIntegrationTest.scala +++ b/phoenix-scala/test/integration/StoreCreditIntegrationTest.scala @@ -242,7 +242,7 @@ class StoreCreditIntegrationTest paymentMethodId = storeCredit.id, paymentMethodType = PaymentMethod.StoreCredit, amount = Some(storeCredit.availableBalance))) - adjustment ← * <~ StoreCredits.auth(storeCredit, Some(payment.id), 10) + adjustment ← * <~ StoreCredits.auth(storeCredit, payment.id, 10) } yield (storeCredit, adjustment, scSecond, payment, scSubType)).gimme } } diff --git a/phoenix-scala/test/integration/models/GiftCardAdjustmentIntegrationTest.scala b/phoenix-scala/test/integration/models/GiftCardAdjustmentIntegrationTest.scala index 876046c5d4..2bc9b90639 100644 --- a/phoenix-scala/test/integration/models/GiftCardAdjustmentIntegrationTest.scala +++ b/phoenix-scala/test/integration/models/GiftCardAdjustmentIntegrationTest.scala @@ -3,6 +3,7 @@ package models import models.payment.giftcard._ import testutils._ import testutils.fixtures.BakedFixtures +import cats.implicits._ import utils.db._ class GiftCardAdjustmentIntegrationTest @@ -17,10 +18,7 @@ class GiftCardAdjustmentIntegrationTest override def gcPaymentAmount = giftCard.availableBalance val failure = GiftCards - .auth(giftCard = giftCard, - orderPaymentId = Some(orderPayments.head.id), - debit = 0, - credit = -1) + .auth(giftCard = giftCard, orderPaymentId = orderPayments.head.id, debit = -1) .runTxn() .futureValue .leftVal @@ -28,13 +26,12 @@ class GiftCardAdjustmentIntegrationTest } "only one of credit or debit can be greater than zero" in new Fixture { - override def gcPaymentAmount = 50 - + override def gcPaymentAmount = giftCard.availableBalance val failure = GiftCards - .auth(giftCard = giftCard, - orderPaymentId = Some(orderPayments.head.id), - debit = 50, - credit = 50) + .adjust(giftCard = giftCard, + orderPaymentId = orderPayments.head.id.some, + debit = 50, + credit = 50) .runTxn() .futureValue .leftVal @@ -45,10 +42,12 @@ class GiftCardAdjustmentIntegrationTest override def gcPaymentAmount = 50 val adjustment = (for { + auth ← * <~ GiftCards.auth(giftCard = giftCard, + orderPaymentId = orderPayments.head.id, + debit = 50) adjustment ← * <~ GiftCards.capture(giftCard = giftCard, - orderPaymentId = Some(orderPayments.head.id), - debit = 50, - credit = 0) + orderPaymentId = orderPayments.head.id, + debit = 50) } yield adjustment).gimme adjustment.id must === (1) @@ -60,55 +59,27 @@ class GiftCardAdjustmentIntegrationTest val pmtId = orderPayments.head.id val updated = (for { - _ ← * <~ GiftCards.capture(giftCard = giftCard, - orderPaymentId = Some(pmtId), - debit = 50, - credit = 0) - _ ← * <~ GiftCards.capture(giftCard = giftCard, - orderPaymentId = Some(pmtId), - debit = 25, - credit = 0) - _ ← * <~ GiftCards.capture(giftCard = giftCard, - orderPaymentId = Some(pmtId), - debit = 15, - credit = 0) - _ ← * <~ GiftCards.capture(giftCard = giftCard, - orderPaymentId = Some(pmtId), - debit = 10, - credit = 0) - _ ← * <~ GiftCards.auth(giftCard = giftCard, - orderPaymentId = Some(pmtId), - debit = 100, - credit = 0) - _ ← * <~ GiftCards.auth(giftCard = giftCard, - orderPaymentId = Some(pmtId), - debit = 50, - credit = 0) - _ ← * <~ GiftCards.auth(giftCard = giftCard, - orderPaymentId = Some(pmtId), - debit = 50, - credit = 0) - _ ← * <~ GiftCards.capture(giftCard = giftCard, - orderPaymentId = Some(pmtId), - debit = 200, - credit = 0) + _ ← * <~ GiftCards.auth(giftCard = giftCard, orderPaymentId = pmtId, debit = 100) + _ ← * <~ GiftCards.auth(giftCard = giftCard, orderPaymentId = pmtId, debit = 50) + _ ← * <~ GiftCards.auth(giftCard = giftCard, orderPaymentId = pmtId, debit = 50) + _ ← * <~ GiftCards.capture(giftCard = giftCard, orderPaymentId = pmtId, debit = 50) + _ ← * <~ GiftCards.capture(giftCard = giftCard, orderPaymentId = pmtId, debit = 25) + _ ← * <~ GiftCards.capture(giftCard = giftCard, orderPaymentId = pmtId, debit = 15) giftCard ← * <~ GiftCards.refresh(giftCard) } yield giftCard).gimme - updated.availableBalance must === (0) - updated.currentBalance must === (200) + updated.availableBalance must === (500 - 50 - 25 - 15) + updated.currentBalance must === (500 - 50 - 25 - 15) } "a Postgres trigger updates the adjustment's availableBalance before insert" in new Fixture { override def gcPaymentAmount = giftCard.availableBalance - val (adj, updated) = (for { - adj ← * <~ GiftCards.capture(giftCard = giftCard, - orderPaymentId = Some(orderPayments.head.id), - debit = 50, - credit = 0) - giftCard ← * <~ GiftCards.refresh(giftCard) - } yield (adj, giftCard)).value.gimme + GiftCards.auth(giftCard = giftCard, orderPaymentId = orderPayments.head.id, debit = 50).gimme + val adj = GiftCards + .capture(giftCard = giftCard, orderPaymentId = orderPayments.head.id, debit = 50) + .gimme + val updated = GiftCards.refresh(giftCard).gimme updated.availableBalance must === (450) updated.currentBalance must === (450) @@ -119,18 +90,13 @@ class GiftCardAdjustmentIntegrationTest override def gcPaymentAmount = giftCard.availableBalance val debits = List(50, 25, 15, 10) - def capture(amount: Int) = - GiftCards.capture(giftCard = giftCard, - orderPaymentId = Some(orderPayments.head.id), - debit = amount, - credit = 0) - val adjustments = DbResultT.sequence((1 to 4).map(capture)).gimme - - DBIO - .sequence(adjustments.map { adj ⇒ - GiftCardAdjustments.cancel(adj.id) - }) - .gimme + def auth(amount: Int) = + GiftCards.auth(giftCard = giftCard, orderPaymentId = orderPayments.head.id, debit = amount) + val adjustments = DbResultT.sequence((1 to 4).map(auth)).gimme + + adjustments.map { adj ⇒ + GiftCardAdjustments.cancel(adj.id).gimme + } val finalGc = GiftCards.refresh(giftCard).gimme (finalGc.originalBalance, finalGc.availableBalance, finalGc.currentBalance) must === ( diff --git a/phoenix-scala/test/integration/models/GiftCardIntegrationTest.scala b/phoenix-scala/test/integration/models/GiftCardIntegrationTest.scala index 38f754d1f2..b38c56c96d 100644 --- a/phoenix-scala/test/integration/models/GiftCardIntegrationTest.scala +++ b/phoenix-scala/test/integration/models/GiftCardIntegrationTest.scala @@ -21,7 +21,8 @@ class GiftCardIntegrationTest } "updates availableBalance if auth adjustment is created + cancel handling" in new Fixture { - val adjustment = GiftCards.capture(giftCard, Some(orderPayments.head.id), 10).gimme + val auth = GiftCards.auth(giftCard, orderPayments.head.id, 10).gimme + val adjustment = GiftCards.capture(giftCard, orderPayments.head.id, 10).gimme val updatedGiftCard = GiftCards.refresh(giftCard).gimme updatedGiftCard.availableBalance must === (giftCard.originalBalance - 10) @@ -30,19 +31,6 @@ class GiftCardIntegrationTest val canceledGiftCard = GiftCards.refresh(giftCard).gimme canceledGiftCard.availableBalance must === (giftCard.originalBalance) } - - "updates availableBalance and currentBalance if capture adjustment is created + cancel handling" in new Fixture { - val adjustment = GiftCards.capture(giftCard, Some(orderPayments.head.id), 0, 10).gimme - - val updatedGiftCard = GiftCards.refresh(giftCard).gimme - updatedGiftCard.availableBalance must === (giftCard.originalBalance + 10) - updatedGiftCard.currentBalance must === (giftCard.originalBalance + 10) - - GiftCardAdjustments.cancel(adjustment.id).gimme - val canceledGiftCard = GiftCards.refresh(giftCard).gimme - canceledGiftCard.availableBalance must === (giftCard.originalBalance) - canceledGiftCard.currentBalance must === (giftCard.originalBalance) - } } trait SimpleFixture extends Reason_Baked with GiftCard_Raw { diff --git a/phoenix-scala/test/integration/models/StoreCreditAdjustmentIntegrationTest.scala b/phoenix-scala/test/integration/models/StoreCreditAdjustmentIntegrationTest.scala index 5f689b4837..13ca460013 100644 --- a/phoenix-scala/test/integration/models/StoreCreditAdjustmentIntegrationTest.scala +++ b/phoenix-scala/test/integration/models/StoreCreditAdjustmentIntegrationTest.scala @@ -29,8 +29,8 @@ class StoreCreditAdjustmentIntegrationTest val adjustments = Table( "adjustments", - StoreCredits.auth(storeCredit = sc, orderPaymentId = Some(payment.id), amount = -1), - StoreCredits.auth(storeCredit = sc, orderPaymentId = Some(payment.id), amount = 0) + StoreCredits.auth(storeCredit = sc, orderPaymentId = payment.id, amount = -1), + StoreCredits.auth(storeCredit = sc, orderPaymentId = payment.id, amount = 0) ) forAll(adjustments) { adjustment ⇒ @@ -49,21 +49,17 @@ class StoreCreditAdjustmentIntegrationTest accountId = customer.accountId)) pay ← * <~ OrderPayments.create(Factories.giftCardPayment .copy(cordRef = cart.refNum, paymentMethodId = sc.id, amount = Some(500))) - _ ← * <~ StoreCredits.capture(storeCredit = sc, orderPaymentId = Some(pay.id), amount = 50) - _ ← * <~ StoreCredits.capture(storeCredit = sc, orderPaymentId = Some(pay.id), amount = 25) - _ ← * <~ StoreCredits.capture(storeCredit = sc, orderPaymentId = Some(pay.id), amount = 15) - _ ← * <~ StoreCredits.capture(storeCredit = sc, orderPaymentId = Some(pay.id), amount = 10) - _ ← * <~ StoreCredits.auth(storeCredit = sc, orderPaymentId = Some(pay.id), amount = 100) - _ ← * <~ StoreCredits.auth(storeCredit = sc, orderPaymentId = Some(pay.id), amount = 50) - _ ← * <~ StoreCredits.auth(storeCredit = sc, orderPaymentId = Some(pay.id), amount = 50) - _ ← * <~ StoreCredits.capture(storeCredit = sc, - orderPaymentId = Some(pay.id), - amount = 200) + _ ← * <~ StoreCredits.auth(storeCredit = sc, orderPaymentId = pay.id, amount = 100) + _ ← * <~ StoreCredits.auth(storeCredit = sc, orderPaymentId = pay.id, amount = 50) + _ ← * <~ StoreCredits.auth(storeCredit = sc, orderPaymentId = pay.id, amount = 50) + _ ← * <~ StoreCredits.capture(storeCredit = sc, orderPaymentId = pay.id, amount = 50) + _ ← * <~ StoreCredits.capture(storeCredit = sc, orderPaymentId = pay.id, amount = 25) + _ ← * <~ StoreCredits.capture(storeCredit = sc, orderPaymentId = pay.id, amount = 15) sc ← * <~ StoreCredits.findOneById(sc.id) } yield sc.value).gimme - sc.availableBalance must === (0) - sc.currentBalance must === (200) + sc.availableBalance must === (500 - 50 - 25 - 15) + sc.currentBalance must === (500 - 50 - 25 - 15) } "a Postgres trigger updates the adjustment's availableBalance before insert" in new Fixture { @@ -76,11 +72,10 @@ class StoreCreditAdjustmentIntegrationTest accountId = customer.accountId)) pay ← * <~ OrderPayments.create(Factories.giftCardPayment .copy(cordRef = cart.refNum, paymentMethodId = sc.id, amount = Some(500))) - adj ← * <~ StoreCredits.capture(storeCredit = sc, - orderPaymentId = Some(pay.id), - amount = 50) - adj ← * <~ StoreCreditAdjustments.refresh(adj) - sc ← * <~ StoreCredits.refresh(sc) + auth ← * <~ StoreCredits.auth(storeCredit = sc, orderPaymentId = pay.id, amount = 50) + adj ← * <~ StoreCredits.capture(storeCredit = sc, orderPaymentId = pay.id, amount = 50) + adj ← * <~ StoreCreditAdjustments.refresh(adj) + sc ← * <~ StoreCredits.refresh(sc) } yield (adj, sc)).value.gimme sc.availableBalance must === (450) @@ -101,19 +96,15 @@ class StoreCreditAdjustmentIntegrationTest } yield (sc, payment)).gimme val debits = List(50, 25, 15, 10) - val adjustments = DbResultT + val auths = DbResultT .sequence(debits.map { amount ⇒ - StoreCredits.capture(storeCredit = sc, - orderPaymentId = Some(payment.id), - amount = amount) + StoreCredits.auth(storeCredit = sc, orderPaymentId = payment.id, amount = amount) }) .gimme - DBIO - .sequence(adjustments.map { adj ⇒ - StoreCreditAdjustments.cancel(adj.id) - }) - .gimme + auths.map { adj ⇒ + StoreCreditAdjustments.cancel(adj.id).gimme + } val finalSc = StoreCredits.findOneById(sc.id).gimme.value (finalSc.originalBalance, finalSc.availableBalance, finalSc.currentBalance) must === ( diff --git a/phoenix-scala/test/integration/models/StoreCreditIntegrationTest.scala b/phoenix-scala/test/integration/models/StoreCreditIntegrationTest.scala index 5ab034a994..8659f6707b 100644 --- a/phoenix-scala/test/integration/models/StoreCreditIntegrationTest.scala +++ b/phoenix-scala/test/integration/models/StoreCreditIntegrationTest.scala @@ -20,7 +20,7 @@ class StoreCreditIntegrationTest } "updates availableBalance if auth adjustment is created + cancel handling" in new Fixture { - val adjustment = StoreCredits.auth(storeCredit, Some(payment.id), 1000).gimme + val adjustment = StoreCredits.auth(storeCredit, payment.id, 1000).gimme val updatedStoreCredit = StoreCredits.findOneById(storeCredit.id).run().futureValue.value updatedStoreCredit.availableBalance must === (storeCredit.availableBalance - 1000) @@ -31,16 +31,14 @@ class StoreCreditIntegrationTest } "updates availableBalance and currentBalance if capture adjustment is created + cancel handling" in new Fixture { - val adjustment = StoreCredits.capture(storeCredit, Some(payment.id), 1000).gimme + val auth = StoreCredits.auth(storeCredit, payment.id, 1000).gimme val updatedStoreCredit = StoreCredits.findOneById(storeCredit.id).run().futureValue.value updatedStoreCredit.availableBalance must === (storeCredit.availableBalance - 1000) - updatedStoreCredit.currentBalance must === (storeCredit.currentBalance - 1000) - StoreCreditAdjustments.cancel(adjustment.id).run().futureValue + StoreCreditAdjustments.cancel(auth.id).run().futureValue val canceledStoreCredit = StoreCredits.findOneById(storeCredit.id).run().futureValue.value canceledStoreCredit.availableBalance must === (storeCredit.availableBalance) - canceledStoreCredit.currentBalance must === (storeCredit.currentBalance) } } diff --git a/phoenix-scala/test/integration/services/CartValidatorTest.scala b/phoenix-scala/test/integration/services/CartValidatorTest.scala index fe6fff9846..298aaaee05 100644 --- a/phoenix-scala/test/integration/services/CartValidatorTest.scala +++ b/phoenix-scala/test/integration/services/CartValidatorTest.scala @@ -114,7 +114,7 @@ class CartValidatorTest extends IntegrationTestBase with TestObjectContext with result1.alerts mustBe 'empty result1.warnings.value.toList must contain(InsufficientFunds(cart.refNum)) // Approves authrorized funds - GiftCards.auth(giftCard, orderPayment.id.some, grandTotal, 0).gimme + GiftCards.auth(giftCard, orderPayment.id, grandTotal).gimme val result2 = CartValidator(refresh(cart)).validate(isCheckout = true).gimme result2.alerts mustBe 'empty result2.warnings.value.toList mustNot contain(InsufficientFunds(cart.refNum)) @@ -132,7 +132,7 @@ class CartValidatorTest extends IntegrationTestBase with TestObjectContext with result1.alerts mustBe 'empty result1.warnings.value.toList must contain(InsufficientFunds(cart.refNum)) // Approves authrorized funds - StoreCredits.auth(storeCredit, orderPayment.id.some, grandTotal).gimme + StoreCredits.auth(storeCredit, orderPayment.id, grandTotal).gimme val result2 = CartValidator(refresh(cart)).validate(isCheckout = true).gimme result2.alerts mustBe 'empty result2.warnings.value.toList mustNot contain(InsufficientFunds(cart.refNum))