private static void PreProcessNonExpandingWildcardChar(ARmDataSet dataSet, object key, out string nonExpandingWildcard)
        {
            bool hasWildcard = false;

            string        originalValue  = (dataSet[key] as string ?? "");
            StringBuilder processedValue = new StringBuilder(originalValue.Length);

            for (int i = 0; i < originalValue.Length; i++)
            {
                if (originalValue[i] == RMReportConstants.NonExpandingWildcardChar)
                {
                    processedValue.Append(RMReportConstants.DefaultWildcardChar);
                    hasWildcard = true;
                }
                else
                {
                    processedValue.Append(originalValue[i]);
                }
            }

            if (hasWildcard == true)
            {
                dataSet[key]         = processedValue.ToString();
                nonExpandingWildcard = originalValue;
            }
            else
            {
                nonExpandingWildcard = null;
            }
        }
Example #2
0
        public ARmDataSet MergeDataSet(IEnumerable <ARmDataSet> list, string expand, MergingMode mode, Func <IEnumerable <ARmDataSet>, string, MergingMode, ARmDataSet> del)
        {
            ARmDataSet dataSet = del(list, expand, mode);

            string rmType = Base.Report.Current.Type;

            if (rmType == ARmReport.PM)
            {
                foreach (ARmDataSet set in list)
                {
                    if (set == null)
                    {
                        continue;
                    }

                    RMReportWildcard.ConcatenateRangeWithDataSet(dataSet, set, Keys.StartAccountGroup, Keys.EndAccountGroup, mode);
                    RMReportWildcard.ConcatenateRangeWithDataSet(dataSet, set, Keys.StartProject, Keys.EndProject, mode);
                    RMReportWildcard.ConcatenateRangeWithDataSet(dataSet, set, Keys.StartProjectTask, Keys.EndProjectTask, mode);
                    RMReportWildcard.ConcatenateRangeWithDataSet(dataSet, set, Keys.StartInventory, Keys.EndInventory, mode);
                }

                dataSet.Expand = list.First().Expand;
            }

            return(dataSet);
        }
Example #3
0
        public void FillDataSource(RMDataSource ds, ARmDataSet dst, string rmType, Action <RMDataSource, ARmDataSet, string> del)
        {
            del(ds, dst, rmType);

            if (rmType == ARmReport.PM)
            {
                FillDataSourceInternal(ds, dst, rmType);
            }
        }
Example #4
0
 private void FillDataSourceInternal(RMDataSource ds, ARmDataSet dst, string rmType)
 {
     if (ds != null && ds.DataSourceID != null)
     {
         RMDataSourcePM dsPM = Base.Caches[typeof(RMDataSource)].GetExtension <RMDataSourcePM>(ds);
         dst[Keys.StartAccountGroup] = dsPM.StartAccountGroup;
         dst[Keys.EndAccountGroup]   = dsPM.EndAccountGroup;
         dst[Keys.StartProject]      = dsPM.StartProject;
         dst[Keys.EndProject]        = dsPM.EndProject;
         dst[Keys.StartProjectTask]  = dsPM.StartProjectTask;
         dst[Keys.EndProjectTask]    = dsPM.EndProjectTask;
         dst[Keys.StartInventory]    = dsPM.StartInventory;
         dst[Keys.EndInventory]      = dsPM.EndInventory;
     }
 }
