using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace AyaNovaQBI { public partial class MainForm : Form { public MainForm() { InitializeComponent(); InitDataSet(); Icon = AyaNovaQBI.Properties.Resources.logo; } async private void MainForm_Load(object sender, EventArgs e) { //Initialize StringBuilder initErrors = new StringBuilder(); if (await util.InitializeQBI(initErrors) == false) { if (initErrors.Length > 0) { if (util.LOG_AVAILABLE) await util.IntegrationLog(initErrors.ToString()); await Task.Run(() => MessageBox.Show($"AyaNova QBI was unable to start:\r\n{initErrors.ToString()}")); } Close(); return; } //Confirm main settings and set any that are missing: if (await util.ValidateSettings(false) == util.pfstat.Cancel) { await util.IntegrationLog("PFC: User settings not completed, user selected cancel"); Close(); return; } Text = "AyaNova QBI - " + util.QCompanyName; //See if there are *any* data mappings, if not then we will prompt the user to start that process if (util.QBIntegration.Items.Count == 0) { //show message about mapping MessageBox.Show( "AyaNova QBI now needs you to map data between QuickBooks and AyaNova.", "Setup mapping", MessageBoxButtons.OK, MessageBoxIcon.Information); Map m = new Map(); if (m.ShowDialog() == DialogResult.Abort) Close(); } //Display billable workorders InitInvoices(); grid.Visible = true; menuStrip1.Enabled = true; } private void grid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { if (e.ColumnIndex == grid.Columns["Status"].Index && e.Value != null) { var isLinked = (bool)grid.Rows[e.RowIndex].Cells["linked"].Value; if (!isLinked) { grid.Rows[e.RowIndex].ErrorText = "Not invoiceable: use \"Invoice\" -> \"Fix problems\" to resolve"; } else { grid.Rows[e.RowIndex].ErrorText = null; } } } private void exitToolStripMenuItem_Click(object sender, EventArgs e) { Close(); } private void multipleWorkordersPerInvoiceToolStripMenuItem_Click(object sender, EventArgs e) { if (grid.SelectedRows.Count < 1) { MessageBox.Show("There are no rows selected, select one or more rows to invoice"); return; } MessageBox.Show($"STUB: MULTIPLEwoperinvoice {grid.SelectedRows.Count} rows selected"); } private void oneWorkOrderPerInvoiceToolStripMenuItem_Click(object sender, EventArgs e) { if (grid.SelectedRows.Count < 1) { MessageBox.Show("There are no rows selected, select one or more rows to invoice"); return; } MessageBox.Show($"STUB: onwoperinvoice {grid.SelectedRows.Count} rows selected"); } private void fixProblemsToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show("STUB: FIX PROBLEMS"); } private void refreshInvoicesToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show("STUB: REFRESH INVOICES"); } private async void preferencesToolStripMenuItem_Click(object sender, EventArgs e) { await util.ValidateSettings(true); } private void mapAndImportToolStripMenuItem_Click(object sender, EventArgs e) { Map m = new Map(); if (m.ShowDialog() == DialogResult.Abort) Close(); else { m.Dispose(); InitInvoices(); } } private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { Assembly a = Assembly.GetExecutingAssembly(); string sVersion = "AyaNova QBI version " + util.DisplayVersion(a.GetName().Version) + "\r\n"; System.Diagnostics.FileVersionInfo fileVersion = System.Diagnostics.FileVersionInfo.GetVersionInfo(a.Location); if (fileVersion.FileBuildPart > 0) sVersion += " (Patch " + fileVersion.FileBuildPart.ToString() + ")\r\n"; sVersion += "Copyright 2000-2022 Ground Zero Tech-Works Inc.\r\n"; MessageBox.Show(sVersion, "About"); } private void onlineManualToolStripMenuItem_Click(object sender, EventArgs e) { util.OpenWebURL("https://ayanova.com/qbi/docs"); } private async void refreshCachedDataToolStripMenuItem_Click(object sender, EventArgs e) { await util.PopulateQBListCache(); await util.PopulateAyaListCache(); } private async void invoiceDescriptiveTextTemplateToolStripMenuItem_Click(object sender, EventArgs e) { InvoiceTemplateBuilder b = new InvoiceTemplateBuilder(); b.ShowDialog(); if (util.QDat.IsDirty) await util.SaveIntegrationObject(); b.Dispose(); } #region DataSet stuff private DataSet dsInvoices; private DataTable Invoices; private DataColumn Client; private DataColumn WorkingID; private DataColumn Linked; private DataColumn ClientID; private DataTable Workorders; private DataColumn InvoiceWorkingID; private DataColumn WorkorderID; private DataColumn wostatus; private DataColumn ServiceNumber; private DataColumn ServiceDate; private DataColumn Project; private DataColumn StatusARGB; private DataColumn dataColumn1; private void InitDataSet() { dsInvoices = new DataSet(); Invoices = new DataTable(); Client = new DataColumn(); WorkingID = new DataColumn(); Linked = new DataColumn(); ClientID = new DataColumn(); Workorders = new DataTable(); InvoiceWorkingID = new DataColumn(); WorkorderID = new DataColumn(); wostatus = new DataColumn(); ServiceNumber = new DataColumn(); ServiceDate = new DataColumn(); Project = new DataColumn(); StatusARGB = new DataColumn(); dataColumn1 = new DataColumn(); // // dsInvoices // dsInvoices.DataSetName = "Invoices"; dsInvoices.Locale = new System.Globalization.CultureInfo("en-US"); dsInvoices.Relations.AddRange(new DataRelation[] { new DataRelation("Relation1", "Invoices", "Workorders", new string[] { "WorkingID"}, new string[] { "InvoiceWorkingID"}, false)}); dsInvoices.Tables.AddRange(new DataTable[] { Invoices, Workorders}); // // Invoices // Invoices.Columns.AddRange(new DataColumn[] { Client, WorkingID, Linked, ClientID}); Invoices.Constraints.AddRange(new Constraint[] { new UniqueConstraint("Constraint1", new string[] { "WorkingID"}, true)}); Invoices.PrimaryKey = new DataColumn[] { WorkingID}; Invoices.TableName = "Invoices"; // // Client // Client.ColumnName = "Client"; // // WorkingID // WorkingID.AllowDBNull = false; WorkingID.AutoIncrement = true; WorkingID.Caption = "WorkingID"; WorkingID.ColumnName = "WorkingID"; WorkingID.DataType = typeof(int); WorkingID.ReadOnly = true; // // Linked // Linked.Caption = "Linked"; Linked.ColumnName = "Linked"; Linked.DataType = typeof(bool); Linked.DefaultValue = false; // // ClientID // ClientID.Caption = "ClientID"; ClientID.ColumnName = "ClientID"; ClientID.DataType = typeof(System.Guid); // // Workorders // Workorders.Columns.AddRange(new DataColumn[] { InvoiceWorkingID, WorkorderID, wostatus, ServiceNumber, ServiceDate, Project, StatusARGB, dataColumn1}); Workorders.Constraints.AddRange(new Constraint[] { new UniqueConstraint("Constraint1", new string[] { "WorkorderID"}, true), new ForeignKeyConstraint("Relation1", "Invoices", new string[] { "WorkingID"}, new string[] { "InvoiceWorkingID"}, AcceptRejectRule.None, Rule.None, Rule.None)}); Workorders.PrimaryKey = new DataColumn[] { WorkorderID}; Workorders.TableName = "Workorders"; // // InvoiceWorkingID // InvoiceWorkingID.Caption = "InvoiceWorkingID"; InvoiceWorkingID.ColumnName = "InvoiceWorkingID"; InvoiceWorkingID.DataType = typeof(int); // // WorkorderID // WorkorderID.AllowDBNull = false; WorkorderID.Caption = "WorkorderID"; WorkorderID.ColumnName = "WorkorderID"; WorkorderID.DataType = typeof(System.Guid); // // wostatus // wostatus.Caption = "Status"; wostatus.ColumnName = "Status"; // // ServiceNumber // ServiceNumber.Caption = "ServiceNumber"; ServiceNumber.ColumnName = "ServiceNumber"; ServiceNumber.DataType = typeof(int); // // ServiceDate // ServiceDate.Caption = "ServiceDate"; ServiceDate.ColumnName = "ServiceDate"; ServiceDate.DataType = typeof(object); // // Project // Project.Caption = "Project"; Project.ColumnName = "Project"; // // StatusARGB // StatusARGB.Caption = "StatusARGB"; StatusARGB.ColumnName = "StatusARGB"; StatusARGB.DataType = typeof(int); // // dataColumn1 // dataColumn1.ColumnName = "Linked"; dataColumn1.DataType = typeof(bool); dataColumn1.DefaultValue = false; } #endregion dataset stuff #region Main workorder grid /// /// Adjusts main form display to either show a list of billable workorders /// or a status indicating there are none and why /// private async Task SetState() { fixProblemsToolStripMenuItem.Enabled = _MisMatches.Count > 0; if (grid.Rows.Count > 0) { grid.Visible = true; lblStatus.Visible = false; } else { StringBuilder sb = new StringBuilder(); sb.Append("No invoiceable work orders found in AyaNova\r\n\r\n"); sb.Append("A work order is invoiceable and will be listed here if it has:\r\n"); sb.Append(" - \"Invoice number\" field empty\r\n"); if (util.QDat.PreWOStatus != 0) { sb.Append(" - \"Work order status\" field set to: "); var r = await util.GetAsync($"work-order-status/{util.QDat.PreWOStatus}"); var status = r.ObjectResponse["data"]["license"].ToObject(); if (status != null) sb.Append(status.Name); else sb.Append("UNKNOWN / MISSING STATUS - recently deleted?"); sb.Append("\r\n"); sb.Append(" (You can change this status under Tools->Preferences in the menu)"); } else sb.Append(" - any \"Locked\" type of Work order status set\r\n"); lblStatus.Text = sb.ToString(); grid.Visible = false; lblStatus.Visible = true; } } /* POST /api/v8.0/data-list {"offset":0,"limit":10,"dataListKey":"WorkOrderDataList","filterId":6,"clientTimeStamp":"2022-07-08T10:49:18.715-07:00"} data-list-column-view: {"userId":1,"listKey":"WorkOrderDataList","columns":"[\"WorkOrderSerialNumber\",\"Customer\",\"WorkOrderServiceDate\",\"WorkOrderCloseByDate\",\"WorkOrderStatus\",\"Project\",\"WorkOrderAge\",\"WorkOrderInvoiceNumber\"]","sort":"{\"WorkOrderSerialNumber\":\"-\"}"} data-list-filter filter "[{\"column\":\"WorkOrderInvoiceNumber\",\"any\":false,\"items\":[{\"op\":\"=\",\"value\":\"*NULL*\"}]}]" */ private List _wolist = null; private List _MisMatches = new List(); private List _PartPriceOverrides = new List(); /// /// Initialize invoices dataset /// from scratch. If a previous /// initialize was done, wipe it and /// repopulate from scratch /// private async Task InitInvoices() { Waiting w = new Waiting(); w.Show(); w.Ops = "Validating invoices..."; try { _MisMatches.Clear(); dsInvoices.Clear(); //[HttpGet("accounting-list-billable/{workOrderStatusId}")] var r = await util.GetAsync($"accounting-list-billable/{util.QDat.PreWOStatus}"); _wolist = r.ObjectResponse["data"].ToObject>(); DataTable dtInvoice = dsInvoices.Tables["Invoices"]; DataTable dtWorkorder = dsInvoices.Tables["Workorders"]; foreach (WorkorderServiceBillableList.WorkorderServiceBillableListInfo i in _wolist) { bool bLinked = Util.ScanLinksOK(i.ID, _MisMatches, _PartPriceOverrides); DataRow dri = InvoiceRowForClientID(i.ClientID); w.Step = "WO: " + i.ServiceNumber; if (dri == null) { dri = dtInvoice.NewRow(); dri["Client"] = i.Client; dri["ClientID"] = i.ClientID; dtInvoice.Rows.Add(dri); } //If any one single workorder is linked //then the invoice is flagged as linked because //you can invoice out anything under it that is linked and the //not linked items simply won't invoice if (bLinked) dri["Linked"] = true; DataRow drw = dtWorkorder.NewRow(); drw["InvoiceWorkingID"] = (int)dri["WorkingID"]; drw["WorkorderID"] = i.ID; drw["Status"] = i.Status; drw["ServiceNumber"] = i.ServiceNumber; drw["ServiceDate"] = i.ServiceDate; drw["Project"] = i.Project; drw["StatusARGB"] = i.StatusARGB; drw["Linked"] = bLinked; dtWorkorder.Rows.Add(drw); } grid.DisplayLayout.Rows.CollapseAll(false); foreach (UltraGridRow r in grid.Rows) { foreach (UltraGridRow rr in r.ChildBands[0].Rows) { if ((bool)rr.Cells["Linked"].Value == false) r.Expanded = true; } } grid.EndUpdate(); } finally { w.Close(); } SetState(); } /// /// Helper for grouping workorders by client /// /// /// null if not found else datarow containing invoice for client private DataRow InvoiceRowForClientID(Guid ClientID) { foreach (DataRow r in dsInvoices.Tables["Invoices"].Rows) { if ((Guid)r["ClientID"] == ClientID) { return r; } } return null; } private void InitializeGrid() { grid.DataSource = dsInvoices; string currentAssemblyDirectoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); //Load the grid layout from file if (System.IO.File.Exists(currentAssemblyDirectoryName + "\\MainGrid.lyt")) grid.DisplayLayout.Load(currentAssemblyDirectoryName + "\\MainGrid.lyt"); grid.DisplayLayout.Bands[0].Columns["WorkingID"].Hidden = true; grid.DisplayLayout.Bands[0].Columns["ClientID"].Hidden = true; grid.DisplayLayout.Bands[0].Columns["Linked"].Hidden = true; grid.DisplayLayout.Bands[0].Columns["Client"].Header.Caption = "Invoice"; grid.DisplayLayout.Bands[1].Columns["InvoiceWorkingID"].Hidden = true; grid.DisplayLayout.Bands[1].Columns["WorkorderID"].Hidden = true; grid.DisplayLayout.Bands[1].Columns["StatusARGB"].Hidden = true; grid.DisplayLayout.Bands[1].Columns["Linked"].Hidden = true; } private void grid_InitializeRow(object sender, Infragistics.Win.UltraWinGrid.InitializeRowEventArgs e) { if (e.Row.Band.Index == 0) { //Prepare invoice row if ((bool)e.Row.Cells["Linked"].Value == true) { e.Row.Cells["Client"].Appearance.Image = Util.AyaImage("OK16");//Util.Image("OK16.png"); } else { e.Row.Cells["Client"].Appearance.Image = Util.AyaImage("Cancel16");//Util.Image("Cancel16.png"); } } else { //prepare workorder row //if backcolor==0 that means no color was set int nColor = (int)e.Row.Cells["StatusARGB"].Value; if (nColor != 0) { e.Row.Cells["Status"].Appearance.BackColor = Color.FromArgb(nColor); e.Row.Cells["Status"].Appearance.ForeColor = Util.InvertColor(Color.FromArgb(nColor)); } //flag whether billable (linked) or not if ((bool)e.Row.Cells["Linked"].Value == true) { e.Row.Cells["ServiceNumber"].Appearance.Image = Util.AyaImage("OK16"); } else { e.Row.Cells["ServiceNumber"].Appearance.Image = Util.AyaImage("Cancel16"); } } } #endregion }//eoc }//eons