/////////////////////////////////////////////////////////// // AppointmentList.cs // Implementation of Class AppointmentList // CSLA type: Read only collection // Created on: 06-Jan-2005 // Object design: John // Coded: 06-Jan-2005 /////////////////////////////////////////////////////////// using System; using System.Data; using GZTW.Data; using CSLA.Data; using CSLA; using System.Text.RegularExpressions; using System.Threading; using CSLA.Security; namespace GZTW.AyaNova.BLL { /// /// A list of appointments for scheduleable users in AyaNova /// This list is what populates the schedule calendar /// /// (Read only collection ) /// [Serializable] public class AppointmentList : ReadOnlyCollectionBase { #region Data structure /// /// /// [Serializable] public struct AppointmentListInfo { //used to indicate there is nothing in this //struct internal bool mIsEmpty; internal Guid mWorkorderID; internal Guid mWorkorderItemID; internal int mServiceNumber; internal RootObjectTypes mAppliesToObjectType; internal Guid mAppliesToObjectID; internal RootObjectTypes mSourceObjectType; //This is the workorderitemscheduleduser record ID internal Guid mSourceObjectID; internal string mSubject; internal string mDetails; internal DateTime mStartDateTime; internal DateTime mEndDateTime; internal bool mAllDay; //If internal bool mShowPriorityFlag; internal int mPriorityARGB; internal int mBackColorARGB; #pragma warning disable 1591 /// /// true if there is nothing in this record /// false if this record contains a valid appointment /// public bool IsEmpty {get{return mIsEmpty;}set{mIsEmpty=value;}} /// /// What object type this appointment applies to, can be single user /// or another object type that represents a group of users /// such as: region, sched user group etc etc /// public RootObjectTypes AppliesToObjectType{get{return mAppliesToObjectType;}} /// /// Object ID appointment applies to /// public Guid AppliesToObjectID{get{return mAppliesToObjectID;}} /// /// Type of appointment /// either ScheduleMarker or WorkorderItemScheduledUser /// public RootObjectTypes SourceObjectType{get{return mSourceObjectType;}} /// /// The ID of the schedulemarker or workorderitemscheduleduser object /// public Guid SourceObjectID {get{return mSourceObjectID;}} /// /// ID of workorder if this appointment is a scheduled workorder user /// public Guid WorkorderID {get{return mWorkorderID;}} /// /// ID of workorder item if this appointment is a scheduled workorder user /// public Guid WorkorderItemID {get{return mWorkorderItemID;}} /// /// Visible sequential workorder number of workorder /// if this appointment is a scheduled workorder user /// public int ServiceNumber {get{return mServiceNumber;}} /// /// Description of appointment, either a schedulemarker /// description or a summary of the workorder /// formatted according to the workorder /// public string Subject {get{return mSubject;}} public string Details {get{return mDetails;}} public DateTime StartDateTime {get{return mStartDateTime;}} public DateTime EndDateTime {get{return mEndDateTime;}} public bool AllDay {get{return mAllDay;}} public int PriorityARGB {get{return mPriorityARGB;}} public bool ShowPriorityFlag {get{return mShowPriorityFlag;}} /// /// Back color of appointment /// could be workorder status or schedmarker colour /// public int BackColorARGB {get{return mBackColorARGB;} } #pragma warning restore 1591 /// /// /// /// public bool Equals(AppointmentListInfo obj) { return this.mSourceObjectID.Equals(obj.mSourceObjectID); } }//end AppointmentListInfo #endregion #region Constructor /// /// /// protected AppointmentList() { // AllowSort=false; // AllowFind=true; // AllowEdit=false; // AllowNew=false; // AllowRemove=false; } #endregion #region Business properties and methods /// /// Get item by index /// /// public AppointmentListInfo this[int Item] { get { return (AppointmentListInfo) List[Item]; } } /// /// Returns AppointmentListInfo item that matches passed in itemid value /// /// public AppointmentListInfo this[Guid o] { get { foreach (AppointmentListInfo child in List) { if(child.mSourceObjectID==o) return child; } throw new ArgumentException("AppointmentList: SourceObjectID not found\r\n"); } } #endregion #region contains /// /// Check if item in collection /// /// public bool Contains(AppointmentListInfo obj) { foreach (AppointmentListInfo child in List) { if(child.Equals(obj)) return true; } return false; } #endregion #region Static methods /// /// Get list of objects. /// If UserID==Guid.Empty then all appointments for all /// active scheduleable users are returned. /// /// If UserID set to an Active *and* scheduleable specific user id then all workorders /// for that specific user only are returned as well as /// all for all users (since the UI has to /// determine if they are applicable or not as some are not /// for any specific user at all). /// /// Closed work orders are *not* returned by default, use GetListWithClosed for that /// behaviour. /// /// /// Start date and time /// End date and time /// A ID for a specific user's appointments /// or Guid.Empty for all user's appointments /// A read only collection of objects (ScheduleMarkers and WorkorderItemScheduledUser data) public static AppointmentList GetList(DateTime Start, DateTime End, Guid UserID) { if (Start == DateTime.MinValue || End == DateTime.MinValue) return new AppointmentList(); return (AppointmentList) DataPortal.Fetch(new Criteria(Start,End, UserID, false)); } /// /// Same as GetList, but includes closed work orders as well. /// /// /// /// /// public static AppointmentList GetListWithClosed(DateTime Start, DateTime End, Guid UserID) { if (Start == DateTime.MinValue || End == DateTime.MinValue) return new AppointmentList(); return (AppointmentList)DataPortal.Fetch(new Criteria(Start, End, UserID, true)); } #endregion #region DAL DATA ACCESS /// /// protected override void DataPortal_Fetch(object Criteria) { string strTemplate=""; //case 1705 System.Collections.Generic.List ListOfAppointmentSubjects = new System.Collections.Generic.List(); //We set global settings during login, but //a 3rd party user of this lib might not, so if the global object is not set then //we'll retrieve it here, this provides some insurance if(AyaBizUtils.GlobalSettings!=null) strTemplate=AyaBizUtils.GlobalSettings.WorkorderSummaryTemplate; else strTemplate=Global.GetItem().WorkorderSummaryTemplate; if(strTemplate==null || strTemplate=="") { strTemplate="WO#: [WorkorderService.Label.ServiceNumber]\r\n (You can customize the information displayed here\r\n in Global Settings workorder summary template)"; } Criteria crit = (Criteria)Criteria; SafeDataReader dr = null; UserListScheduleable ulist=UserListScheduleable.GetList(); try { #region appointments string q = "SELECT aWorkorderItemScheduledUser.aStartDate, aWorkorderItemScheduledUser.aStopDate, " + " aWorkorderItemScheduledUser.aUserID, " + " aWorkorderService.aServiceNumber, " + " aWorkorderItem.aWorkorderID, aWorkorderItemScheduledUser.aWorkorderItemID, " + " aWorkorderItem.aTechNotes, " + " aWorkorderStatus.AARGB AS aStatusARGB, aWorkorderItem.aWorkorderStatusID, " + " aWorkorderItem.aPriorityID, " + " aPriority.AARGB AS aPriorityARGB, aWorkorderItemScheduledUser.aID " + "AS aSCHEDITEMID, aClient.aName " + "AS aCLIENTNAME, aWorkorder.aCustomerContactName, " + " aWorkorder.aCustomerReferenceNumber, aWorkorder.aInternalReferenceNumber, aWorkorder.aOnsite, " +//case 987 " aProject.aName AS aPROJECTNAME, " + " aWorkorder.aSummary AS aWORKORDERSUMMARY, " + " aWorkorderCategory.aName AS aWORKORDERCATEGORYNAME, " + " aWorkorderService.aInvoiceNumber, aWorkorderItem.aRequestDate, " + " aWorkorderItem.aSummary AS aWORKORDERITEMSUMMARY, " + " aUnit.aSerial, aUnitModel.aName " + "AS aUNITMODELNAME, aUnitModelCategory.aName AS aUNITMODELCATEGORYNAME, " + " aWorkorderStatus.aName AS aWORKORDERITEMSTATUSNAME, " + " aDispatchZone.aName AS aDISPATCHZONENAME, " + " aVendor.aName AS aUNITMODELVENDORNAME, " + " aWorkorder.aWorkorderType FROM aWorkorderItemScheduledUser " + "inner JOIN ( aWorkorder INNER JOIN aWorkorderService " + "ON aWorkorder.aID = aWorkorderService.aWorkorderID " + "INNER JOIN aWorkorderItem ON aWorkorder.aID " + "= aWorkorderItem.aWorkorderID LEFT JOIN " + "aUnit ON aWorkorderItem.aUnitID = aUnit.aID LEFT JOIN " + "aUnitModel ON aUnit.aUnitModelID = aUnitModel.aID " + "LEFT JOIN aVendor ON aUnitModel.aVendorID = aVendor.aID " + "LEFT JOIN aUnitModelCategory ON aUnitModel.aUnitModelCategoryID " + "= aUnitModelCategory.aID LEFT " + "JOIN aWorkorderCategory ON aWorkorder.aWorkorderCategoryID " + "= aWorkorderCategory.aID LEFT JOIN aProject " + "ON aWorkorder.aProjectID = aProject.aID LEFT " + "JOIN aClient ON aWorkorder.aClientID = aClient.aID " + "LEFT JOIN aDispatchZone ON aClient.aDispatchZoneID " + "= aDispatchZone.aID LEFT JOIN aPriority ON aWorkorderItem.aPriorityID " + "= aPriority.aID LEFT JOIN " + "aWorkorderStatus ON aWorkorderItem.aWorkorderStatusID " + "= aWorkorderStatus.aID ) ON aWorkorderItem.aID " + "= aWorkorderItemScheduledUser.aWorkorderItemID " + "WHERE (aWorkorder.aWorkorderType=1) AND " + "(NOT((aWorkorderItemScheduledUser.aStopDate < @StartDateTime) or (aWorkorderItemScheduledUser.aStartDate > @EndDateTime)))"; //case 1075 //"(aWorkorderItemScheduledUser.aStartDate > @StartDateTime) AND (aWorkorderItemScheduledUser.aStartDate < @EndDateTime) "; if (crit.UserID != Guid.Empty) q = q + "AND (aWorkorderItemScheduledUser.aUserID=@UserID) "; //case 540 if (crit.IncludeClosed == false) q = q + "AND (aWorkorder.aClosed=@False) "; //Case 58 if (User.CurrentUserRegionID != Region.DefaultRegionID) { //wrong, what about clients in all regions? // q = q + " AND (aClient.aRegionID = '{" + User.CurrentUserRegionID.ToString().ToUpperInvariant() + "}') "; q = q + DBUtil.RegionAClientClause;//case 1117 } DBCommandWrapper cm = DBUtil.DB.GetSqlStringCommandWrapper(q); //Add parameters if (crit.UserID != Guid.Empty) cm.AddInParameter("@UserID", DbType.Guid, crit.UserID); if (crit.IncludeClosed == false) cm.AddInParameter("@False", DbType.Boolean, false); cm.AddInParameter("@StartDateTime", DbType.DateTime, DBUtil.ToUTC(crit.Start)); cm.AddInParameter("@EndDateTime", DbType.DateTime, DBUtil.ToUTC(crit.End)); dr=new SafeDataReader(DBUtil.DB.ExecuteReader(cm)); //First result: Workorder item scheduled users while (dr.Read()) { //screen the user to make sure they are scheduleable and active //If not, skip 'em if(dr.GetGuid("aUserID")!=Guid.Empty)//is it a user? { if(!ulist.ContainsActiveUser(dr.GetGuid("aUserID"))) continue; } AppointmentListInfo info=new AppointmentListInfo(); info.mAllDay=false; info.mIsEmpty=false; info.mAppliesToObjectType=RootObjectTypes.User; info.mAppliesToObjectID=dr.GetGuid("aUserID"); info.mSourceObjectType=RootObjectTypes.WorkorderItemScheduledUser; info.mSourceObjectID=dr.GetGuid("aSCHEDITEMID"); info.mWorkorderID=dr.GetGuid("aWorkorderID"); info.mWorkorderItemID=dr.GetGuid("aWorkorderItemID"); info.mServiceNumber=dr.GetInt32("aServiceNumber"); //Not used currently, all data goes into subject info.mDetails=""; //Copy template to subject info.mSubject=strTemplate; //case 1705 //need unique subject guaranteed for case 1705 string UniqueSubject = info.mSubject; int n = 0; while (ListOfAppointmentSubjects.Contains(UniqueSubject)) { n++; UniqueSubject = info.mSubject + " (" + n.ToString() + ")"; } if (info.mSubject != UniqueSubject) { info.mSubject = UniqueSubject; } //Match all keywords and substitute queried data for them MatchCollection matches=Regex.Matches(strTemplate,@"\[(.|\n)*?\]",RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); foreach(Match KeyMatch in matches) { switch(KeyMatch.Value) { case "[O.Client]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aCLIENTNAME")); break; case "[Workorder.Label.CustomerContactName]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aCustomerContactName")); break; case "[Workorder.Label.CustomerReferenceNumber]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aCustomerReferenceNumber")); break; case "[Workorder.Label.InternalReferenceNumber]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aInternalReferenceNumber")); break; //case 987 case "[Workorder.Label.Onsite]": info.mSubject = info.mSubject.Replace(KeyMatch.Value, dr.GetBoolean("aOnsite").ToString()); break; case "[O.Project]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aPROJECTNAME")); break; case "[Workorder.Label.Summary]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aWORKORDERSUMMARY")); break; case "[O.WorkorderCategory]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aWORKORDERCATEGORYNAME")); break; case "[WorkorderService.Label.InvoiceNumber]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aInvoiceNumber")); break; case "[WorkorderService.Label.ServiceNumber]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetInt32("aServiceNumber").ToString()); break; case "[WorkorderItem.Label.RequestDate]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,DBUtil.ToLocal(dr.GetSmartDate("aRequestDate")).ToString()); break; case "[WorkorderItem.Label.Summary]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aWORKORDERITEMSUMMARY")); break; case "[WorkorderItem.Label.TechNotes]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aTechNotes")); break; case "[O.Unit]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aSerial")); break; case "[O.UnitModel]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aUNITMODELNAME")); break; case "[O.UnitModelCategory]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aUNITMODELCATEGORYNAME")); break; case "[O.Vendor]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aUNITMODELVENDORNAME")); break; case "[O.WorkorderStatus]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aWORKORDERITEMSTATUSNAME")); break; case "[O.DispatchZone]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,dr.GetString("aDISPATCHZONENAME")); break; case "[WorkorderItemScheduledUser.Label.StartDate]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,DBUtil.ToLocal(dr.GetSmartDate("aStartDate")).ToString()); break; case "[WorkorderItemScheduledUser.Label.StopDate]": info.mSubject=info.mSubject.Replace(KeyMatch.Value,DBUtil.ToLocal(dr.GetSmartDate("aStopDate")).ToString()); break; case "[O.WorkorderItemScheduledUser]": if(dr.GetGuid("aUserID")!=Guid.Empty) info.mSubject=info.mSubject.Replace(KeyMatch.Value,ulist[dr.GetGuid("aUserID")].Name(AyaBizUtils.GlobalSettings.DefaultScheduleableUserNameDisplayFormat)); else info.mSubject=info.mSubject.Replace(KeyMatch.Value,"-?-"); break; } } info.mEndDateTime=DBUtil.ToLocal(dr.GetDateTime("aStopDate")); info.mStartDateTime=DBUtil.ToLocal(dr.GetDateTime("aStartDate")); //Added:30-Oct-2006 for wbi infra schedule seems to ignore times exactly //at midnight //if (info.mEndDateTime.Hour == 12 && info.mEndDateTime.Minute == 0 && info.mEndDateTime.Second == 0) // info.mEndDateTime.AddSeconds(-59); //if (info.mStartDateTime.Hour == 0 && info.mStartDateTime.Minute == 0 && info.mStartDateTime.Second == 0) // info.mStartDateTime.AddSeconds(59); if(dr.GetGuid("aPriorityID")!=Guid.Empty) { info.mPriorityARGB=dr.GetInt32("aPriorityARGB"); info.mShowPriorityFlag=true; } else { info.mPriorityARGB=0; info.mShowPriorityFlag=false; } //Workorder status color info.mBackColorARGB=dr.GetInt32("aStatusARGB"); // info.mBold=dr.GetBoolean("aBold"); // info.mUnderline=dr.GetBoolean("aUnderline"); InnerList.Add(info); } if(dr!=null) dr.Close(); #endregion appointments #region Schedule markers //Not filtered by user if specific user is specified which is what we want //next query DBCommandWrapper cmSM = DBUtil.DB.GetSqlStringCommandWrapper( "SELECT aScheduleMarkerSourceType, aSourceID,aID,aNotes,aName, " + "aStopDate,aStartDate, AARGB FROM aScheduleMarker " + "WHERE (NOT((aStopDate < @StartDateTime) or (aStartDate > @EndDateTime)))" //case 1075 //"WHERE aScheduleMarker.aStartDate BETWEEN @StartDateTime AND @EndDateTime" ); //Add parameters cmSM.AddInParameter("@StartDateTime",DbType.DateTime,DBUtil.ToUTC(crit.Start)); cmSM.AddInParameter("@EndDateTime",DbType.DateTime,DBUtil.ToUTC(crit.End)); dr=new SafeDataReader(DBUtil.DB.ExecuteReader(cmSM)); while (dr.Read()) { AppointmentListInfo info=new AppointmentListInfo(); info.mAllDay=false; info.mIsEmpty=false; ScheduleMarkerSourceTypes smt=(ScheduleMarkerSourceTypes)dr.GetInt16("aScheduleMarkerSourceType"); info.mAppliesToObjectID = dr.GetGuid("aSourceID"); switch(smt) { case ScheduleMarkerSourceTypes.User: info.mAppliesToObjectType=RootObjectTypes.User; //screen the user to make sure they are scheduleable and active //If not, skip 'em if(dr.GetGuid("aSourceID")!=Guid.Empty && dr.GetGuid("aSourceID")!=ScheduleMarker.ScheduleMarkerGlobalSourceID)//is it a user? { if(!ulist.ContainsActiveUser(dr.GetGuid("aSourceID"))) continue; } break; case ScheduleMarkerSourceTypes.Regional: //Case 58 //If it applies to a specific region besides default and the current user is not default region and the applies to region id is not //the current users then don't show it if ((info.mAppliesToObjectID!=Region.DefaultRegionID) && (User.CurrentUserRegionID != Region.DefaultRegionID) && info.mAppliesToObjectID != User.CurrentUserRegionID) continue; info.mAppliesToObjectType=RootObjectTypes.Region; break; case ScheduleMarkerSourceTypes.Global: info.mAppliesToObjectType=RootObjectTypes.Global; break; case ScheduleMarkerSourceTypes.DispatchZone: info.mAppliesToObjectType=RootObjectTypes.DispatchZone; break; case ScheduleMarkerSourceTypes.ScheduleableUserGroup: info.mAppliesToObjectType=RootObjectTypes.ScheduleableUserGroup; break; } info.mSourceObjectType=RootObjectTypes.ScheduleMarker; info.mSourceObjectID=dr.GetGuid("aID"); info.mDetails=dr.GetString("aNotes"); info.mSubject=dr.GetString("aName"); info.mEndDateTime=DBUtil.ToLocal(dr.GetDateTime("aStopDate")); info.mStartDateTime=DBUtil.ToLocal(dr.GetDateTime("aStartDate")); //priority doesn't apply here, but don't //leave it dangling info.mShowPriorityFlag=false; info.mPriorityARGB=0; info.mBackColorARGB=dr.GetInt32("AARGB"); InnerList.Add(info); } #endregion schedulemarkers } finally { if(dr!=null) dr.Close(); } } #endregion #region criteria /// /// Criteria for identifying existing object /// [Serializable] private class Criteria { public DateTime Start; public DateTime End; public Guid UserID; public bool IncludeClosed;//Added case 540 public Criteria(DateTime _Start, DateTime _End, Guid _UserID, bool _IncludeClosed) { Start = _Start; End = _End; UserID = _UserID; IncludeClosed = _IncludeClosed; } } #endregion }//end AppointmentList }//end namespace GZTW.AyaNova.BLL