Example #5
0
        public bool IsParameter(ARmDataSet ds, string name, ValueBucket value, Func <ARmDataSet, string, ValueBucket, bool> del)
        {
            bool flag = del(ds, name, value);

            if (!flag)
            {
                Keys key;
                if (_keysDictionary.TryGetValue(name, out key))
                {
                    value.Value = ds[key];
                    return(true);
                }
                return(false);
            }
            return(flag);
        }
        public static void ConcatenateRangeWithDataSet(ARmDataSet target, ARmDataSet source, object startKey, object endKey, MergingMode mergingMode)
        {
            string startValue = (source[startKey] as string ?? String.Empty);
            string endValue   = (source[endKey] as string ?? String.Empty);
            char   mergeChar  = default(char);

            switch (mergingMode)
            {
            case MergingMode.Intersection:
                mergeChar = RMReportConstants.RangeIntersectionChar;
                break;

            case MergingMode.Union:
                mergeChar = RMReportConstants.RangeUnionChar;
                break;

            default:
                throw new NotSupportedException(PXMessages.LocalizeFormatNoPrefixNLA(Messages.MergingModeNotSupported, mergingMode));
            }

            if (!(startValue == String.Empty && endValue == String.Empty))
            {
                if (startValue.Contains(RMReportConstants.RangeDelimiterChar) && endValue != String.Empty)
                {
                    throw new ArgumentException(PXMessages.LocalizeFormatNoPrefixNLA(Messages.ValueShouldBeEmpty, startKey, startValue, endKey, endValue));
                }
                else if (startValue == String.Empty && endValue != String.Empty)
                {
                    throw new ArgumentException(PXMessages.LocalizeFormatNoPrefixNLA(Messages.DataSourceIncomplete, endKey, startKey, endValue));
                }

                if (!String.IsNullOrEmpty(target[startKey] as string))
                {
                    // There's existing data stored; we will intersect/union previous range with this new range at a later stage
                    target[startKey] += mergeChar.ToString();
                }

                target[startKey] += startValue;
                if (endValue != String.Empty)
                {
                    target[startKey] += RMReportConstants.RangeDelimiterChar.ToString() + endValue;
                }
            }
        }
Example #7
0
        public static void ConcatenateRangeWithDataSet(ARmDataSet target, ARmDataSet source, object startKey, object endKey, MergingMode mergingMode)
        {
            string startValue = (source[startKey] as string ?? String.Empty);
            string endValue   = (source[endKey] as string ?? String.Empty);
            char   mergeChar  = default(char);

            switch (mergingMode)
            {
            case MergingMode.Intersection:
                mergeChar = RMReportConstants.RangeIntersectionChar;
                break;

            case MergingMode.Union:
                mergeChar = RMReportConstants.RangeUnionChar;
                break;

            default:
                throw new NotSupportedException(String.Format("Merging mode '{0}' is not supported.", mergingMode));
            }

            if (!(startValue == String.Empty && endValue == String.Empty))
            {
                if (startValue.Contains(RMReportConstants.RangeDelimiterChar) && endValue != String.Empty)
                {
                    throw new ArgumentException(String.Format("A range is specified in {0}; end value should be empty. {0}: {1}. {2}: {3}.", startKey, startValue, endKey, endValue));
                }
                else if (startValue == String.Empty && endValue != String.Empty)
                {
                    throw new ArgumentException(String.Format("Data Source is incomplete. If you set {0}, you should also define {1}. {0}: {2}.", endKey, startKey, endValue));
                }

                if (!String.IsNullOrEmpty(target[startKey] as string))
                {
                    // There's existing data stored; we will intersect/union previous range with this new range at a later stage
                    target[startKey] += mergeChar.ToString();
                }

                target[startKey] += startValue;
                if (endValue != String.Empty)
                {
                    target[startKey] += RMReportConstants.RangeDelimiterChar.ToString() + endValue;
                }
            }
        }
        public static List <ARmUnit> ExpandUnit(RMReportReader report, RMDataSource ds, ARmUnit unit, object startKey, object endKey, Func <ARmDataSet, List <T> > fetchRangePredicate, Func <T, string> unitCodePredicate, Func <T, string> unitDescriptionPredicate, Action <T, string> applyWildcardToItemAction)
        {
            string nonExpandingWildcard = null;

            PreProcessNonExpandingWildcardChar(unit.DataSet, startKey, out nonExpandingWildcard);

            //This will validate Start/End range and merge the pair into a single range-like value that FillSubsList expects.
            ARmDataSet rangeToFetch = new ARmDataSet();

            RMReportWildcard.ConcatenateRangeWithDataSet(rangeToFetch, unit.DataSet, startKey, endKey, MergingMode.Intersection);

            List <T> unitItems = fetchRangePredicate(rangeToFetch);

            if (nonExpandingWildcard != null)
            {
                //TODO: Build a better auto-description based on segment values instead of copying code to description?
                unitItems = ReduceListByNonExpandingWildcard(nonExpandingWildcard, unitItems,
                                                             (u) => unitCodePredicate(u),
                                                             (u, mv) => applyWildcardToItemAction(u, mv));
            }

            switch (ds.RowDescription.Trim().ToUpper())
            {
            case RowDescriptionType.CodeDescription:
                unitItems.Sort((x, y) => (unitCodePredicate(x) + unitDescriptionPredicate(x)).CompareTo(unitCodePredicate(y) + unitDescriptionPredicate(y)));
                break;

            case RowDescriptionType.DescriptionCode:
                unitItems.Sort((x, y) => (unitDescriptionPredicate(x) + unitCodePredicate(x)).CompareTo(unitDescriptionPredicate(y) + unitCodePredicate(y)));
                break;

            case RowDescriptionType.Description:
                unitItems.Sort((x, y) => unitDescriptionPredicate(x).CompareTo(unitDescriptionPredicate(y)));
                break;

            default:
                unitItems.Sort((x, y) => unitCodePredicate(x).CompareTo(unitCodePredicate(y)));
                break;
            }

            List <ARmUnit> units = new List <ARmUnit>();
            int            n     = 0;

            foreach (T unitItem in unitItems)
            {
                n++;
                var u = new ARmUnit();
                report.FillDataSource(ds, u.DataSet, report.Report.Current.Type);
                u.DataSet[startKey] = unitCodePredicate(unitItem);
                u.DataSet[endKey]   = null;
                u.Code = unit.Code + n.ToString("D5");

                switch (ds.RowDescription.Trim())
                {
                case RowDescriptionType.CodeDescription:
                    u.Description = string.Format("{0}{1}{2}", unitCodePredicate(unitItem).Trim(), "-", unitDescriptionPredicate(unitItem));
                    break;

                case RowDescriptionType.DescriptionCode:
                    u.Description = string.Format("{0}{1}{2}", unitDescriptionPredicate(unitItem), "-", unitCodePredicate(unitItem).Trim());
                    break;

                case RowDescriptionType.Description:
                    u.Description = unitDescriptionPredicate(unitItem);
                    break;

                default:
                    u.Description = unitCodePredicate(unitItem).Trim();
                    break;
                }

                u.Formula       = unit.Formula;
                u.PrintingGroup = unit.PrintingGroup;
                units.Add(u);
            }
            return(units);
        }
