def currentPlans()

in membership-common/src/main/scala/com/gu/memsub/subsv2/Subscription.scala [55:94]


  def currentPlans(sub: Subscription, date: LocalDate, catalog: Catalog): String \/ NonEmptyList[RatePlan] = {

    val currentPlans = sub.ratePlans.sortBy(_.totalChargesMinorUnit).reverse.map { plan =>
      val product = plan.product(catalog)
      // 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.contractEffectiveDate <= date && sub.customerAcceptanceDate > date) sub.customerAcceptanceDate else date

      val unvalidated = Validation.s[NonEmptyList[DiscardedPlan]](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 =
        if (product == Contribution)
          ensureStarted(_ => sub.contractEffectiveDate <= date)
        else
          ensureStarted(_.effectiveStartDate <= dateToCheck)
      val contributorPlanCancelled =
        alreadyStarted.ensure(DiscardedPlan(plan, "has a contributor plan which has been cancelled or removed").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)(_)
      if (product == Product.Contribution)
        contributorPlanCancelled(_ => !sub.isCancelled && !plan.lastChangeType.contains("Remove"))
      else if (product == Product.Digipack && plan.billingPeriod.toOption.contains(OneTimeChargeBillingPeriod))
        digipackGiftEnded(_ => sub.termEndDate >= dateToCheck)
      else
        paidPlanEnded(_ => {
          val inGracePeriodAndNotCancelled = plan.effectiveEndDate == dateToCheck && !sub.isCancelled && !plan.lastChangeType.contains("Remove")
          plan.effectiveEndDate > dateToCheck || inGracePeriodAndNotCancelled
        })
    }

    Sequence(
      currentPlans.map(
        _.leftMap(_.map(discard => s"Discarded ${discard.plan.id.get} because it ${discard.why}").list.toList.mkString("\n")).toDisjunction,
      ),
    )
  }