Error while creating Asset "ValidationException: Unexpected properties for type org.hyperledger.composer.system.AddAsset: registryType, registryId" - hyperledger

I'm new to Hyperledger Composer. I'm following composer tutorial of how to interact with other business network
hyperledger_composer_tutorial
I have followed the step 1,2,3 mention in the tutorial. While i am trying to perform step 4: Create the assest
I am able to create participant in business network A.
I am getting error while creating asset in business network A
ValidationException: Unexpected properties for type org.hyperledger.composer.system.AddAsset: registryType, registryId
here is my code
Network A
/**
* My commodity trading network
*/
namespace org.example.mynetwork
asset Commodity identified by tradingSymbol {
o String tradingSymbol
o String description
o String mainExchange
o Double quantity
--> Trader owner
}
participant Trader identified by tradeId {
o String tradeId
o String firstName
o String lastName
}
transaction Trade {
--> Commodity commodity
--> Trader newOwner
}
2) logic.js
/**
* Track the trade of a commodity from one trader to another
* #param {org.example.mynetwork.Trade} trade - the trade to be processed
* #transaction
*/
async function tradeCommodity(trade) {
trade.commodity.owner = trade.newOwner;
const otherNetworkData = await getNativeAPI().invokeChaincode('other-tutorial-network', ['getResourceInRegistry', 'Asset', 'org.example.mynetwork.Commodity', trade.commodity.tradingSymbol], 'composerchannel');
const stringAsset = new Buffer(otherNetworkData.payload.toArrayBuffer()).toString('utf8');
const asset = getSerializer().fromJSON(JSON.parse(stringAsset));
trade.commodity.quantity = asset.quantity;
const assetRegistry = await getAssetRegistry('org.example.mynetwork.Commodity');
await assetRegistry.update(trade.commodity);
}
Network B
/**
* My commodity trading network
*/
namespace org.example.mynetwork
asset Commodity identified by tradingSymbol {
o String tradingSymbol
o String description
o String mainExchange
o Double quantity
--> Trader owner
}
participant Trader identified by tradeId {
o String tradeId
o String firstName
o String lastName
}
transaction Trade {
--> Commodity commodity
--> Trader newOwner
}
2) logic.js
/**
* Track the trade of a commodity from one trader to another
* #param {org.example.mynetwork.Trade} trade - the trade to be processed
* #transaction
*/
async function tradeCommodity(trade) {
trade.commodity.owner = trade.newOwner;
let assetRegistry = await getAssetRegistry('org.example.mynetwork.Commodity');
await assetRegistry.update(trade.commodity);
}
How do i resolve this problem??

I believe that the JSON that you are providing as an input in the command line could be wrong, Since you are adding an Asset of type Commodity.
Try this command instead
composer transaction submit --card networkA -d '{"$class": "org.example.mynetwork.Commodity","tradingSymbol": "Ag","owner": "resource: org.example.mynetwork.Trader#bob#example.com","description": "Some", "mainExchange": "exhange","quantity": 25}'

Related

Is there a difference between token price fixed on smart contract and token price on market?

