Implement contracts

This commit is contained in:
Paul-Henri Froidmont 2025-10-06 18:30:22 +02:00
parent 31014d1a0c
commit efdc50eb1d
Signed by: phfroidmont
GPG key ID: BE948AFD7E7873BE
33 changed files with 879 additions and 173 deletions

View file

@ -0,0 +1,24 @@
package lu.foyer
package contracts
import zio.*
import zio.Console.*
import zio.http.*
import zio.http.codec.*
import zio.http.codec.PathCodec.path
import zio.http.endpoint.*
import zio.schema.*
import java.net.URI
import java.time.LocalDate
import java.util.UUID
class ContractController(
val commandEngine: CommandEngine[ContractCommand, ContractEvent, ContractState])
extends CommandEngineController[ContractCommand, ContractEvent, ContractState]:
override val onthology = "org:example:insurance:contract"
override val entityName = "contracts"
override val allEntities = List("clients", "contracts")
object ContractController:
val layer = ZLayer.fromFunction(ContractController.apply)

View file

@ -0,0 +1,23 @@
package lu.foyer
package contracts
import java.util.UUID
import zio.*
class ContractEventRepositoryInMemory(events: Ref[Map[UUID, Event[ContractEvent]]])
extends EventRepository[ContractEvent]
with InMemoryRepository[Event[ContractEvent]](events):
def fetchOne(entityId: UUID, eventId: UUID): Task[Option[Event[ContractEvent]]] =
events.get.map(_.get(eventId))
def fetchMany(entityId: UUID, page: Page): Task[Paged[Event[ContractEvent]]] =
events.get
.map(entities =>
val items = entities.values
.filter(_.entityId == entityId)
.drop(page.number.getOrElse(0) * page.size.getOrElse(50))
.take(page.size.getOrElse(50))
Paged(items.toList, if page.totals.getOrElse(false) then Some(entities.size) else None)
)
object ContractEventRepositoryInMemory:
val layer = ZLayer.fromZIO(Ref.make(Map.empty).map(ContractEventRepositoryInMemory(_)))

View file

@ -0,0 +1,12 @@
package lu.foyer
package contracts
import java.util.UUID
import zio.*
class ContractStateRepositoryInMemory(clients: Ref[Map[UUID, Entity[ContractState]]])
extends StateRepository[ContractState]
with InMemoryRepository[Entity[ContractState]](clients)
object ContractStateRepositoryInMemory:
val layer = ZLayer.fromZIO(Ref.make(Map.empty).map(ContractStateRepositoryInMemory(_)))

View file

@ -0,0 +1,24 @@
package lu.foyer
package contracts
import java.util.UUID
import zio.*
import lu.foyer.clients.Address
import scala.math.BigDecimal.RoundingMode
import java.util.Currency
import lu.foyer.clients.Country
object EmployeeServiceImpl extends EmployeeService:
def fetchOne(subject: String): Either[String, Employee] =
if subject == "usr:top" then
Right(
Employee(
subject,
EmployeeDisplayName.assume("MEIER Christoph"),
Email.assume("top@foyer.lu")
)
)
else Left("Invalid Employee")
val layer = ZLayer.succeed(EmployeeServiceImpl)

View file

@ -0,0 +1,53 @@
package lu.foyer
package contracts
import java.util.UUID
import zio.*
import lu.foyer.clients.Address
import scala.math.BigDecimal.RoundingMode
import java.util.Currency
import lu.foyer.clients.Country
object PremiumServiceImpl extends PremiumService:
private val EUR = Currency.getInstance("EUR")
private val brands =
Map(
"renault" -> BigDecimal(0.5),
"fiat" -> BigDecimal(0.6),
"ford" -> BigDecimal(0.7),
"nissan" -> BigDecimal(0.75),
"peugeot" -> BigDecimal(0.8),
"volkswagen" -> BigDecimal(1.1),
"audi" -> BigDecimal(1.25),
"bmw" -> BigDecimal(1.25),
"mercedes" -> BigDecimal(1.25),
"ferrari" -> BigDecimal(2.3),
"bugatti" -> BigDecimal(2.6),
"tesla" -> BigDecimal(5.0)
)
private val countries =
Map(Country.CH -> BigDecimal(0.8), Country.LU -> BigDecimal(1.2))
private def computeAmountEUR(base: BigDecimal, brand: VehicleBrand, country: Country)
: BigDecimal =
val brandMultiplier = brands.getOrElse(brand.toLowerCase, BigDecimal(1))
val countryMultiplier = countries.getOrElse(country, BigDecimal(1))
val amount = base * brandMultiplier * countryMultiplier
amount.setScale(EUR.getDefaultFractionDigits, RoundingMode.HALF_UP).rounded
override def computePremium(formula: FormulaType, vehicle: Vehicle, residentialAddress: Address)
: Either[String, Amount] =
if vehicle.insuredValue.value > formula.maxCoverage then
Left(s"The $formula formula only covers amounts up to ${formula.maxCoverage} EUR")
else
Right(
Amount(
computeAmountEUR(formula.basePremium, vehicle.brand, residentialAddress.country),
EUR
)
)
val layer = ZLayer.succeed(PremiumServiceImpl)
end PremiumServiceImpl