From f00c081cdae10fb1c9215df54043850d859be6bd Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Thu, 29 Jul 2021 17:16:50 +0000 Subject: [PATCH] --- server/AyaNova/biz/PMBiz.cs | 1147 ++++++++++++++++++------------- server/AyaNova/models/PM.cs | 12 +- server/AyaNova/util/AySchema.cs | 4 +- 3 files changed, 668 insertions(+), 495 deletions(-) diff --git a/server/AyaNova/biz/PMBiz.cs b/server/AyaNova/biz/PMBiz.cs index 499e1236..24bc3820 100644 --- a/server/AyaNova/biz/PMBiz.cs +++ b/server/AyaNova/biz/PMBiz.cs @@ -13,489 +13,6 @@ using System.Collections.Generic; namespace AyaNova.Biz { - #region v7 code for Generate service workorder from PM - /* /// - /// Generates a service workorder from a PM type Workorder - /// - /// ID of PM - /// A new service workorder - public static Workorder NewServiceWorkorderFromPM(Guid SourceWorkorderID) - { - //Fetch the source workorder and verify it's a PM - Workorder source = Workorder.GetItemNoMRU(SourceWorkorderID); - if (source.WorkorderType != WorkorderTypes.PreventiveMaintenance) - throw new NotSupportedException(LocalizedTextTable.GetLocalizedTextDirect("Workorder.Label.Error.SourceInvalidType")); - - //if it's inactive then there is nothing to Process - //this is a backstop, the list the pm is being generated off of - //should have already selected only active items that have not reached their - //expiry date - if (source.WorkorderPreventiveMaintenance.Active == false) - { - throw new System.ApplicationException("NewServiceWorkorderFromPM: source PM workorder is not active"); - } - - if (source.WorkorderPreventiveMaintenance.StopGeneratingDate != System.DBNull.Value && - source.WorkorderPreventiveMaintenance.dtStopGeneratingDate < DBUtil.CurrentWorkingDateTime) - { - throw new System.ApplicationException("NewServiceWorkorderFromPM: source PM workorder is past StopGeneratingDate"); - } - - //Ok, so far so good, create the new one - bool bUseInventory = AyaBizUtils.GlobalSettings.UseInventory; - - //case 1387 - Workorder dest = Workorder.NewItem(WorkorderTypes.Service); - - ////NOTE: THIS DOESN'T CALL THE SHARED NEW ITEM METHOD - //Workorder dest = new Workorder(); - //dest.WorkorderType=WorkorderTypes.Service; - //dest.mService=WorkorderService.NewItem(dest); - - #region copy workorder data - - //WORKORDER HEADER - dest.ClientID = source.ClientID; - dest.CustomerContactName = source.CustomerContactName; - dest.CustomerReferenceNumber = source.CustomerReferenceNumber; - dest.mFromPMID = source.WorkorderPreventiveMaintenance.ID; - dest.InternalReferenceNumber = source.InternalReferenceNumber; - dest.Onsite = source.Onsite; - dest.ProjectID = source.ProjectID; - //dest.RegionID=source.RegionID; - dest.Summary = source.Summary; - dest.WorkorderCategoryID = source.WorkorderCategoryID; - - //PM SPECIFIC - dest.WorkorderService.WorkorderStatusID = source.WorkorderPreventiveMaintenance.WorkorderStatusID; - //Date stuff (note that date is assumed to have been advanced the last time a workorder was - //generated off the pm (see bottom of this method for that)) - dest.WorkorderService.ServiceDate = source.WorkorderPreventiveMaintenance.NextServiceDate; - - - //WORKORDERITEMS - foreach (WorkorderItem wisource in source.WorkorderItems) - { - WorkorderItem widest = dest.WorkorderItems.Add(dest); - widest.Custom0 = wisource.Custom0; - widest.Custom1 = wisource.Custom1; - widest.Custom2 = wisource.Custom2; - widest.Custom3 = wisource.Custom3; - widest.Custom4 = wisource.Custom4; - widest.Custom5 = wisource.Custom5; - widest.Custom6 = wisource.Custom6; - widest.Custom7 = wisource.Custom7; - widest.Custom8 = wisource.Custom8; - widest.Custom9 = wisource.Custom9; - widest.PriorityID = wisource.PriorityID; - widest.RequestDate = wisource.RequestDate; - widest.Summary = wisource.Summary; - widest.TechNotes = wisource.TechNotes; - widest.TypeID = wisource.TypeID; - widest.UnitID = wisource.UnitID; - widest.WarrantyService = wisource.WarrantyService; - widest.WorkorderItemUnitServiceTypeID = wisource.WorkorderItemUnitServiceTypeID; - widest.WorkorderStatusID = wisource.WorkorderStatusID; - - //PARTS - foreach (WorkorderItemPart partsource in wisource.Parts) - { - WorkorderItemPart partdest = widest.Parts.Add(widest); - partdest.Cost = partsource.Cost; - partdest.Description = partsource.Description; - partdest.Discount = partsource.Discount; - partdest.DiscountType = partsource.DiscountType; - partdest.PartID = partsource.PartID; - partdest.PartWarehouseID = partsource.PartWarehouseID; - partdest.Price = partsource.Price; - if (bUseInventory) - { - partdest.QuantityReserved = partsource.Quantity; - partdest.Quantity = 0; - } - else - partdest.Quantity = partsource.Quantity; - partdest.TaxPartSaleID = partsource.TaxPartSaleID; - - - } - - //********************************************************** - //Part requests would be here if copying a service workorder - //********************************************************** - - //SCHEDULED USERS - foreach (WorkorderItemScheduledUser usersource in wisource.ScheduledUsers) - { - WorkorderItemScheduledUser userdest = widest.ScheduledUsers.Add(widest); - userdest.EstimatedQuantity = usersource.EstimatedQuantity; - userdest.ServiceRateID = usersource.ServiceRateID; - userdest.StartDate = usersource.StartDate; - userdest.StopDate = usersource.StopDate; - userdest.UserID = usersource.UserID; - - } - - //LABOR - foreach (WorkorderItemLabor laborsource in wisource.Labors) - { - WorkorderItemLabor labordest = widest.Labors.Add(widest); - labordest.NoChargeQuantity = laborsource.NoChargeQuantity; - labordest.ServiceDetails = laborsource.ServiceDetails; - labordest.ServiceRateID = laborsource.ServiceRateID; - labordest.ServiceRateQuantity = laborsource.ServiceRateQuantity; - labordest.ServiceStartDate = laborsource.ServiceStartDate; - labordest.ServiceStopDate = laborsource.ServiceStopDate; - labordest.TaxRateSaleID = laborsource.TaxRateSaleID; - labordest.UserID = laborsource.UserID; - - } - - //********************************************************** - //Expenses would be here if copying a service workorder - //********************************************************** - - - //********************************************************** - //Loans would be here if copying a service workorder - //********************************************************** - - //TRAVEL - foreach (WorkorderItemTravel travelsource in wisource.Travels) - { - WorkorderItemTravel traveldest = widest.Travels.Add(widest); - traveldest.TravelDetails = travelsource.TravelDetails; - traveldest.TravelRateID = travelsource.TravelRateID; - traveldest.TravelRateQuantity = travelsource.TravelRateQuantity; - traveldest.TravelStartDate = travelsource.TravelStartDate; - traveldest.TravelStopDate = travelsource.TravelStopDate; - traveldest.TaxRateSaleID = travelsource.TaxRateSaleID; - traveldest.UserID = travelsource.UserID; - traveldest.Distance = travelsource.Distance; - traveldest.Notes = travelsource.Notes; - traveldest.NoChargeQuantity = travelsource.NoChargeQuantity; - } - - - - //TASKS - foreach (WorkorderItemTask tasksource in wisource.Tasks) - { - WorkorderItemTask taskdest = widest.Tasks.Add(widest); - taskdest.TaskGroupID = tasksource.TaskGroupID; - taskdest.TaskID = tasksource.TaskID; - - } - - //********************************************************** - //Outside service would be here if copying a service workorder - //********************************************************** - - }//foreach workorderitem loop - - //case 1387 - - //Delete the auto-created dummy workorder item - //if there are more than it present - if (dest.WorkorderItems.Count > 1) - dest.WorkorderItems.RemoveAt(0); - - #endregion copy workorder data - - //Now save it to ensure it was created properly so - //that we know it's now safe to advance the next service date and all others - - //case 868 previously didn't set dest to result of save causing it to be a copy - dest = (Workorder)dest.Save(); - - - - - #region Calculate reschedule dates - //Get the current next service date for calcs - DateTime dtNext = GetDateFromSpanAndUnit(source.WorkorderPreventiveMaintenance.dtNextServiceDate, - source.WorkorderPreventiveMaintenance.GenerateSpanUnit, - source.WorkorderPreventiveMaintenance.GenerateSpan); - - //Get to the desired day of the week if necessary... - if (source.mWorkorderPreventiveMaintenance.DayOfTheWeek != AyaDayOfWeek.AnyDayOfWeek) - { - DayOfWeek desired = AyaToSystemDayOfWeek(source.mWorkorderPreventiveMaintenance.DayOfTheWeek); - while (dtNext.DayOfWeek != desired) - { - dtNext = dtNext.AddDays(1); - } - - } - - //Get the time span to add to all the other relevant dates on teh workorder to match - //the amount the next service date has been advanced - System.TimeSpan tsToNext = dtNext - source.WorkorderPreventiveMaintenance.dtNextServiceDate; - - - - - - #endregion - - //Will the next workorder service date fall after the - //stop generating date? - if (source.WorkorderPreventiveMaintenance.StopGeneratingDate != System.DBNull.Value && - source.WorkorderPreventiveMaintenance.dtStopGeneratingDate < dtNext) - { - //Yes it will, so set it to inactive and bail out - source.WorkorderPreventiveMaintenance.Active = false; - source.Save(); - return dest; - } - - #region Reschedule PM - - source.WorkorderPreventiveMaintenance.dtNextServiceDate = dtNext; - //Calcs the generate date (threshold date) - source.WorkorderPreventiveMaintenance.SetGenerateDate(); - //WORKORDERITEMS - foreach (WorkorderItem wisource in source.WorkorderItems) - { - - wisource.RequestDate = wisource.RequestDate; - - - //PARTS - //no date changes required - - - //SCHEDULED USERS - foreach (WorkorderItemScheduledUser usersource in wisource.ScheduledUsers) - { - //Changed: 2-Oct-2006 - //check to not add a date if the original date was empty - if (usersource.StartDate != System.DBNull.Value) - usersource.dtStartDate = usersource.dtStartDate.Add(tsToNext); - - if (usersource.StopDate != System.DBNull.Value) - usersource.dtStopDate = usersource.dtStopDate.Add(tsToNext); - - - } - - //LABOR - foreach (WorkorderItemLabor laborsource in wisource.Labors) - { - //Changed: 2-Oct-2006 - //check to not add a date if the original date was empty - if (laborsource.ServiceStartDate != System.DBNull.Value) - laborsource.dtServiceStartDate = laborsource.dtServiceStartDate.Add(tsToNext); - - if (laborsource.ServiceStopDate != System.DBNull.Value) - laborsource.dtServiceStopDate = laborsource.dtServiceStopDate.Add(tsToNext); - - } - - //********************************************************** - //Expenses would be here if copying a service workorder - //********************************************************** - - - //********************************************************** - //Loans would be here if copying a service workorder - //********************************************************** - - //TRAVEL - foreach (WorkorderItemTravel travelsource in wisource.Travels) - { - //Changed: 2-Oct-2006 - //check to not add a date if the original date was empty - if (travelsource.TravelStartDate != DBNull.Value) - travelsource.dtTravelStartDate = travelsource.dtTravelStartDate.Add(tsToNext); - - if (travelsource.TravelStopDate != DBNull.Value) - travelsource.dtTravelStopDate = travelsource.dtTravelStopDate.Add(tsToNext); - - } - - - - //TASKS - - - //********************************************************** - //Outside service would be here if copying a service workorder - //********************************************************** - - }//foreach workorderitem loop - #endregion reschedule pm - - //Ok, Source PM is now rescheduled, save it - - - - //case 1959 try catch block added to prevent infinite generation issue - try - { - source = (Workorder)source.Save(); - } - catch (Exception exx) - { - dest.Delete(); - dest.Save(); - //crack the exception - while (exx.InnerException != null) - exx = exx.InnerException; - - Memo mwarn = Memo.NewItem(); - mwarn.ToID = User.AdministratorID; - - //case 3826 - if (User.CurrentUserType == UserTypes.Utility) - { - //Utility accounts should not be sending memos, it fucks up downstream - //trying to view the memo, also it's confusing - mwarn.FromID = User.AdministratorID; - } - else - { - mwarn.FromID = User.CurrentThreadUserID; - } - - - mwarn.Subject = "SYSTEM WARNING: Preventive Maintenance WO PROBLEM"; - StringBuilder sb = new StringBuilder(); - sb.AppendLine("This is an automated message sent on behalf of the current user from the \"NewServiceWorkorderFromPM\" module."); - sb.AppendLine("This message concerns Preventive Maintenance workorder number " + source.WorkorderPreventiveMaintenance.PreventiveMaintenanceNumber.ToString()); - sb.AppendLine("The Preventive Maintenance workorder had an error when trying to save it during generation of a service workorder."); - sb.AppendLine("This kind of problem could result in loop which generates a very large number of identical service workorders."); - sb.AppendLine("In order to prevent this the operation has been stopped and this message generated so you can fix the problem with the source PM workorder."); - sb.AppendLine("See below for details and examine the PM workorder for problems or contact support@ayanova.com for help with the information in this message."); - sb.AppendLine("Here are the details of the error preventing save:"); - sb.AppendLine("================================="); - sb.AppendLine("Exception saving source PM:"); - sb.AppendLine(exx.Message); - sb.AppendLine("================================="); - string sSourceErr = source.GetBrokenRulesString(); - if (!string.IsNullOrWhiteSpace(sSourceErr)) - { - sb.AppendLine("Broken business rules on PM object:"); - sb.AppendLine(sSourceErr); - sb.AppendLine("=============================="); - } - mwarn.Message = sb.ToString(); - mwarn.Save(); - throw new System.ApplicationException("Workorder->NewServiceWorkorderFromPM: Error during service workorder generation. Memo with details sent to Administrator account."); - - - - } - - //case 1630 - //copy wikipage from pm to service workorder - if (dest.CanWiki && source.HasWiki) - { - try - { - WikiPage wpSource = WikiPage.GetItem(new TypeAndID(RootObjectTypes.WorkorderPreventiveMaintenance, source.ID)); - WikiPage wpDest = WikiPage.GetItem(new TypeAndID(RootObjectTypes.WorkorderService, dest.ID)); - wpDest.SetContent(wpSource.GetContent()); - wpDest.Save(); - } - catch { }; - - } - - return dest; - - - - - - } - - - - /// - /// Calculate generate date based on service date and - /// threshold span and unit - /// - internal void SetGenerateDate() - { - if (this.mNextServiceDate.IsEmpty) return; - if (this.mThresholdSpan == 0) - { - this.mGenerateDate = this.mNextServiceDate; - MarkDirty(); - return; - } - mGenerateDate = new SmartDate(Workorder.GetDateFromSpanAndUnit(mNextServiceDate.Date, this.mThresholdSpanUnit, -mThresholdSpan)); - MarkDirty(); - } - - - #region Date time calcs helpers - //Takes an AyaNova day of week and returns - //a System.DayOfWeek - //Assumes that AyaDayOfWeek is NOT "AnyDay" - internal static System.DayOfWeek AyaToSystemDayOfWeek(AyaDayOfWeek day) - { - switch (day) - { - case AyaDayOfWeek.Monday: - return DayOfWeek.Monday; - case AyaDayOfWeek.Tuesday: - return DayOfWeek.Tuesday; - case AyaDayOfWeek.Wednesday: - return DayOfWeek.Wednesday; - case AyaDayOfWeek.Thursday: - return DayOfWeek.Thursday; - case AyaDayOfWeek.Friday: - return DayOfWeek.Friday; - case AyaDayOfWeek.Saturday: - return DayOfWeek.Saturday; - case AyaDayOfWeek.Sunday: - return DayOfWeek.Sunday; - - - - } - - throw new System.ArgumentOutOfRangeException("DayOfWeekConverter: AyaDayOfWeek.AnyDayOfWeek is not supported"); - } - - - internal static DateTime GetDateFromSpanAndUnit(DateTime StartDate, AyaUnitsOfTime unit, int multiple) - { - switch (unit) - { - case AyaUnitsOfTime.Seconds: - return StartDate.AddSeconds(multiple); - - case AyaUnitsOfTime.Minutes: - return StartDate.AddMinutes(multiple); - - case AyaUnitsOfTime.Hours: - return StartDate.AddHours(multiple); - - case AyaUnitsOfTime.Days: - return StartDate.AddDays(multiple); - - case AyaUnitsOfTime.Weeks: - throw new System.NotSupportedException("GetDateFromSpanAndUnit: Weeks not supported"); - - case AyaUnitsOfTime.Months: - return StartDate.AddMonths(multiple); - - case AyaUnitsOfTime.Years: - return StartDate.AddYears(multiple); - - - } - - //fail safe: - return StartDate; - } - - */ - #endregion gen service wo from pm - internal class PMBiz : BizObject, IJobObject, ISearchAbleObject, IReportAbleObject, IExportAbleObject { internal PMBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles, UserType currentUserType) @@ -903,6 +420,75 @@ namespace AyaNova.Biz } } + /// + /// Calculate generate date based on service date and + /// generate before span and unit + /// + internal static void SetGenerateDate(PM p) + { + + if (p.GenerateBeforeInterval == 0) + { + p.GenerateDate = p.NextServiceDate; + return; + } + p.GenerateDate = GetDateFromSpanAndUnit(p.NextServiceDate, p.GenerateBeforeUnit, -System.Math.Abs(p.GenerateBeforeInterval)); + + } + + // //Takes an AyaNova day of week and returns + // //a System.DayOfWeek + // //Assumes that AyaDayOfWeek is NOT "AnyDay" + // internal static System.DayOfWeek AyaToSystemDayOfWeek(AyaDayOfWeek day) + // { + // switch (day) + // { + // case AyaDayOfWeek.Monday: + // return DayOfWeek.Monday; + // case AyaDayOfWeek.Tuesday: + // return DayOfWeek.Tuesday; + // case AyaDayOfWeek.Wednesday: + // return DayOfWeek.Wednesday; + // case AyaDayOfWeek.Thursday: + // return DayOfWeek.Thursday; + // case AyaDayOfWeek.Friday: + // return DayOfWeek.Friday; + // case AyaDayOfWeek.Saturday: + // return DayOfWeek.Saturday; + // case AyaDayOfWeek.Sunday: + // return DayOfWeek.Sunday; + + + + // } + + // throw new System.ArgumentOutOfRangeException("DayOfWeekConverter: AyaDayOfWeek.AnyDayOfWeek is not supported"); + // } + + + internal static DateTime GetDateFromSpanAndUnit(DateTime StartDate, PMTimeUnit unit, int multiple) + { + switch (unit) + { + case PMTimeUnit.Minutes: + return StartDate.AddMinutes(multiple); + + case PMTimeUnit.Hours: + return StartDate.AddHours(multiple); + + case PMTimeUnit.Days: + return StartDate.AddDays(multiple); + + case PMTimeUnit.Months: + return StartDate.AddMonths(multiple); + + case PMTimeUnit.Years: + return StartDate.AddYears(multiple); + } + //default + return StartDate; + } + private async Task AutoSetAddressAsync(PM newObj) { if (newObj.CustomerId == 0) @@ -5644,21 +5230,600 @@ namespace AyaNova.Biz #if (DEBUG) log.LogInformation("PMBiz - Generating"); #endif - //Get a list of items + //Get a list of PM id's ready for conversion now + var l = await ct.PM.AsNoTracking() + .Where(z => z.GenerateDate > DateTime.UtcNow && (z.StopGeneratingDate == null || z.StopGeneratingDate > DateTime.UtcNow) && z.Active == true) + .Select(z => z.Id) + .ToListAsync(); +#if (DEBUG) + log.LogInformation($"PMBiz - Found {l.Count} ready to generate PM items"); +#endif //process those items - //make new workorder - //fixup dates and update pm + foreach (long pmid in l) + { +#if (DEBUG) + log.LogInformation($"PMBiz - processing pm id {pmid}"); +#endif + //make new workorder + //fixup dates and update pm + } + + + - //dummy query to turn off errors - var v = await ct.PM.AsNoTracking() - .Where(z => z.Serial == 1) - .Select(z => z.Id) - .SingleOrDefaultAsync(); } #endregion + + #region v7 code for Generate service workorder from PM + /* + + /// + /// Loop through PM workorders ready for generation and + /// Process them / advance dates + /// + public static void GeneratePMWorkorders() + { + WorkorderPMReadyForServiceList l=WorkorderPMReadyForServiceList.GetList(); + foreach(WorkorderPMReadyForServiceList.WorkorderPMReadyForServiceListInfo i in l) + { + Workorder.NewServiceWorkorderFromPM(i.PMWorkorderID); + } + + } + + + protected override void DataPortal_Fetch(object Criteria) + { + SafeDataReader dr = null; + try + { + + //Changed: 26-April-2006 this query was missing the last bit + //that filtered out wo's with an expired stop generating date + //which in turn was throwing an exception in the Workorder.NewServiceWorkorderFromPM + //which was written to assume that this query was filtering them out and threw + //an exception as a "backstop" in case someone was calling the method from an api + + //Changed: 30-Aug-2006 added further check for stop generating date being null + //as it is when there is nothing selected + DBCommandWrapper cm = DBUtil.DB.GetSqlStringCommandWrapper( + //************************************************************ + //"SELECT aWorkorderID FROM aWorkorderPreventiveMaintenance " + + //"WHERE (AACTIVE = @aTrue) AND (aGenerateDate < @aNow) " + + //"AND (aStopGeneratingDate IS NULL OR aStopGeneratingDate > @aNow) " + + //CASE 789 ignore pm templates + "SELECT AWORKORDERID, ACLIENTID from AWORKORDERPREVENTIVEMAINTENANCE " + + "LEFT OUTER JOIN AWORKORDER " + + "ON (AWORKORDERPREVENTIVEMAINTENANCE.AWORKORDERID=AWORKORDER.AID) " + + "WHERE AWORKORDER.AWORKORDERTYPE='2' " + + "AND (AACTIVE = @aTrue) AND (aGenerateDate < @aNow) " + + "AND (aStopGeneratingDate IS NULL OR aStopGeneratingDate > @aNow) " + + + //************************************************************ + ); + cm.AddInParameter("@aTrue",DbType.Boolean,true); + cm.AddInParameter("@aNow", DbType.DateTime, DBUtil.ToUTC(DBUtil.CurrentWorkingDateTime));//case 957 + dr=new SafeDataReader(DBUtil.DB.ExecuteReader(cm)); + + while(dr.Read()) + { + //******************************************* + //case 3701 - check if client for this pm is active or not and only process for an active client + if (ClientActiveChecker.ClientActive(dr.GetGuid("ACLIENTID"))) + { + WorkorderPMReadyForServiceListInfo info = new WorkorderPMReadyForServiceListInfo(); + info._PMWorkorderID = dr.GetGuid("aWorkorderID"); + InnerList.Add(info); + } + //******************************************* + } + } + catch + { + throw; + } + finally + { + if(dr!=null) dr.Close(); + } + + + + + } + + #endregion + + + + + /// + /// Generates a service workorder from a PM type Workorder + /// + /// ID of PM + /// A new service workorder + public static Workorder NewServiceWorkorderFromPM(Guid SourceWorkorderID) + { + //Fetch the source workorder and verify it's a PM + Workorder source = Workorder.GetItemNoMRU(SourceWorkorderID); + if (source.WorkorderType != WorkorderTypes.PreventiveMaintenance) + throw new NotSupportedException(LocalizedTextTable.GetLocalizedTextDirect("Workorder.Label.Error.SourceInvalidType")); + + //if it's inactive then there is nothing to Process + //this is a backstop, the list the pm is being generated off of + //should have already selected only active items that have not reached their + //expiry date + if (source.WorkorderPreventiveMaintenance.Active == false) + { + throw new System.ApplicationException("NewServiceWorkorderFromPM: source PM workorder is not active"); + } + + if (source.WorkorderPreventiveMaintenance.StopGeneratingDate != System.DBNull.Value && + source.WorkorderPreventiveMaintenance.dtStopGeneratingDate < DBUtil.CurrentWorkingDateTime) + { + throw new System.ApplicationException("NewServiceWorkorderFromPM: source PM workorder is past StopGeneratingDate"); + } + + //Ok, so far so good, create the new one + bool bUseInventory = AyaBizUtils.GlobalSettings.UseInventory; + + //case 1387 + Workorder dest = Workorder.NewItem(WorkorderTypes.Service); + + ////NOTE: THIS DOESN'T CALL THE SHARED NEW ITEM METHOD + //Workorder dest = new Workorder(); + //dest.WorkorderType=WorkorderTypes.Service; + //dest.mService=WorkorderService.NewItem(dest); + + #region copy workorder data + + //WORKORDER HEADER + dest.ClientID = source.ClientID; + dest.CustomerContactName = source.CustomerContactName; + dest.CustomerReferenceNumber = source.CustomerReferenceNumber; + dest.mFromPMID = source.WorkorderPreventiveMaintenance.ID; + dest.InternalReferenceNumber = source.InternalReferenceNumber; + dest.Onsite = source.Onsite; + dest.ProjectID = source.ProjectID; + //dest.RegionID=source.RegionID; + dest.Summary = source.Summary; + dest.WorkorderCategoryID = source.WorkorderCategoryID; + + //PM SPECIFIC + dest.WorkorderService.WorkorderStatusID = source.WorkorderPreventiveMaintenance.WorkorderStatusID; + //Date stuff (note that date is assumed to have been advanced the last time a workorder was + //generated off the pm (see bottom of this method for that)) + dest.WorkorderService.ServiceDate = source.WorkorderPreventiveMaintenance.NextServiceDate; + + + //WORKORDERITEMS + foreach (WorkorderItem wisource in source.WorkorderItems) + { + WorkorderItem widest = dest.WorkorderItems.Add(dest); + widest.Custom0 = wisource.Custom0; + widest.Custom1 = wisource.Custom1; + widest.Custom2 = wisource.Custom2; + widest.Custom3 = wisource.Custom3; + widest.Custom4 = wisource.Custom4; + widest.Custom5 = wisource.Custom5; + widest.Custom6 = wisource.Custom6; + widest.Custom7 = wisource.Custom7; + widest.Custom8 = wisource.Custom8; + widest.Custom9 = wisource.Custom9; + widest.PriorityID = wisource.PriorityID; + widest.RequestDate = wisource.RequestDate; + widest.Summary = wisource.Summary; + widest.TechNotes = wisource.TechNotes; + widest.TypeID = wisource.TypeID; + widest.UnitID = wisource.UnitID; + widest.WarrantyService = wisource.WarrantyService; + widest.WorkorderItemUnitServiceTypeID = wisource.WorkorderItemUnitServiceTypeID; + widest.WorkorderStatusID = wisource.WorkorderStatusID; + + //PARTS + foreach (WorkorderItemPart partsource in wisource.Parts) + { + WorkorderItemPart partdest = widest.Parts.Add(widest); + partdest.Cost = partsource.Cost; + partdest.Description = partsource.Description; + partdest.Discount = partsource.Discount; + partdest.DiscountType = partsource.DiscountType; + partdest.PartID = partsource.PartID; + partdest.PartWarehouseID = partsource.PartWarehouseID; + partdest.Price = partsource.Price; + if (bUseInventory) + { + partdest.QuantityReserved = partsource.Quantity; + partdest.Quantity = 0; + } + else + partdest.Quantity = partsource.Quantity; + partdest.TaxPartSaleID = partsource.TaxPartSaleID; + + + } + + //********************************************************** + //Part requests would be here if copying a service workorder + //********************************************************** + + //SCHEDULED USERS + foreach (WorkorderItemScheduledUser usersource in wisource.ScheduledUsers) + { + WorkorderItemScheduledUser userdest = widest.ScheduledUsers.Add(widest); + userdest.EstimatedQuantity = usersource.EstimatedQuantity; + userdest.ServiceRateID = usersource.ServiceRateID; + userdest.StartDate = usersource.StartDate; + userdest.StopDate = usersource.StopDate; + userdest.UserID = usersource.UserID; + + } + + //LABOR + foreach (WorkorderItemLabor laborsource in wisource.Labors) + { + WorkorderItemLabor labordest = widest.Labors.Add(widest); + labordest.NoChargeQuantity = laborsource.NoChargeQuantity; + labordest.ServiceDetails = laborsource.ServiceDetails; + labordest.ServiceRateID = laborsource.ServiceRateID; + labordest.ServiceRateQuantity = laborsource.ServiceRateQuantity; + labordest.ServiceStartDate = laborsource.ServiceStartDate; + labordest.ServiceStopDate = laborsource.ServiceStopDate; + labordest.TaxRateSaleID = laborsource.TaxRateSaleID; + labordest.UserID = laborsource.UserID; + + } + + //********************************************************** + //Expenses would be here if copying a service workorder + //********************************************************** + + + //********************************************************** + //Loans would be here if copying a service workorder + //********************************************************** + + //TRAVEL + foreach (WorkorderItemTravel travelsource in wisource.Travels) + { + WorkorderItemTravel traveldest = widest.Travels.Add(widest); + traveldest.TravelDetails = travelsource.TravelDetails; + traveldest.TravelRateID = travelsource.TravelRateID; + traveldest.TravelRateQuantity = travelsource.TravelRateQuantity; + traveldest.TravelStartDate = travelsource.TravelStartDate; + traveldest.TravelStopDate = travelsource.TravelStopDate; + traveldest.TaxRateSaleID = travelsource.TaxRateSaleID; + traveldest.UserID = travelsource.UserID; + traveldest.Distance = travelsource.Distance; + traveldest.Notes = travelsource.Notes; + traveldest.NoChargeQuantity = travelsource.NoChargeQuantity; + } + + + + //TASKS + foreach (WorkorderItemTask tasksource in wisource.Tasks) + { + WorkorderItemTask taskdest = widest.Tasks.Add(widest); + taskdest.TaskGroupID = tasksource.TaskGroupID; + taskdest.TaskID = tasksource.TaskID; + + } + + //********************************************************** + //Outside service would be here if copying a service workorder + //********************************************************** + + }//foreach workorderitem loop + + //case 1387 + + //Delete the auto-created dummy workorder item + //if there are more than it present + if (dest.WorkorderItems.Count > 1) + dest.WorkorderItems.RemoveAt(0); + + #endregion copy workorder data + + //Now save it to ensure it was created properly so + //that we know it's now safe to advance the next service date and all others + + //case 868 previously didn't set dest to result of save causing it to be a copy + dest = (Workorder)dest.Save(); + + + + + #region Calculate reschedule dates + //Get the current next service date for calcs + DateTime dtNext = GetDateFromSpanAndUnit(source.WorkorderPreventiveMaintenance.dtNextServiceDate, + source.WorkorderPreventiveMaintenance.GenerateSpanUnit, + source.WorkorderPreventiveMaintenance.GenerateSpan); + + //Get to the desired day of the week if necessary... + if (source.mWorkorderPreventiveMaintenance.DayOfTheWeek != AyaDayOfWeek.AnyDayOfWeek) + { + DayOfWeek desired = AyaToSystemDayOfWeek(source.mWorkorderPreventiveMaintenance.DayOfTheWeek); + while (dtNext.DayOfWeek != desired) + { + dtNext = dtNext.AddDays(1); + } + + } + + //Get the time span to add to all the other relevant dates on teh workorder to match + //the amount the next service date has been advanced + System.TimeSpan tsToNext = dtNext - source.WorkorderPreventiveMaintenance.dtNextServiceDate; + + + + + + #endregion + + //Will the next workorder service date fall after the + //stop generating date? + if (source.WorkorderPreventiveMaintenance.StopGeneratingDate != System.DBNull.Value && + source.WorkorderPreventiveMaintenance.dtStopGeneratingDate < dtNext) + { + //Yes it will, so set it to inactive and bail out + source.WorkorderPreventiveMaintenance.Active = false; + source.Save(); + return dest; + } + + #region Reschedule PM + + source.WorkorderPreventiveMaintenance.dtNextServiceDate = dtNext; + //Calcs the generate date (threshold date) + source.WorkorderPreventiveMaintenance.SetGenerateDate(); + //WORKORDERITEMS + foreach (WorkorderItem wisource in source.WorkorderItems) + { + + wisource.RequestDate = wisource.RequestDate; + + + //PARTS + //no date changes required + + + //SCHEDULED USERS + foreach (WorkorderItemScheduledUser usersource in wisource.ScheduledUsers) + { + //Changed: 2-Oct-2006 + //check to not add a date if the original date was empty + if (usersource.StartDate != System.DBNull.Value) + usersource.dtStartDate = usersource.dtStartDate.Add(tsToNext); + + if (usersource.StopDate != System.DBNull.Value) + usersource.dtStopDate = usersource.dtStopDate.Add(tsToNext); + + + } + + //LABOR + foreach (WorkorderItemLabor laborsource in wisource.Labors) + { + //Changed: 2-Oct-2006 + //check to not add a date if the original date was empty + if (laborsource.ServiceStartDate != System.DBNull.Value) + laborsource.dtServiceStartDate = laborsource.dtServiceStartDate.Add(tsToNext); + + if (laborsource.ServiceStopDate != System.DBNull.Value) + laborsource.dtServiceStopDate = laborsource.dtServiceStopDate.Add(tsToNext); + + } + + //********************************************************** + //Expenses would be here if copying a service workorder + //********************************************************** + + + //********************************************************** + //Loans would be here if copying a service workorder + //********************************************************** + + //TRAVEL + foreach (WorkorderItemTravel travelsource in wisource.Travels) + { + //Changed: 2-Oct-2006 + //check to not add a date if the original date was empty + if (travelsource.TravelStartDate != DBNull.Value) + travelsource.dtTravelStartDate = travelsource.dtTravelStartDate.Add(tsToNext); + + if (travelsource.TravelStopDate != DBNull.Value) + travelsource.dtTravelStopDate = travelsource.dtTravelStopDate.Add(tsToNext); + + } + + + + //TASKS + + + //********************************************************** + //Outside service would be here if copying a service workorder + //********************************************************** + + }//foreach workorderitem loop + #endregion reschedule pm + + //Ok, Source PM is now rescheduled, save it + + + + //case 1959 try catch block added to prevent infinite generation issue + try + { + source = (Workorder)source.Save(); + } + catch (Exception exx) + { + dest.Delete(); + dest.Save(); + //crack the exception + while (exx.InnerException != null) + exx = exx.InnerException; + + Memo mwarn = Memo.NewItem(); + mwarn.ToID = User.AdministratorID; + + //case 3826 + if (User.CurrentUserType == UserTypes.Utility) + { + //Utility accounts should not be sending memos, it fucks up downstream + //trying to view the memo, also it's confusing + mwarn.FromID = User.AdministratorID; + } + else + { + mwarn.FromID = User.CurrentThreadUserID; + } + + + mwarn.Subject = "SYSTEM WARNING: Preventive Maintenance WO PROBLEM"; + StringBuilder sb = new StringBuilder(); + sb.AppendLine("This is an automated message sent on behalf of the current user from the \"NewServiceWorkorderFromPM\" module."); + sb.AppendLine("This message concerns Preventive Maintenance workorder number " + source.WorkorderPreventiveMaintenance.PreventiveMaintenanceNumber.ToString()); + sb.AppendLine("The Preventive Maintenance workorder had an error when trying to save it during generation of a service workorder."); + sb.AppendLine("This kind of problem could result in loop which generates a very large number of identical service workorders."); + sb.AppendLine("In order to prevent this the operation has been stopped and this message generated so you can fix the problem with the source PM workorder."); + sb.AppendLine("See below for details and examine the PM workorder for problems or contact support@ayanova.com for help with the information in this message."); + sb.AppendLine("Here are the details of the error preventing save:"); + sb.AppendLine("================================="); + sb.AppendLine("Exception saving source PM:"); + sb.AppendLine(exx.Message); + sb.AppendLine("================================="); + string sSourceErr = source.GetBrokenRulesString(); + if (!string.IsNullOrWhiteSpace(sSourceErr)) + { + sb.AppendLine("Broken business rules on PM object:"); + sb.AppendLine(sSourceErr); + sb.AppendLine("=============================="); + } + mwarn.Message = sb.ToString(); + mwarn.Save(); + throw new System.ApplicationException("Workorder->NewServiceWorkorderFromPM: Error during service workorder generation. Memo with details sent to Administrator account."); + + + + } + + //case 1630 + //copy wikipage from pm to service workorder + if (dest.CanWiki && source.HasWiki) + { + try + { + WikiPage wpSource = WikiPage.GetItem(new TypeAndID(RootObjectTypes.WorkorderPreventiveMaintenance, source.ID)); + WikiPage wpDest = WikiPage.GetItem(new TypeAndID(RootObjectTypes.WorkorderService, dest.ID)); + wpDest.SetContent(wpSource.GetContent()); + wpDest.Save(); + } + catch { }; + + } + + return dest; + + + + + + } + + + + /// + /// Calculate generate date based on service date and + /// threshold span and unit + /// + internal void SetGenerateDate() + { + if (this.mNextServiceDate.IsEmpty) return; + if (this.mThresholdSpan == 0) + { + this.mGenerateDate = this.mNextServiceDate; + MarkDirty(); + return; + } + mGenerateDate = new SmartDate(Workorder.GetDateFromSpanAndUnit(mNextServiceDate.Date, this.mThresholdSpanUnit, -mThresholdSpan)); + MarkDirty(); + } + + + #region Date time calcs helpers + //Takes an AyaNova day of week and returns + //a System.DayOfWeek + //Assumes that AyaDayOfWeek is NOT "AnyDay" + internal static System.DayOfWeek AyaToSystemDayOfWeek(AyaDayOfWeek day) + { + switch (day) + { + case AyaDayOfWeek.Monday: + return DayOfWeek.Monday; + case AyaDayOfWeek.Tuesday: + return DayOfWeek.Tuesday; + case AyaDayOfWeek.Wednesday: + return DayOfWeek.Wednesday; + case AyaDayOfWeek.Thursday: + return DayOfWeek.Thursday; + case AyaDayOfWeek.Friday: + return DayOfWeek.Friday; + case AyaDayOfWeek.Saturday: + return DayOfWeek.Saturday; + case AyaDayOfWeek.Sunday: + return DayOfWeek.Sunday; + + + + } + + throw new System.ArgumentOutOfRangeException("DayOfWeekConverter: AyaDayOfWeek.AnyDayOfWeek is not supported"); + } + + + internal static DateTime GetDateFromSpanAndUnit(DateTime StartDate, AyaUnitsOfTime unit, int multiple) + { + switch (unit) + { + case AyaUnitsOfTime.Seconds: + return StartDate.AddSeconds(multiple); + + case AyaUnitsOfTime.Minutes: + return StartDate.AddMinutes(multiple); + + case AyaUnitsOfTime.Hours: + return StartDate.AddHours(multiple); + + case AyaUnitsOfTime.Days: + return StartDate.AddDays(multiple); + + case AyaUnitsOfTime.Weeks: + throw new System.NotSupportedException("GetDateFromSpanAndUnit: Weeks not supported"); + + case AyaUnitsOfTime.Months: + return StartDate.AddMonths(multiple); + + case AyaUnitsOfTime.Years: + return StartDate.AddYears(multiple); + + + } + + //fail safe: + return StartDate; + } + + */ + #endregion gen service wo from pm + ///////////////////////////////////////////////////////////////////// }//eoc diff --git a/server/AyaNova/models/PM.cs b/server/AyaNova/models/PM.cs index bef21126..8f4b8daf 100644 --- a/server/AyaNova/models/PM.cs +++ b/server/AyaNova/models/PM.cs @@ -23,14 +23,22 @@ namespace AyaNova.Models //---- public DateTime? StopGeneratingDate { get; set; } - public DaysOfWeek ExcludeDaysOfWeek { get; set; }//bit field flags set + [Required] + public DaysOfWeek ExcludeDaysOfWeek { get; set; }//bit field flags set + [Required] public bool Active { get; set; } + [Required] public DateTime NextServiceDate { get; set; } + [Required] public PMTimeUnit RepeatUnit { get; set; } + [Required] public PMTimeUnit GenerateBeforeUnit { get; set; } + [Required] public int RepeatInterval { get; set; } + [Required] public int GenerateBeforeInterval { get; set; } - public DateTime? GenerateDate { get; set; }//Internal, not exposed to UI and is the date to do the next generate on that takes into account generatebefore and next service date and is calcd when first created and after that when generate happens for next time see pmbiz top with reference v7 code + //This property is calculated by biz object and set internally + public DateTime? GenerateDate { get; set; } //---- [Required] diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index adfca1f6..f45058fa 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -24,7 +24,7 @@ namespace AyaNova.Util internal const long EXPECTED_COLUMN_COUNT = 1221; internal const long EXPECTED_INDEX_COUNT = 145; - internal const long EXPECTED_CHECK_CONSTRAINTS = 516; + internal const long EXPECTED_CHECK_CONSTRAINTS = 517; internal const long EXPECTED_FOREIGN_KEY_CONSTRAINTS = 190; internal const long EXPECTED_VIEWS = 7; internal const long EXPECTED_ROUTINES = 2; @@ -1016,7 +1016,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); + "notes TEXT, wiki TEXT, customfields TEXT, tags VARCHAR(255) ARRAY, customerid BIGINT NOT NULL REFERENCES acustomer (id), " + "projectid BIGINT REFERENCES aproject, contractid BIGINT NULL, internalreferencenumber text, " + "customerreferencenumber text, customercontactname text, createddate TIMESTAMP NOT NULL, onsite BOOL NOT NULL, " - + "stopgeneratingdate TIMESTAMP, nextservicedate TIMESTAMP NOT NULL, generatedate TIMESTAMP, excludedaysofweek INTEGER NOT NULL, active BOOL NOT NULL, " + + "stopgeneratingdate TIMESTAMP, nextservicedate TIMESTAMP NOT NULL, generatedate TIMESTAMP NOT NULL, excludedaysofweek INTEGER NOT NULL, active BOOL NOT NULL, " + "repeatunit INTEGER NOT NULL, generatebeforeunit INTEGER NOT NULL, repeatinterval INTEGER NOT NULL, generatebeforeinterval INTEGER NOT NULL, " + "postaddress TEXT, postcity TEXT, postregion TEXT, postcountry TEXT, postcode TEXT, address TEXT, city TEXT, region TEXT, country TEXT, latitude DECIMAL(9,6), longitude DECIMAL(9,6) " + ")");