All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.atedeg.mdm.pricing.Actions.scala Maven / Gradle / Ivy

The newest version!
package dev.atedeg.mdm.pricing

import java.time.LocalDateTime

import cats.data.NonEmptyList
import cats.syntax.all.*

import dev.atedeg.mdm.pricing.utils.{ percent as _, * }
import dev.atedeg.mdm.utils.*
import dev.atedeg.mdm.utils.given

/**
 * Computes the price of an order line, according to any promotion that may be applied for a certain client.
 */
@SuppressWarnings(Array("org.wartremover.warts.ListAppend"))
def priceOrderLine(
    priceList: PriceList,
    clientPromotions: List[Promotion],
    today: LocalDateTime,
)(orderLine: IncomingOrderLine): PriceInEuroCents =
  val IncomingOrderLine(quantity, product) = orderLine
  val baseUnitPrice = priceList.priceList(product)
  val activePromotions = clientPromotions.filter(_.expiryDate.isAfter(today))
  val lines = activePromotions.flatMap(_.lines.toList).filter(_.product === product)
  val fixedDiscounts = lines.collect { case p: PromotionLine.Fixed => p }
  val thresholdDiscounts = lines.collect { case p: PromotionLine.Threshold => p }
  val basePrice = PriceInEuroCents(baseUnitPrice.n * quantity.n)
  basePrice
    |> applyFixedDiscount(fixedDiscounts)
    |> applyThresholdDiscount(thresholdDiscounts, quantity)

private def applyFixedDiscount(promotionLines: List[PromotionLine.Fixed])(
    basePrice: PriceInEuroCents,
): PriceInEuroCents =
  promotionLines.map(_.discount).reduceOption(_ * _) match
    case Some(discount) => basePrice withDiscount discount
    case None => basePrice

private def applyThresholdDiscount(promotionLines: List[PromotionLine.Threshold], quantity: Quantity)(
    basePrice: PriceInEuroCents,
): PriceInEuroCents =
  def listToPair[A](l: List[A]): (A, A) = (l(1), l(0))

  val filteredLines = promotionLines
    .filter(_.threshold.n < quantity.n)
    .sortBy(_.threshold.n)
    .map(pl => (pl.threshold, pl.discount))
  if filteredLines.isEmpty
  then basePrice
  else
    val percentages = filteredLines.map(_._2.toPercentage.inverted).scan(100.0.percent)(_ * _)
    val ranges = filteredLines.map(_._1.n.value).prepended(0).appended(quantity.n.value)
    val distances = ranges.sliding(2).map(listToPair).map(_ - _)
    val multiplier = 1.0 - (percentages.zip(distances).map(_.value * _).sum / quantity.n.value)
    val discount = DiscountPercentage(coerce(multiplier))
    basePrice withDiscount discount




© 2015 - 2024 Weber Informatics LLC | Privacy Policy