/**
* Find the most up-to-date user state for the particular billing period.
*/
- def findLatestUserStateForBillingMonth(userId: String, yearOfBillingMonth: Int, billingMonth: Int): Maybe[UserState]
+ def findLatestUserStateForEndOfBillingMonth(userId: String, yearOfBillingMonth: Int, billingMonth: Int): Maybe[UserState]
}
/**
CreditSnapshot(0, now),
AgreementSnapshot(Agreement(agreementName, now, -1) :: Nil, now),
RolesSnapshot(List(), now),
- PaymentOrdersSnapshot(Nil, now),
- OwnedGroupsSnapshot(Nil, now),
- GroupMembershipsSnapshot(Nil, now),
OwnedResourcesSnapshot(List(), now)
)
}
CreditSnapshot(0, now),
AgreementSnapshot(Agreement(agreementName, now, - 1) :: Nil, now),
RolesSnapshot(List(), now),
- PaymentOrdersSnapshot(Nil, now),
- OwnedGroupsSnapshot(Nil, now),
- GroupMembershipsSnapshot(Nil, now),
OwnedResourcesSnapshot(List(), now)
)
}
*
* Always compute, taking into account any "out of sync" resource events
*/
- def computeUserStateAtStartOfBillingPeriod(billingYear: Int,
+ def computeUserStateAtEndOfBillingPeriod(billingYear: Int,
billingMonth: Int,
knownUserState: UserState,
accounting: Accounting): Maybe[EndOfBillingState] = {
// }
}
+
+ def findBillingStateAtEndOfBillingPeriod(yearOfBillingMonth: Int,
+ billingMonth: Int,
+ userId: String,
+ userStateCache: UserStateCache,
+ accounting: Accounting): Maybe[EndOfBillingState] = {
+ userStateCache.findLatestUserStateForEndOfBillingMonth(userId, yearOfBillingMonth, billingMonth) match {
+ case Just(userState) ⇒
+ case NoVal ⇒
+ case failed @ Failed(e, m) ⇒
+ }
+
+ Just(EndOfBillingState(createFirstUserState(userId), ImplicitOffEvents(Nil), OutOfSyncWalletEntries(Nil)))
+ }
/**
* Find the previous resource event, if needed by the event's cost policy,
NoVal
}
- def updatePreviousRCEventWith(previousRCEventsMap: mutable.Map[ResourceEvent.FullResourceType, ResourceEvent],
+ type FullResourceType = ResourceEvent.FullResourceType
+ def updatePreviousRCEventWith(previousRCEventsMap: mutable.Map[FullResourceType, ResourceEvent],
newRCEvent: ResourceEvent): Unit = {
previousRCEventsMap(newRCEvent.fullResourceInfo) = newRCEvent
}
// Check if this value is already cached and valid, otherwise compute the value
// TODO : cache it in case of new computation
- val cachedStartUserStateM = userStateCache.findLatestUserStateForBillingMonth(
+ val cachedStartUserStateM = userStateCache.findLatestUserStateForEndOfBillingMonth(
userId,
yearOfPrevBillingMonth,
prevBillingMonth)
} else {
// Oops, there are "out of sync" resource event. Must compute (potentially recursively)
logger.debug("Recompute start user state...")
- val computedUserStateAtStartOfBillingPeriod = computeUserStateAtStartOfBillingPeriod(
+ val computedUserStateAtStartOfBillingPeriod = computeUserStateAtEndOfBillingPeriod(
yearOfPrevBillingMonth,
prevBillingMonth,
cachedStartUserState,
case NoVal ⇒
// We do not even have a cached value, so compute one!
logger.debug("Do not have a cachedStartUserState, computing one...")
- val computedUserStateAtStartOfBillingPeriod = computeUserStateAtStartOfBillingPeriod(
+ val computedUserStateAtStartOfBillingPeriod = computeUserStateAtEndOfBillingPeriod(
yearOfPrevBillingMonth,
prevBillingMonth,
currentUserState,
logger.debug("allBillingPeriodRelevantRCEvents [%s] = %s".format(allBillingPeriodRelevantRCEvents.size, allBillingPeriodRelevantRCEvents))
type FullResourceType = ResourceEvent.FullResourceType
+ // For each type and instance of resource, we keep the previously met resource event.
val previousRCEventsMap = mutable.Map[FullResourceType, ResourceEvent]()
+ // Since we may already have some implicit events from the beginning of the billing period, we put
+ // them to the map.
+ // TODO:
val impliedRCEventsMap = mutable.Map[FullResourceType, ResourceEvent]() // those which do not exists but are
// implied in order to do billing calculations (e.g. the "off" vmtime resource event)
// Our temporary state holder.
var _workingUserState = newStartUserState
val nowMillis = TimeHelpers.nowMillis
+ var _counter = 0
for(currentResourceEvent <- allBillingPeriodRelevantRCEvents) {
+ _counter = _counter + 1
val resource = currentResourceEvent.resource
val instanceId = currentResourceEvent.instanceId
- logger.debug("Processing %s".format(currentResourceEvent.toDebugString(defaultResourcesMap, true)))
+ logger.debug("%02d. Processing %s".format(_counter, currentResourceEvent.toDebugString(defaultResourcesMap, true)))
// ResourCe events Debug
// = = =
- def RCD(fmt: String, args: Any*) = logger.debug(" => " + fmt.format(args:_*))
+ def RCD(fmt: String, args: Any*) = logger.debug(" ⇒ " + fmt.format(args:_*))
+
+ RCD("previousRCEventsMap: ")
+ for {
+ (k, v) <- previousRCEventsMap
+ } {
+ RCD(" %s ⇒ %s".format(k, v.toDebugString(defaultResourcesMap, true)))
+ }
// We need to do these kinds of calculations:
// 1. Credit state calculations
costPolicyOpt match {
case Some(costPolicy) ⇒
RCD("Found costPolicy = %s".format(costPolicy))
- ///////////////////////////////////////
- // A. Update user state with new resource instance amount
- // TODO: Check if we are at beginning of billing period, so as to use
- // costPolicy.computeResourceInstanceAmountForNewBillingPeriod
- val DefaultResourceInstanceAmount = costPolicy.getResourceInstanceInitialAmount
- RCD("DefaultResourceInstanceAmount = %s".format(DefaultResourceInstanceAmount))
-
- val previousAmount = currentUserState.getResourceInstanceAmount(resource, instanceId, DefaultResourceInstanceAmount)
- RCD("previousAmount = %s".format(previousAmount))
- val newAmount = costPolicy.computeNewResourceInstanceAmount(previousAmount, currentResourceEvent.value)
- RCD("newAmount = %s".format(newAmount))
-
- _workingUserState = _workingUserState.copyForResourcesSnapshotUpdate(resource, instanceId, newAmount, nowMillis)
- // A. Update user state with new resource instance amount
- ///////////////////////////////////////
-
-
- ///////////////////////////////////////
- // B. Update user state with new credit
- val previousRCEventM = findPreviousRCEventOf(currentResourceEvent, costPolicy, previousRCEventsMap)
- _workingUserState.findResourceInstanceSnapshot(resource, instanceId)
- // B. Update user state with new credit
- ///////////////////////////////////////
-
-
- ///////////////////////////////////////
- // C. Update ??? state with wallet entries
-
- // C. Update ??? state with wallet entries
- ///////////////////////////////////////
+
+ // If this is an event for which no action is required, then OK, proceed with the next one
+ // Basically, we do nothing for ON events but we treat everything polymorphically here
+ costPolicy.isBillableEventBasedOnValue(currentResourceEvent.value) match {
+ case true ⇒
+ ///////////////////////////////////////
+ // A. Update user state with new resource instance amount
+ // TODO: Check if we are at beginning of billing period, so as to use
+ // costPolicy.computeResourceInstanceAmountForNewBillingPeriod
+ val DefaultResourceInstanceAmount = costPolicy.getResourceInstanceInitialAmount
+ RCD("DefaultResourceInstanceAmount = %s".format(DefaultResourceInstanceAmount))
+
+ val previousAmount = currentUserState.getResourceInstanceAmount(resource, instanceId, DefaultResourceInstanceAmount)
+ RCD("previousAmount = %s".format(previousAmount))
+ val newAmount = costPolicy.computeNewResourceInstanceAmount(previousAmount, currentResourceEvent.value)
+ RCD("newAmount = %s".format(newAmount))
+
+ _workingUserState = _workingUserState.copyForResourcesSnapshotUpdate(resource, instanceId, newAmount, nowMillis)
+ // A. Update user state with new resource instance amount
+ ///////////////////////////////////////
+
+
+ ///////////////////////////////////////
+ // B. Update user state with new credit
+ val previousRCEventM = findPreviousRCEventOf(currentResourceEvent, costPolicy, previousRCEventsMap)
+ _workingUserState.findResourceInstanceSnapshot(resource, instanceId)
+ // B. Update user state with new credit
+ ///////////////////////////////////////
+
+
+ ///////////////////////////////////////
+ // C. Update ??? state with wallet entries
+
+ // C. Update ??? state with wallet entries
+ ///////////////////////////////////////
+
+ case false ⇒ // costPolicy.isBillableEventBasedOnValue(currentResourceEvent.value)
+ RCD("Ignoring not billabe (%s) %s".format(
+ currentResourceEvent.beautifyValue(defaultResourcesMap),
+ currentResourceEvent.toDebugString(defaultResourcesMap, true)))
+ }
case None ⇒
() // ERROR