From 06b4cc86fac5c3494328af1d6e67cd9a936641f8 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Tue, 12 Jul 2022 00:32:03 +0000 Subject: [PATCH] --- AyaNovaQBI/AyaNovaQBI.csproj | 1 + AyaNovaQBI/FixInvoiceProblems.cs | 4 +- AyaNovaQBI/WorkOrderState.cs | 32 ++++++ AyaNovaQBI/util.cs | 161 +++++++++++++++++++++++++++---- 4 files changed, 177 insertions(+), 21 deletions(-) create mode 100644 AyaNovaQBI/WorkOrderState.cs diff --git a/AyaNovaQBI/AyaNovaQBI.csproj b/AyaNovaQBI/AyaNovaQBI.csproj index 9a53919..b4c097a 100644 --- a/AyaNovaQBI/AyaNovaQBI.csproj +++ b/AyaNovaQBI/AyaNovaQBI.csproj @@ -257,6 +257,7 @@ + Form diff --git a/AyaNovaQBI/FixInvoiceProblems.cs b/AyaNovaQBI/FixInvoiceProblems.cs index 5272b8e..69aa6f0 100644 --- a/AyaNovaQBI/FixInvoiceProblems.cs +++ b/AyaNovaQBI/FixInvoiceProblems.cs @@ -81,7 +81,7 @@ namespace AyaNovaQBI var wip = w.Items.First(z => z.Id == mm.WorkOrderItemId).Parts.First(z => z.Id == mm.WorkOrderItemPartId); wip.PriceOverride = mm.QBPrice; - await util.PostAsync("workorder", Newtonsoft.Json.JsonConvert.SerializeObject(w)); + await PutAsync("workorder/items/parts", Newtonsoft.Json.JsonConvert.SerializeObject(wip)); } break; @@ -98,7 +98,7 @@ namespace AyaNovaQBI var wip = w.Items.First(z => z.Id == mm.WorkOrderItemId).Parts.First(z => z.Id == mm.WorkOrderItemPartId); long PartId = wip.PartId; wip.PriceOverride = mm.QBPrice; - await util.PostAsync("workorder", Newtonsoft.Json.JsonConvert.SerializeObject(w)); + await PutAsync("workorder/items/parts", Newtonsoft.Json.JsonConvert.SerializeObject(wip)); if (PartId != 0) { diff --git a/AyaNovaQBI/WorkOrderState.cs b/AyaNovaQBI/WorkOrderState.cs new file mode 100644 index 0000000..22f4cdb --- /dev/null +++ b/AyaNovaQBI/WorkOrderState.cs @@ -0,0 +1,32 @@ +using System; + +namespace AyaNovaQBI +{ + public class WorkOrderState + { + public long Id { get; set; } + public uint Concurrency { get; set; } + + public long WorkOrderId { get; set; } + + public long WorkOrderStatusId { get; set; } + + public DateTime Created { get; set; } = DateTime.UtcNow; + + public long UserId { get; set; } + + public string UserViz { get; set; } + + public string NameViz { get; set; } + + public string ColorViz { get; set; } + + public bool CompletedViz { get; set; } + + public bool LockedViz { get; set; } + + public uint NewWOConcurrency { get; set; } + + + }//eoc +} diff --git a/AyaNovaQBI/util.cs b/AyaNovaQBI/util.cs index 3a6520c..1b73c2e 100644 --- a/AyaNovaQBI/util.cs +++ b/AyaNovaQBI/util.cs @@ -21,6 +21,9 @@ namespace AyaNovaQBI { get { return new Guid("{82CD3609-4601-4C1A-9633-7836F92D2D06}"); } } + + private const string ACCOUNTING_INTEGRATION_WORK_ORDER_STATUS_NAME = "z_ACCOUNTING_INTEGRATION_REQUIRED_STATUS"; + public const string TEST_ROUTE = "notify/hello"; public const string API_BASE_ROUTE = "api/v8/"; private const int MAX_TRIES = 3;//max times to retry an api call before giving up @@ -47,6 +50,9 @@ namespace AyaNovaQBI internal static bool USE_INVENTORY { get; set; } = false; + internal static long ACCOUNTING_INTEGRATION_WORKORDER_STATUS_ID { get; set; } = 0; + + public static void InitClient() @@ -561,6 +567,8 @@ namespace AyaNovaQBI return false; LOG_AVAILABLE = true; + + //PFC - get global setttings for use inventory and others await window.$gz.api.get("global-biz-setting/client"); r = await GetAsync("global-biz-setting/client"); USE_INVENTORY = r.ObjectResponse["data"]["useInventory"].Value(); @@ -608,6 +616,10 @@ namespace AyaNovaQBI //PFC - verify integration mapped objects still exist in QB if (!await ValidateQuickBooksHasMappedItems(initErrors)) return false; + + if (!await EnsureAccountingIntegrationWorkOrderStatus(initErrors)) + return false; + await IntegrationLog("PFC: QBI initialized and ready for use"); return true; } @@ -678,6 +690,88 @@ namespace AyaNovaQBI } } + + + /// + /// Ensure existance of special work order status for accounting integration modification purposes + /// + /// + /// + public static async Task EnsureAccountingIntegrationWorkOrderStatus(StringBuilder initErrors) + { + /* + * var a = await util.GetAsync($"workorder/{mm.WorkOrderId}"); + WorkOrder w = a.ObjectResponse["data"].ToObject(); + if (w == null) + throw new Exception($"FixInvoiceProblems:CHANGEAYA:PRICE: WorkOrder with id {mm.WorkOrderId} was not found in AyaNova and may have just been deleted.\r\nUnable to update wo."); + + var wip = w.Items.First(z => z.Id == mm.WorkOrderItemId).Parts.First(z => z.Id == mm.WorkOrderItemPartId); + long PartId = wip.PartId; + wip.PriceOverride = mm.QBPrice; + await PutAsync("workorder/items/parts", Newtonsoft.Json.JsonConvert.SerializeObject(wip)); + + CREATE STATUS: {"data":{"id":0, "name":"z_ACCOUNTING_INTEGRATION_REQUIRED_STATUS","active":true, + "notes":"Special status required for Accounting integration applications to enable modifying locked work orders.", + "color":"#000000FF","selectRoles":64,"removeRoles":64,"completed":false,"locked":false}} + POST STATE TO WO: {"workOrderId":557,"workOrderStatusId":8,"userId":5,"userViz":"Marian Green - Service","created":"2022-07-11T22:54:11.528Z"} + */ + //see if we already have an integration status set + WorkOrderStatus AccountingStatus = _woStatuslist.FirstOrDefault(z => z.Name == ACCOUNTING_INTEGRATION_WORK_ORDER_STATUS_NAME); + + //validate accounting status + if (AccountingStatus != null) + { + ACCOUNTING_INTEGRATION_WORKORDER_STATUS_ID = AccountingStatus.Id; + bool hasError = false; + + if (!AccountingStatus.SelectRoles.HasFlag(AuthorizationRoles.Accounting)) + { + hasError = true; + initErrors.AppendLine($"Work order Status \"{AccountingStatus.Name}\" SELECT Roles must include Accounting Role"); + } + + if (!AccountingStatus.RemoveRoles.HasFlag(AuthorizationRoles.Accounting)) + { + hasError = true; + initErrors.AppendLine($"Work order Status \"{AccountingStatus.Name}\" REMOVE Roles must include Accounting Role"); + } + + if (!AccountingStatus.Active) + { + hasError = true; + initErrors.AppendLine($"Work order Status \"{AccountingStatus.Name}\" must be set to Active=true"); + } + return !hasError; + } + + //Create accounting status + ApiResponse r = null; + try + { + + AccountingStatus.Name = ACCOUNTING_INTEGRATION_WORK_ORDER_STATUS_NAME; + AccountingStatus.Active = true; + AccountingStatus.Completed = false; + AccountingStatus.Locked = false; + AccountingStatus.SelectRoles = AuthorizationRoles.Accounting; + AccountingStatus.RemoveRoles = AuthorizationRoles.Accounting; + AccountingStatus.Notes = "DO NOT MODIFY - Special status required for Accounting integration applications to modify locked Work orders."; + AccountingStatus.Color = "#6D3C2176";//ugly transparentish brown + r = await PostAsync($"work-order-status", Newtonsoft.Json.JsonConvert.SerializeObject(AccountingStatus)); + ACCOUNTING_INTEGRATION_WORKORDER_STATUS_ID = IdFromResponse(r); + + return true; + } + catch (Exception ex) + { + initErrors.AppendLine($"Error creating required Work order Status \"{AccountingStatus.Name}\""); + initErrors.AppendLine(ex.Message); + initErrors.AppendLine(r.CompactResponse); + return false; + } + } + + /// /// Ensure mapped items still existing in QuickBooks /// @@ -5659,8 +5753,6 @@ namespace AyaNovaQBI #endregion export to quickbooks - - #region Workorder mismatch scanning public enum MisMatchReason @@ -6117,7 +6209,6 @@ namespace AyaNovaQBI if (w == null) throw new Exception($"util.Invoice: WorkOrder with record id {SelectedWorkOrderId} was not found in AyaNova and may have just been deleted.\r\nUnable to invoice this wo."); - var OrderedWorkOrderItems = w.Items.OrderBy(z => z.Sequence).ToList(); if (bFirstLoop) @@ -6402,7 +6493,7 @@ namespace AyaNovaQBI if (s.IndexOf("~SERVICE_START~") != -1) { - s = s.Replace("~SERVICE_START~",DateToLocalString(wl.ServiceStartDate)); + s = s.Replace("~SERVICE_START~", DateToLocalString(wl.ServiceStartDate)); } if (s.IndexOf("~SERVICE_STOP~") != -1) @@ -6578,12 +6669,12 @@ namespace AyaNovaQBI if (s.IndexOf("~ITEM~") != -1) { - s = s.Replace("~ITEM~",l.LoanUnitViz); + s = s.Replace("~ITEM~", l.LoanUnitViz); } if (s.IndexOf("~LOANED~") != -1) { - s = s.Replace("~LOANED~",DateToLocalString(l.OutDate)); + s = s.Replace("~LOANED~", DateToLocalString(l.OutDate)); } if (s.IndexOf("~LOAN_RETURNED~") != -1) @@ -6655,23 +6746,55 @@ namespace AyaNovaQBI //Loop through all workorders again and set their invoice number, status and close them //Loop through alworkorders - foreach (object o in selectedWorkOrderIdList) + foreach (long WorkOrderId in selectedWorkOrderIdList) { - Workorder w = Workorder.GetItem((Guid)o); - if (QDat.PostWOStatus != Guid.Empty) + /* + * var a = await util.GetAsync($"workorder/{mm.WorkOrderId}"); + WorkOrder w = a.ObjectResponse["data"].ToObject(); + if (w == null) + throw new Exception($"FixInvoiceProblems:CHANGEAYA:PRICE: WorkOrder with id {mm.WorkOrderId} was not found in AyaNova and may have just been deleted.\r\nUnable to update wo."); + + var wip = w.Items.First(z => z.Id == mm.WorkOrderItemId).Parts.First(z => z.Id == mm.WorkOrderItemPartId); + long PartId = wip.PartId; + wip.PriceOverride = mm.QBPrice; + await PutAsync("workorder/items/parts", Newtonsoft.Json.JsonConvert.SerializeObject(wip)); + + CREATE STATUS: {"data":{"id":0, "name":"zACCOUNTING_INTEGRATION_REQUIRED_STATUS","active":true,"notes":"Special status required for Accounting integration applications to enable modifying locked work orders.","color":"#000000FF","selectRoles":64,"removeRoles":64,"completed":false,"locked":false}} + POST STATE TO WO: {"workOrderId":557,"workOrderStatusId":8,"userId":5,"userViz":"Marian Green - Service","created":"2022-07-11T22:54:11.528Z"} + */ + //First, set status to editable one and also to show that QBI made changes + + var a = await GetAsync($"workorder/{WorkOrderId}"); + WorkOrder w = a.ObjectResponse["data"].ToObject(); + if (w != null) { + long? StatusOfWorkOrderBeforeModifications = w.LastStatusId; + //set work order state to accounting status + var newState = (await PostAsync("workorder/states", Newtonsoft.Json.JsonConvert.SerializeObject(new WorkOrderState { WorkOrderId = w.Id, UserId = 0, WorkOrderStatusId = ACCOUNTING_INTEGRATION_WORKORDER_STATUS_ID }))).ObjectResponse["data"].ToObject(); + w.Concurrency = newState.NewWOConcurrency; + + //set invoice number + w.InvoiceNumber = InvoiceNumber; + //only allowed to put header not rest of wo + w.Items.Clear(); + + await PutAsync("workorder", Newtonsoft.Json.JsonConvert.SerializeObject(w)); + + + if (QDat.PostWOStatus != 0) + { + await PostAsync("workorder/states", Newtonsoft.Json.JsonConvert.SerializeObject(new WorkOrderState { WorkOrderId = w.Id, UserId = 0, WorkOrderStatusId = QDat.PostWOStatus })); + } + else + { + //put it back to the status it was before we modified it as user has selected no status change + if (StatusOfWorkOrderBeforeModifications != null) + await PostAsync("workorder/states", Newtonsoft.Json.JsonConvert.SerializeObject(new WorkOrderState { WorkOrderId = w.Id, UserId = 0, WorkOrderStatusId = (long)StatusOfWorkOrderBeforeModifications })); + } + - w.WorkorderService.WorkorderStatusID = QDat.PostWOStatus; } - w.WorkorderService.InvoiceNumber = InvoiceNumber; - - //Case 7 - if (QDat.AutoClose) - w.Closed = true; - - w.Save(); - } }//end try block @@ -6694,7 +6817,7 @@ namespace AyaNovaQBI } - +