/////////////////////////////////////////////////////////// // ServiceBank.cs // Implementation of Class ServiceBank // CSLA type: Editable Root // Created on: 27-Jan-2005 // Object design: John // Coded: John 27-Jan-2005 /////////////////////////////////////////////////////////// using System; using System.Data; using CSLA.Data; using GZTW.Data; using CSLA; using System.Threading; using CSLA.Security; using System.ComponentModel; namespace GZTW.AyaNova.BLL { /// /// Service bank /// /// This is a write only object and can not /// be deleted or modified once saved to protect /// the integrity of the service bank balances. /// /// Adjustments may be made by entering them in as another entry /// not by changing existing data. /// /// This follows standard accounting practices to ensure /// balance integrity /// [Serializable] public class ServiceBank : BusinessBase { #region Attributes private bool bReadOnly; private Guid mID; private SmartDate mCreated; //private SmartDate mModified; private Guid mCreator; //private Guid mModifier; private string mDescription=""; private Guid mSourceRootObjectID; private RootObjectTypes mSourceRootObjectType; private Guid mAppliesToRootObjectID; private RootObjectTypes mAppliesToRootObjectType; private SmartDate mEffectiveDate; private decimal mIncidents; private decimal mIncidentsBalance; private decimal mCurrency; private decimal mCurrencyBalance; private decimal mHours; private decimal mHoursBalance; //A shared transaction that is set by the workorderitemlabor when reversing //a bank or internally here when a new bank entry is made from the bank itself internal IDbTransaction transaction=null; #endregion #region Constructor /// /// Private constructor to prevent direct instantiation /// private ServiceBank() { //Set to read / write initially so that properties //can be set bReadOnly=false; //New ID mID = Guid.NewGuid(); mEffectiveDate = new SmartDate(DBUtil.CurrentWorkingDateTime); //Set record history to defaults mCreated = new SmartDate(DBUtil.CurrentWorkingDateTime); //mModified=new SmartDate(); mCreator=Guid.Empty; //mModifier=Guid.Empty; mIncidents=0; mIncidentsBalance=0; mCurrency=0; mCurrencyBalance=0; mHours=0; mHoursBalance=0; //Default indicates manual entry... mSourceRootObjectID=Guid.Empty; mSourceRootObjectType=RootObjectTypes.ServiceBank; } #endregion #region Business properties /// /// Get internal id number Read only property because it's set internally, not /// externally /// public Guid ID { get { return mID; } } /// /// Get created date /// /// /// public string Created { get { return mCreated.ToString(); } } // /// // /// Get modified date // /// // /// // /// // public string Modified // { // get // { // return mModified.ToString(); // } // } /// /// Get user record ID of person who created this record /// /// /// public Guid Creator { get { return mCreator; } } // /// // /// Get user ID of person who modified this record // /// // /// // /// // public Guid Modifier // { // get // { // return mModifier; // } // } /// /// The source of the service bank entry, where it came from, object's ID /// public Guid SourceRootObjectID { get { return mSourceRootObjectID; } set { if(bReadOnly) ThrowSetError(); else { if(mSourceRootObjectID!=value) { mSourceRootObjectID = value; MarkDirty(); } } } } /// /// The source of a service bank entry's object type /// public RootObjectTypes SourceRootObjectType { get { return mSourceRootObjectType; } set { if(bReadOnly) ThrowSetError(); else { if(mSourceRootObjectType!=value) { mSourceRootObjectType = value; MarkDirty(); } } } } /// /// The ID of the object that the service bank entry applies against /// public Guid AppliesToRootObjectID { get { return mAppliesToRootObjectID; } set { if(bReadOnly) ThrowSetError(); else { if(mAppliesToRootObjectID!=value) { mAppliesToRootObjectID = value; MarkDirty(); } } } } /// /// The type of object that the service bank entry applies against /// public RootObjectTypes AppliesToRootObjectType { get { return mAppliesToRootObjectType; } set { if(bReadOnly) ThrowSetError(); else { if(mAppliesToRootObjectType!=value) { mAppliesToRootObjectType = value; MarkDirty(); } } } } /// /// Effective date this entry applies to /// public object EffectiveDate { get { return mEffectiveDate.DBValue; } set { if(bReadOnly) ThrowSetError(); else { if (!AyaBizUtils.SmartDateEquals(mEffectiveDate, value)) //Case 298 { mEffectiveDate.DBValue = value; MarkDirty(); } } } } /// /// Short text description or notes about service bank entry /// public string Description { get { return mDescription; } set { if(bReadOnly) ThrowSetError(); else { if(mDescription!=value) { mDescription = value; BrokenRules.Assert("DescriptionLength", "Error.Object.FieldLengthExceeded255,ServiceBank.Label.Description","Description",value.Length>255); MarkDirty(); } } } } /// /// Number of incidents consumed by this entry /// public decimal Incidents { get { return mIncidents; } set { if(bReadOnly) ThrowSetError(); else { if(mIncidents!=value) { mIncidents = value; MarkDirty(); } } } } /// /// Read only property showing current balance of Incidents /// public decimal IncidentsBalance { get { return mIncidentsBalance; } // set // { // if(bReadOnly) // ThrowSetError(); // else // { // if(mIncidentsBalance!=value) // { // mIncidentsBalance = value; // MarkDirty(); // // } // } // } } /// /// Amount of currency this service bank entry consumes /// public decimal Currency { get { return mCurrency; } set { if(bReadOnly) ThrowSetError(); else { if(mCurrency!=value) { mCurrency = value; MarkDirty(); } } } } /// /// Balance of currency for this item /// (read only) /// public decimal CurrencyBalance { get { return mCurrencyBalance; } // set // { // if(bReadOnly) // ThrowSetError(); // else // { // if(mCurrencyBalance!=value) // { // mCurrencyBalance = value; // MarkDirty(); // // } // } // } } /// /// Hours consumed by this service bank entry /// public decimal Hours { get { return mHours; } set { if(bReadOnly) ThrowSetError(); else { if(mHours!=value) { mHours = value; MarkDirty(); } } } } /// /// Balance of hours in service bank for this item (read only) /// public decimal HoursBalance { get { return mHoursBalance; } // set // { // if(bReadOnly) // ThrowSetError(); // else // { // if(mHoursBalance!=value) // { // mHoursBalance = value; // MarkDirty(); // // } // } // } } /// /// Throw an error when a read only user /// tries to set a property /// (this should normally never be called unless someone is using the developer api since the UI /// should prevent it from happening initially) /// private void ThrowSetError() { throw new System.Security.SecurityException ( string.Format ( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToChange"), LocalizedTextTable.GetLocalizedTextDirect("O.ServiceBank") ) ); } #endregion #region System.Object overrides /// /// /// /// public override string ToString() { return "ServiceBank" + mID.ToString(); } /// /// /// /// /// public override bool Equals(Object obj) { if ( obj == null || GetType ( ) != obj.GetType ( ) ) return false; ServiceBank c=(ServiceBank)obj; return mID==c.mID; } /// /// /// /// public override int GetHashCode() { return ("ServiceBank" + mID).GetHashCode(); } #endregion #region Searching /// /// Returns a search result object based on search terms /// for the ID specified /// /// /// /// public static SearchResult GetSearchResult(Guid ID, string[]searchTerms) { SearchResult sr=new SearchResult(); System.Text.StringBuilder sb = new System.Text.StringBuilder(); SafeDataReader dr = null; try {//AAPPLIESTOROOTOBJECTTYPE dr=DBUtil.GetReaderFromSQLString( "SELECT aCreated, aCreator, aDescription, aSourceRootObjectID, " + " aSourceRootObjectType, AAPPLIESTOROOTOBJECTID, " + " AAPPLIESTOROOTOBJECTTYPE FROM aServiceBank WHERE " + "(aID = @ID)" ,ID); if(!dr.Read()) return new SearchResult();//DBUtil.ThrowFetchError("SearchResult for ServiceBankID: " + ID.ToString()); //Get description for later adding to description string sr.Description=LocalizedTextTable.GetLocalizedTextDirect("O.ServiceBank"); sb.Append(dr.GetString("aDescription")); //Source ID points to the object to open where the entry was made from //however if it's empty then it means the entry was made from the //servicebank screen itself so we then need to use the //applies to object id instead to open the object in question sr.AncestorRootObjectID=dr.GetGuid("aSourceRootObjectID"); if(sr.AncestorRootObjectID==Guid.Empty) { //Changed: 2-Oct-2006 //incorrect column name in sql throwing exception when appearing in search results sr.AncestorRootObjectID = dr.GetGuid("AAPPLIESTOROOTOBJECTID"); //sr.AncestorRootObjectID=dr.GetGuid("AAPPLIESTOSOURCEROOTOBJECTID");//AAPPLIESTOSOURCEROOTOBJECTID sr.AncestorRootObjectType=(RootObjectTypes)dr.GetInt16("AAPPLIESTOROOTOBJECTTYPE"); } else sr.AncestorRootObjectType=(RootObjectTypes)dr.GetInt16("aSourceRootObjectType"); //No modification in service bank so //create and mod are the same sr.Created=DBUtil.ToLocal(dr.GetSmartDate("aCreated")); sr.Modified=DBUtil.ToLocal(dr.GetSmartDate("aCreated")); sr.Creator=dr.GetGuid("aCreator"); sr.Modifier=dr.GetGuid("aCreator"); } finally { if(dr!=null) dr.Close(); } //Case 58 //Is the document's ancestor within their region? if ( !AyaBizUtils.InYourRegion( ObjectRegionIDFetcher.ObjectRegion( new TypeAndID( sr.AncestorRootObjectType, sr.AncestorRootObjectID ) ) ) ) return new SearchResult();//case 58 //Security check..do they have rights to the ancestor object? if(AyaBizUtils.Right("Object." + sr.AncestorRootObjectType.ToString())<(int)SecurityLevelTypes.ReadOnly) return new SearchResult(); //Formulate results ExtractAndRank er = new ExtractAndRank(); er.Process(sb.ToString().Trim(),searchTerms); sr.Extract=er.Extract; sr.Rank=er.Ranking; return sr; } #endregion #region Static methods /// /// Get new object /// /// public static ServiceBank NewItem() { if(AyaBizUtils.Right("Object.ServiceBank")>(int)SecurityLevelTypes.ReadOnly) return new ServiceBank(); else throw new System.Security.SecurityException( string.Format( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToCreate"), LocalizedTextTable.GetLocalizedTextDirect("O.ServiceBank"))); } /// /// ServiceBank Guid public static ServiceBank GetItem(Guid _ID) { if(AyaBizUtils.Right("Object.ServiceBank")>(int)SecurityLevelTypes.NoAccess) return (ServiceBank)DataPortal.Fetch(new Criteria(_ID)); else throw new System.Security.SecurityException( string.Format( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToRetrieve"), LocalizedTextTable.GetLocalizedTextDirect("O.ServiceBank"))); } /// /// Records a reverse entry in the service bank. /// (used when a banked WorkorderItemLabor is deleted) /// internal static void ReverseItem(Guid _ID, IDbTransaction _transaction) { //Ensure there *is* a bank first if(_ID==Guid.Empty) return; ServiceBank source = ServiceBank.GetItem(_ID); ServiceBank dest=ServiceBank.NewItem(); dest.transaction=_transaction; dest.AppliesToRootObjectID=source.AppliesToRootObjectID; dest.AppliesToRootObjectType=source.AppliesToRootObjectType; dest.Currency=-source.Currency; dest.Incidents=-source.Incidents; dest.Hours=-source.Hours; dest.Description="(-"+source.Description+")"; dest.EffectiveDate=source.EffectiveDate; dest.SourceRootObjectID=source.SourceRootObjectID; dest.SourceRootObjectType=source.SourceRootObjectType; dest.Save(); } #endregion #region Shared Notification Message Processor internal static NotifyMessage GetNotificationMessage(NotifyMessageRequestData d) { //string d.Language=User.GetUserLanguage(MessageForUserID); ServiceBank s=ServiceBank.GetItem(d.RootObjectID); string sEventDescription=""; switch(d.EventType) { case (int)ServiceBankEvent.CurrencyBalanceZero: sEventDescription=LocalizedTextTable.GetLocalizedTextDirect("ServiceBank.Label.Event.CurrencyBalanceZero",d.Language); break; case (int)ServiceBankEvent.HoursBalanceZero: sEventDescription=LocalizedTextTable.GetLocalizedTextDirect("ServiceBank.Label.Event.HoursBalanceZero",d.Language); break; case (int)ServiceBankEvent.IncidentsBalanceZero: sEventDescription=LocalizedTextTable.GetLocalizedTextDirect("ServiceBank.Label.Event.IncidentsBalanceZero",d.Language); break; } //Get name of object this applies to string sBankedObjectName=EnumDescConverter.GetEnumDescription(s.AppliesToRootObjectType); switch(s.AppliesToRootObjectType) { //Changed: 4-Sept-2006 the following 3 items as they were using the notification id instead of the //actual object id which meant the names were not captured here case RootObjectTypes.Unit: sBankedObjectName+=" " + UnitNameFetcher.GetUnitNameFromUnitID(s.AppliesToRootObjectID) + ", " + LocalizedTextTable.GetLocalizedTextDirect("O.Client") + ": "+ Unit.GetOwnerNameForUnit(s.AppliesToRootObjectID); break; case RootObjectTypes.Client: sBankedObjectName+=" " + NameFetcher.GetItem("aClient","aName",s.AppliesToRootObjectID).RecordName; break; case RootObjectTypes.HeadOffice: sBankedObjectName+=" " + NameFetcher.GetItem("aHeadOffice","aName",s.AppliesToRootObjectID).RecordName; break; } string sMessage=sEventDescription; string sSubject=sEventDescription; NotifyMessage nm=null; if(d.Format==NotifyDeliveryMessageFormats.Brief) { sMessage+="-" + sBankedObjectName; if(d.MaxCharacters > 0 && sMessage.Length>d.MaxCharacters) nm=new NotifyMessage("", sMessage.Substring(0,d.MaxCharacters)); else nm=new NotifyMessage("", sMessage); } else { sSubject+=": " + sBankedObjectName; sMessage+=": " + sBankedObjectName; nm=new NotifyMessage(sSubject,sMessage); } return nm; } #endregion #region DAL DATA ACCESS #region Fetch /// /// protected override void DataPortal_Fetch(object Criteria) { //set to false to load items initially bReadOnly=false; Criteria crit = (Criteria)Criteria; SafeDataReader dr = null; try { dr=DBUtil.GetReaderFromSQLString("SELECT * FROM aServiceBank WHERE aID=@ID;",crit.ID); if(!dr.Read()) DBUtil.ThrowFetchError("ServiceBank ID: " + crit.ID.ToString()); //Standard fields mID=dr.GetGuid("aID"); mCreated=DBUtil.ToLocal(dr.GetSmartDate("aCreated")); mCreator=dr.GetGuid("aCreator"); //ServiceBank fields mSourceRootObjectID=dr.GetGuid("aSourceRootObjectID"); mSourceRootObjectType=(RootObjectTypes)dr.GetInt16("aSourceRootObjectType"); mAppliesToRootObjectID=dr.GetGuid("AAPPLIESTOROOTOBJECTID"); mAppliesToRootObjectType=(RootObjectTypes)dr.GetInt16("AAPPLIESTOROOTOBJECTTYPE"); mEffectiveDate=DBUtil.ToLocal(dr.GetSmartDate("aEffectiveDate")); mDescription=dr.GetString("aDescription"); mIncidents=dr.GetDecimal("aIncidents"); mIncidentsBalance=dr.GetDecimal("aIncidentsBalance"); mCurrency=dr.GetDecimal("aCurrency"); mCurrencyBalance=dr.GetDecimal("aCurrencyBalance"); mHours=dr.GetDecimal("aHours"); mHoursBalance=dr.GetDecimal("aHoursBalance"); } finally { if(dr!=null) dr.Close(); } MarkOld(); //this is a write once object so it's always read only if fetched bReadOnly=true; } #endregion fetch #region Update /// /// Called by DataPortal to delete/add/update data into the database /// protected override void DataPortal_Update() { //Service bank is unchangeable add only: if(!IsNew) throw new System.ApplicationException ( string.Format ( LocalizedTextTable.GetLocalizedTextDirect("Error.Object.NotChangeable"), LocalizedTextTable.GetLocalizedTextDirect("O.ServiceBank") ) ); if(IsDeleted) throw new System.ApplicationException ( string.Format ( LocalizedTextTable.GetLocalizedTextDirect("Error.Object.NotDeleteable"), LocalizedTextTable.GetLocalizedTextDirect("O.ServiceBank") ) ); //Start a transaction here and locklevel=System.Data.IsolationLevel.Serializable using (IDbConnection connection = DBUtil.DB.GetConnection()) { connection.Open(); bool bCommit=false; if(transaction==null) { transaction = connection.BeginTransaction(System.Data.IsolationLevel.Serializable); bCommit=true; } SafeDataReader dr = null; try { //Get old balance //Firstly get the balances so they can be updated and carried forward: decimal dOldIncidentsBalance=0; decimal dOldCurrencyBalance=0; decimal dOldHoursBalance=0; decimal dNewIncidentsBalance=0; decimal dNewCurrencyBalance=0; decimal dNewHoursBalance=0; DBCommandWrapper cmGetBalance = DBUtil.DB.GetSqlStringCommandWrapper( "SELECT TOP 1 aIncidentsBalance, aCurrencyBalance, aHoursBalance " + "FROM aServiceBank WHERE (AAPPLIESTOROOTOBJECTID " + "= @ID) ORDER BY " + "aCreated DESC" ); cmGetBalance.AddInParameter("@ID",DbType.Guid,this.AppliesToRootObjectID); dr= new SafeDataReader(DBUtil.DB.ExecuteReader(cmGetBalance,transaction)); //There may not be an existing record yet so if it can't read //assumes that there are no prior bank records for this object yet if(dr.Read()) { dOldIncidentsBalance=dr.GetDecimal("aIncidentsBalance"); dOldCurrencyBalance=dr.GetDecimal("aCurrencyBalance"); dOldHoursBalance=dr.GetDecimal("aHoursBalance"); } dr.Close(); //Adjust balances dNewIncidentsBalance=dOldIncidentsBalance+this.mIncidents; dNewCurrencyBalance=dOldCurrencyBalance+this.mCurrency; dNewHoursBalance=dOldHoursBalance+this.mHours; //Update record DBCommandWrapper cm=DBUtil.GetCommandFromSQL( "INSERT INTO aServiceBank (aID, aDescription, aSourceRootObjectID, " + "aSourceRootObjectType, aEffectiveDate, " + "aIncidents, aIncidentsBalance, aCurrency, " + "aCurrencyBalance, aHours, aHoursBalance, AAPPLIESTOROOTOBJECTID, " + "AAPPLIESTOROOTOBJECTTYPE, aCreated,aCreator) " + "VALUES (@ID,@Description,@SourceRootObjectID,@SourceRootObjectType, " + "@EffectiveDate, @Incidents,@NewIncidentsBalance, " + "@Currency,@NewCurrencyBalance, " + "@Hours,@NewHoursBalance, @AppliesToRootObjectID,@AppliesToRootObjectType, " + "@Created, @CurrentUserID)"); cm.AddInParameter("@ID",DbType.Guid,mID); cm.AddInParameter("@SourceRootObjectID",DbType.Guid,mSourceRootObjectID); cm.AddInParameter("@SourceRootObjectType",DbType.Int16,(int)mSourceRootObjectType); cm.AddInParameter("@AppliesToRootObjectID",DbType.Guid,mAppliesToRootObjectID); cm.AddInParameter("@AppliesToRootObjectType",DbType.Int16,(int)mAppliesToRootObjectType); cm.AddInParameter("@EffectiveDate",DbType.DateTime,DBUtil.ToUTC(mEffectiveDate).DBValue); cm.AddInParameter("@Description",DbType.String,mDescription); cm.AddInParameter("@Incidents",DbType.Decimal,mIncidents); cm.AddInParameter("@Currency",DbType.Decimal,mCurrency); cm.AddInParameter("@Hours",DbType.Decimal,mHours); cm.AddInParameter("@NewIncidentsBalance",DbType.Decimal,dNewIncidentsBalance); cm.AddInParameter("@NewCurrencyBalance",DbType.Decimal,dNewCurrencyBalance); cm.AddInParameter("@NewHoursBalance",DbType.Decimal,dNewHoursBalance); //Standard fields cm.AddInParameter("@CurrentUserID",DbType.Guid, CurrentUserID); cm.AddInParameter("@Created",DbType.DateTime, DBUtil.ToUTC(mCreated).DBValue); DBUtil.DB.ExecuteNonQuery(cm,transaction); DBUtil.ProcessKeywords(transaction,this.mID,RootObjectTypes.ServiceBank,IsNew,AyaBizUtils.Break(false, mDescription)); MarkOld();//db is now synched with object // Commit the transaction if(bCommit) transaction.Commit(); #region Notify if (AyaBizUtils.GlobalSettings.UseNotification)//Case 510 { //Process incident balance depleted event if(dOldIncidentsBalance>0 && dNewIncidentsBalance<=0) { NotifyEvent.AddOrUpdateEvent(RootObjectTypes.ServiceBank,this.mID,(int)ServiceBankEvent.IncidentsBalanceZero,Guid.Empty,new SmartDate(),Guid.Empty); } //Process Currency balance depleted event if(dOldCurrencyBalance>0 && dNewCurrencyBalance<=0) { NotifyEvent.AddOrUpdateEvent(RootObjectTypes.ServiceBank,this.mID,(int)ServiceBankEvent.CurrencyBalanceZero,Guid.Empty,new SmartDate(),Guid.Empty); } //Process Hours balance depleted event if(dOldHoursBalance > 0 && dNewHoursBalance<=0) { NotifyEvent.AddOrUpdateEvent(RootObjectTypes.ServiceBank,this.mID,(int)ServiceBankEvent.HoursBalanceZero,Guid.Empty,new SmartDate(),Guid.Empty); } } #endregion } catch { // Rollback transaction if(bCommit) transaction.Rollback(); throw; } finally { if(dr!=null) dr.Close(); connection.Close(); } }//end of using block } #endregion update #endregion #region Override IsValid / IsDirty //Override base class version if there are child objects /* public override bool IsValid { get { return base.IsValid && ChildItem.IsValid; } } public override bool IsDirty { get { return base.IsDirty || ChildItem.IsDirty; } } */ #endregion #region criteria /// /// Criteria for identifying existing object /// [Serializable] private class Criteria { public Guid ID; public Criteria(Guid _ID) { ID=_ID; } } #endregion }//end ServiceBank #region Notification events #pragma warning disable 1591 public enum ServiceBankEvent : int { //Only fires event when balance goes from non-zero to zero or less //that way it only applies to items that are using that type of bank in the //first place [Description("LT:ServiceBank.Label.Event.IncidentsBalanceZero")] IncidentsBalanceZero=1, [Description("LT:ServiceBank.Label.Event.HoursBalanceZero")] HoursBalanceZero=2, [Description("LT:ServiceBank.Label.Event.CurrencyBalanceZero")] CurrencyBalanceZero=3 } #endregion #pragma warning restore 1591 }//end namespace GZTW.AyaNova.BLL