* @author Christos KK Loverdos <loverdos@gmail.com>
*/
trait Accounting extends DSLUtils with Loggable {
-
- def computeWalletEntriesForAgreement(userId: String,
- totalCredits: Double,
- costPolicy: DSLCostPolicy,
- previousResourceEventM: Maybe[ResourceEvent],
- previousAccumulatingAmountM: Maybe[Double],
- currentResourceEvent: ResourceEvent,
- defaultResourcesMap: DSLResourcesMap,
- agreement: DSLAgreement): Maybe[Traversable[WalletEntry]] = Maybe {
- val resource = currentResourceEvent.resource
- val instanceId = currentResourceEvent.instanceId
- val currentValue = currentResourceEvent.value
- val currentOccurredMillis = currentResourceEvent.occurredMillis
-
- /////////////////////////////////////////////////////////////////////
- // Validations
- /////////////////////////////////////////////////////////////////////
- // 1. Validate cost policy
- val actualCostPolicyM = currentResourceEvent.findCostPolicyM(defaultResourcesMap)
- val currentResourceEventDebugStr = currentResourceEvent.toDebugString(defaultResourcesMap, false)
- actualCostPolicyM match {
- case Just(actualCostPolicy) ⇒
- if(costPolicy != actualCostPolicy) {
- throw new Exception("Actual cost policy %s is not the same as provided %s for event %s".
- format(actualCostPolicy, costPolicy, currentResourceEventDebugStr))
- }
- case _ ⇒
- throw new Exception("Could not verify cost policy %s for event %s".
- format(costPolicy, currentResourceEventDebugStr))
- }
-
- // 2. Validate previous resource event
- previousResourceEventM match {
- case Just(previousResourceEvent) ⇒
- if(!costPolicy.needsPreviousEventForCreditAndAmountCalculation) {
- throw new Exception("Provided previous event but cost policy %s does not need one".format(costPolicy))
- }
-
- // 3. resource and instanceId
- val previousResource = previousResourceEvent.resource
- val previousInstanceId = previousResourceEvent.instanceId
- (resource == previousResource, instanceId == previousInstanceId) match {
- case (true, true) ⇒
-
- case (true, false) ⇒
- throw new Exception("Resource instance IDs do not match (%s vs %s) for current and previous event".
- format(instanceId, previousInstanceId))
-
- case (false, true) ⇒
- throw new Exception("Resource types do not match (%s vs %s) for current and previous event".
- format(resource, previousResource))
-
- case (false, false) ⇒
- throw new Exception("Resource types and instance IDs do not match (%s vs %s) for current and previous event".
- format((resource, instanceId), (previousResource, previousInstanceId)))
- }
-
- case NoVal ⇒
- if(costPolicy.needsPreviousEventForCreditAndAmountCalculation) {
- throw new Exception("Did not provid previous event but cost policy %s needa one".format(costPolicy))
- }
-
- case failed @ Failed(e, m) ⇒
- throw new Exception("Error obtaining previous event".format(m), e)
- }
-
- // 4. Make sure this is billable.
- // It is the caller's responsibility to provide us with billable events
- costPolicy.isBillableEventBasedOnValue(currentValue) match {
- case true ⇒
- case false ⇒
- throw new Exception("Event not billable %s".format(currentResourceEventDebugStr))
- }
- /////////////////////////////////////////////////////////////////////
-
- // Construct a proper value map
- val valueMap = costPolicy.makeValueMap(
- totalCredits,
- previousAccumulatingAmountM.getOr(0.0),
- currentOccurredMillis - previousResourceEventM.map(_.occurredMillis).getOr(currentOccurredMillis),
- previousResourceEventM.map(_.value).getOr(0.0),
- currentValue)
- // Now, we need to find the proper agreement(s) and feed the data to them.
-
-
- Nil
- }
-
+
def chargeEvent2( oldResourceEventM: Maybe[ResourceEvent],
newResourceEvent: ResourceEvent,
dslAgreement: DSLAgreement,
* Creates a list of wallet entries by applying the agreement provisions on
* the resource state.
*
- * @param resourceEvent The resource event to create charges for
+ * @param currentResourceEvent The resource event to create charges for
* @param agreement The agreement applicable to the user mentioned in the event
- * @param currentValue The current state of the resource
- * @param currentSnapshotDate The last time the resource state was updated
+ * @param previousAmount The current state of the resource
+ * @param previousOccurred The last time the resource state was updated
*/
- def chargeEvent(resourceEvent: ResourceEvent,
- agreement: DSLAgreement,
- currentValue: Double,
- currentSnapshotDate: Date,
+ def chargeEvent(currentResourceEvent: ResourceEvent,
+ agreements: List[String],
+ previousAmount: Double,
+ previousOccurred: Date,
related: List[WalletEntry]): Maybe[List[WalletEntry]] = {
- assert(currentSnapshotDate.getTime <= resourceEvent.occurredMillis)
+ assert(previousOccurred.getTime <= currentResourceEvent.occurredMillis)
- if (!resourceEvent.validate())
+ if (!currentResourceEvent.validate())
return Failed(new AccountingException("Event not valid"))
val policy = Policy.policy
- val dslResource = policy.findResource(resourceEvent.resource) match {
+ val dslResource = policy.findResource(currentResourceEvent.resource) match {
case Some(x) => x
- case None => return Failed(new AccountingException("No resource [%s]".format(resourceEvent.resource)))
+ case None => return Failed(new AccountingException("No resource [%s]".format(currentResourceEvent.resource)))
}
/* This is a safeguard against the special case where the last
* this is the first time the resource state has been recorded.
* Charging in this case only makes sense for discrete resources.
*/
- if (currentSnapshotDate.getTime == resourceEvent.occurredMillis) {
+ if (previousOccurred.getTime == currentResourceEvent.occurredMillis) {
dslResource.costPolicy match {
case DiscreteCostPolicy => //Ok
case _ => return Some(List())
}
}
- val creditCalculationValueM = dslResource.costPolicy.getValueForCreditCalculation(Just(currentValue), resourceEvent.value)
+ val creditCalculationValueM = dslResource.costPolicy.getValueForCreditCalculation(Just(previousAmount), currentResourceEvent.value)
val amount = creditCalculationValueM match {
case failed @ Failed(_, _) ⇒
return failed
// above, since this point won't be reached in case of error.
val isFinal = dslResource.costPolicy match {
case OnOffCostPolicy =>
- OnOffPolicyResourceState(currentValue) match {
+ OnOffPolicyResourceState(previousAmount) match {
case OnResourceState => false
case OffResourceState => true
}
}
val timeslot = dslResource.costPolicy match {
- case DiscreteCostPolicy => Timeslot(new Date(resourceEvent.occurredMillis),
- new Date(resourceEvent.occurredMillis + 1))
- case _ => Timeslot(currentSnapshotDate, new Date(resourceEvent.occurredMillis))
+ case DiscreteCostPolicy => Timeslot(new Date(currentResourceEvent.occurredMillis),
+ new Date(currentResourceEvent.occurredMillis + 1))
+ case _ => Timeslot(previousOccurred, new Date(currentResourceEvent.occurredMillis))
}
val chargeChunks = calcChangeChunks(agreement, amount, dslResource, timeslot)
val timeReceived = System.currentTimeMillis
- val rel = related.map{x => x.sourceEventIDs}.flatten ++ List(resourceEvent.id)
+ val rel = related.map{x => x.sourceEventIDs}.flatten ++ List(currentResourceEvent.id)
val entries = chargeChunks.map {
c =>
WalletEntry(
id = CryptoUtils.sha1(c.id),
- occurredMillis = resourceEvent.occurredMillis,
+ occurredMillis = currentResourceEvent.occurredMillis,
receivedMillis = timeReceived,
sourceEventIDs = rel,
value = c.cost,
reason = c.reason,
- userId = resourceEvent.userId,
- resource = resourceEvent.resource,
- instanceId = resourceEvent.instanceId,
+ userId = currentResourceEvent.userId,
+ resource = currentResourceEvent.resource,
+ instanceId = currentResourceEvent.instanceId,
finalized = isFinal
)
}