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
}
-
+