Rollover Framework
Change log:
Date: | Author: | Version: | Changes: | Completed | Ext. | Int. | Is in Core | Jira Ref. |
---|---|---|---|---|---|---|---|---|
11 September 2012 | Antoni Piotr Oleksicki | 1.0 | Doc. created | Yes | x |
| N/A |
|
22 February 2016 | Allan Rolschau | 1.1 | Added migration information | x |
Summary
Rollover bundles are used to enable transfer of remaining free units from one period to another subsequent period. The purpose of this document is to describe how this functionality is implemented as part of the core project.
Rollover Concept
Rollover is the term to describe that some part of free units from one SubscriptionBundle which have not been spent in one period, can be used later by another subsequent SubscriptionBundle. The amount to be 'rolled over' to another bundle can also be referred to as a 'surplus. The Rollover can be described with four parameters:
- Maximum rollover amount per SubscriptionBundle - how much can be transferred from one period to another.
- Rollover validity\Rollover period length - for how long is the rollover valid, i.e. how many periods from the past can be used.
- Rollover usage mode - in use, should free units first be subtracted from surplus or from the current SubscriptionBundle.
- Internal order of the surplus - are the 'newer' or 'older' free units used first.
Design and Implementation
The starting point of the design was the fact, that a subscription bundle has to work in two contexts:
- in the original - when a billing record has the charge date between start and end date of the subscription bundle
- as a container of surplus available for other subscription_bundles.
In the rating process, it should be the responsibility of the bundle to fetch and update both original and additional, used for surplus subscription bundles.
SubscriptionBundle
The existing SubscriptionBundle_ has been extended with methods defined by RolloverSurplus interface. These are:
- getSurplus - returns amount of units that can be used by other subscription bundles,
- updateSurplus - updates the surplus with the given value, and returns the part that is above the maximum surplus value (which is defined on VALUE_3),
- save - saves the object.
A subscription_bundle that is used by rollover bundles, utilizes all four value parameters:
- VALUE_1 - maximum number of units available for current subscription bundle period.
- VALUE_2 - total number of units used of current subscription bundle period, i.e. the sum of units used by the subscription bundle itself and by other subscription bundle periods (when the bundle is used as a surplus bundle).
- VALUE_3 - maximum number of units that can be rolled over, i.e. the maximum number of units that can be used as surplus by other subscription bundle periods. As this value indicates a part of value_1, it will not make sense to have a number greater than value_1.
- VALUE_4 - total number of units used by other subscription bundle periods. Value cannot exceed value_3. Note that value_4 is not a simple value, it is used as "a value from which other information can be calculated" (for more information, see below).
Value_4 Explained
The difference between value_3 and value_4 (i.e. value_3 - value_4) indicate how many units are available for rollover.
This means that every time units are used for the current subscription bundle period, value_4 is not updated unless the number of free units (value_1 - value_2) exceeds the rollover limit (value_3). In this case value_4 is decreased to ensure the available rollover units do not exceed the number of free units.
The rules for how/when to update value_4 are:
- When the bundle is used as a surplus bundle by another bundle, then value_4 is updated with the amount of units used by that other bundle.
- Note that the process that updates value_4 shall make sure to also update value_2 with the same amount of units.
- When value_2 is updated it needs to be checked if the total units available in the bundle is now less than the number of units that can be used as surplus. If this is the case, then value_4 needs to be updated such that this is no longer the case.
- In more mathematical terms, if (value_1-value_2)<(value_3-value_4), then adjust value_4 such that the '<' becomes '='. This means that value_4 shall be set equal to value_3-(value_1-value_2).
Example how value_4 is used for current period
Suppose a bundle has value_1 = 500, value_3 = 200. This means the bundle has 500 units for charges in actual period, from which 200 units may be used by charges from other periods.
Lets assume a subscription bundle is created initially and some usage is added to this subscription bundle period:
value_1 | value_2 | value_3 | value_4 | comment | |
---|---|---|---|---|---|
500 | 0 | 200 | 0 | Initial values | |
usage 190 | 500 | 190 | 200 | 0 | value_2 = 0+190. Still 310 units left. |
usage 80 | 500 | 270 | 200 | 0 | value_2 = 190+80. Still 230 units left. |
usage 100 | 500 | 370 | 200 | 70 | value_2 = 270+100. Still 130 units left. value_4 is incremented as free units in current period now is less than rollover units |
usage 5 | 500 | 375 | 200 | 75 | value_2 = 370+5. Still 125 units left. value_4 is incremented as free units in current period now is less than rollover units |
usage 200 | 500 | 500 | 200 | 200 | value_2 = 500 as 375+200 exceeds value_1. value_4 = 200 as available rollover units are exhausted. |
Example how value_4 is used for rollover periods
Now lets assume a subscription bundle is created initially but no usage is added for the subscription bundle period. Now the subscription bundle period is used by another period using rollover functionality:
value_1 | value_2 | value_3 | value_4 | comment | |
---|---|---|---|---|---|
500 | 0 | 200 | 0 | Initial values | |
usage 90 | 500 | 90 | 200 | 90 | value_2 = 0+90. Still 410 units left. value_4 = 0+90 as usage is added due to rollover. Still 110 units left for rollover. |
usage 80 | 500 | 170 | 200 | 170 | value_2 = 90+80. Still 330 units left. value_4 = 90+80 as usage is added due to rollover. Still 30 units left for rollover. |
usage 30 | 500 | 200 | 200 | 200 | value_2 = 270+30. Still 200 units left. |
Note, the last usage may actually be higher than 30, however the actual subscription bundle do only allow usage up to 30 units to be handled.
Example how value_4 is used for combined usage in current and rollover periods
Actual period | Other periods (rollover) | value_1 | value_2 | value_3 | value_4 | comment |
---|---|---|---|---|---|---|
500 | 0 | 200 | 0 | Initial values | ||
usage 190 |
| 500 | 190 | 200 | 0 | value_2 = 0+190. Still 310 units left. Still 200 units available for rollover. |
usage 80 | 500 | 270 | 200 | 80 | value_2 = 190+80. Still 230 units left. value_4 = 0+80 as used for rollover. | |
usage 100 | 500 | 370 | 200 | 70 | value_2 = 270+100. Still 130 units left. value_4 is incremented as free units in current period now is less than rollover units | |
usage 5 | 500 | 375 | 200 | 75 | value_2 = 370+5. Still 125 units left. value_4 = 70+5 as used for rollover | |
usage 200 | 500 | 500 | 200 | 200 | value_2 = 500 as 375+200 exceeds value_1. value_4 = 200 as available rollover units are exhausted. |
Subscription Bundle Update Manager
An update manager is a class responsible for setting and getting the right amounts from the SubscriptionBundle when queried. Currently a number of update managers are implemented, see below.
The rollover functionality can be enabled by enabling the rollover version of the update manager. This is done by adding the following line to the bundle parameters:
UPDATE_MANAGER=ROLLOVER
By not setting the update manager to anything the DefaultUpdateManager will be used which does not consider rollover functionality.
Updating SubscriptionBundle
Standard Update
When a SubscriptionBundle object is used in the normal way, the used units are accumulated on VALUE_2. When VALUE_2 is greater than VALUE_1 - VALUE_3, then VALUE_4 gets a new value calculated in the following way VALUE_2 - (VALUE_1 - VALUE_3). If VALUE_4 is greater than VALUE_3 then VALUE_3 is set.
Update through rollover
The surplus units are aggregated on VALUE_2 and VALUE_4, until the maximum defined by VALUE_3 is reached.
Bundle
If a bundle is designed as a rollover bundle, it must implement the RolloverBundle interface. The interface has five methods and two enumeration types:
- getMaxSurplus - returns the maximum amount of units that can be transferred as surplus from a SubscriptionBundle,
- getSurplusPeriodOrder - returns one of the values of SurplusPeriodOrder, which indicates whether SubscriptionBundle used for surplus should be ordered from newer to older units, or the other way round,
- getSurplusUsageMode - returns one of the values of SurplusUsageMode and is used to define, whether surplus should be used before or after SubscriptionBundle,
- getAmountOfRolloverPeriods - returns an amount (integer value) of previous periods from which surplus can be used
- SurplusPeriodOrder - an enum with two values: NEWER_FIRST and OLDER_FIRST used by getSurplusPeriodOrder,
- SurplusUsageMode - an enum with two values: USE_ROLLOVER_BEFORE_BUNDLE and USE_ROLLOVER_AFTER_BUNDLE used by getSurplusUsageMode,
Currently, the only bundles that implement the RolloverBundle interface is RestrictActive and PostRatingBundleV2.
One thing worth pointing out: a new method, for calculating the amount of free units on a SubscriptionBundle, has been introduced on the Bundle_ class. It is called getFreeUnits. This was done, because the classical VALUE_1 - VALUE_2 is no longer valid, when surplus is used. The update manager now has this responsibility.
SurplusContainer
SurplusContainer is a wrapper object that encapsulates a list of SubscriptionBundle objects. It has two methods:
- getSurplusValue returns the sum of surplus for the whole period,
- updateSurplus updates the whole surplus by looping through the list of RolloverSurplus; returns the remainder from the last RolloverSurplus.updateSurplus.
The class does not have a default constructor and can be only constructed by SurplusContainter(List<RolloverSurplus> surplusList). The list can be constructed with a helper class SurplusContainerFetcher.
SurplusContainerFetcher
An implementation of the SurplusContainerFetcher interface is responsible for creation of SurplusContainer objects. The used implementation is defined in the bundle parameter ROLLOVER.SURPLUS.FETCHER. Its value should be a name of the class that implements the SurplusContainerFetcher interface
Implementation Name | Logic | Notes |
---|---|---|
DefaultSurplusContainerFetcher | Implements the logic described on this page. | This implementation is used if the bundle parameter ROLLOVER.SURPLUS.FETCHER is not defined. Warning 1: This implementation only works if the subscription bundle's bundle uses the 'recurrence_id' when calculating the periods its subscription bundles cover. Behavior is undefined, if it does not. Warning 2: This implementation only works if the subscription bundle's bundle is of the type {@link RolloverBundle}. A runtime exception is thrown, if it is not. |
AllSubscriptionBundlesOnDate | Fetches all subscription bundles (for same subscription campaign and same bundle) that are active on the charge date. | Note: Only subscription bundles that already exist in the database are found by this implementation, no new subscription bundles will be created by this implementation. Warning: This implementation only works for subscription bundles that use the 'to_date' and 'from_date' fields to indicate the period it covers. Behaviour is undefined if the 'period' field is used. |
Configuration
In order to configure DataBundle, DurationBundle, EventBundle or ForwardBundle to use rollover functionality the following parameters must be set up in the bundle's parameters and meta data:
Name | Description |
---|---|
ROLLOVER.PERIOD.ORDER | Defines whether older or newer surplus should be used first. Two possible values are possible: NEWER_FIRST and OLDER_FIRST |
ROLLOVER.USAGE.MODE | Indicates whether surplus should be used before or after the SubscriptionBundles. Again two values are possible: USE_ROLLOVER_BEFORE_BUNDLE, USE_ROLLOVER_AFTER_BUNDLE |
ROLLOVER.PERIODS | Amount of previous SubscriptionBundles used in rollover. In other words, the number defines how many times a surplus can be rolled over |
UPDATE_MANAGER | DEFAULT for the standard functionality without rollover. This is the standard implementation used if this parameter is not set. ROLLOVER for enabling the rollover functionality. Meta data properties are: UNLIMITED for a variation of the standard functionality, where zero free units (VALUE_1=0) are considered unlimited number of free units. |
Migration of existing subscription bundles
When existing bundles are re-configured to have rollover functionality then the existing subscription bundles need to be updated.
First initialize the field for the max rollover.
update subscription_bundle set value3 = (select value3 from bundle b where b.id = subscription_bundle.bundle_id) where exists (select * from bundle b where b.id = subscription_bundle.bundle_id and b.parameters like '%UPDATE_MANAGER=ROLLOVER%');
Next set the field for used rollover units. If this one (value4) has a value (other than 0) then it is because the usage (value2) started leaching of the amount that can be rolled over. Value4 is the excess amount past value1-value3.
update subscription_bundle set value4=case when value2 > (value1-value3) then value2 - (value1-value3) else 0 end where exists (select * from bundle b where b.id = subscription_bundle.bundle_id and b.parameters like '%UPDATE_MANAGER=ROLLOVER%');