i set an initial price for my token in solidity, how does It Works the price on the market? if i set a fixed variable TokenPrice in my smart contract, can my token price change thanks to request_offer of market?
The price that i fixed in the smart contract and the price of the market are differents?
OK, I show you a small example of solidity and JS code of ICO and how you can do it
solidity:
contract OwnerContract{
address owner;
constructor(){
owner = msg.sender;
}
modifier isOwner(){
require(msg.sender == owner, "Access denied!");
_;
}
}
//Interface of standard token if you want to accept for example USDT token (as in this example)
interface IERC20{
function allowance(address owner, address spender) external view returns (uint256);
function decimals() external view returns (uint8);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}
contract ICO_Contract is ownerContract{
//Declare the price and ICO token sell amount
uint256 eachTokenPrice;
uint256 maximumAmount;
uint256 sellAmount;
constructor(uint256 maximumAmount_){
maximumAmount = maximumAmount_;
}
modifier maximumReached(uint256 _amount){
require((sellAmount + _amount) <= maximumAmount, "Maximum amount of ICO reached!");
_;
}
//declare the USDT token
address USDTtokenAddress = USDT_TOKEN_ADDRESS_ON_DEPLOYED_NETWORK;
function changeUSDTtokenAddress(address _address) public isOwner{
USDTtokenAddress = _address;
}
address ICOtokenAddress = TOKEN_ADDRESS_ICO;
function changeICOtokenAddress(address _address) public isOwner{
ICOtokenAddress = _address;
}
IERC20 USDTtoken = IERC20(USDTtokenAddress);
IERC20 ICOtoken = IERC20(ICOtokenAddress);
function buyToken(uint256 _amount) public maximumReached(_amount){
require(_amount > 0, "You need to spend USDT");
uint256 approvedAmount = USDTtoken.allowance(msg.sender, address(this));
require(approvedAmount == _amount, "Check the token allowance, not enough approved!");
uint256 totalPrice = price * _amount * (USDtoken.decimals()/ICOtoken.decimals());
USDTtoken.transferFrom(msg.sender, address(this), totalPrice);
//ICOtoken is in the contract
ICOtoken.transfer(msg.sender, _amount);
sellAmount += _amount;
}
function showPrice() external view returns(uint256){
return price;
}
function showsoldAmount() external view returns(uint256){
return sellAmount;
}
function showMaxAmount() external view returns(uint256){
return maximumAmount;
}
}
I guess this is it. (I did not test and it definitely will throw errors which you have to handle it)
JS code (ethers.js & ethereum libraries) (Because I don't know back-end language I only deploy front-end and just use front-end):
const {ethers} = require('ethers');
let price;
let soldAmount;
let maxAmount;
let provider;
let signer;
let signerAddress;
let contract;
const contractAddress = ICO_CONTRACT_ADDRESS;
const contractABI = {'function showsoldAmount() external view returns(uint256)',
'function showPrice() external view returns(uint256)',
'function buyToken(uint256 _amount) external maximumReached(_amount)',
'function showMaxAmount() external view returns(uint256)'}
//Actually I am not sure if it needs modifier in the interface or not
async () => {
await ethereum.request({ method: 'eth_requestAccounts' });
provider = new ethers.providers.Web3Provider(window.ethereum);
signer = provider.getSigner();
signerAddress = await signer.getAddress();
contract = await new ethers.Contract(contractAddress, contractABI, signer);
price = await contract.showPrice();
soldAmount= await contract.showSoldAmount();
maxAmount = contract.showMaxAmount();
//Show price and sold amount and maximum amount of selling in your page somewhere you want
}
//set a button and input in the page to call the buyToken function and connect it to function below.
function buyToken(){
let amount = document.getElementById("INPUT_TO_KNOW_AMOUNT_ID").value;
contract.buyToken(ethers.utils.parseEther(amount));
}
I hope I did not forget anything but if you have any question simply ask
Best regards :)

increase total supply of TRC20 token daily

I want to issue a token on Tron blockchain and I got it's template from the address below :
https://github.com/TRON-Developer-Hub/TRC20-Contract-Template
The problem is that I want to set my token to automatically mint a specified number of tokens daily. (For example mint 2000 tokens per day)
What should I add to the template?
you can add a functionm, below like that.
But you have to excute mintDaily() manually
uint256 constant private dailyMinted = 2000e18;
uint256 lastMintTime;
address public _owner;
constructor(
string memory name,
string memory symbol,
uint8 decimals,
address owner
) public {
_name = name;
_symbol = symbol;
_decimals = decimals;
_owner = owner;
lastMintTime = block.timestamp;
}
function mintDaily() public {
require(_owner == msg.sender, "not permitted");
// 24h = 86400
require(lastMintTime + 86400 >= block.timestamp, "mint already" );
_mint(msg.sender, dailyMinted);
lastMintTime = block.timestamp;
}

Related to hyperledger composer playground place order issue

