Finally, I created a custom aggregator. I pass a context object in the constructor, in this object I have the position of the field "employeeId"(the order of the fields can change) and the current index "dimension". I'm just creating a tree for example:
Sum(Holidays)->employeeId,Sum(Holidays)->employeeId,year,Sum(Holidays)->employeeId,year,date,Holidays
public class AccumAggregatorFactory : IAggregatorFactory {
private readonly CubeContext cc;
public AccumAggregatorFactory(CubeContext cc) {
this.cc = cc;
}
public IAggregator Create() => (IAggregator)new AccumAggregator(cc);
public IAggregator Create(object state) => (IAggregator)new AccumAggregator(cc, state);
public override bool Equals(object obj) => obj is AccumAggregatorFactory aggregatorFactory;
public override string ToString() => "Accum of Holidays";
}
public class AccumAggregator : IAggregator {
private object id;
private Decimal total;
private Decimal closuresValue;
private uint count;
private readonly CubeContext cc;
public AccumAggregator(CubeContext cc) {
this.cc = cc;
}
public object Value => total;
public uint Count => count;
public string Keys { get; internal set; }
public void Merge(IAggregator aggr) {
if (!(aggr is AccumAggregator accum))
throw new ArgumentException(nameof(aggr));
decimal curAccum = 0; //Add to accumulated value if is needed
if (cc.DimIndexIdItem == cc.dim) {
curAccum = closuresValue; //I need to accum closures
if (IsClosure(accum, cc.DateEnd))
closuresValue += accum.total;
} else if (cc.dim < cc.DimIndexIdItem) {
if (IsClosure(accum, cc.DateEnd)) {
curAccum = closuresValue;
closuresValue += accum.total;
}
}
total = curAccum + accum.total;
id = accum.id;
date = accum.date;
}
private static bool IsClosure(AccumAggregator accum, DateTime dateEnd) {
if (accum.id == null)
return false;
if (accum.date == DateTime.MinValue)
return false;
if (dateEnd == accum.date)
return true;
return accum.date.Month==12 && accum.date.Day==31;
}
public object GetState() => new object[2] { Count, Value };
}