private def createSubscriptionData()

in lib/zuora-core/src/main/scala/com/gu/zuora/subscription/SubscriptionData.scala [68:144]


  private def createSubscriptionData(
      nonZeroRatePlanChargeDatas: List[RatePlanChargeData],
      zuoraProductType: ZuoraProductType,
      productAnnualIssueLimitPerEdition: Int,
      subscription: Subscription,
  ): SubscriptionData = {
    new SubscriptionData {
      def issueDataForDate(issueDate: LocalDate): Either[ZuoraApiFailure, IssueData] = {
        for {
          ratePlanChargeData <- ratePlanChargeDataForDate(nonZeroRatePlanChargeDatas, issueDate)
          billingPeriod <- ratePlanChargeData.billingSchedule.billDatesCoveringDate(issueDate)
        } yield {
          applyAnyDiscounts(IssueData(issueDate, billingPeriod, ratePlanChargeData.issueCreditAmount))
        }
      }

      // Calculate credit by taking into account potential discounts, otherwise return original credit
      def applyAnyDiscounts(issueData: IssueData): IssueData = {
        import issueData._

        def isActiveDiscount(start: LocalDate, end: LocalDate): Boolean =
          (start.isEqual(issueDate) || start.isBefore(issueDate)) && end.isAfter(issueDate)

        val discounts: List[Double] =
          subscription.ratePlans.iterator
            .filter(_.productName == "Discounts")
            .flatMap(
              _.ratePlanCharges.map(rpc => (rpc.discountPercentage, rpc.effectiveStartDate, rpc.effectiveEndDate)),
            )
            .collect { case (percent, start, end) if percent.isDefined && isActiveDiscount(start, end) => percent }
            .flatten
            .map(_ / 100)
            .toList

        def verify(discountedCredit: Double): Double = {
          discountedCredit
            .tap(v =>
              assert(abs(v) <= abs(issueData.credit), "Discounted credit should not be more than un-discounted"),
            )
            .tap(v => assert(v <= 0, "Credit should be negative"))
            .tap(v =>
              assert(v.toString.dropWhile(_ != '.').tail.length <= 2, "Credit should have up to two decimal places"),
            )
            // an arbitrarily high threshold - any discount higher than this is probably a mistaken calculation
            .tap(v => assert(abs(v) < 15.0, "Credit should not go beyond maximum bound"))
            .tap(v =>
              if (discounts.isEmpty)
                assert(v == issueData.credit, "Credit should not be affected if there are no discounts"),
            )
        }

        discounts
          .foldLeft(issueData.credit) { case (acc, next) => acc * (1 - next) }
          .pipe(round2Places)
          .pipe(verify)
          .pipe(discountedCredit => issueData.copy(credit = discountedCredit))
      }

      def issueDataForPeriod(startDateInclusive: LocalDate, endDateInclusive: LocalDate): List[IssueData] = {
        nonZeroRatePlanChargeDatas
          .flatMap(_.getIssuesForPeriod(startDateInclusive, endDateInclusive))
          .map(applyAnyDiscounts)
          .sortBy(_.issueDate)(Ordering.fromLessThan(_.isBefore(_)))
      }

      override def productType: ZuoraProductType = {
        zuoraProductType
      }

      override def subscriptionAnnualIssueLimit: Int = {
        productAnnualIssueLimitPerEdition * editionDaysOfWeek.size
      }

      override def editionDaysOfWeek: List[DayOfWeek] =
        nonZeroRatePlanChargeDatas.map(_.issueDayOfWeek).distinct
    }
  }