Example #9
0
        public virtual List <ARmUnit> ExpandUnit(RMDataSource ds, ARmUnit unit, Func <RMDataSource, ARmUnit, List <ARmUnit> > del)
        {
            string rmType = Base.Report.Current.Type;

            if (rmType == ARmReport.PM)
            {
                if (unit.DataSet.Expand != ExpandType.Nothing)
                {
                    PMEnsureInitialized();
                    if (ds.Expand == ExpandType.AccountGroup)
                    {
                        return(RMReportUnitExpansion <PMAccountGroup> .ExpandUnit(Base, ds, unit, Keys.StartAccountGroup, Keys.EndAccountGroup,
                                                                                  rangeToFetch => _accountGroupsRangeCache.GetItemsInRange(rangeToFetch[Keys.StartAccountGroup] as string,
                                                                                                                                           accountGroup => accountGroup.GroupCD,
                                                                                                                                           (accountGroup, code) => accountGroup.GroupCD = code),
                                                                                  accountGroup => accountGroup.GroupCD, accountGroup => accountGroup.Description,
                                                                                  (accountGroup, wildcard) => { accountGroup.GroupCD = wildcard; accountGroup.Description = wildcard; }));
                    }
                    else if (ds.Expand == ExpandType.Project)
                    {
                        return(RMReportUnitExpansion <PMProject> .ExpandUnit(Base, ds, unit, Keys.StartProject, Keys.EndProject,
                                                                             rangeToFetch => _projectsRangeCache.GetItemsInRange(rangeToFetch[Keys.StartProject] as string,
                                                                                                                                 project => project.ContractCD,
                                                                                                                                 (project, code) => project.ContractCD = code),
                                                                             project => project.ContractCD, project => project.Description,
                                                                             (project, wildcard) => { project.ContractCD = wildcard; project.Description = wildcard; }));
                    }
                    else if (ds.Expand == ExpandType.ProjectTask)
                    {
                        return(RMReportUnitExpansion <PMTask> .ExpandUnit(Base, ds, unit, Keys.StartProjectTask, Keys.EndProjectTask,
                                                                          rangeToFetch => {
                            List <PMTask> tasks = _tasksRangeCache.GetItemsInRange(rangeToFetch[Keys.StartProjectTask] as string,
                                                                                   task => task.TaskCD,
                                                                                   (task, code) => task.TaskCD = code);

                            ARmDataSet projectRange = new ARmDataSet();
                            RMReportWildcard.ConcatenateRangeWithDataSet(projectRange, unit.DataSet, Keys.StartProject, Keys.EndProject, MergingMode.Intersection);

                            if (!String.IsNullOrEmpty(projectRange[Keys.StartProject] as string))
                            {
                                //A project range is specified in the unit; restrict tasks to tasks of this project range.
                                List <PMProject> projects = _projectsRangeCache.GetItemsInRange(projectRange[Keys.StartProject] as string,
                                                                                                project => project.ContractCD,
                                                                                                (project, code) => project.ContractCD = code);
                                tasks = tasks.Where(t => projects.Any(p => t.ProjectID == p.ContractID)).ToList <PMTask>();
                            }

                            //Same project TaskCD can be reused in multiple projects; it only makes sense to get distinct values for the purpose of filling the unit tree
                            List <PMTask> groupedTasks = tasks.GroupBy(t => t.TaskCD).Select(g => new PMTask()
                            {
                                TaskCD = g.Key, Description = g.Min(t => t.Description)
                            }).ToList <PMTask>();
                            return groupedTasks;
                        },
                                                                          task => task.TaskCD, project => project.Description,
                                                                          (task, wildcard) => { task.TaskCD = wildcard; task.Description = wildcard; }));
                    }
                    else if (ds.Expand == ExpandType.Inventory)
                    {
                        return(RMReportUnitExpansion <InventoryItem> .ExpandUnit(Base, ds, unit, Keys.StartInventory, Keys.EndInventory,
                                                                                 rangeToFetch => _itemRangeCache.GetItemsInRange(rangeToFetch[Keys.StartInventory] as string,
                                                                                                                                 item => item.InventoryCD,
                                                                                                                                 (item, code) => item.InventoryCD = code),
                                                                                 item => item.InventoryCD, item => item.Descr,
                                                                                 (item, wildcard) => { item.InventoryCD = wildcard; item.Descr = wildcard; }));
                    }
                }
            }
            else
            {
                return(del(ds, unit));
            }

            return(null);
        }
