/* ====================================================================
* Limited Evaluation License:
*
* This software is open source, but licensed. The license with this package
* is an evaluation license, which may not be used for productive systems. If
* you want a full license, please contact us.
*
* The exclusive owner of this work is the OpenRate project.
* This work, including all associated documents and components
* is Copyright of the OpenRate project 2006-2015.
*
* The following restrictions apply unless they are expressly relaxed in a
* contractual agreement between the license holder or one of its officially
* assigned agents and you or your organisation:
*
* 1) This work may not be disclosed, either in full or in part, in any form
* electronic or physical, to any third party. This includes both in the
* form of source code and compiled modules.
* 2) This work contains trade secrets in the form of architecture, algorithms
* methods and technologies. These trade secrets may not be disclosed to
* third parties in any form, either directly or in summary or paraphrased
* form, nor may these trade secrets be used to construct products of a
* similar or competing nature either by you or third parties.
* 3) This work may not be included in full or in part in any application.
* 4) You may not remove or alter any proprietary legends or notices contained
* in or on this work.
* 5) This software may not be reverse-engineered or otherwise decompiled, if
* you received this work in a compiled form.
* 6) This work is licensed, not sold. Possession of this software does not
* imply or grant any right to you.
* 7) You agree to disclose any changes to this work to the copyright holder
* and that the copyright holder may include any such changes at its own
* discretion into the work
* 8) You agree not to derive other works from the trade secrets in this work,
* and that any such derivation may make you liable to pay damages to the
* copyright holder
* 9) You agree to use this software exclusively for evaluation purposes, and
* that you shall not use this software to derive commercial profit or
* support your business or personal activities.
*
* This software is provided "as is" and any expressed or impled warranties,
* including, but not limited to, the impled warranties of merchantability
* and fitness for a particular purpose are disclaimed. In no event shall
* The OpenRate Project or its officially assigned agents be liable to any
* direct, indirect, incidental, special, exemplary, or consequential damages
* (including but not limited to, procurement of substitute goods or services;
* Loss of use, data, or profits; or any business interruption) however caused
* and on theory of liability, whether in contract, strict liability, or tort
* (including negligence or otherwise) arising in any way out of the use of
* this software, even if advised of the possibility of such damage.
* This software contains portions by The Apache Software Foundation, Robert
* Half International.
* ====================================================================
*/
package OpenRate.process;
import OpenRate.cache.BalanceCache;
import OpenRate.cache.ICacheManager;
import OpenRate.exception.InitializationException;
import OpenRate.lang.BalanceGroup;
import OpenRate.lang.Counter;
import OpenRate.lang.DiscountInformation;
import OpenRate.record.BalanceImpact;
import OpenRate.record.IRatingRecord;
import OpenRate.resource.CacheFactory;
import OpenRate.utils.PropertyUtils;
/**
* Now that we have the prioritised list of products and promotions, we can
* work out the consuming of the balances that there might be, before we pass
* into rating the values of what is left after consumption. This will decrement
* balances, passing the results on for rating.
*/
public abstract class AbstractBalanceHandlerPlugIn extends AbstractTransactionalPlugIn
{
// get the Cache manager for the balance cache
private ICacheManager BG;
/**
* The balance cache object
*/
protected BalanceCache BC;
/**
* The discount flag tells us what the discounting module did. The value
* DISCOUNT_FLAG_NO_DISCOUNT tells us that the discount was not applied.
*/
public final static int DISCOUNT_FLAG_NO_DISCOUNT = 0;
/**
* The discount flag tells us what the discounting module did. The value
* DISCOUNT_FLAG_FULLY_DISCOUNTED tells us that the discount was applied and
* the discount was not exhausted. The event was therefore fully discounted.
*/
public final static int DISCOUNT_FLAG_FULLY_DISCOUNTED = 1;
/**
* The discount flag tells us what the discounting module did. The value
* DISCOUNT_FLAG_PARTIALLY_DISCOUNTED tells us that the discount was applied
* but the discount was exhausted. The event was therefore only discounted for
* some of the value of the event.
*/
public final static int DISCOUNT_FLAG_PARTIALLY_DISCOUNTED = 2;
/**
* The discount flag tells us what the discounting module did. The value
* DISCOUNT_FLAG_REFUNDED tells us that the discount was refunded.
*/
public final static int DISCOUNT_FLAG_REFUNDED = 3;
/**
* The discount flag tells us what the discounting module did. The value
* DISCOUNT_FLAG_AGGREGATED tells us that the aggregation was applied onto the
* discount.
*/
public final static int DISCOUNT_FLAG_AGGREGATED = 4;
// -----------------------------------------------------------------------------
// ------------------ Start of initialisation functions ------------------------
// -----------------------------------------------------------------------------
/**
* Initialise the module. Called during pipeline creation to initialise:
* - Configuration properties that are defined in the properties file.
* - The references to any cache objects that are used in the processing
* - The symbolic name of the module
*
* @param pipelineName The name of the pipeline this module is in
* @param moduleName The name of this module in the pipeline
* @throws OpenRate.exception.InitializationException
*/
@Override
public void init(String pipelineName, String moduleName)
throws InitializationException
{
String CacheObjectName;
// Register ourself with the client manager
setSymbolicName(moduleName);
// do the inherited initialisation
super.init(pipelineName,moduleName);
// Get the cache object reference
CacheObjectName = PropertyUtils.getPropertyUtils().getPluginPropertyValueDef(pipelineName,
moduleName,
"DataCache",
"None");
if (CacheObjectName.equals("None"))
{
message = "Not able to find cache property entry for module <"+moduleName+"> in pipeline <"+pipelineName+">";
throw new InitializationException(message,getSymbolicName());
}
// Load up the customer information held in the Cached Object
BG = CacheFactory.getGlobalManager(CacheObjectName);
if (BG == null)
{
message = "Could not find cache entry for cache <" + CacheObjectName + "> in module <"+moduleName+"> in pipeline <"+pipelineName+">";
throw new InitializationException(message,getSymbolicName());
}
// Load up the mapping arrays
BC = (BalanceCache)BG.get(CacheObjectName);
if (BC == null)
{
getPipeLog().fatal("Could not find cache entry for <" + CacheObjectName + ">");
throw new InitializationException("Could not find cache entry for <" +
CacheObjectName + ">",getSymbolicName());
}
}
// -----------------------------------------------------------------------------
// --------------- Start of transactional layer functions ----------------------
// -----------------------------------------------------------------------------
/**
* Called when the underlying transaction is commanded to start.
* This should return 0 if everything was OK, otherwise -1.
*
* @param transactionNumber
* @return it is OK to start the transaction
*/
@Override
public int startTransaction(int transactionNumber)
{
return 0;
}
/**
* Called when the underlying transaction is commanded to FLUSH, that means to
* close down all processing objects and go into a quiescent state.
* This should return 0 if everything was OK, otherwise -1.
*
* @param transactionNumber The number of the transaction
* @return 0 if the transaction was flushed OK
*/
@Override
public int flushTransaction(int transactionNumber)
{
return 0;
}
/**
* Called when the underlying transaction is commanded to commit that means to
* fix any data and finish.
*
* @param transactionNumber The number of the transaction
*/
@Override
public void commitTransaction(int transactionNumber)
{
// NOP
}
/**
* Called when the underlying transaction is commanded to roll back, that means
* to undo any data and finish.
*
* @param transactionNumber The number of the transaction
*/
@Override
public void rollbackTransaction(int transactionNumber)
{
// NOP
}
/**
* Close Transaction is the trigger to clean up transaction related information
* such as variables, status etc.
*
* @param transactionNumber The transaction we are working on
*/
@Override
public void closeTransaction(int transactionNumber)
{
// NOP
}
// -----------------------------------------------------------------------------
// ----------------- Start of low level exposed functions ----------------------
// -----------------------------------------------------------------------------
/**
* Get a balance group from the cache
*
* @param balanceGroupId The balance group to recover
* @return The balance group, or null if not found
*/
public BalanceGroup getBalanceGroup(long balanceGroupId)
{
return BC.getBalanceGroup(balanceGroupId);
}
/**
* Add a new balance group to the cache
*
* @param balanceGroupId The Id of the group to add
* @return The newly created balance group
*/
public BalanceGroup addBalanceGroup(long balanceGroupId)
{
return BC.addBalanceGroup(balanceGroupId);
}
/**
* Check if a balance exists at the given date
*
* @param balanceGroupId The id of the balance group
* @param counterId The id of the counter
* @param utcEventDate The date to check for
* @return True if the balance exists, otherwise false
*/
public Counter checkCounterExists(long balanceGroupId, int counterId, long utcEventDate)
{
return BC.checkCounterExists(balanceGroupId, counterId, utcEventDate);
}
/**
* Add a value into the BalanceCache. Does not check for current existence.
* Intended for use by user applications, as it lets the counter group manage
* the record id.
*
* @param balanceGroupId The id of the balance group
* @param counterId The ID of the counter in the balance group
* @param validFrom The start of the validity period for the counter period
* @param validTo The end of the validity period for the counter period
* @param currentBal The current balance to assign to the counter period
* @return The created counter
*/
public Counter addCounter(long balanceGroupId, int counterId, long validFrom, long validTo, double currentBal)
{
return BC.addCounter(balanceGroupId, counterId, validFrom, validTo, currentBal);
}
/**
* Gets a counter from a balance group by counter id and UTC date
*
* @param balanceGroup The balance group we are dealing with
* @param counterId The counter id to retrieve for
* @param utcEventDate The date to retrieve for
* @return The counter or null
*/
public Counter getCounter(long balanceGroup, int counterId, long utcEventDate)
{
return BC.getCounter(balanceGroup, counterId, utcEventDate);
}
/**
* Gets a counter balance from a balance group by counter id and UTC date
*
* @param balanceGroup The balance group to retrieve for
* @param counterId The counter id to retrieve for
* @param utcEventDate The date to retrieve for
* @param initialValue The initial value of the counter in the case we create it
* @return The counter or null
*/
public double getCounterBalance(long balanceGroup, int counterId, long utcEventDate, double initialValue)
{
Counter tmpCounterReq;
tmpCounterReq = BC.getCounter(balanceGroup, counterId, utcEventDate);
if (tmpCounterReq == null)
{
return initialValue;
}
else
{
return tmpCounterReq.CurrentBalance;
}
}
// -----------------------------------------------------------------------------
// ----------------- Start of high level exposed functions ---------------------
// -----------------------------------------------------------------------------
/**
* Consumes the given RUM value from a IRatingRecord record, creating a balance
* if this is not found. The balance impacts are written directly back into
* the record, and a discounting summary is passed back to the caller.
*
* This method assumes that the counter has an initial value, which is
* progressively consumed towards 0.
*
* The RUM value is reduced by the amount that could be allocated to the
* balance. (i.e. It is consumed).
*
* This method will consume up until exhaustion of the counter value, splitting
* the input value if we are crossing a threshold.
*
* The discounting summary tells the user:
* - If the record was discounted
* - If a balance impact was created
* - The ID and Record ID of the counter impacted
* - The value consumed
* - The value of the counter after the impact
*
* In addition the following fields from the IRatingRecord are used:
* - UTCEventDate
*
* Some parameters are only used if the counter bucket is created. These are
* marked with (*).
*
* @param currentRecord The record to be discounted, inherited from IRatingRecord
* @param discountName The name of the discount
* @param balanceGroupId The ID of the balance group
* @param rumToUse The RUM to consume
* @param counterId The ID of the counter to impact
* @param initialBalance The initial value of the counter (*)
* @param utcBalanceStartValidity The start of bucket validity (*)
* @param UTCBalanceEndValidity The end of bucket validity (*)
* @return The DiscountInformation summary object
*/
public DiscountInformation discountConsumeRUM(IRatingRecord currentRecord, String discountName, long balanceGroupId, String rumToUse, int counterId, double initialBalance, long utcBalanceStartValidity, long UTCBalanceEndValidity)
{
BalanceImpact tmpBalImpact;
double tmpRUMValue;
double tmpDiscount;
DiscountInformation tmpReturnInfo = new DiscountInformation();
tmpRUMValue = currentRecord.getRUMValue(rumToUse);
Counter tmpCounter = checkCounterExists(balanceGroupId, counterId, currentRecord.getUTCEventDate());
if (tmpCounter == null)
{
tmpCounter = addCounter(balanceGroupId,counterId,utcBalanceStartValidity,UTCBalanceEndValidity,initialBalance);
// Add the balance impact
tmpBalImpact = new BalanceImpact();
tmpBalImpact.type = "D";
tmpBalImpact.balanceGroup = balanceGroupId;
tmpBalImpact.cpiName = discountName;
tmpBalImpact.ruleName = "CREATION";
tmpBalImpact.rumUsed = rumToUse;
tmpBalImpact.counterID = counterId;
tmpBalImpact.recID = tmpCounter.RecId;
tmpBalImpact.rumValueAfter = 0.0;
tmpBalImpact.rumValueUsed = 0;
tmpBalImpact.balanceAfter = initialBalance;
tmpBalImpact.balanceDelta = initialBalance;
tmpBalImpact.startDate = utcBalanceStartValidity;
tmpBalImpact.endDate = UTCBalanceEndValidity;
if (tmpBalImpact.balanceDelta != 0)
{
currentRecord.addBalanceImpact(tmpBalImpact);
tmpReturnInfo.setBalanceCreated(true);
}
}
// see if we have used up all of the counter
if (tmpRUMValue > tmpCounter.CurrentBalance)
{
if (tmpCounter.CurrentBalance <= 0)
{
// we have used up the counter, leave cost alone
}
else
{
// we are crossing a threshold
tmpDiscount = tmpCounter.CurrentBalance;
currentRecord.updateRUMValue(rumToUse,-tmpCounter.CurrentBalance);
double oldBal = tmpCounter.CurrentBalance;
tmpCounter.CurrentBalance = 0;
// Add the balance impact
tmpBalImpact = new BalanceImpact();
tmpBalImpact.type = "D";
tmpBalImpact.balanceGroup = balanceGroupId;
tmpBalImpact.cpiName = discountName;
tmpBalImpact.ruleName = "Consume" + rumToUse;
tmpBalImpact.rumUsed = rumToUse;
tmpBalImpact.counterID = counterId;
tmpBalImpact.recID = tmpCounter.RecId;
tmpBalImpact.rumValueAfter = currentRecord.getRUMValue(rumToUse);
tmpBalImpact.rumValueUsed = tmpDiscount;
tmpBalImpact.balanceAfter = 0;
tmpBalImpact.balanceDelta = tmpBalImpact.balanceAfter - oldBal;
tmpBalImpact.startDate = tmpCounter.validFrom;
tmpBalImpact.endDate = tmpCounter.validTo;
if (tmpBalImpact.balanceDelta != 0)
{
currentRecord.addBalanceImpact(tmpBalImpact);
// Prepare the return value
tmpReturnInfo.setDiscountApplied(true);
tmpReturnInfo.setCounterId(counterId);
tmpReturnInfo.setRecId(tmpCounter.RecId);
tmpReturnInfo.setDiscountedValue(tmpDiscount);
tmpReturnInfo.setNewBalanceValue(0); // was implicitly 0, now explicit
// Set the discount flag to "threshold crossing"
tmpReturnInfo.setDiscountFlag(DISCOUNT_FLAG_PARTIALLY_DISCOUNTED);
}
}
}
else
{
if (tmpCounter.CurrentBalance <= 0)
{
// we have used up the counter, leave Volume alone
}
else
{
// we are just decrementing the counter, using all of the impact
double oldBal = tmpCounter.CurrentBalance;
tmpCounter.CurrentBalance -= tmpRUMValue;
tmpDiscount = tmpRUMValue;
currentRecord.updateRUMValue(rumToUse,-currentRecord.getRUMValue(rumToUse));
tmpReturnInfo.setDiscountApplied(true);
// Add the balance impact
tmpBalImpact = new BalanceImpact();
tmpBalImpact.type = "D";
tmpBalImpact.balanceGroup = balanceGroupId;
tmpBalImpact.cpiName = discountName;
tmpBalImpact.ruleName = "Consume" + rumToUse;
tmpBalImpact.rumUsed = rumToUse;
tmpBalImpact.counterID = counterId;
tmpBalImpact.recID = tmpCounter.RecId;
tmpBalImpact.rumValueAfter = 0.0;
tmpBalImpact.rumValueUsed = tmpDiscount;
tmpBalImpact.balanceAfter = tmpCounter.CurrentBalance;
tmpBalImpact.balanceDelta = tmpBalImpact.balanceAfter - oldBal;
tmpBalImpact.startDate = tmpCounter.validFrom;
tmpBalImpact.endDate = tmpCounter.validTo;
if (tmpBalImpact.balanceDelta != 0)
{
currentRecord.addBalanceImpact(tmpBalImpact);
// Prepare the return value
tmpReturnInfo.setDiscountApplied(true);
tmpReturnInfo.setCounterId(counterId);
tmpReturnInfo.setRecId(tmpCounter.RecId);
tmpReturnInfo.setDiscountedValue(tmpDiscount);
tmpReturnInfo.setNewBalanceValue(tmpCounter.CurrentBalance);
// Set the discount flag to "fully discounted"
tmpReturnInfo.setDiscountFlag(DISCOUNT_FLAG_FULLY_DISCOUNTED);
}
}
}
return tmpReturnInfo;
}
/**
* Refunds a previous consumed RUM value from a IRatingRecord record. The
* balance impacts are written directly back into the record, and a discounting
* summary is passed back to the caller.
*
* The RUM value is increased by the amount that could be allocated to the
* balance. (i.e. Consumption is refunded). We don't change the RUM value in
* this case, it would have no meaning.
*
* The discounting summary tells the user:
* - If the record was discounted
* - If a balance impact was created
* - The ID and Record ID of the counter impacted
* - The value consumed
* - The value of the counter after the impact
*
* In addition the following fields from the IRatingRecord are used:
* - UTCEventDate
*
* @param currentRecord The record to be discounted, inherited fro IRatingRecord
* @param discountName The name of the discount
* @param balanceGroupId The ID of the balance group
* @param rumToUse The RUM to consume
* @param counterId The ID of the counter to impact
* @param initialBalance The initial balance the counter had, serves as a maximum limit in refunds
* @return The DiscountInformation summary object
*/
public DiscountInformation refundConsumeRUM(IRatingRecord currentRecord, String discountName, long balanceGroupId, String rumToUse, int counterId, double initialBalance)
{
BalanceImpact tmpBalImpact;
double tmpRUMValue;
double tmpDiscount;
DiscountInformation tmpReturnInfo = new DiscountInformation();
tmpRUMValue = currentRecord.getRUMValue(rumToUse);
Counter tmpCounter = checkCounterExists(balanceGroupId, counterId, currentRecord.getUTCEventDate());
if (tmpCounter == null)
{
// can't refund onto a non-existent counter
return null;
}
// we give the value back
if ((tmpCounter.CurrentBalance + tmpRUMValue) > initialBalance)
{
// we can't go over the initial value, so limit what we refund
tmpRUMValue = initialBalance - tmpCounter.CurrentBalance;
}
tmpCounter.CurrentBalance += tmpRUMValue;
tmpDiscount = tmpRUMValue;
// Add the balance impact
tmpBalImpact = new BalanceImpact();
tmpBalImpact.type = "D";
tmpBalImpact.balanceGroup = balanceGroupId;
tmpBalImpact.cpiName = discountName;
tmpBalImpact.ruleName = "Refund" + rumToUse;
tmpBalImpact.rumUsed = rumToUse;
tmpBalImpact.counterID = counterId;
tmpBalImpact.recID = tmpCounter.RecId;
tmpBalImpact.rumValueAfter = tmpCounter.CurrentBalance;
tmpBalImpact.rumValueUsed = currentRecord.getRUMValue(rumToUse);
tmpBalImpact.balanceAfter = tmpCounter.CurrentBalance;
tmpBalImpact.balanceDelta = tmpDiscount;
tmpBalImpact.startDate = tmpCounter.validFrom;
tmpBalImpact.endDate = tmpCounter.validTo;
if (tmpBalImpact.balanceDelta != 0)
{
tmpReturnInfo.setDiscountApplied(true);
currentRecord.addBalanceImpact(tmpBalImpact);
// Prepare the return value
tmpReturnInfo.setDiscountApplied(true);
tmpReturnInfo.setCounterId(counterId);
tmpReturnInfo.setRecId(tmpCounter.RecId);
tmpReturnInfo.setDiscountedValue(tmpDiscount);
tmpReturnInfo.setNewBalanceValue(tmpCounter.CurrentBalance);
// Set the discount flag to "refund"
tmpReturnInfo.setDiscountFlag(DISCOUNT_FLAG_REFUNDED);
}
return tmpReturnInfo;
}
/**
* Aggregates the given RUM value from a IRatingRecord record, creating a balance
* if this is not found. The balance impacts are written directly back into
* the record, and a discounting summary is passed back to the caller.
*
* This method assumes that the counter has 0 as an initial value, which is
* progressively incremented. There are no thresholds to be taken into account
* in this case, so the management of the counter is relatively straightforward.
*
* The RUM value is left untouched (it is not consumed).
*
* The discounting summary tells the user:
* - If the record was discounted
* - If a balance impact was created
* - The ID and Record ID of the counter impacted
* - The value aggregated
* - The value of the counter after the impact
*
* In addition the following fields from the IRatingRecord are used:
* - UTCEventDate
*
* Some parameters are only used if the counter bucket is created. These are
* marked with (*).
*
* @param currentRecord The record to be discounted, inherited fro IRatingRecord
* @param discountName The name of the discount
* @param balanceGroupId The ID of the balance group
* @param rumToUse The RUM to consume
* @param counterId The ID of the counter to impact
* @param initialBalance The initial value of the counter (*)
* @param utcBalanceStartValidity The start of bucket validity (*)
* @param UTCBalanceEndValidity The end of bucket validity (*)
* @return The DiscountInformation summary object
*/
public DiscountInformation discountAggregateRUM(IRatingRecord currentRecord, String discountName, long balanceGroupId, String rumToUse, int counterId, double initialBalance, long utcBalanceStartValidity, long UTCBalanceEndValidity)
{
BalanceImpact tmpBalImpact;
double tmpRUMValue;
double tmpDiscount;
DiscountInformation tmpReturnInfo = new DiscountInformation();
tmpRUMValue = currentRecord.getRUMValue(rumToUse);
Counter tmpCounter = checkCounterExists(balanceGroupId, counterId, currentRecord.getUTCEventDate());
if (tmpCounter == null)
{
tmpCounter = addCounter(balanceGroupId,counterId,utcBalanceStartValidity,UTCBalanceEndValidity,initialBalance);
// Add the balance impact
tmpBalImpact = new BalanceImpact();
tmpBalImpact.type = "D";
tmpBalImpact.balanceGroup = balanceGroupId;
tmpBalImpact.cpiName = discountName;
tmpBalImpact.ruleName = "CREATION";
tmpBalImpact.rumUsed = rumToUse;
tmpBalImpact.counterID = counterId;
tmpBalImpact.recID = tmpCounter.RecId;
tmpBalImpact.rumValueAfter = 0.0;
tmpBalImpact.rumValueUsed = 0;
tmpBalImpact.balanceAfter = initialBalance;
tmpBalImpact.balanceDelta = initialBalance;
tmpBalImpact.startDate = utcBalanceStartValidity;
tmpBalImpact.endDate = UTCBalanceEndValidity;
currentRecord.addBalanceImpact(tmpBalImpact);
tmpReturnInfo.setBalanceCreated(true);
}
// now that we are sure we have a balance, update it
tmpCounter.CurrentBalance += tmpRUMValue;
tmpDiscount = tmpRUMValue;
tmpReturnInfo.setDiscountApplied(true);
// Add the balance impact
tmpBalImpact = new BalanceImpact();
tmpBalImpact.type = "D";
tmpBalImpact.balanceGroup = balanceGroupId;
tmpBalImpact.cpiName = discountName;
tmpBalImpact.ruleName = "Aggregate" + rumToUse;
tmpBalImpact.rumUsed = rumToUse;
tmpBalImpact.counterID = counterId;
tmpBalImpact.recID = tmpCounter.RecId;
tmpBalImpact.rumValueAfter = tmpRUMValue;
tmpBalImpact.rumValueUsed = tmpRUMValue;
tmpBalImpact.balanceAfter = tmpCounter.CurrentBalance;
tmpBalImpact.balanceDelta = tmpDiscount;
tmpBalImpact.startDate = tmpCounter.validFrom;
tmpBalImpact.endDate = tmpCounter.validTo;
if (tmpBalImpact.balanceDelta != 0)
{
currentRecord.addBalanceImpact(tmpBalImpact);
// Prepare the return value
tmpReturnInfo.setDiscountApplied(true);
tmpReturnInfo.setCounterId(counterId);
tmpReturnInfo.setRecId(tmpCounter.RecId);
tmpReturnInfo.setDiscountedValue(tmpDiscount);
tmpReturnInfo.setNewBalanceValue(tmpCounter.CurrentBalance);
// Set the discount flag to "aggregate"
tmpReturnInfo.setDiscountFlag(DISCOUNT_FLAG_AGGREGATED);
}
return tmpReturnInfo;
}
}