I want to build a medicine-network in hyperledger composer playground online.
I have created one cto file and one js file for that. I am new to this blockchain and want to create a business network for tracking a secure box. here participants are:- customer, Manufacturer, transport company
Asset- order,Securebox,beauty products in the securebox
I am unable to get the results for the placeorder event.
I posted the file which I have created
securebox.cto
/**
* securebox tracking
*/
namespace org.acme.securebox
/**
* The types of BOX that could be moved
*/
enum BoxType {
o SecureBox
o NORMALBOX
o OTHERS
}
/**
* Status of an order
*/
enum OrderStatus {
o ORDER_PLACED
o IN_FIELD
o IN_TRANSIT
o DELIVERED
}
/**
* The production type associated with a Box
*/
enum ProductionType {
o SUGAR_MEDICINE
o BLOODPRESSURE_MEDICINE
o OTHER
}
/**
* The Lock status associated with a Box
*/
enum BoxStatusType {
o LOCK
o UNLOCK
}
/**
* A Person participant
*/
abstract participant User identified by email {
o String email
o String firstName
o String lastName
}
participant Customer identified by custId extends User {
o String custId
o String address1 optional
o String address2 optional
o String county optional
}
/**
* A manufacturer of medicine
*/
participant Manufacturer identified by companyName {
o String companyName
}
/**
* An order for a medicine/others to be fulfilled by a manufacturer
* and dispatched to an orderer (Person).
*/
asset Order identified by orderId {
o String orderId
o ProductionType productiontype
o OrderStatus orderStatus
--> Customer customer
--> Manufacturer manufacturer
}
/**
* An Medicine asset, which is related to a order
*/
asset Medicine identified by medicineName {
o String medicineName
o BoxType box
o OrderStatus movementStatus
o ProductionType productionType
--> Manufacturer owner
}
/**
* An order for a medicine/others to be fulfilled by a manufacturer
* and dispatched to an orderer (Person).
*/
asset SecureBox identified by secureBoxId {
o String secureBoxId
o BoxStatusType statustype
}
/**
* Transaction to create an order
*/
transaction PlaceOrder {
o String orderId
--> Customer customer
--> Manufacturer manufacturer
}
event PlaceOrderEvent {
o String orderId
}
securebox.js
'use strict';
/**
*
* #param {org.acme.securebox.PlaceOrder} placeOrder - the placeOrder transaction
* #transaction
*/
function PlaceOrder(placeOrder) {
console.log('placeOrder');
var factory = getFactory();
var NS_M = 'org.acme.securebox';
var NS = 'org.acme';
var order = factory.newResource(NS_M, 'Order', placeOrder.orderId);
order.productiontype = 'SUGAR_MEDICINE';
order.orderStatus = 'ORDER_PLACED';
order.manufacturer = placeOrder.manufacturer;
// save the order
return getAssetRegistry(order.getFullyQualifiedType())
.then(function (registry) {
return registry.add(order);
})
.then(function(){
var placeOrderEvent = factory.newEvent(NS_M, 'PlaceOrderEvent');
placeOrderEvent.orderId = order.orderId;
emit(placeOrderEvent);
});
}
while submit the transaction, I am unable to get the output as expected.
Getting the error # Instance org.acme.securebox.Order#1234 missing required field customer
Please help me out this
You are missing one sentence in securebox.js in PlaceOrder() function:
just add one line there:
order.customer = placeOrder.customer;

HasMany relationship of two child Domain Classes in Grails