Example #10
0
        private object CalculateAndExpandValue(bool drilldown, RMDataSource ds, RMDataSourceGL dsGL, RMDataSourcePM dsPM, ARmDataSet dataSet, List <PMAccountGroup> accountGroups, List <PMProject> projects, List <PMTask> tasks, List <InventoryItem> items, List <object[]> splitret)
        {
            decimal totalAmount = 0;
            object  locker      = new object();
            Dictionary <PMHistoryKeyTuple, PXResult <PMHistory, PMProject, PMTask, PMAccountGroup, InventoryItem> > drilldownData = null;

            if (drilldown)
            {
                drilldownData = new Dictionary <PMHistoryKeyTuple, PXResult <PMHistory, PMProject, PMTask, PMAccountGroup, InventoryItem> >();
            }

            Parallel.For(0, accountGroups.Count, accountGroup =>
            {
                PMAccountGroup currentAccountGroup = accountGroups[accountGroup];
                if (!_historySegments.Contains(new PMHistoryKeyTuple(0, String.Empty, currentAccountGroup.GroupID.Value, 0)))
                {
                    return;
                }

                Parallel.For(0, projects.Count, project =>
                {
                    PMProject currentProject = projects[project];
                    if (!_historySegments.Contains(new PMHistoryKeyTuple(currentProject.ContractID.Value, String.Empty, currentAccountGroup.GroupID.Value, 0)))
                    {
                        return;
                    }

                    Parallel.For(0, tasks.Count, task =>
                    {
                        PMTask currentTask = tasks[task];
                        if (!_historySegments.Contains(new PMHistoryKeyTuple(currentProject.ContractID.Value, currentTask.TaskCD, currentAccountGroup.GroupID.Value, 0)))
                        {
                            return;
                        }
                        if (ds.Expand != ExpandType.ProjectTask && currentTask.ProjectID != currentProject.ContractID)
                        {
                            return;
                        }

                        Parallel.For(0, items.Count, item =>
                        {
                            InventoryItem currentItem = items[item];
                            List <PMHistory> periods  = GetPeriodsToCalculate(currentProject, currentTask, currentAccountGroup, currentItem, ds, dsGL);
                            if (periods == null)
                            {
                                return;
                            }

                            foreach (var hist in periods)
                            {
                                decimal amount = GetAmountFromPMHistory(ds, hist);
                                lock (locker)
                                {
                                    totalAmount += amount;
                                }

                                if (drilldown)
                                {
                                    var key = new PMHistoryKeyTuple(currentProject.ContractID.Value, currentTask.TaskCD, currentAccountGroup.GroupID.Value, currentItem.InventoryID.Value);
                                    PXResult <PMHistory, PMProject, PMTask, PMAccountGroup, InventoryItem> drilldownRow = null;

                                    lock (drilldownData)
                                    {
                                        if (!drilldownData.TryGetValue(key, out drilldownRow))
                                        {
                                            drilldownRow = new PXResult <PMHistory, PMProject, PMTask, PMAccountGroup, InventoryItem>(new PMHistory(), currentProject, currentTask, currentAccountGroup, currentItem);
                                            drilldownData.Add(key, drilldownRow);
                                        }
                                    }

                                    lock (drilldownRow)
                                    {
                                        AggregatePMHistoryForDrilldown(drilldownRow, hist);
                                    }
                                }

                                if (ds.Expand == ExpandType.AccountGroup)
                                {
                                    lock (currentAccountGroup)
                                    {
                                        splitret[accountGroup][2] = (decimal)splitret[accountGroup][2] + amount;
                                        if (splitret[accountGroup][3] == null)
                                        {
                                            var dataSetCopy = new ARmDataSet(dataSet);
                                            dataSetCopy[PX.Objects.CS.RMReportReaderPM.Keys.StartAccountGroup] = dataSetCopy[PX.Objects.CS.RMReportReaderPM.Keys.EndAccountGroup] = currentAccountGroup.GroupCD;
                                            splitret[accountGroup][3] = dataSetCopy;
                                        }
                                    }
                                }
                                else if (ds.Expand == ExpandType.Project)
                                {
                                    lock (currentProject)
                                    {
                                        splitret[project][2] = (decimal)splitret[project][2] + amount;
                                        if (splitret[project][3] == null)
                                        {
                                            var dataSetCopy = new ARmDataSet(dataSet);
                                            dataSetCopy[PX.Objects.CS.RMReportReaderPM.Keys.StartProject] = dataSetCopy[PX.Objects.CS.RMReportReaderPM.Keys.EndProject] = currentProject.ContractCD;
                                            splitret[project][3] = dataSetCopy;
                                        }
                                    }
                                }
                                else if (ds.Expand == ExpandType.ProjectTask)
                                {
                                    lock (currentItem)
                                    {
                                        splitret[task][2] = (decimal)splitret[task][2] + amount;
                                        if (splitret[task][3] == null)
                                        {
                                            var dataSetCopy = new ARmDataSet(dataSet);
                                            dataSetCopy[PX.Objects.CS.RMReportReaderPM.Keys.StartProjectTask] = dataSetCopy[PX.Objects.CS.RMReportReaderPM.Keys.EndProjectTask] = currentTask.TaskCD;
                                            splitret[task][3] = dataSetCopy;
                                        }
                                    }
                                }
                                else if (ds.Expand == ExpandType.Inventory)
                                {
                                    lock (currentItem)
                                    {
                                        splitret[item][2] = (decimal)splitret[item][2] + amount;
                                        if (splitret[item][3] == null)
                                        {
                                            var dataSetCopy = new ARmDataSet(dataSet);
                                            dataSetCopy[PX.Objects.CS.RMReportReaderPM.Keys.StartInventory] = dataSetCopy[PX.Objects.CS.RMReportReaderPM.Keys.EndInventory] = currentItem.InventoryCD;
                                            splitret[item][3] = dataSetCopy;
                                        }
                                    }
                                }
                            }
                        });
                    });
                });
            });

            if (drilldown)
            {
                var resultset = new PXResultset <PMHistory, PMProject, PMTask, PMAccountGroup, InventoryItem>();
                foreach (var r in
                         from row in drilldownData.Values
                         let projectCD = ((PMProject)row[typeof(PMProject)]).With(_ => _.ContractCD)
                                         let taskCD = ((PMTask)row[typeof(Sub)]).With(_ => _.TaskCD)
                                                      let accGroupCD = ((PMAccountGroup)row[typeof(PMAccountGroup)]).With(_ => _.GroupCD)
                                                                       let inventoryCD = ((InventoryItem)row[typeof(InventoryItem)]).With(_ => _.InventoryCD)
                                                                                         orderby projectCD, taskCD, accGroupCD, inventoryCD
                         select row)
                {
                    resultset.Add(r);
                }
                return(resultset);
            }
            else if (ds.Expand != ExpandType.Nothing)
            {
                return(splitret);
            }
            else
            {
                return(totalAmount);
            }
        }
