in app/com/gu/memsub/subsv2/Subscription.scala [102:141]
def bestCancelledPlan[P <: SubscriptionPlan.AnyPlan](sub: Subscription[P]): Option[P] =
if (sub.isCancelled && sub.termEndDate.isBefore(LocalDate.now()))
sub.plans.list.sorted(planGoodnessOrder).reverse.headOption
else None
case class DiscardedPlan[+P <: SubscriptionPlan.AnyPlan](plan: P, why: String)
def apply[P <: SubscriptionPlan.AnyPlan](sub: Subscription[P], date: LocalDate): String \/ NonEmptyList[P] = {
val currentPlans = sub.plans.list.toList.sorted(planGoodnessOrder).reverse.map { plan =>
//If the sub hasn't been paid yet but has started we should fast-forward to the date of first payment (free trial)
val dateToCheck = if(sub.startDate <= date && sub.acceptanceDate > date) sub.acceptanceDate else date
val unvalidated = Validation.s[NonEmptyList[DiscardedPlan[P]]](plan)
/*
Note that a Contributor may have future sub.acceptanceDate and plan.startDate values if the user has
updated their payment amount via MMA since starting the contribution. In this case the alreadyStarted assessment
just checks that the sub.startDate is before, or the same as, the date received by this function.
*/
val ensureStarted = unvalidated.ensure(DiscardedPlan(plan, s"hasn't started as of $dateToCheck").wrapNel)(_)
val alreadyStarted = plan match {
case _: Contributor => ensureStarted(_ => sub.startDate <= date)
case _ => ensureStarted(_.start <= dateToCheck)
}
val freePlanCancelled = alreadyStarted.ensure(DiscardedPlan(plan, "has a free plan which has been cancelled").wrapNel)(_)
val contributorPlanCancelled = alreadyStarted.ensure(DiscardedPlan(plan, "has a contributor plan which has been cancelled").wrapNel)(_)
val paidPlanEnded = alreadyStarted.ensure(DiscardedPlan(plan, "has a paid plan which has ended").wrapNel)(_)
val digipackGiftEnded = alreadyStarted.ensure(DiscardedPlan(plan, "has a digipack gift plan which has ended").wrapNel)(_)
plan match {
case _: FreeSubscriptionPlan[_, _] => freePlanCancelled(_ => !sub.isCancelled)
case plan: PaidSubscriptionPlan[_, _] if plan.product == Product.Contribution => contributorPlanCancelled(_ => !sub.isCancelled)
case plan: PaidSubscriptionPlan[_, _] if plan.product == Product.Digipack && plan.charges.billingPeriod == OneTimeChargeBillingPeriod =>
digipackGiftEnded(_ => sub.termEndDate >= dateToCheck)
case plan: PaidSubscriptionPlan[_, _] => paidPlanEnded(_ => plan.end >= dateToCheck)
}
}
Sequence(currentPlans.map(_.leftMap(_.map(discard => s"Discarded ${discard.plan.id.get} because it ${discard.why}").list.toList.mkString("\n")).disjunction))
}