Good Day to all, I am having an Architecture issue with the way I designed my Domain Class.
I have a parent domain class called TxnBatch which has a One to Many relationship with Transactions class, and also two other child class ProcessorTxnBatch and TamsTerminalTxnBatch that extend the TxnBatch class
The logic is that when a transaction is being performed, I first add it to processorTxnBatch.addToTransactions(transactions), After the transaction is processed, I then add it to the Terminal Batch tamsTerminalTxnBatch.addToTransactions(transaction) which is the other child class
The issue now is that when i add the same transaction to the transactions of another Batch, It removes the Transaction from the former Batch I added it to.
Parent Class
package com.itex.cipg
class TxnBatch implements Serializable{
static final String OPEN = "OPEN"
static final String CLOSED = "CLOSED"
/**
*The Batch number of this Transaction
**Set when the Batch is created
**/
String batchNo = "0"
/**
*The Batch Status of the transaction
**/
String batchStatus = OPEN
/**
*The Time this batch was started
**/
Date startTime
/**
*The Time this batch was Closed
**/
Date lastUpdated
/**
*The Time this batch was Closed
**/
Date closeTime
/**
*The NextSequence Number of this Batch
**/
Integer seqNo = 1
//Attributes Updated During a payService.updateBatch() Function call in payService
/**
* The Total Amount of All Transactions
*/
double allTransactionsSum = 0
/**
* The Total Amount Charged for the Approved Transactions
*/
double approvedTransactionsCharge = 0
/**
* The Total Amount of Approved Transactions
*/
double approvedTransactionsSum = 0
/**
* The Total Amount of Declined Transactions
*/
double declinedTransactionsSum = 0
/**
* The Total Amount of CANCELED Transactions
*/
double canceledTransactionsSum = 0
/**
* The Total Amount of CANCELED Transactions
*/
double errorTransactionsSum = 0
/**
* The Total number of ALL Transactions
*/
int allTransactionsCount = 0
/**
* The Total number of Approved Transactions
*/
int approvedTransactionsCount= 0
/**
* The Total number of declined Transactions
*/
int declinedTransactionsCount = 0
/**
* The Total number of canceled Transactions
*/
int canceledTransactionsCount = 0
/**
*The Total Sum of Unprocessed Transactions
*/
int unProcessedTransactionsSum = 0
/**
*The Total number of Unprocessed Transactions
**/
int unProcessedTransactionsCount = 0
/**
* The Total number of canceled Transactions
*/
int errorTransactionsCount = 0
/**
*The manimun batch Number to reach before closing this Batch
**/
int maxBatchNoLimit = 100
static hasMany = [transactions: Transaction]
/**
*The result of this batch
*as gotten from TAMS when
*the batch is closed
**/
String result
//Implements the Constraints of this Object
static constraints = {
batchStatus()
batchNo(blank:false)
seqNo()
allTransactionsCount()
unProcessedTransactionsSum()
unProcessedTransactionsCount()
allTransactionsSum(display:true)
approvedTransactionsCount()
approvedTransactionsSum()
canceledTransactionsCount(display:true)
canceledTransactionsSum()
errorTransactionsCount(display:true)
errorTransactionsSum(display:true)
declinedTransactionsCount(display:true)
declinedTransactionsSum(display:true)
closeTime(nullable:true)
result(nullable:true)
}
String toString()
{
return batchStatus + "-" + batchNo + ":" + seqNo
}
}
First Child Class
package com.itex.cipg
class ProcessorTxnBatch extends TxnBatch implements Serializable{
/**
*The Server Processor of the Transaction Batch
*
**/
Server server
}
The second child Class
package com.itex.cipg
class TamsTerminalTxnBatch extends TxnBatch implements Serializable{
/**
*The TAMS Terminal this batch belongs TO
**/
TamsTerminal terminal
}
I will want to add a transaction to the TamsTerminalTxnBatch and also to the ProcessorTxnBatch Please note static hasMany = [transactions:transaction]
After I do
def transaction = new Transaction //arbitrarily creating a Transaction
processorTxnBatch.addToTransactions(transaction)
assert processorTxnBatch.transactions.size() = 1 //true
tamsTerminalTxnBatch.addToTransactions(transaction)
assert tamsTerminalTxnBatch.transactions.size() = 1 //true
assert processorTxnBatch.transactions.size() = 0 //true
I dont Know Why..........Please Kindly Help me out
Though They are stored on the same Table..........Thanks

ActivePivot leaf level aggregation and Analysis dimension