Example #11
0
        public virtual object GetHistoryValue(ARmDataSet dataSet, bool drilldown, Func <ARmDataSet, bool, object> del)
        {
            string rmType = Base.Report.Current.Type;

            if (rmType == ARmReport.PM)
            {
                RMDataSource   ds   = Base.DataSourceByID.Current;
                RMDataSourcePM dsPM = Base.Caches[typeof(RMDataSource)].GetExtension <RMDataSourcePM>(ds);

                ds.AmountType          = (short?)dataSet[RMReportReaderGL.Keys.AmountType];
                dsPM.StartAccountGroup = dataSet[Keys.StartAccountGroup] as string ?? "";
                dsPM.EndAccountGroup   = dataSet[Keys.EndAccountGroup] as string ?? "";
                dsPM.StartProject      = dataSet[Keys.StartProject] as string ?? "";
                dsPM.EndProject        = dataSet[Keys.EndProject] as string ?? "";
                dsPM.StartProjectTask  = dataSet[Keys.StartProjectTask] as string ?? "";
                dsPM.EndProjectTask    = dataSet[Keys.EndProjectTask] as string ?? "";
                dsPM.StartInventory    = dataSet[Keys.StartInventory] as string ?? "";
                dsPM.EndInventory      = dataSet[Keys.EndInventory] as string ?? "";

                RMDataSourceGL dsGL = Base.Caches[typeof(RMDataSource)].GetExtension <RMDataSourceGL>(ds);
                dsGL.StartBranch           = dataSet[RMReportReaderGL.Keys.StartBranch] as string ?? "";
                dsGL.EndBranch             = dataSet[RMReportReaderGL.Keys.EndBranch] as string ?? "";
                dsGL.EndPeriod             = ((dataSet[RMReportReaderGL.Keys.EndPeriod] as string ?? "").Length > 2 ? ((dataSet[RMReportReaderGL.Keys.EndPeriod] as string ?? "").Substring(2) + "    ").Substring(0, 4) : "    ") + ((dataSet[RMReportReaderGL.Keys.EndPeriod] as string ?? "").Length > 2 ? (dataSet[RMReportReaderGL.Keys.EndPeriod] as string ?? "").Substring(0, 2) : dataSet[RMReportReaderGL.Keys.EndPeriod] as string ?? "");
                dsGL.EndPeriodOffset       = (short?)(int?)dataSet[RMReportReaderGL.Keys.EndOffset];
                dsGL.EndPeriodYearOffset   = (short?)(int?)dataSet[RMReportReaderGL.Keys.EndYearOffset];
                dsGL.StartPeriod           = ((dataSet[RMReportReaderGL.Keys.StartPeriod] as string ?? "").Length > 2 ? ((dataSet[RMReportReaderGL.Keys.StartPeriod] as string ?? "").Substring(2) + "    ").Substring(0, 4) : "    ") + ((dataSet[RMReportReaderGL.Keys.StartPeriod] as string ?? "").Length > 2 ? (dataSet[RMReportReaderGL.Keys.StartPeriod] as string ?? "").Substring(0, 2) : dataSet[RMReportReaderGL.Keys.StartPeriod] as string ?? "");
                dsGL.StartPeriodOffset     = (short?)(int?)dataSet[RMReportReaderGL.Keys.StartOffset];
                dsGL.StartPeriodYearOffset = (short?)(int?)dataSet[RMReportReaderGL.Keys.StartYearOffset];

                List <object[]> splitret = null;

                if (ds.Expand != ExpandType.Nothing)
                {
                    splitret = new List <object[]>();
                }

                if (ds.AmountType == null || ds.AmountType == 0)
                {
                    return(0m);
                }

                PMEnsureInitialized();
                EnsureHistoryLoaded(dsPM);
                NormalizeDataSource(dsPM);

                List <PMAccountGroup> accountGroups = _accountGroupsRangeCache.GetItemsInRange(dataSet[Keys.StartAccountGroup] as string,
                                                                                               group => group.GroupCD,
                                                                                               (group, code) => group.GroupCD = code);
                List <PMProject> projects = _projectsRangeCache.GetItemsInRange(dataSet[Keys.StartProject] as string,
                                                                                project => project.ContractCD,
                                                                                (project, code) => project.ContractCD = code);
                List <PMTask> tasks = _tasksRangeCache.GetItemsInRange(dataSet[Keys.StartProjectTask] as string,
                                                                       task => task.TaskCD,
                                                                       (task, code) => task.TaskCD = code);
                List <InventoryItem> items = _itemRangeCache.GetItemsInRange(dataSet[Keys.StartInventory] as string,
                                                                             item => item.InventoryCD,
                                                                             (item, code) => item.InventoryCD = code);

                if (ds.Expand == ExpandType.AccountGroup)
                {
                    accountGroups.ForEach(a => splitret.Add(new object[] { a.GroupCD, a.Description, 0m, null, string.Empty }));
                }
                else if (ds.Expand == ExpandType.Project)
                {
                    projects.ForEach(p => splitret.Add(new object[] { p.ContractCD, p.Description, 0m, null, string.Empty }));
                }
                else if (ds.Expand == ExpandType.ProjectTask)
                {
                    //To avoid useless expansion, first restrict list of tasks to those tasks which point to current projects
                    tasks = tasks.Where(t => projects.Any(p => t.ProjectID == p.ContractID)).ToList <PMTask>();
                    tasks = tasks.GroupBy(t => t.TaskCD).Select(g => new PMTask()
                    {
                        TaskCD = g.Key, TaskID = g.Min(t => t.TaskID), Description = g.Min(t => t.Description)
                    }).ToList <PMTask>();
                    tasks.ForEach(pt => splitret.Add(new object[] { pt.TaskCD, pt.Description, 0m, null, string.Empty }));
                }
                else if (ds.Expand == ExpandType.Inventory)
                {
                    items.ForEach(i => splitret.Add(new object[] { i.InventoryCD, i.Descr, 0m, null, string.Empty }));
                }

                return(CalculateAndExpandValue(drilldown, ds, dsGL, dsPM, dataSet, accountGroups, projects, tasks, items, splitret));
            }
            else
            {
                return(del(dataSet, drilldown));
            }
        }