Skip to content

Instantly share code, notes, and snippets.

@juliano
Created April 26, 2020 09:51
Show Gist options
  • Save juliano/042748ba547b72b8cf6445782edb8896 to your computer and use it in GitHub Desktop.
Save juliano/042748ba547b72b8cf6445782edb8896 to your computer and use it in GitHub Desktop.
Matching invoices
object Exercise {
sealed trait InvoiceType
case object Approved extends InvoiceType
case object UnApproved extends InvoiceType
type Money = Double
type FundingFacilityId = String
case class Invoice(`type`: InvoiceType, amount: Money)
case class FundingFacility(id: FundingFacilityId,
fundedInvoiceTypes: List[InvoiceType],
limit: Option[Money],
totalDrawnDownAmount: Money) {
def updateDrawnAmount(invoice: Invoice) =
copy(totalDrawnDownAmount = this.totalDrawnDownAmount + invoice.amount)
}
def fundingFacilitySelector(invoices: List[Invoice],
fundingFacilities: List[FundingFacility]
): List[(Invoice, FundingFacilityId)] = {
val seed = (fundingFacilities, List.empty[(Invoice, FundingFacilityId)])
invoices.foldLeft(seed) { (acc, invoice) =>
val (ffs, matchedInvoices) = acc
val opt = for {
theFF <- suitableFF(ffs, invoice)
updatedFFs = updatedFundingFacilities(ffs, theFF, invoice)
newMatch = (invoice, theFF.id)
} yield (updatedFFs, matchedInvoices :+ newMatch)
opt.getOrElse(throw new RuntimeException("Couldn't find a suitable Funding Facility"))
}._2
}
private def suitableFF(ffs: List[FundingFacility], invoice: Invoice) =
ffs.find { f =>
f.fundedInvoiceTypes.contains(invoice.`type`) &&
f.limit.exists(_ - f.totalDrawnDownAmount >= invoice.amount)
}
private def updatedFundingFacilities(ffs: List[FundingFacility],
f: FundingFacility,
i: Invoice): List[FundingFacility] = {
val index = ffs.indexWhere(_ == f)
ffs.updated(index, f.updateDrawnAmount(i))
}
}
import Exercise._
import zio.test.Assertion.equalTo
import zio.test.{DefaultRunnableSpec, assert, suite, test}
object ExerciseSpec extends DefaultRunnableSpec {
def spec = suite("Exercise Suite")(
selectFFSuite
)
val inv1 = Invoice(Approved, 1000.0)
val inv2 = Invoice(Approved, 100.0)
val inv3 = Invoice(UnApproved, 100.0)
val inv4 = Invoice(UnApproved, 50.0)
val inv5 = Invoice(Approved, 150.0)
val inv6 = Invoice(UnApproved, 150.0)
val allInvoices = List(inv1, inv2, inv3, inv4, inv5, inv6)
val ff1 = FundingFacility("f1", List(Approved), Some(200.0), 0.0)
val ff2 = FundingFacility("f2", List(UnApproved), Some(200.0), 0.0)
val emergencyFF = FundingFacility("emergency", List(Approved, UnApproved), Some(Double.MaxValue), 0.0)
val fundingFacilities = List(ff1, ff2, emergencyFF)
val selectFFSuite = suite("Select Funding Facility")(
test("approved") {
val result = fundingFacilitySelector(List(inv2), fundingFacilities)
val expected = List[(Invoice, FundingFacilityId)](
(inv2, "f1")
)
assert(result)(equalTo(expected))
},
test("approved and unaproved") {
val result = fundingFacilitySelector(List(inv2, inv3), fundingFacilities)
val expected = List[(Invoice, FundingFacilityId)](
(inv2, "f1"),
(inv3, "f2")
)
assert(result)(equalTo(expected))
},
test("consumes limit") {
val result = fundingFacilitySelector(List(inv3, inv4, inv6), fundingFacilities)
val expected = List[(Invoice, FundingFacilityId)](
(inv3, "f2"),
(inv4, "f2"),
(inv6, "emergency")
)
assert(result)(equalTo(expected))
},
test("matches different invoices to ffs") {
val result = fundingFacilitySelector(allInvoices, fundingFacilities)
val expected = List[(Invoice, FundingFacilityId)](
(inv1, "emergency"),
(inv2, "f1"),
(inv3, "f2"),
(inv4, "f2"),
(inv5, "emergency"),
(inv6, "emergency")
)
assert(result)(equalTo(expected))
}
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment