Skip to content

Commit

Permalink
Merge 2a325a3 into b9e4005
Browse files Browse the repository at this point in the history
  • Loading branch information
Mario Galic committed Sep 28, 2019
2 parents b9e4005 + 2a325a3 commit bdee479
Show file tree
Hide file tree
Showing 18 changed files with 294 additions and 56 deletions.
Expand Up @@ -284,8 +284,8 @@ class HandlerTest extends FlatSpec with Matchers {
),
List(
IssueSpecifics(
SundayVoucherIssueSuspensionConstants.firstAvailableDate(LocalDate.now()),
SundayVoucherIssueSuspensionConstants.issueDayOfWeek.getValue
SundayVoucherSuspensionConstants.issueConstants(0).firstAvailableDate(LocalDate.now()),
SundayVoucherSuspensionConstants.issueConstants(0).issueDayOfWeek.getValue
)
),
Some(SundayVoucherSuspensionConstants.annualIssueLimit)
Expand Down
Expand Up @@ -16,8 +16,10 @@ object Processor {

case Right(zuoraAccessToken) =>
List(
processProduct(config, Salesforce.holidayStopRequests(config.sfConfig)(GuardianWeekly, processDateOverride), _, _, _),
processProduct(config, Salesforce.holidayStopRequests(config.sfConfig)(SundayVoucher, processDateOverride), _, _, _),
processProduct(config, Salesforce.holidayStopRequests(config.sfConfig)(GuardianWeekly, processDateOverride), _, _, _)
processProduct(config, Salesforce.holidayStopRequests(config.sfConfig)(WeekendVoucher, processDateOverride), _, _, _),
processProduct(config, Salesforce.holidayStopRequests(config.sfConfig)(SixdayVoucher, processDateOverride), _, _, _)
) map {
_.apply(
Zuora.subscriptionGetResponse(config, zuoraAccessToken, backend),
Expand Down
Expand Up @@ -3,20 +3,20 @@ package com.gu.holidaystopprocessor
import java.time.LocalDate

import com.gu.effects.RawEffects
import com.gu.holiday_stops.ActionCalculator.{GuardianWeeklyIssueSuspensionConstants, SundayVoucherIssueSuspensionConstants}
import com.gu.holiday_stops.ActionCalculator.GuardianWeeklyIssueSuspensionConstants
import com.gu.salesforce.SalesforceAuthenticate.SFAuthConfig
import com.gu.salesforce.SalesforceClient
import com.gu.salesforce.holiday_stops.SalesforceHolidayStopRequestsDetail
import com.gu.salesforce.holiday_stops.SalesforceHolidayStopRequestsDetail.{ProductRatePlanKey, _}
import com.gu.util.resthttp.JsonHttp
import scalaz.{-\/, \/-}
import com.gu.holiday_stops.{SalesforceHolidayError, SalesforceHolidayResponse}
import com.gu.holiday_stops.{ActionCalculator, SalesforceHolidayError, SalesforceHolidayResponse}

object Salesforce {
def calculateProcessDate(product: Product, processDateOverride: Option[LocalDate]) = {
processDateOverride.getOrElse(LocalDate.now.plusDays {
product match {
case SundayVoucher => SundayVoucherIssueSuspensionConstants.processorRunLeadTimeDays.toLong
case SundayVoucher => ActionCalculator.VoucherProcessorLeadTime
case GuardianWeekly => GuardianWeeklyIssueSuspensionConstants.processorRunLeadTimeDays.toLong
}
})
Expand All @@ -27,13 +27,21 @@ object Salesforce {
SalesforceClient(RawEffects.response, sfCredentials).value.flatMap { sfAuth =>
val sfGet = sfAuth.wrapWith(JsonHttp.getWithParams)
product match {
case SundayVoucher =>
val fetchOp = SalesforceHolidayStopRequestsDetail.FetchSundayVoucherHolidayStopRequestsDetails(sfGet)
fetchOp(ProductRatePlanKey(SundayVoucher), processDate)

case GuardianWeekly =>
val fetchOp = SalesforceHolidayStopRequestsDetail.LookupPendingByProductNamePrefixAndDate(sfGet)
fetchOp(ProductName("Guardian Weekly"), processDate)

case SundayVoucher =>
val fetchOp = SalesforceHolidayStopRequestsDetail.FetchVoucherHolidayStopRequestsDetails(sfGet)
fetchOp(ProductRatePlanKey(SundayVoucher), processDate)

case WeekendVoucher =>
val fetchOp = SalesforceHolidayStopRequestsDetail.FetchVoucherHolidayStopRequestsDetails(sfGet)
fetchOp(ProductRatePlanKey(WeekendVoucher), processDate)

case SixdayVoucher =>
val fetchOp = SalesforceHolidayStopRequestsDetail.FetchVoucherHolidayStopRequestsDetails(sfGet)
fetchOp(ProductRatePlanKey(SixdayVoucher), processDate)
}
}.toDisjunction match {
case -\/(failure) => Left(SalesforceHolidayError(failure.toString))
Expand Down
Expand Up @@ -58,7 +58,7 @@ object ActionCalculator {
*/
sealed abstract class IssueSuspensionConstants(
val issueDayOfWeek: DayOfWeek,
val processorRunLeadTimeDays: Int,
val processorRunLeadTimeDays: Int
) {
/**
* The first date a holiday can started on for this issue when creating a stop on the supplied date
Expand Down Expand Up @@ -108,19 +108,42 @@ object ActionCalculator {
}
}

val SundayVoucherSuspensionConstants = SuspensionConstants(
annualIssueLimit = 6,
issueConstants = List(SundayVoucherIssueSuspensionConstants)
val SundayVoucherSuspensionConstants = voucherSuspensionConstans(
List(voucherIssueSuspensionConstants(DayOfWeek.SUNDAY))
)

case object SundayVoucherIssueSuspensionConstants extends IssueSuspensionConstants(
issueDayOfWeek = DayOfWeek.SUNDAY,
processorRunLeadTimeDays = 1,
) {
def firstAvailableDate(today: LocalDate): LocalDate = {
today.plus(processorRunLeadTimeDays.toLong, ChronoUnit.DAYS)
val WeekendVoucherSuspensionConstants = voucherSuspensionConstans(
List(
voucherIssueSuspensionConstants(DayOfWeek.SATURDAY),
voucherIssueSuspensionConstants(DayOfWeek.SUNDAY)
)
)

val SixdayVoucherSuspensionConstants = voucherSuspensionConstans(
List(
voucherIssueSuspensionConstants(DayOfWeek.MONDAY),
voucherIssueSuspensionConstants(DayOfWeek.TUESDAY),
voucherIssueSuspensionConstants(DayOfWeek.WEDNESDAY),
voucherIssueSuspensionConstants(DayOfWeek.THURSDAY),
voucherIssueSuspensionConstants(DayOfWeek.FRIDAY),
voucherIssueSuspensionConstants(DayOfWeek.SATURDAY),
)
)

def voucherSuspensionConstans(issueSuspensionConstants: List[IssueSuspensionConstants]) =
SuspensionConstants(issueSuspensionConstants.size * 6, issueSuspensionConstants)

lazy val VoucherProcessorLeadTime: Int = 1

def voucherIssueSuspensionConstants(dayOfWeek: DayOfWeek): IssueSuspensionConstants =
new IssueSuspensionConstants(
issueDayOfWeek = dayOfWeek,
processorRunLeadTimeDays = VoucherProcessorLeadTime
) {
def firstAvailableDate(today: LocalDate): LocalDate = {
today.plus(processorRunLeadTimeDays.toLong, ChronoUnit.DAYS)
}
}
}

// TODO this will likely need to change to return an array of days of week (when we support more than just GW)
def suspensionConstantsByProduct(productNamePrefix: ProductName): SuspensionConstants =
Expand All @@ -134,6 +157,10 @@ object ActionCalculator {
def suspensionConstantsByProductRatePlanKey(
productKey: ProductRatePlanKey
): Either[ActionCalculatorError, SuspensionConstants] = productKey match {
case ProductRatePlanKey(ProductType("Newspaper - Voucher Book"), ProductRatePlanName("Sixday")) =>
Right(SixdayVoucherSuspensionConstants)
case ProductRatePlanKey(ProductType("Newspaper - Voucher Book"), ProductRatePlanName("Weekend")) =>
Right(WeekendVoucherSuspensionConstants)
case ProductRatePlanKey(ProductType("Newspaper - Voucher Book"), ProductRatePlanName("Sunday")) =>
Right(SundayVoucherSuspensionConstants)
case ProductRatePlanKey(ProductType("Guardian Weekly"), _) =>
Expand All @@ -158,15 +185,15 @@ object ActionCalculator {
): Either[ActionCalculatorError, ProductSpecifics] = {
suspensionConstantsByProductRatePlanKey(productRatePlanChargeId)
.map { constants =>
ProductSpecifics(
constants.annualIssueLimit,
constants.issueConstants.map { issueConstants =>
IssueSpecifics(
issueConstants.firstAvailableDate(today),
issueConstants.issueDayOfWeek.getValue
)
}
)
ProductSpecifics(
constants.annualIssueLimit,
constants.issueConstants.map { issueConstants =>
IssueSpecifics(
issueConstants.firstAvailableDate(today),
issueConstants.issueDayOfWeek.getValue
)
}
)
}
}

Expand Down Expand Up @@ -206,4 +233,4 @@ object ActionCalculator {

}

case class ActionCalculatorError(message: String)
case class ActionCalculatorError(message: String)
16 changes: 12 additions & 4 deletions lib/holiday-stops/src/main/scala/com/gu/holiday_stops/Config.scala
Expand Up @@ -10,7 +10,9 @@ case class Config(
sfConfig: SFAuthConfig,
holidayCreditProduct: HolidayCreditProduct,
guardianWeeklyConfig: GuardianWeeklyHolidayStopConfig,
sundayVoucherConfig: SundayVoucherHolidayStopConfig
sundayVoucherConfig: SundayVoucherHolidayStopConfig,
weekendVoucherConfig: WeekendVoucherHolidayStopConfig,
sixdayVoucherConfig: SixdayVoucherHolidayStopConfig
)

case class ZuoraConfig(
Expand Down Expand Up @@ -58,23 +60,29 @@ object Config {
sfConfig,
HolidayCreditProduct.Prod,
GuardianWeeklyHolidayStopConfig.Prod,
SundayVoucherHolidayStopConfig.Prod
SundayVoucherHolidayStopConfig.Prod,
WeekendVoucherHolidayStopConfig.Prod,
SixdayVoucherHolidayStopConfig.Prod
)
case "CODE" =>
Config(
zuoraConfig,
sfConfig,
HolidayCreditProduct.Code,
GuardianWeeklyHolidayStopConfig.Code,
SundayVoucherHolidayStopConfig.Code
SundayVoucherHolidayStopConfig.Code,
WeekendVoucherHolidayStopConfig.Code,
SixdayVoucherHolidayStopConfig.Code
)
case "DEV" =>
Config(
zuoraConfig,
sfConfig,
HolidayCreditProduct.Dev,
GuardianWeeklyHolidayStopConfig.Dev,
SundayVoucherHolidayStopConfig.Dev
SundayVoucherHolidayStopConfig.Dev,
WeekendVoucherHolidayStopConfig.Dev,
SixdayVoucherHolidayStopConfig.Dev
)
}
}
Expand Down
@@ -0,0 +1,17 @@
package com.gu.holiday_stops

case class SixdayVoucherHolidayStopConfig(
productRatePlanId: String
)

object SixdayVoucherHolidayStopConfig {
val Prod = SixdayVoucherHolidayStopConfig(
productRatePlanId = "2c92a0fd56fe270b0157040e42e536ef"
)
val Code = SixdayVoucherHolidayStopConfig(
productRatePlanId = "2c92c0f955ca02910155da254a641fb3"
)
val Dev = SixdayVoucherHolidayStopConfig(
productRatePlanId = "2c92c0f8555ce5cf01556e7f01771b8a"
)
}
Expand Up @@ -4,6 +4,7 @@ import java.time.LocalDate

import cats.syntax.either._
import com.gu.holiday_stops._
import com.gu.salesforce.holiday_stops.SalesforceHolidayStopRequestsDetail.StoppedPublicationDate
import com.typesafe.scalalogging.LazyLogging
import mouse.all._

Expand All @@ -16,13 +17,26 @@ object Credit extends LazyLogging {
)(stoppedPublicationDate: LocalDate, subscription: Subscription): Either[ZuoraHolidayError, Double] =
guardianWeeklyCredit(config, stoppedPublicationDate)(subscription)
.orElse(sundayVoucherCredit(config, stoppedPublicationDate)(subscription))
.orElse(weekendVoucherCredit(config, stoppedPublicationDate)(subscription))
.orElse(sixdayVoucherCredit(config, stoppedPublicationDate)(subscription))
.orElse(Left(ZuoraHolidayError(s"Could not calculate credit for subscription: ${subscription.subscriptionNumber}")))
.<| (logger.error("Failed to calculate holiday stop credits", _))

def guardianWeeklyCredit(config: Config, stoppedPublicationDate: LocalDate)(subscription: Subscription): Either[ZuoraHolidayError, Double] =
CurrentGuardianWeeklySubscription(subscription, config).map(GuardianWeeklyHolidayCredit(_, stoppedPublicationDate))

def sundayVoucherCredit(config: Config, stoppedPublicationDate: LocalDate)(subscription: Subscription): Either[ZuoraHolidayError, Double] =
CurrentSundayVoucherSubscription(subscription, config).map(SundayVoucherHolidayCredit(_, stoppedPublicationDate))

}
CurrentSundayVoucherSubscription(subscription, config).map(VoucherHolidayCredit(_))

def weekendVoucherCredit(config: Config, stoppedPublicationDate: LocalDate)(subscription: Subscription): Either[ZuoraHolidayError, Double] = {
CurrentWeekendVoucherSubscription(
subscription,
config,
StoppedPublicationDate(stoppedPublicationDate)
).map(VoucherHolidayCredit(_))
}

def sixdayVoucherCredit(config: Config, stoppedPublicationDate: LocalDate)(subscription: Subscription): Either[ZuoraHolidayError, Double] = {
CurrentSixdayVoucherSubscription(subscription, config, StoppedPublicationDate(stoppedPublicationDate)).map(VoucherHolidayCredit(_))
}
}
@@ -0,0 +1,89 @@
package com.gu.holiday_stops.subscription

import com.gu.holiday_stops.{Config, ZuoraHolidayError}
import com.gu.salesforce.holiday_stops.SalesforceHolidayStopRequestsDetail.StoppedPublicationDate
import enumeratum._

object CurrentSixdayVoucherSubscriptionPredicates {
def ratePlanIsSixdayVoucher(ratePlan: RatePlan, sixdayVoucherProductRatePlanId: String): Boolean =
ratePlan.productRatePlanId == sixdayVoucherProductRatePlanId

def stoppedPublicationIsNotOnSunday(stoppedPublicationDate: StoppedPublicationDate): Boolean =
List("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday").contains(stoppedPublicationDate.getDayOfWeek)

def ratePlanHasBeenInvoicedForAllCharges(ratePlan: RatePlan): Boolean = {
ratePlan.ratePlanCharges.forall { ratePlanCharge =>
(for {
fromInclusive <- ratePlanCharge.processedThroughDate
toExclusive <- ratePlanCharge.chargedThroughDate
} yield {
toExclusive isAfter fromInclusive
}).getOrElse(false)
}
}

def billingPeriodIsAnnualOrMonthOrQuarterOrSemiAnnual(ratePlan: RatePlan): Boolean = {
val billingPeriods = ratePlan.ratePlanCharges.map(_.billingPeriod)
val allPeriodsAreTheSame = billingPeriods.headOption.exists(bp => billingPeriods.forall(_ == bp))
val expectedBillingPeriod = billingPeriods.forall(List(Some("Annual"), Some("Month"), Some("Quarter"), Some("Semi-Annual")).contains)
allPeriodsAreTheSame && expectedBillingPeriod
}
}

case class CurrentSixdayVoucherSubscription(
subscriptionNumber: String,
billingPeriod: String,
price: Double,
invoicedPeriod: CurrentInvoicedPeriod,
ratePlanId: String,
productRatePlanId: String,
dayOfWeek: VoucherDayOfWeek
) extends CurrentVoucherSubscription


object CurrentSixdayVoucherSubscription {
private def findSixdayVoucherRatePlan(
subscription: Subscription,
sixdayVoucherProductRatePlanId: String,
stoppedPublicationDate: StoppedPublicationDate): Option[RatePlan] = {

subscription
.ratePlans
.find { ratePlan =>
import CurrentSixdayVoucherSubscriptionPredicates._
List(
stoppedPublicationIsNotOnSunday(stoppedPublicationDate),
ratePlanIsSixdayVoucher(ratePlan, sixdayVoucherProductRatePlanId),
ratePlanHasBeenInvoicedForAllCharges(ratePlan), // FIXME: Why is Saturday not billed?
billingPeriodIsAnnualOrMonthOrQuarterOrSemiAnnual(ratePlan),
).forall(_ == true)
}
}

def apply(
subscription: Subscription,
config: Config,
stoppedPublicationDate: StoppedPublicationDate
): Either[ZuoraHolidayError, CurrentSixdayVoucherSubscription] = {
findSixdayVoucherRatePlan(subscription, config.sixdayVoucherConfig.productRatePlanId, stoppedPublicationDate).flatMap { currentSixdayVoucherRatePlan =>
for {
rpc <- currentSixdayVoucherRatePlan.ratePlanCharges.find(_.name == stoppedPublicationDate.getDayOfWeek) // find particular RPC, Saturday or Sunday
billingPeriod <- rpc.billingPeriod
startDateIncluding <- rpc.processedThroughDate
endDateExcluding <- rpc.chargedThroughDate
} yield new CurrentSixdayVoucherSubscription(
subscriptionNumber = subscription.subscriptionNumber,
billingPeriod = billingPeriod,
price = rpc.price,
invoicedPeriod = CurrentInvoicedPeriod(
startDateIncluding = startDateIncluding,
endDateExcluding = endDateExcluding
),
ratePlanId = currentSixdayVoucherRatePlan.id,
productRatePlanId = currentSixdayVoucherRatePlan.productRatePlanId,
dayOfWeek = VoucherDayOfWeek.withName(stoppedPublicationDate.getDayOfWeek)
)
}.toRight(ZuoraHolidayError(s"Failed to determine Sixday Voucher Newspaper Guardian rate plan: $subscription"))
}
}

Expand Up @@ -38,7 +38,7 @@ case class CurrentSundayVoucherSubscription(
ratePlanId: String,
productRatePlanId: String,
productRatePlanChargeId: String // unique identifier of product
)
) extends CurrentVoucherSubscription

object CurrentSundayVoucherSubscription {

Expand Down
@@ -0,0 +1,7 @@
package com.gu.holiday_stops.subscription

trait CurrentVoucherSubscription {
def price: Double
def billingPeriod: String
def subscriptionNumber: String
}

0 comments on commit bdee479

Please sign in to comment.