/// <summary>
        /// Function used to Find a TreeItem with the specified id
        /// </summary>
        /// <param name="item">Item to check if has the correct id</param>
        /// <param name="data">DataRow containing the value of the group (contains id, dgrp_id, and so on).</param>
        /// <returns>true of item has the correctid</returns>
        protected bool FindItemById(UnorderedTree.TreeItem item, object data)
        {
            CmpStatus.StatusTreeItem stitem = (CmpStatus.StatusTreeItem)item.Data;
            DataRow group = (DataRow)data;
            bool    bType = Convert.ToBoolean(group["TipoAgrupacion"]);
            int     id    = Convert.ToInt32(group["Agrupacion"]);
            bool    bRet  = bType ? stitem.IdType == id : stitem.Id == id;

            return(bRet);
        }
 /// <summary>
 /// Filters the OPERATIONS DataTable
 /// </summary>
 /// <param name="dtOperations">DataTable to filter</param>
 /// <param name="titem">Item which represents the group filtered by</param>
 protected void FilterOperationsTableByGroup(DataTable dtOperations, UnorderedTree.TreeItem titem)
 {
     if (dtOperations.Columns["TMP_MANTAIN"] == null)
     {
         DataColumn dc = dtOperations.Columns.Add("TMP_MANTAIN", typeof(bool));
     }
     DoFilterOperationsTableByGroup(dtOperations, titem);
     // Remove all rows that have TMP_MANTAIN distinct of true
     foreach (DataRow dr in dtOperations.Rows)
     {
         if (dr["TMP_MANTAIN"] == DBNull.Value || Convert.ToBoolean(dr["TMP_MANTAIN"]) != true)
         {
             dr.Delete();
         }
     }
     // Remove the temporal column used...
     dtOperations.Columns.Remove("TMP_MANTAIN");
     dtOperations.AcceptChanges();
 }
        /// <summary>
        /// Computes the minimum MP.
        /// Foreach group in the table of groups:
        ///		Get all operations of the group (for article and vehicle passed)
        ///		Sums all OPE_DURATION times
        ///		Set CalculoMP of the group to the value MP - Sum(OPE_DURATION)
        /// The dtConstraints DataTable is modified (CalculoMP field is set).
        /// </summary>
        /// <param name="dtConstraints">DataTable of constraints (obtained with CmpConstraints::GetConstraints())</param>
        /// <param name="artid">ID of the article (use -1 for NULL)</param>
        /// <param name="vehid">ID of the vehicle (cannot be NULL)</param>
        /// <param name="pInDate">DateTime of the new intended operation (OPE_INIDATE or OPE_MOVDATE if passed in the message).</param>
        public void CalculateMP(DataTable dtConstraints, DateTime pInDate, int artid, string vehid)
        {
            CmpOperationsDB cmp = new CmpOperationsDB();

            _treeGroups.EvalHandler = new OTS.Framework.Collections.UnorderedTree.EvalItem(TreeItemToId);
            foreach (DataRow drGroup in dtConstraints.Rows)
            {
                // Can use Agrupación, we don't have GROUP_TYPEs at that step
                // (were all removed in FilterConstraintsTable)
                int group = Convert.ToInt32(drGroup["Agrupacion"]);
                UnorderedTree.TreeItem titem = _treeGroups.FindItem(new UnorderedTree.FindItemDelegate(FindItemById), drGroup);
                // Get an arraylist with the IDs of all descendants of the current group.
                ArrayList groupChilds = titem.MapcarDescendants();
                // Get all operations.
                int      mp    = drGroup["MP"] != DBNull.Value ? Convert.ToInt32(drGroup["MP"]) : 0;
                DateTime opend = pInDate;
                if (mp > 0)
                {
                    opend = opend.Subtract(new TimeSpan(0, mp, 0));
                }
                DataTable dtOperations = cmp.GetData(null, "OPE_ENDDATE > @OPERATIONS.OPE_ENDDATE@ AND OPE_ART_ID = @OPERATIONS.OPE_ART_ID@ AND OPE_VEHICLEID = @OPERATIONS.OPE_VEHICLEID@",
                                                     "OPE_ENDDATE DESC", new object[] { opend, artid != -1 ? (object)artid : (object)DBNull.Value, vehid });
                // Iterates throught the operations DataTable. We will exit when we found an OPERATION that:
                //	a) Were not related to the group we are processing (group variable)
                //	b) AND were not related to any of the group_childs of the group...
                int sumOpeDuration = 0;
                foreach (DataRow drOperation in dtOperations.Rows)
                {
                    int opeGroup = Convert.ToInt32(drOperation["OPE_GRP_ID"]);
                    // Check if opeGroup (group of the operation) is the group we are processing (group) or any
                    // descendant of the group we are processing..
                    if (!groupChilds.Contains(opeGroup))
                    {
                        break;                                                                  // finish the iteration over operations table
                    }
                    sumOpeDuration += Convert.ToInt32(drOperation["OPE_DURATION"]);
                }
                // At that point we have the sumOpeDuration for the current group, so store it in the CalculoMP row
                drGroup["CalculoMP"] = mp - sumOpeDuration;
            }
            // Now we have "CalculoMP" computed by all operations.
            _treeGroups.EvalHandler = null;
        }
 /// <summary>
 /// Marks with true all the rows of their OPE_ID is equal to the ID of titem.
 /// Do a recursive call to check all childs of the titem.
 /// </summary>
 /// <param name="dtOperations">DataTable to filter</param>
 /// <param name="titem">UnorderedTree::TreeItem which the group to filter by.</param>
 protected void DoFilterOperationsTableByGroup(DataTable dtOperations, UnorderedTree.TreeItem titem)
 {
     // Filter by the childs of stitem.
     if (titem.ChildsCount > 0)
     {
         foreach (object o in titem.Childs)
         {
             //FilterOperationsTableByGroup (dtOperations, (UnorderedTree.TreeItem)o);
             DoFilterOperationsTableByGroup(dtOperations, (UnorderedTree.TreeItem)o);
         }
     }
     // Filter by stitem...
     foreach (DataRow dr in dtOperations.Rows)
     {
         int grpid = Convert.ToInt32(dr["OPE_GRP_ID"]);
         if (grpid == ((CmpStatus.StatusTreeItem)titem.Data).Id)
         {
             dr["TMP_MANTAIN"] = true;
         }
     }
 }
        // That method is used to sort the list obtained by the tree... The list will be
        // sorted by the PHY_ORDER of the StatusTreeItem contained in the TreeItem objects of the
        // tree
        private int CompareStatusTreeItems(UnorderedTree.TreeItem item1, UnorderedTree.TreeItem item2)
        {
            StatusTreeItem sitem1 = (StatusTreeItem)item1.Data;
            StatusTreeItem sitem2 = (StatusTreeItem)item2.Data;

            if ((sitem1.IsUnit && sitem2.IsUnit) && (sitem1.Id == sitem2.Id))
            {
                return(0);
            }
            else if (sitem1.PhyOrder < sitem2.PhyOrder || sitem1.IsUnit)
            {
                return(-1);
            }
            else if (sitem1.PhyOrder > sitem2.PhyOrder)
            {
                return(1);
            }
            else
            {
                return(0);
            }
        }
 /// <summary>
 /// Method used to evaluate a TreeItem, and returning the id of the group contained by that item.
 /// </summary>
 /// <param name="item">Item to evaluate</param>
 /// <returns>Int32 with the Id or null if item is not a group (is Unit or Type)</returns>
 private object TreeItemToId(UnorderedTree.TreeItem item)
 {
     CmpStatus.StatusTreeItem stitem = (CmpStatus.StatusTreeItem)item.Data;
     return(stitem.IsGroup ? (object)stitem.Id : (object)null);
 }
        /// <summary>
        /// Validates the TR (tiempo de reentrada) constraints for a given table of constraints and groups
        /// That method is called after get the constraints (CmpConstraints::GetConstraints) and
        /// after filtering the constraints obtained (CmpConstraints::FilterConstraintsTable)
        /// </summary>
        /// <param name="dtConstraints">DataTable with constraints and groups (not groups_types) to process.</param>
        /// <param name="pInDate">DateTime when te operation was done (usually MOV_OPDATE of M01 message)</param>
        /// <param name="artid">ID of the ARTICLE (-1 for NULL)</param>
        /// <param name="vehicleid">ID of the VEHICLE (CANNOT be null)</param>
        /// <param name="operationId">OUT parameter with value of DATE of previous Operation (only used if return code is PreviousDate)</param>
        /// <returns>A ValidateTRReturnCodes specifying the result of validation (non valid, ok with previous date, ok)</returns>
        public ValidateTRReturnCodes ValidateTR(DataTable dtConstraints, DateTime pInDate,
                                                int artid, string vehicleid, out int operationId)
        {
            //previousDate = DateTime.MinValue;
            operationId = -1;
            CmpOperationsDB cmp = new CmpOperationsDB();

            foreach (DataRow group in dtConstraints.Rows)
            {
                int       tr    = group["TR"] != DBNull.Value ? Convert.ToInt32(group["TR"]) : 0;
                int       tc    = group["TC"] != DBNull.Value ? Convert.ToInt32(group["TC"]) : 0;
                DateTime  opend = pInDate.Subtract(new TimeSpan(0, tr, 0));
                DataTable dtOperations;
                if (artid != -1)
                {
                    dtOperations = cmp.GetAllData(null, "OPE_ENDDATE >= @OPERATIONS.OPE_ENDDATE@ "
                                                  + "AND OPE_VEHICLEID = @OPERATIONS.OPE_VEHICLEID@ "
                                                  + "AND OPE_ART_ID = @OPERATIONS.OPE_ART_ID@",
                                                  "OPE_ENDDATE DESC", new object[] { opend, vehicleid, artid });
                }
                else
                {
                    dtOperations = cmp.GetAllData(null, "OPE_ENDDATE >= @OPERATIONS.OPE_ENDDATE@ "
                                                  + "AND OPE_VEHICLEID = @OPERATIONS.OPE_VEHICLEID@",
                                                  "OPE_ENDDATE DESC", new object[] { opend, vehicleid });
                }
                if (dtOperations.Rows.Count > 0)
                {
                    // Ok.. At that point we have:
                    //		ALL operations of the current vehicle and current article for ALL zones in the TR period of time.
                    //		We must filter that table for having only the operations for the CURRENT zone and ALL HIS CHILDS.
                    //		The rest of operations are discarded at that point (but, of course, we could find them again when
                    //		processing another zone
                    UnorderedTree.TreeItem titem = _treeGroups.FindItem(new UnorderedTree.FindItemDelegate(FindItemById), group);
                    // Delete from dtOperations table ALL operations that are not associated by titem or any childs of titem.
                    FilterOperationsTableByGroup(dtOperations, titem);
                }
                if (dtOperations.Rows.Count > 0)
                {
//					return ValidateTRReturnCodes.OperationNonValid;
                    // At that point we have only the operations related with the current group (or any of his childs).
                    DataRow  operation = dtOperations.Rows[0];
                    DateTime opendtc   = Convert.ToDateTime(operation["OPE_ENDDATE"]).AddMinutes(tc);
                    if (pInDate > opendtc)
                    {
                        // Operation not valid. At the first non-valid operation we return
                        return(ValidateTRReturnCodes.OperationNonValid);
                    }
                    else                     // An ampliation of previous operation is possible
                    {
                        // Find the group of the current operation
                        int currentGroup = -1;
                        foreach (object o in _lstGroups)
                        {
                            CmpStatus.StatusTreeItem item = (CmpStatus.StatusTreeItem)((UnorderedTree.TreeItem)o).Data;
                            if (item.IsUnit)
                            {
                                continue;
                            }
                            else if (item.IsGroup)
                            {
                                currentGroup = item.Id;
                                break;
                            }
                        }

                        // Let's check if the groups are the same.
                        // If not, exit as an extension is not possible.
                        int operationGroup = Convert.ToInt32(operation["OPE_GRP_ID"]);
                        if (currentGroup != operationGroup)
                        {
                            return(ValidateTRReturnCodes.OperationNonValid);
                        }

                        operationId = Convert.ToInt32(operation["OPE_ID"]);
                        return(ValidateTRReturnCodes.Extension);
                        /////////////////////////////////////////////////////////
                        //int groupid = Convert.ToInt32 (group["Agrupacion"]);
                        //if (opeid == groupid && Convert.ToBoolean (group["PagoTC"]))
                        //{
                        //	// An ampliation is allowed.
                        //	previousDate = Convert.ToDateTime (operation["OPE_ENDDATE"]);
                        //	return ValidateTRReturnCodes.PreviousDate;
                        //}
                    }
                }
            }
            return(ValidateTRReturnCodes.OnlyOk);
        }
        /// <summary>
        /// Get the list of all parent (and parent types) of the group or unit specified by groupid
        /// </summary>
        /// <param name="groupId">ID of the group</param>
        /// <param name="isUnit">true if groupId is a ID of a UNIT instead a ID of a GROUP</param>
        /// <param name="searchPhyGroups">If true, only physical groups (DGRP_PHYORDER NOT NULL) will be searched</param>
        /// <param name="tree">Out parameter containing the tree of GROUP with groupId and ALL HIS parents</param>
        /// <returns>An ArrayList of OTS.Framework.Collection.UnorderedTree::TreeItem objects.
        /// Each item of ArrayList contains a CmpStatus::StatusTreeItem object in its Data property</returns>
        public ArrayList GetUnitTree(int groupId, bool isUnit, bool searchPhyGroups, out UnorderedTree tree)
        {
            tree = null;

            // Find the group type and physical order for the passed groupId
            int            groupDefId       = -1;
            int            groupDefPhyorder = -1;
            CmpGroupsDefDB gddb             = new CmpGroupsDefDB();
            DataTable      dtgd             = gddb.GetGroupDefByGroup(groupId);

            if (dtgd.Rows.Count > 0)
            {
                groupDefId       = Convert.ToInt32(dtgd.Rows[0]["DGRP_ID"]);
                groupDefPhyorder = Convert.ToInt32(dtgd.Rows[0]["DGRP_PHYORDER"]);
            }

            StatusTreeItem item = new StatusTreeItem(groupId, groupDefId, groupDefPhyorder, false, isUnit);

            GetUnitTree(item, null, ref tree, searchPhyGroups);
            ArrayList list = tree.ToArrayList(new UnorderedTree.OrderItemDelegate(CompareStatusTreeItems));
            // At that point we have the list the groupId and ALL his parents. Now we have to put the GROUPS_DEF
            // items in the list (such as <sectores> or <zonas>,... (the list must have at least two elements: groupId and
            // his parent)
            StatusTreeItem itemAnt = null;
            StatusTreeItem itemAct = null;

            if (list.Count > 1)
            {
                itemAnt = (StatusTreeItem)((UnorderedTree.TreeItem)list[0]).Data;
                for (int i = 1; i < list.Count; i++)
                {
                    itemAct = (StatusTreeItem)((UnorderedTree.TreeItem)list[i]).Data;
                    if (itemAnt.IdType != itemAct.IdType && !itemAnt.IsUnit)
                    {
                        // We must insert a new item AFTER itemAnt
                        // (for example list[i-1] is a Zona and list[i] is a Sector, so we have to add
                        // a <Zona> element in middle of both
                        StatusTreeItem toInsert = new StatusTreeItem(-1, itemAnt.IdType, itemAnt.PhyOrder, true, false);
                        // In order for consistency insert the StatusTreeItem in the tree. The item is inserted
                        // as an Orphan item, because there is not really a parent-child relation...
                        UnorderedTree.TreeItem titem = tree.Add(toInsert);
                        // For consistency again: Insert a UnorderedTree::TreeItem in the list
                        // instead of a StatusTreeItem. Of course tha UnorderedTree::TreeItem inserted
                        // will have only a StatusTreeItem object (in its data property)
                        list.Insert(i, titem);
                        i++;                                    // just skip the new element
                    }
                    itemAnt = (StatusTreeItem)((UnorderedTree.TreeItem)list[i]).Data;
                }
            }
            // Add the final GROUPS_DEF item (the GROUPS_DEF item corresponding to the last element)
            item = (StatusTreeItem)((UnorderedTree.TreeItem)list[list.Count - 1]).Data;
            if (item.IsGroup)                                   // item must be a group, nor a type or unit
            {
                StatusTreeItem toInsert = new StatusTreeItem(-1, item.IdType, item.PhyOrder, true, false);
                // Insert the last item in the tree AND in the list
                UnorderedTree.TreeItem titem = tree.Add(toInsert);
                list.Add(titem);
            }

            return(list);
        }