lets say I have an ActivePivot cube with facts containing just Value, and Currency.
lets say my cube has Currency as a regular dimension.
We fill the cube with facts that have many currencies.
We have a forex service that takes the currency and reference currency to get a rate.
Now, Value.SUM doesn't make any sense, we are adding up values with different currencies, so we want to have a post processor that can convert all values to a reference currency, say, USD, then sum them, so we write a post processor that extends ADynamicAggregationPostProcessor, specify Currency as a leaf level dimension, and use the forex service to do the conversion, and we are happy.
But, lets say we don't want to convert just to USD, we want to convert to 10 different currencies and see the results next to each other on the screen.
So we create an Analysis dimension, say ReferenceCurrency, with 10 members.
My question is: how can I alter the above post processor to handle the Analysis dimension? The plain vanilla ADynamicAggregationPostProcessor does not handle Analysis dimensions, only the default member is visible to this post processor. Other post processors that handle Analysis dimensions, like DefaultAggregatePostProcessor do not have a means for specifying leaf levels, so I cannot get the aggregates by Currency, and so cannot do the forex conversion. How can I have my cake and eat it too?
It looks like you want to use two advanced features of ActivePivot at the same time (Analysis dimensions to expose several outcomes of the same aggregate, and dynamic aggregation to aggregate amounts expressed in different currencies).
Separately each one is fairly easy to setup through configuration and a few lines of code to inject. But to interleave both you will need to understand the internals of post processor evaluation, and inject business logic at the right places.
Here is an example based on ActivePivot 4.3.3. It has been written in the open-source Sandbox Application so that you can run it quickly before adapting it to your own project.
First we need a simple analysis dimension to hold the possible reference currencies:
package com.quartetfs.pivot.sandbox.postprocessor.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import com.quartetfs.biz.pivot.cube.hierarchy.axis.impl.AAnalysisDimension;
import com.quartetfs.fwk.QuartetExtendedPluginValue;
/**
*
* An analysis dimension bearing the
* list of possible reference currencies.
*
* #author Quartet FS
*
*/
#QuartetExtendedPluginValue(interfaceName = "com.quartetfs.biz.pivot.cube.hierarchy.IDimension", key = ReferenceCurrencyDimension.TYPE)
public class ReferenceCurrencyDimension extends AAnalysisDimension {
/** serialVersionUID */
private static final long serialVersionUID = 42706811331081328L;
/** Default reference currency */
public static final String DEFAULT_CURRENCY = "EUR";
/** Static list of non-default possible reference currencies */
public static final List<Object[]> CURRENCIES;
static {
List<Object[]> currencies = new ArrayList<Object[]>();
currencies.add(new Object[] {"USD"});
currencies.add(new Object[] {"GBP"});
currencies.add(new Object[] {"JPY"});
CURRENCIES = Collections.unmodifiableList(currencies);
}
/** Plugin type */
public static final String TYPE = "REF_CCY";
/** Constructor */
public ReferenceCurrencyDimension(String name, int ordinal, Properties properties, Set<String> measureGroups) {
super(name, ordinal, properties, measureGroups);
}
#Override
public Object getDefaultDiscriminator(int levelOrdinal) { return DEFAULT_CURRENCY; }
#Override
public Collection<Object[]> buildDiscriminatorPaths() { return CURRENCIES; }
#Override
public int getLevelsCount() { return 1; }
#Override
public String getLevelName(int levelOrdinal) {
return levelOrdinal == 0 ? "Currency" : super.getLevelName(levelOrdinal);
}
#Override
public String getType() { return TYPE; }
}
Then the post processor itself, a customized dynamic aggregation post processor modified to handle the analysis dimension and output the same aggregate multiple times, one time per reference currency.
package com.quartetfs.pivot.sandbox.postprocessor.impl;
import java.util.List;
import java.util.Properties;
import com.quartetfs.biz.pivot.IActivePivot;
import com.quartetfs.biz.pivot.ILocation;
import com.quartetfs.biz.pivot.ILocationPattern;
import com.quartetfs.biz.pivot.aggfun.IAggregationFunction;
import com.quartetfs.biz.pivot.cellset.ICellSet;
import com.quartetfs.biz.pivot.cube.hierarchy.IDimension;
import com.quartetfs.biz.pivot.cube.hierarchy.axis.IAxisMember;
import com.quartetfs.biz.pivot.impl.Location;
import com.quartetfs.biz.pivot.postprocessing.impl.ADynamicAggregationPostProcessor;
import com.quartetfs.biz.pivot.postprocessing.impl.ADynamicAggregationProcedure;
import com.quartetfs.biz.pivot.query.IQueryCache;
import com.quartetfs.biz.pivot.query.aggregates.IAggregatesRetriever;
import com.quartetfs.biz.pivot.query.aggregates.RetrievalException;
import com.quartetfs.fwk.QuartetException;
import com.quartetfs.fwk.QuartetExtendedPluginValue;
import com.quartetfs.pivot.sandbox.service.impl.ForexService;
import com.quartetfs.tech.type.IDataType;
import com.quartetfs.tech.type.impl.DoubleDataType;
/**
* Forex post processor with two features:
* <ul>
* <li>Dynamically aggregates amounts in their native currencies into reference currency
* <li>Applies several reference currencies, exploded along an analysis dimension.
* </ul>
*
* #author Quartet FS
*/
#QuartetExtendedPluginValue(interfaceName = "com.quartetfs.biz.pivot.postprocessing.IPostProcessor", key = ForexPostProcessor.TYPE)
public class ForexPostProcessor extends ADynamicAggregationPostProcessor<Double> {
/** serialVersionUID */
private static final long serialVersionUID = 15874126988574L;
/** post processor plugin type */
public final static String TYPE = "FOREX";
/** Post processor return type */
private static final IDataType<Double> DATA_TYPE = new DoubleDataType();
/** Ordinal of the native currency dimension */
protected int nativeCurrencyDimensionOrdinal;
/** Ordinal of the native currency level */
protected int nativeCurrencyLevelOrdinal;
/** Ordinal of the reference currencies dimension */
protected int referenceCurrenciesOrdinal;
/** forex service*/
private ForexService forexService;
/** constructor */
public ForexPostProcessor(String name, IActivePivot pivot) {
super(name, pivot);
}
/** Don't forget to inject the Forex service into the post processor */
public void setForexService(ForexService forexService) {
this.forexService = forexService;
}
/** post processor initialization */
#Override
public void init(Properties properties) throws QuartetException {
super.init(properties);
nativeCurrencyDimensionOrdinal = leafLevelsOrdinals.get(0)[0];
nativeCurrencyLevelOrdinal = leafLevelsOrdinals.get(0)[1];
IDimension referenceCurrenciesDimension = getDimension("ReferenceCurrencies");
referenceCurrenciesOrdinal = referenceCurrenciesDimension.getOrdinal();
}
/**
* Handling of the analysis dimension:<br>
* Before retrieving leaves, wildcard the reference currencies dimension.
*/
protected ICellSet retrieveLeaves(ILocation location, IAggregatesRetriever retriever) throws RetrievalException {
ILocation baseLocation = location;
if(location.getLevelDepth(referenceCurrenciesOrdinal-1) > 0) {
Object[][] array = location.arrayCopy();
array[referenceCurrenciesOrdinal-1][0] = null; // wildcard
baseLocation = new Location(array);
}
return super.retrieveLeaves(baseLocation, retriever);
}
/**
* Perform the evaluation of the post processor on a leaf (as defined in the properties).
* Here the leaf level is the UnderlierCurrency level in the Underlyings dimension .
*/
#Override
protected Double doLeafEvaluation(ILocation leafLocation, Object[] underlyingMeasures) throws QuartetException {
// Extract the native and reference currencies from the evaluated location
String currency = (String) leafLocation.getCoordinate(nativeCurrencyDimensionOrdinal-1, nativeCurrencyLevelOrdinal);
String refCurrency = (String) leafLocation.getCoordinate(referenceCurrenciesOrdinal-1, 0);
// Retrieve the measure in the native currency
double nativeAmount = (Double) underlyingMeasures[0];
// If currency is reference currency or measureNative is equal to 0.0 no need to convert
if ((currency.equals(refCurrency)) || (nativeAmount == .0) ) return nativeAmount;
// Retrieve the rate and rely on the IQueryCache
// in order to retrieve the same rate for the same currency for our query
IQueryCache queryCache = pivot.getContext().get(IQueryCache.class);
Double rate = (Double) queryCache.get(currency + "_" + refCurrency);
if(rate == null) {
Double rateRetrieved = forexService.retrieveQuotation(currency, refCurrency);
Double rateCached = (Double) queryCache.putIfAbsent(currency + "_" + refCurrency, rateRetrieved);
rate = rateCached == null ? rateRetrieved : rateCached;
}
// Compute equivalent in reference currency
return rate == null ? nativeAmount : nativeAmount * rate;
}
#Override
protected IDataType<Double> getDataType() { return DATA_TYPE; }
/** #return the type of this post processor, within the post processor extended plugin. */
#Override
public String getType() { return TYPE; }
/**
* #return our own custom dynamic aggregation procedure,
* so that we can inject our business logic.
*/
protected DynamicAggregationProcedure createProcedure(ICellSet cellSet, IAggregationFunction aggregationFunction, ILocationPattern pattern) {
return new DynamicAggregationProcedure(cellSet, aggregationFunction, pattern);
}
/**
* Custom dynamic aggregation procedure.<br>
* When the procedure is executed over a leaf location,
* we produce several aggregates instead of only one:
* one aggregate for each of the visible reference currencies.
*/
protected class DynamicAggregationProcedure extends ADynamicAggregationProcedure<Double> {
protected DynamicAggregationProcedure(ICellSet cellSet, IAggregationFunction aggregationFunction, ILocationPattern pattern) {
super(ForexPostProcessor.this, aggregationFunction, cellSet, pattern);
}
/**
* Execute the procedure over one row of the leaf cell set.
* We compute one aggregate for each of the reference currencies.
*/
#Override
public boolean execute(ILocation location, int rowId, Object[] measures) {
if(location.getLevelDepth(referenceCurrenciesOrdinal-1) > 0) {
// Lookup the visible reference currencies
IDimension referenceCurrenciesDimension = pivot.getDimensions().get(referenceCurrenciesOrdinal);
List<IAxisMember> referenceCurrencies = (List<IAxisMember>) referenceCurrenciesDimension.retrieveMembers(0);
for(IAxisMember member : referenceCurrencies) {
Object[][] array = location.arrayCopy();
array[referenceCurrenciesOrdinal-1][0] = member.getDiscriminator();
ILocation loc = new Location(array);
super.execute(loc, rowId, measures);
}
return true;
} else {
return super.execute(location, rowId, measures);
}
}
#Override
protected Double doLeafEvaluation(ILocation location, Object[] measures) throws QuartetException {
return ForexPostProcessor.this.doLeafEvaluation(location, measures);
}
}
}
In the description of your cube, the analysis dimension and the post processor would be exposed like this:
...
<dimension name="ReferenceCurrencies" pluginKey="REF_CCY" />
...
<measure name="cross" isIntrospectionMeasure="false">
<postProcessor pluginKey="FOREX">
<properties>
<entry key="id" value="pv.SUM" />
<entry key="underlyingMeasures" value="pv.SUM" />
<entry key="leafLevels" value="UnderlierCurrency#Underlyings" />
</properties>
</postProcessor>
</measure>
...
Analysis Dimensions brings so much complexity they should be considered apart of the others features of your cube. One way to handle your issue is then:
Add a first measure expanding properly along the analysis dimension. In your case, it will simply copy the underlying measure along ReferenceCurrency, optionally doing the FX conversion. This measure may be used as underlying of several measures.
Add a second measure, based on usual dynamic aggregation. This second implementation is very simple as it does not know there is an analysis dimension.

Resources