@@ -11,76 +11,81 @@ import scala.reflect.runtime.universe.TypeTag
1111
1212object Property {
1313 // define property types
14- trait Property extends :: [Property ]{
14+ trait propertyEvent {
15+ val propertyId : Long
16+ val amount : Double
17+ val date : LocalDate
18+
19+
20+ }
21+ type PropertyEventDeps = Date
22+ trait Property extends (PropertyEventDeps ==> Property ) with produces[Seq [propertyEvent]]{
1523 val id : Long
24+ val dateRange : DateRange
25+ def preceeds (date: Date ): Boolean = date.isBefore(dateRange.start)
26+ def halted (date: Date ): Boolean = date.isAfter(dateRange.end)
27+ def isActive (date: Date ): Boolean = ! preceeds(date) && ! halted(date)
1628 }
17- case class RentalProperty (id: Long , monthlyRent: Double ) extends Property
18- case class Home (id: Long ,costBasis: Double ,marketValue: Double )
19- // define property events
20- case class rentPaymentDue (propertyId: Long ,amount: Double ,date: Date ) extends Event
21- case class rentPaymentPaid (propertyId: Long ,amount: Double ,date: LocalDate ) extends Event
22- // generate rend due payments in model
23- type RentGenDeps = Properties with Date
24- case class GenerateRentDue (value: Seq [rentPaymentDue]) extends
25- (RentGenDeps ==> GenerateRentDue ) with
26- produces[Seq [rentPaymentDue]]{
27- override def apply (src : dataset[RentGenDeps ]): dataset[GenerateRentDue ] = for {
28- properties <- src.properties
29+ case class RentalProperty (id: Long , rent: Double ,dateRange: DateRange ,eventLog: Seq [propertyEvent] = Seq ()) extends Property {
30+ override val value = eventLog
31+ override def apply (src : dataset[PropertyEventDeps ]): dataset[Property ] = for {
32+ range <- dateRange.getRange
2933 date <- src.currentDate
30- }yield GenerateRentDue (
31- date match {
32- case w: Week if w.value.getDayOfMonth < 7 => properties.collect({
33- case r: RentalProperty => rentPaymentDue(r.id,r.monthlyRent,w)
34- })
35- case m: Month => properties.collect({
36- case r: RentalProperty => rentPaymentDue(r.id,r.monthlyRent,m)
37- })
38- case y: Year => properties.collect({
39- case r: RentalProperty => (0 to 11 ).foldLeft(Seq [rentPaymentDue]())(
40- (accum,n) => rentPaymentDue(r.id,r.monthlyRent,Month (y.value.plusMonths(n))) +: accum
41- )
42- }).flatten
43- case _ => Seq ()
44- })
34+ }yield {
35+ if (isActive(date)){
36+ val events = range.findClosestPeriodRange(src).map(d => rentPaymentDue(id,rent,d)) ++ eventLog
37+ this .copy(eventLog = events ,dateRange = range)
38+ }else this
39+ }
4540 }
46- implicit class RentGeneratorGrammarPaymentHelper [A <: Properties with Date ](src: dataset[A ])(implicit taga: TypeTag [A ]){
47- def rentDue : dataset[GenerateRentDue ] =
48- src
49- .+- (GenerateRentDue (Seq ()))
50- .<-+ [GenerateRentDue ]
41+ case class Home (id: Long ,costBasis: Double ,marketValue: Double ,dateRange: DateRange ,value: Seq [propertyEvent] = Seq ()) extends Property {
42+ override def apply (v1 : dataset[PropertyEventDeps ]): dataset[Property ] = this
5143 }
44+ // define property events
45+ case class rentPaymentDue (propertyId: Long ,amount: Double ,date: LocalDate ) extends propertyEvent
46+ case class rentPaymentPaid (propertyId: Long ,amount: Double ,date: LocalDate ) extends propertyEvent
47+ // generate rend due payments in model
5248
5349
54- case class Properties (value: Seq [Property ],eventLog: Seq [Event ]) extends (Properties ==> Properties ) with produces[Seq [Property ]]{
50+ type PropertyEventGenDeps = Date
51+ case class Properties (value: Seq [Property ],eventLog: Seq [propertyEvent]) extends (PropertyEventGenDeps with Properties ==> Properties ) with produces[Seq [Property ]]{
5552 lazy val propertyMap : Map [Long ,Property ] = value.map(a => a.id -> a).toMap
56- override def apply (v1 : dataset[Properties ]): dataset[Properties ] = v1.<-- [Properties ]
57- private def apply (account: Property ): dataset[Properties ] = {
53+ override def apply (src : dataset[PropertyEventGenDeps with Properties ]): dataset[Properties ] = for {
54+ properties <- src.properties
55+ }yield {
56+ properties.value.foldLeft(src)((accumSrc, i) => for {
57+ inc <- accumSrc.++ (i).<-+ [Property ]
58+ incomes <- accumSrc.properties
59+ updatedIncomes <- incomes.update(inc)
60+ } yield accumSrc ++ updatedIncomes).properties
61+ }
62+
63+ private def apply (property: Property ): dataset[Properties ] = {
5864 val pptyMap = this .propertyMap
59- val exists = pptyMap.get(account .id).isDefined
60- lazy val updatedMap = pptyMap.updated(account .id,account )
61- val newAcctColl = if (exists) updatedMap.values.toSeq else value :+ account
65+ val exists = pptyMap.get(property .id).isDefined
66+ lazy val updatedMap = pptyMap.updated(property .id,property )
67+ val newAcctColl = if (exists) updatedMap.values.toSeq else value :+ property
6268 new Properties (newAcctColl,this .eventLog){
63- override lazy val propertyMap = if (pptyMap != null ) updatedMap else Map (account .id -> account )
69+ override lazy val propertyMap = if (pptyMap != null ) updatedMap else Map (property .id -> property )
6470 }
6571 }
66- private [Property ] def addEvents (events: Event * ) = Properties (value, events ++ eventLog)
72+ private [Property ] def addEvents (events: propertyEvent * ) = this .copy(eventLog = events ++ eventLog)
6773 def get (id: Long ): dataset[Property ] = propertyMap(id)
6874 def update (property: Property ): dataset[Properties ] = apply(property)
6975
7076 }
7177 implicit class PropertiesAPI [A <: Properties ](src: dataset[A ])(implicit taga: TypeTag [A ]){
7278 def properties : dataset[Properties ] = if (src.isInstanceOf [Properties ]) src else src.<-- [Properties ]
73- def events : produces[Seq [Event ]] = src.properties.biMap[produces[Seq [Event ]]](err => noVal(err.value:_* ))(d => someval(d.asInstanceOf [Properties ].eventLog))
79+ def events : produces[Seq [propertyEvent]] = src.properties.biMap[produces[Seq [propertyEvent]]](err => noVal(err.value:_* ))(d => someval(d.asInstanceOf [Properties ].eventLog ++ d.asInstanceOf [Properties ].value.flatMap(_.value)))
80+ def eventsAtDate (date : Date ): produces[Seq [propertyEvent]] = someval(
81+ src.events.filter(e => date.isWithinPeriod(Year (e.date)))
82+ )
7483 }
7584 implicit class GrowRents [A <: Properties with Date ](src: dataset[A ])(implicit taga: TypeTag [A ]){
76- def accrueRent : dataset[A ] = for {
77- properties <- src.properties
78- rentdue <- src.rentDue
79- }yield src.++ (
80- properties.addEvents(rentdue.value:_* )
81- )
85+ def accrueRent : dataset[A ] = src.+-> [Properties ]
8286 }
8387 implicit class PayRents [A <: Properties with Accounts with Date ](src: dataset[A ])(implicit taga: TypeTag [A ]){
88+
8489 def payRents : dataset[A ] = for {
8590 properties <- src.properties
8691 accounts <- src.accounts
@@ -89,24 +94,14 @@ object Property{
8994 .collectFirst({case c: CheckingAccount => c})
9095 .asInstanceOf [Option [Account ]]
9196 .fromOption
92- rentdue <- src.rentDue
93- }yield date match {
94- case _:Month | _:Week =>
95- val rentpaymentsdue = properties.events.collect({
96- case r: rentPaymentDue if scala.math.abs(date.getDayOfMonth() - r.date.getDayOfMonth()) < date.numberOfDays => r
97- })
98- rentdue.foldLeft(src)((accumSrc,paymentDue) =>
99- accumSrc
100- .withdraw(rentAcct,paymentDue.amount)
101- .++ (
102- properties.addEvents(
103- rentPaymentPaid(paymentDue.propertyId,paymentDue.amount,date)
104- )
105- )
106- )
107- case _ => DatasetError [A ](new Error (" rent payments other than Monthly not implemented" ))
108-
109-
97+ }yield {
98+ val rentPaymentsPaid = properties.eventsAtDate(date).collect({
99+ case r: rentPaymentDue => rentPaymentPaid(r.propertyId,r.amount,r.date)
100+ })
101+ rentPaymentsPaid.foldLeft(src)(
102+ (accumSrc,event) =>
103+ accumSrc.withdraw(rentAcct,event.amount)
104+ ) ++ properties.addEvents(rentPaymentsPaid:_* )
110105 }
111106 }
112107
0 commit comments