using GraphQL; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; using System.IO; using System.Timers; using RestSharp; namespace BodyshopPartner.Utils { public static class PPGMixData { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static string PpgDataQuery = @"query PPGDataPump($today: timestamptz!, $todayplus5: timestamptz!, $shopid: uuid!) { bodyshops_by_pk(id:$shopid) { id shopname imexshopid } jobs(where: {_or: [{_and: [{scheduled_in: {_lte: $todayplus5}}, {scheduled_in: {_gte: $today}}]}, {inproduction: {_eq: true}}]}) { id ro_number status ownr_fn ownr_ln ownr_co_nm v_vin v_model_yr v_make_desc v_model_desc v_color plate_no ins_co_nm est_ct_fn est_ct_ln rate_mapa rate_lab job_totals vehicle{ v_paint_codes } labhrs: joblines_aggregate(where: {mod_lbr_ty: {_neq: ""LAR""}, removed: {_eq: false}}) { aggregate { sum { mod_lb_hrs } } } larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: ""LAR""}, removed: { _eq: false} }) { aggregate { sum { mod_lb_hrs } } } } } "; private static Timer MixDataImportTimer = new Timer(); public static void StartMixTimer() { MixDataImportTimer.Stop(); double Interval =0; bool parseSuccesful = double.TryParse(Properties.Settings.Default.PaintScaleImportTimer, out Interval); if (parseSuccesful && Interval > 0) { logger.Debug("Starting Mix Data Import timer. ", Interval * 1000 * 60); MixDataImportTimer.Interval = Interval * 1000*60; MixDataImportTimer.Elapsed += MixDataImportTimer_Tick; MixDataImportTimer.Start(); } } public static async Task test() { logger.Info("Starting MixData import."); //Check to make sure that the directory exists. Directory.CreateDirectory(Properties.Settings.Default.PaintScaleImportPath); Directory.CreateDirectory(Path.Combine(Properties.Settings.Default.PaintScaleImportPath, "archive")); //Check to see if there is a file that exists. string[] fileEntries = Directory.GetFiles(Properties.Settings.Default.PaintScaleImportPath); foreach (string fileName in fileEntries) { try { logger.Debug("Reading file in directory: " + fileName); RestRequest request = new RestRequest("/mixdata/upload", Method.Post); request.AddHeader("Authorization", "Bearer " + Utils.Auth.authlink.FirebaseToken); request.AddFile("file", fileName); request.AddJsonBody(new { bodyshopid = AppMetaData.ActiveShopId }); RestResponse _ = await AppMetaData.RestClient.ExecuteAsync(request); if (_.IsSuccessful) { logger.Info("Successful upload"); string newPath = Path.Combine(Properties.Settings.Default.PaintScaleImportPath, "archive") + @"\\" + DateTime.Now.Ticks + ".xml"; File.Move(fileName, newPath); } else { logger.Error("Unable to process file " + fileName + ". " + _.ErrorMessage); } }catch (Exception ex) { Directory.CreateDirectory(Path.Combine(Properties.Settings.Default.PaintScaleImportPath, "errored")); string newPath = Path.Combine(Properties.Settings.Default.PaintScaleImportPath, "errored") + @"\\" + DateTime.Now.Ticks + ".xml"; File.Move(fileName, newPath); logger.Error(ex); } } } public static async void MixDataImportTimer_Tick(object sender, ElapsedEventArgs e) { logger.Info("Starting MixData import "); //Check to make sure that the directory exists. Directory.CreateDirectory(Properties.Settings.Default.PaintScaleImportPath); Directory.CreateDirectory(Path.Combine(Properties.Settings.Default.PaintScaleImportPath, "archive")); //Check to see if there is a file that exists. string[] fileEntries = Directory.GetFiles(Properties.Settings.Default.PaintScaleImportPath); foreach (string fileName in fileEntries) { logger.Debug("Reading file in directory: " + fileName); RestRequest request = new RestRequest("/mixdata/upload",Method.Post); //Check whether to update the token first. await Utils.Auth.Refresh(); request.AddHeader("Authorization", "Bearer " + Utils.Auth.authlink.FirebaseToken); request.AddFile("file", Path.Combine(Properties.Settings.Default.PaintScaleImportPath, fileName)); RestResponse _ = await AppMetaData.RestClient.ExecuteAsync(request); if (_.IsSuccessful) { logger.Info("Successful upload"); } } //If a file does exist, read it, and send it up to the API with the Active Shop ID & job. //On succesful send, archive the file //Make sure the archive directory exists //Rename and move the file to the archive directory. } public static async Task PushDataToPPG() { try { //Get the path for the XML file that should be written. Ensure that we can read and write to that directory. //Query the database based on the active shop for all work that's coming within the next 5 days + all active work. var r = new GraphQLRequest { Query = PpgDataQuery, Variables = new { today = DateTime.Now.Date, todayplus5 = DateTime.Now.Date.AddDays(5), shopid = AppMetaData.ActiveShopId } }; var data = await Utils.GraphQL.ExecuteQuery(r); //Create an XML document node XDocument doc = new XDocument( new XElement("PPG", new XElement("Header", new XElement("Protocol", new XElement("Message", "PaintShopInterface"), new XElement("Name", "PPG"), new XElement("Version", "1.5.0")), new XElement("Transaction", new XElement("TransactionID", null), new XElement("TransactionDate", DateTime.Now.ToString("yyyy-MM-hh:mm:ss")) ), new XElement("Product", new XElement("Name", "ImEX Online"), new XElement("Version", null) ) ), new XElement("DataInterface", new XElement("ROData", new XElement("ShopInfo", new XElement("ShopID", data?.bodyshops_by_pk?.imexshopid?.Value), new XElement("ShopName", data?.bodyshops_by_pk?.shopname?.Value) ), new XElement("RepairOrders", new XElement("ROCount", data?.jobs?.Count) ) ) ) ) ); foreach (dynamic job in data.jobs) { logger.Debug($"{job.ro_number}"); doc.Element("PPG").Element("DataInterface").Element("ROData").Element("RepairOrders").Add(new XElement("RO", new XElement("RONumber", job.ro_number?.Value), new XElement("ROStatus", "Open"), new XElement("Customer", $"{job.ownr_ln}, {job.ownr_fn}"), new XElement("ROPainterNotes", ""), new XElement("LicensePlateNum", job.plate_no?.Value), new XElement("VIN", job.v_vin?.Value), new XElement("ModelYear", job.v_model_yr?.Value), new XElement("MakeDesc", job.v_make_desc?.Value), new XElement("ModelName", job.v_model_desc?.Value), new XElement("OEMColorCode", job.vehicle != null ? job.vehicle?.v_paint_codes == null ? "a" : job.vehicle?.v_paint_codes?.paint_cd1.Value : "b"), new XElement("RefinishLaborHours", job.larhrs?.aggregate?.sum.mod_lb_hrs?.Value), new XElement("InsuranceCompanyName", job.ins_co_nm?.Value), new XElement("EstimatorName", $"{job.est_ct_ln}, {job.est_ct_fn}"), new XElement("PaintMaterialsRevenue", ((float)(job.job_totals != null ? job.job_totals?.rates?.mapa?.total?.amount?.Value : 0)) / 100), new XElement("PaintMaterialsRate", job.rate_mapa?.Value), new XElement("BodyHours", job.labhrs?.aggregate.sum?.mod_lb_hrs?.Value), new XElement("BodyLaborRate", job.rate_lab?.Value), new XElement("TotalCostOfRepairs", ((float)(job.job_totals != null ? job.job_totals?.totals?.subtotal?.amount?.Value : 0)) / 100) )); } Directory.CreateDirectory(Properties.Settings.Default.PaintScalePath); doc.Save(Properties.Settings.Default.PaintScalePath + @"\PPGPaint.xml"); } catch (Exception ex) { logger.Error(ex, "Error while pushing data to PPG paint scale."); } } } }