+ val newWalletEntry = WalletEntry(
+ userID,
+ sumOfCreditsToSubtract,
+ _oldTotalCredits,
+ newTotalCredits,
+ TimeHelpers.nowMillis(),
+ referenceTimeslot,
+ billingMonthInfo.year,
+ billingMonthInfo.month,
+ fullChargeslots,
+ previousResourceEventOpt.map(List(_, currentResourceEvent)).getOrElse(List(currentResourceEvent)),
+ resourceType,
+ currentResourceEvent.isSynthetic
+ )
+
+ logger.debug("newWalletEntry = {}", newWalletEntry.toJsonString)
+
+ walletEntryRecorder.apply(newWalletEntry)
+
+ (1, newTotalCredits)
+ }
+
+ protected def removeChargingData[T: Manifest](
+ chargingData: mutable.Map[String, Any],
+ envKey: TypedKey[T]
+ ) = {
+
+ chargingData.remove(envKey.name).asInstanceOf[Option[T]]
+ }
+
+ protected def getChargingData[T: Manifest](
+ chargingData: mutable.Map[String, Any],
+ envKey: TypedKey[T]
+ ) = {
+
+ chargingData.get(envKey.name).asInstanceOf[Option[T]]
+ }
+
+ protected def setChargingData[T: Manifest](
+ chargingData: mutable.Map[String, Any],
+ envKey: TypedKey[T],
+ value: T
+ ) = {
+
+ chargingData(envKey.name) = value
+ }
+
+ def selectEffectivePriceTable(
+ fullPriceTable: FullPriceTable,
+ chargingData: mutable.Map[String, Any],
+ currentResourceEvent: ResourceEventModel,
+ referenceTimeslot: Timeslot,
+ previousValue: Double,
+ totalCredits: Double,
+ oldAccumulatingAmount: Double,
+ newAccumulatingAmount: Double
+ ): EffectivePriceTable = {
+
+ val selectorPath = computeSelectorPath(
+ chargingData,
+ currentResourceEvent,
+ referenceTimeslot,
+ previousValue,
+ totalCredits,
+ oldAccumulatingAmount,
+ newAccumulatingAmount
+ )
+
+ fullPriceTable.effectivePriceTableOfSelectorForResource(selectorPath, currentResourceEvent.safeResource)
+ }
+
+ /**
+ *
+ * @param aquarium
+ * @param currentResourceEvent
+ * @param resourceType
+ * @param billingMonthInfo
+ * @param previousResourceEventOpt
+ * @param userAgreements
+ * @param chargingData
+ * @param totalCredits
+ * @param walletEntryRecorder
+ * @param clogOpt
+ * @return The number of wallet entries recorded and the new total credits
+ */
+ def chargeResourceEvent(
+ aquarium: Aquarium,
+ currentResourceEvent: ResourceEventModel,
+ resourceType: ResourceType,
+ billingMonthInfo: BillingMonthInfo,
+ previousResourceEventOpt: Option[ResourceEventModel],
+ userAgreements: AgreementHistory,
+ chargingData: mutable.Map[String, Any],
+ totalCredits: Double,
+ walletEntryRecorder: WalletEntry ⇒ Unit,
+ clogOpt: Option[ContextualLogger] = None
+ ): (Int, Double) = {
+
+ val clog = ContextualLogger.fromOther(clogOpt, logger, "chargeResourceEvent(%s)", currentResourceEvent.id)
+ val currentResourceEventDebugInfo = rcDebugInfo(currentResourceEvent)
+
+ val isBillable = this.isBillableEvent(currentResourceEvent)
+ val retval = if(!isBillable) {
+ // The resource event is not billable.
+ clog.debug("Ignoring not billable %s", currentResourceEventDebugInfo)
+ (0, totalCredits)
+ } else {
+ // The resource event is billable.
+ // Find the previous event if needed.
+ // This is (potentially) needed to calculate new credit amount and new resource instance amount
+ if(this.needsPreviousEventForCreditAndAmountCalculation) {
+ if(previousResourceEventOpt.isDefined) {
+ val previousResourceEvent = previousResourceEventOpt.get
+ val previousValue = previousResourceEvent.value
+
+ clog.debug("I have previous event %s", previousResourceEvent.toDebugString)
+
+ computeChargeslots(
+ chargingData,
+ previousResourceEventOpt,
+ currentResourceEvent,
+ billingMonthInfo,
+ Timeslot(previousResourceEvent.occurredMillis, currentResourceEvent.occurredMillis),
+ resourceType,
+ userAgreements.agreementByTimeslot,
+ previousValue,
+ totalCredits,
+ aquarium.policyStore,
+ walletEntryRecorder
+ )
+ } else {
+ // We do not have the needed previous event, so this must be the first resource event of its kind, ever.
+ // Let's see if we can create a dummy previous event.
+ val actualFirstEvent = currentResourceEvent
+
+ // FIXME: Why && ?
+ if(this.isBillableFirstEvent(actualFirstEvent) && this.mustGenerateDummyFirstEvent) {
+ clog.debug("First event of its kind %s", currentResourceEventDebugInfo)
+
+ val dummyFirst = this.constructDummyFirstEventFor(currentResourceEvent, billingMonthInfo.monthStartMillis)
+ clog.debug("Dummy first event %s", dummyFirst.toDebugString)
+
+ val previousResourceEvent = dummyFirst
+ val previousValue = previousResourceEvent.value
+
+ computeChargeslots(
+ chargingData,
+ Some(previousResourceEvent),
+ currentResourceEvent,
+ billingMonthInfo,
+ Timeslot(previousResourceEvent.occurredMillis, currentResourceEvent.occurredMillis),
+ resourceType,
+ userAgreements.agreementByTimeslot,
+ previousValue,
+ totalCredits,
+ aquarium.policyStore,
+ walletEntryRecorder
+ )
+ } else {
+ clog.debug("Ignoring first event of its kind %s", currentResourceEventDebugInfo)
+ // userStateWorker.updateIgnored(currentResourceEvent)
+ (0, totalCredits)
+ }
+ }
+ } else {
+ // No need for previous event. One event does it all.
+ computeChargeslots(
+ chargingData,
+ None,
+ currentResourceEvent,
+ billingMonthInfo,
+ Timeslot(currentResourceEvent.occurredMillis, currentResourceEvent.occurredMillis + 1),
+ resourceType,
+ userAgreements.agreementByTimeslot,
+ this.getResourceInstanceUndefinedAmount,
+ totalCredits,
+ aquarium.policyStore,
+ walletEntryRecorder
+ )
+ }
+ }
+
+ retval
+ }