Преглед на файлове

Merge remote-tracking branch 'origin/kenric' into InABox.Client.Rest

# Conflicts:
#	prs.mobile/comal.timesheets/StoreRequis/StoreRequiConfirmationPage.xaml.cs
#	prs.mobile/comal.timesheets/StoreRequis/StoreRequiScannerPage.xaml.cs
Frank van den Bos преди 2 години
родител
ревизия
5520dd2d0e

+ 27 - 11
prs.desktop/Dashboards/HumanResources/EmployeeQualificationDashboard.xaml.cs

@@ -97,7 +97,7 @@ namespace PRSDesktop
     /// <summary>
     /// Interaction logic for EmployeeQualificationDashboard.xaml
     /// </summary>
-    public partial class EmployeeQualificationDashboard : UserControl, IDashboardWidget<HumanResources, EmployeeQualificationDashboardProperties>, IRequiresCanView<EmployeeQualification>, IActionsDashboard
+    public partial class EmployeeQualificationDashboard : UserControl, IDashboardWidget<HumanResources, EmployeeQualificationDashboardProperties>, IRequiresCanView<EmployeeQualification>, IHeaderDashboard
     {
         private DataTable DataTable;
         private CoreTable CoreTable;
@@ -124,6 +124,9 @@ namespace PRSDesktop
                 .Or(x => x.FinishDate).IsGreaterThan(DateTime.Now));
         
         public EmployeeQualificationDashboardProperties Properties { get; set; }
+
+        public DashboardHeader Header { get; set; } = new();
+
         public event LoadSettings<EmployeeQualificationDashboardProperties>? LoadSettings;
         public event SaveSettings<EmployeeQualificationDashboardProperties>? SaveSettings;
 
@@ -261,7 +264,7 @@ namespace PRSDesktop
 
             var employees = results[nameof(Employee)].ToObjects<Employee>().ToDictionary(
                 x => x.ID,
-                x => IsEmployeeActive(x));
+                IsEmployeeActive);
             if (Properties.Employees is not null)
             {
                 employees = employees
@@ -283,6 +286,8 @@ namespace PRSDesktop
             Qualifications = qualifications;
 
             DataGrid.CellDoubleTapped += DataGrid_CellDoubleTapped;
+
+            SetupHeader();
         }
 
         public void Shutdown()
@@ -291,7 +296,26 @@ namespace PRSDesktop
 
         #endregion
 
-        #region Actions
+        #region Header
+
+        public void SetupHeader()
+        {
+            var exportButton = new Button
+            {
+                Content = "Export",
+                Padding = new Thickness(5)
+            };
+            exportButton.Click += (o, e) =>
+            {
+                DoExport();
+            };
+
+            Header.BeginUpdate()
+                .Clear()
+                .AddRight(exportButton);
+
+            Header.EndUpdate();
+        }
 
         private void DoExport()
         {
@@ -328,14 +352,6 @@ namespace PRSDesktop
             ExcelExporter.DoExport(newTable, "EmployeeQualifications");
         }
 
-        public void BuildActionsMenu(ContextMenu menu)
-        {
-            if (Security.CanExport<EmployeeQualification>())
-            {
-                menu.AddItem("Export", null, DoExport);
-            }
-        }
-
         #endregion
 
         #region Grid Events

+ 5 - 4
prs.desktop/MainWindow.xaml.cs

@@ -73,6 +73,7 @@ using InABox.Wpf;
 using javax.xml.crypto;
 using PRSDesktop.Components.Spreadsheet;
 using InABox.Wpf.Reports;
+using InABox.IPC;
 
 namespace PRSDesktop
 {
@@ -168,19 +169,19 @@ namespace PRSDesktop
             switch (DatabaseType)
             {
                 case DatabaseType.Standalone:
-                    ClientFactory.SetClientType(typeof(LocalClient<>), "Wpf", CoreUtils.GetVersion());
+                    ClientFactory.SetClientType(typeof(LocalClient<>), Platform.Desktop, CoreUtils.GetVersion());
                     DbFactory.ColorScheme = App.DatabaseSettings.ColorScheme;
                     DbFactory.Logo = App.DatabaseSettings.Logo;
                     break;
                 
                 case DatabaseType.Networked:
                     var url = RestClient<User>.Ping(App.DatabaseSettings.URLs, out DatabaseInfo info);
-                    ClientFactory.SetClientType(typeof(RestClient<>), "Wpf", CoreUtils.GetVersion(), 
+                    ClientFactory.SetClientType(typeof(RestClient<>), Platform.Desktop, CoreUtils.GetVersion(), 
                         url, true);
                     break;
                 
                 case DatabaseType.Local:
-                    ClientFactory.SetClientType(typeof(PipeIPCClient<>), "Wpf", CoreUtils.GetVersion(), 
+                    ClientFactory.SetClientType(typeof(IPCClient<>), Platform.Desktop, CoreUtils.GetVersion(), 
                         DatabaseServerProperties.GetPipeName(App.DatabaseSettings.LocalServerName));
                     break;
             }
@@ -1674,7 +1675,7 @@ namespace PRSDesktop
                 return false;
 
             return _timesheets.Rows.Any(r =>
-                r.Get<TimeSheet, DateTime>(c => c.Date).Date == DateTime.Today && r.Get<TimeSheet, TimeSpan>(c => c.Finish) == new TimeSpan());
+                r.Get<TimeSheet, DateTime>(c => c.Date).Date == DateTime.Today && r.Get<TimeSheet, TimeSpan>(c => c.Finish) == TimeSpan.Zero);
         }
 
         #endregion

+ 4 - 2
prs.desktop/Utils/SelectDatabase.xaml.cs

@@ -13,6 +13,7 @@ using InABox.Client.IPC;
 using InABox.Clients;
 using InABox.Configuration;
 using InABox.Core;
+using InABox.IPC;
 using InABox.Wpf;
 using InABox.WPF;
 using PRSServer;
@@ -131,9 +132,10 @@ namespace PRSDesktop
 
                     Task.Run(() =>
                     {
-                        DatabaseInfo info = new DatabaseInfo() { Version = CoreUtils.GetVersion(), ColorScheme = db.ColorScheme, Logo = db.Logo };
+                        DatabaseInfo info = new DatabaseInfo(db.ColorScheme, db.Logo, CoreUtils.GetVersion(), true);
+                        
                         if (db.DatabaseType == DatabaseType.Local)
-                            info = new PipeIPCClient<User>(DatabaseServerProperties.GetPipeName(db.LocalServerName)).Info();
+                            info = new IPCClient<User>(DatabaseServerProperties.GetPipeName(db.LocalServerName)).Info();
                         else if (db.DatabaseType == DatabaseType.Networked)
                             RestClient<User>.Ping(db.URLs, out info);
                         UpdateInfo(key, db, info, border, image, dbver);

+ 126 - 0
prs.media/PRSDigital/Web Content.docx

@@ -0,0 +1,126 @@
+ Employee Planning
+--------------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+Assigning staff to jobs in a construction company requires careful planning and consideration of a variety of factors. Here are some key steps to follow:
+  1. --------------------------------------------------------------------------------
+  Assess the project requirements: Before assigning staff to a job, it's important to assess the project requirements, including the scope of work, timeline, and budget. This will help determine the number and type of staff needed for the job.
+     1. --------------------------------------------------------------------------------
+     Project Planner
+     2. --------------------------------------------------------------------------------
+     Activity Budgets
+  2. --------------------------------------------------------------------------------
+  Identify staff skills and expertise: Once you know what the project requires, you can identify the skills and expertise needed from staff. Consider factors such as experience, qualifications, and certifications when making staffing decisions.
+     1. --------------------------------------------------------------------------------
+     Employee Qualifications
+     2. --------------------------------------------------------------------------------
+     Employee Teams
+  3. --------------------------------------------------------------------------------
+  Evaluate staff availability: Evaluate the availability of staff to ensure that they are able to work the required hours and days for the job. Consider other ongoing projects or commitments that may impact their availability.
+     1. --------------------------------------------------------------------------------
+     Employee Rostering
+     2. --------------------------------------------------------------------------------
+     Standard Leaves
+     3. --------------------------------------------------------------------------------
+     Leave Requests
+  4. --------------------------------------------------------------------------------
+  Make staffing decisions: Based on the above factors, make decisions on which staff members are best suited for each job. Consider factors such as workload balance, project timelines, and availability of resources.
+     1. --------------------------------------------------------------------------------
+     Daily Planner
+     2. --------------------------------------------------------------------------------
+     Employee Resource Planner
+     3. --------------------------------------------------------------------------------
+     Job resource Planner
+  5. --------------------------------------------------------------------------------
+  Communicate assignments: Once you have made staffing decisions, communicate the assignments clearly to staff members. Provide them with all necessary information about the job, including expectations, timelines, and requirements.
+     1. --------------------------------------------------------------------------------
+     PRS Mobile
+  6. --------------------------------------------------------------------------------
+  Monitor performance: Monitor the performance of staff members throughout the project to ensure that they are meeting expectations and that the job is progressing as planned. Make adjustments to staffing as needed to ensure success.
+     1. --------------------------------------------------------------------------------
+     PRS Mobile
+     2. --------------------------------------------------------------------------------
+     Digital Forms
+     3. --------------------------------------------------------------------------------
+     Daily Reports
+
+ Factory Management
+--------------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+There are several advantages of using a factory management software package, including:
+  1. --------------------------------------------------------------------------------
+  Increased efficiency: A factory management software package can automate many processes, such as inventory management, production scheduling, and quality control, which can help to streamline operations and reduce manual labor.
+  2. --------------------------------------------------------------------------------
+  Improved accuracy: With a factory management software package, data is entered and processed automatically, which reduces the likelihood of errors that can occur when information is manually entered.
+  3. --------------------------------------------------------------------------------
+  Better decision-making: Factory management software can provide real-time data on production, inventory, and other key metrics, which can help managers make informed decisions more quickly and accurately.
+  4. --------------------------------------------------------------------------------
+  Cost savings: By automating processes, reducing errors, and improving efficiency, a factory management software package can help to reduce costs and increase profitability.
+  5. --------------------------------------------------------------------------------
+  Improved collaboration: A factory management software package can provide a centralized platform for collaboration between different departments and teams, which can help to improve communication and coordination.
+  6. --------------------------------------------------------------------------------
+  Better customer service: With real-time data on inventory and production, factory management software can help to improve customer service by providing accurate delivery estimates and reducing the likelihood of stockouts.
+--------------------------------------------------------------------------------
+Overall, a factory management software package can help to improve efficiency, accuracy, decision-making, collaboration, and customer service, while reducing costs and increasing profitability.
+
+ GPS Tracking
+Here are three key reasons why you should put GPS trackers on your company assets:
+  1. --------------------------------------------------------------------------------
+  Theft prevention: GPS trackers can deter theft of your company assets and help you recover them quickly in the event they are stolen. This can save you significant amounts of money in replacement costs and insurance premiums, as well as avoid any operational delays due to asset loss.
+  2. --------------------------------------------------------------------------------
+  Improved asset utilization: GPS tracking can help you optimize the use of your assets by providing valuable insights into how they are being used. You can identify underutilized assets, track usage patterns, and better allocate resources to improve efficiency and reduce costs.
+  3. --------------------------------------------------------------------------------
+  Real-time tracking: With GPS tracking, you can monitor the location and status of your assets in real-time, providing greater visibility and control over your operations. This can help you make better decisions, respond to emergencies more quickly, and better manage your resources for optimal performance.
+
+
+ Service Companies
+--------------------------------------------------------------------------------
+Service companies are always looking for ways to improve their efficiency and effectiveness, and one of the best ways to do so is by utilizing software designed specifically for their needs.
+--------------------------------------------------------------------------------
+Whether you're in the healthcare industry, hospitality, transportation, or any other service-based industry, software can help streamline your operations and boost your bottom line.
+--------------------------------------------------------------------------------
+With software for service companies, you can automate tedious tasks like scheduling, billing, and inventory management, freeing up valuable time for you and your team to focus on more important tasks.
+--------------------------------------------------------------------------------
+Additionally, software can help you track and analyze important data like customer satisfaction rates, employee productivity, and revenue streams, giving you invaluable insights into your business's performance and helping you make informed decisions about where to focus your resources.
+--------------------------------------------------------------------------------
+The benefits of software for service companies are clear, and the options available on the market today are more powerful and user-friendly than ever before. Don't wait to start improving your business - consider investing in software today and take your service company to the next level.
+
+ Manufacturing Businesses
+When looking for software for your manufacturing business, here are some key features you should consider:
+  1. --------------------------------------------------------------------------------
+  Production Planning and Scheduling: The software should have the ability to plan and schedule production, including the allocation of resources and materials.
+  2. --------------------------------------------------------------------------------
+  Inventory Management: The software should have the ability to manage inventory levels and track the movement of raw materials, work-in-progress and finished goods throughout the production process.
+  3. --------------------------------------------------------------------------------
+  Quality Control: The software should have the ability to manage quality control processes such as inspection, testing, and corrective action.
+  4. --------------------------------------------------------------------------------
+  Traceability: The software should have the ability to trace the movement of products and components throughout the production process, including their origin, production date, and any processing steps they went through.
+  5. --------------------------------------------------------------------------------
+  Reporting and Analytics: The software should have the ability to generate reports and analytics on production processes, inventory levels, quality control data, and other key performance indicators.
+  6. --------------------------------------------------------------------------------
+  Integration: The software should have the ability to integrate with other systems such as accounting and supply chain management systems.
+  7. --------------------------------------------------------------------------------
+  User Interface: The software should have a user-friendly interface that is easy to navigate and use.
+  8. --------------------------------------------------------------------------------
+  Scalability: The software should have the ability to scale with your business as it grows and expands.
+  9. --------------------------------------------------------------------------------
+  Security: The software should have robust security features to protect sensitive data and prevent unauthorized access.
+  10. --------------------------------------------------------------------------------
+  Customer Support: The software provider should offer reliable customer support to help you address any issues or questions that arise.
+
+ Kanban Based Task Tracking
+
+--------------------------------------------------------------------------------
+Introducing the most efficient way to track your tasks - Kanban-based task tracking!
+--------------------------------------------------------------------------------
+Are you tired of keeping track of your never-ending to-do list? Do you find yourself overwhelmed with the number of tasks you have to complete? Look no further than PRS Task tracking!
+--------------------------------------------------------------------------------
+PRS contains a  visual project management system that allows you to easily track the progress of your tasks. With PRS task tracking, you can quickly and easily organize your tasks into different categories or stages such as "to do," "in progress," and "done." This system makes it easy to see which tasks are a priority and which ones can wait.
+--------------------------------------------------------------------------------
+Our Kanban-based task tracking system is user-friendly, allowing you to easily drag and drop tasks into different categories as you complete them. You can also assign tasks to team members, set deadlines, and add notes or attachments to each task.
+--------------------------------------------------------------------------------
+By using Kanban-based task tracking, you can increase your productivity, reduce your stress levels, and stay on top of all your tasks. With a clear visualization of your workflow, you can quickly identify bottlenecks and make adjustments to keep your projects moving forward.
+--------------------------------------------------------------------------------
+Don't waste any more time trying to keep track of your tasks on a piece of paper or in a cluttered spreadsheet. Upgrade to PRS task tracking today and start seeing results!
+

+ 346 - 0
prs.mobile/comal.timesheets/StoreRequis/StoreRequisMainPage.xaml.cs

@@ -0,0 +1,346 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xamarin.Essentials;
+using Xamarin.Forms;
+using ZXing;
+using ZXing;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using comal.timesheets.CustomControls;
+using Comal.Classes;
+using InABox.Core;
+using InABox.Clients;
+using System.Threading;
+using static comal.timesheets.RequiItems;
+
+namespace comal.timesheets.StoreRequis
+{
+    [XamlCompilation(XamlCompilationOptions.Compile)]
+    public partial class StoreRequisMainPage : ContentPage
+    {
+
+        public delegate bool OnScanEvent(object sender, String barcode);
+
+        public event OnScanEvent OnScan;
+
+        Dictionary<StoreRequiItem, string> itemRowScannerRawResultPairs;
+
+        Dictionary<StoreRequiItem, string> itemRowScannerProcessedResultPairs;
+
+        Dictionary<int, StoreRequiItem> idItemRowPairs;
+
+        bool choosingLocation;
+
+        Requisition requisition;
+
+        bool newRequisition;
+
+        int count;
+
+        int itemsCount;
+
+        #region Constructor, appearing and disappearing
+
+        public StoreRequisMainPage(Guid _requiID)
+        {
+            InitializeComponent();
+            var options = new ZXing.Mobile.MobileBarcodeScanningOptions()
+            {
+                PossibleFormats = new List<ZXing.BarcodeFormat>() { ZXing.BarcodeFormat.QR_CODE },
+                AutoRotate = false,
+                TryInverted = true,
+                TryHarder = true,
+            };
+            _scanView.Options = options;
+            _scanView.IsAnalyzing = false;
+            _scanView.IsScanning = true;
+            _scanView.OnScanResult += ScanView_OnScanResult;
+            count = 0;
+            choosingLocation = false;
+            itemRowScannerRawResultPairs = new Dictionary<StoreRequiItem, string>();
+            itemRowScannerProcessedResultPairs = new Dictionary<StoreRequiItem, string>();
+            idItemRowPairs = new Dictionary<int, StoreRequiItem>();
+
+            RequiItems.NewRequisitionRows = new List<StoreRequiItem>();
+            RequiItems.OldRequisitionItems = new List<RequisitionItem>();
+
+            if (_requiID != Guid.Empty)
+            {
+                Title = "Loading";
+                requisition = new Requisition();
+                requisition.ID = _requiID;
+                LoadExistingRequi();
+            }
+            else
+            {
+                Title = "Scan Items";
+                newRequisition = true;
+                requisition = new Requisition();
+            }
+        }
+        private async void LoadExistingRequi()
+        {
+            await Task.Run(() =>
+            {
+                requisition = new Client<Requisition>().Query(
+                    new Filter<Requisition>(x => x.ID).IsEqualTo(requisition.ID)
+                    ).Rows.FirstOrDefault().ToObject<Requisition>();
+                if (!string.IsNullOrWhiteSpace(requisition.Request))
+                {
+                    Label notesLbl = new Label()
+                    {
+                        Text = requisition.Request,
+                        Margin = 0,
+                        Padding = 0
+                    };
+                    
+                    
+                    Device.BeginInvokeOnMainThread(() =>
+                    {
+                        requestFrame.Content = notesLbl;
+                        requestFrame.IsVisible = true;
+                    });
+                }
+
+                if (RequiItems.OldRequisitionItems.Count > 0)
+                    RequiItems.OldRequisitionItems.Clear();
+                CoreTable table = new Client<RequisitionItem>().Query
+                    (
+                    new Filter<RequisitionItem>(x => x.RequisitionLink.ID).IsEqualTo(requisition.ID)
+                    );
+                if (table.Rows.Any())
+                {
+                    foreach (CoreRow row in table.Rows)
+                    {
+                        RequisitionItem requisitionItem = row.ToObject<RequisitionItem>();
+                        LoadProduct(new Tuple<string, double>(requisitionItem.Product.Code, requisitionItem.Quantity), requisitionItem.Product.Code, requisitionItem);
+                        RequiItems.OldRequisitionItems.Add(requisitionItem);
+                    }
+                }
+                Device.BeginInvokeOnMainThread(() =>
+                {
+                    Title = "Scan Items";
+                });
+
+            });
+        }
+
+        protected override void OnAppearing()
+        {
+            base.OnAppearing();
+            _scanView.IsAnalyzing = true;
+        }
+
+        protected override void OnDisappearing()
+        {
+            _scanView.IsAnalyzing = false;
+            base.OnDisappearing();
+        }
+
+        #endregion
+
+        #region Scan Complete and process results
+        private void ScanView_OnScanResult(ZXing.Result result)
+        {
+            Device.BeginInvokeOnMainThread(() =>
+            {
+                if (!choosingLocation)
+                {
+                    if (RequiItems.HoldingsLoaded)
+                    {
+                        bool bOK = true;
+                        if (OnScan != null)
+                            bOK = OnScan(this, result.Text);
+                        if (bOK)
+                        {
+                            if (!itemRowScannerRawResultPairs.Values.Contains(result.Text))
+                            {
+                                if (!itemRowScannerProcessedResultPairs.Values.Contains(result.Text))
+                                {
+                                    Vibration.Vibrate();
+                                    string rawResult = result.Text;
+                                    Tuple<string, double> tuple = ProcessResult(result.Text);
+                                    LoadProduct(tuple, rawResult, new RequisitionItem());
+                                }
+                            }
+                        }
+                    }
+                }
+            });
+        }
+
+        private Tuple<string, double> ProcessResult(string result)
+        {
+            double qty = 1;
+            if (result.Contains("*"))
+            {
+                try
+                {
+                    int i = result.IndexOf("*");
+                    string remainder = result.Substring(i);
+                    result = result.Remove(i);                    
+                    string s1 = remainder.Substring(1);
+                    qty = Convert.ToDouble(s1);
+                }
+                catch { }
+            }
+            Tuple<string, double> tuple = new Tuple<string, double>(result, qty);
+            return tuple;
+        }
+
+        private void LoadProduct(Tuple<string, double> processedResultQtyTuple, string rawResult, RequisitionItem requisitionItem)
+        {
+            Device.BeginInvokeOnMainThread(async () =>
+            {
+                //lookup product in productshells cache
+                ProductShell product = GlobalVariables.ProductShells.Find(x => x.Code.Equals(processedResultQtyTuple.Item1));
+
+                //lookup holding for product in holdings cache
+                //List<HoldingsCacheShell> list = new List<HoldingsCacheShell>();
+                string itemLocation = "";
+                Guid holdingID = Guid.Empty;
+
+                if (requisitionItem.ID == Guid.Empty)
+                {
+                    try
+                    {
+                        List<HoldingsCacheShell> list = holdingsCache.Where(x => x.ProductID.Equals(product.ID)).ToList();
+                        if (list.Count == 1) //one stockholding - auto choose shelf
+                        {
+                            HoldingsCacheShell holding = list.First();
+                            itemLocation = holding.LocationName;
+                            holdingID = holding.ID;
+                        }
+                        else if (list.Count > 1)  //more than one stockholding - choose shelf
+                        {
+                            choosingLocation = true;
+                            Dictionary<string, Guid> holdingIDLocations = new Dictionary<string, Guid>();
+                            foreach (HoldingsCacheShell holding in list)
+                            {
+                                if (!holdingIDLocations.ContainsKey(holding.LocationName + " (Qty: " + holding.Units + ")"))
+                                {
+                                    holdingIDLocations.Add(holding.LocationName + " (Qty: " + holding.Units + ")", holding.ID);
+                                }
+                            }
+                            string[] array = holdingIDLocations.Keys.ToArray();
+                            string chosenOption = await DisplayActionSheet("Choose Location", "Cancel", null, array);
+                            if (chosenOption != null && chosenOption != "Cancel")
+                            {
+                                itemLocation = chosenOption;
+                                holdingID = holdingIDLocations[chosenOption];
+                            }
+                            else
+                                return;
+                        }
+                        else if (list.Count == 0)
+                        {
+                            DisplayAlert("No Holdings Found for Product", "", "OK");
+                            return;
+                        }
+                    }
+                    catch(Exception e)
+                    {
+                        DisplayAlert("Error", e.Message, "OK");
+                        return;
+                    }
+                }
+                else
+                {
+                    itemLocation = requisitionItem.Location.Description;
+                    holdingID = requisitionItem.Location.ID;
+                }
+
+                StoreRequiItem storeRequiItem = new StoreRequiItem(product, processedResultQtyTuple.Item2, itemLocation, holdingID) //default qty is 1
+                {
+                    ID = count,
+                };
+                storeRequiItem.RequiItemID = requisitionItem.ID;
+                storeRequiItem.OnZeroSelected += StoreRequiItem_OnZeroSelected;
+                storeRequiItem.OnParseError += OnParseError;
+
+                idItemRowPairs.Add(count, storeRequiItem);
+                itemRowScannerRawResultPairs.Add(storeRequiItem, rawResult);
+                itemRowScannerProcessedResultPairs.Add(storeRequiItem, processedResultQtyTuple.Item1);
+                itemRowsFlexlayout.Children.Add(storeRequiItem);
+                count++;
+                itemsCount++;
+                Title = "Store Requis (" + itemsCount + ")";
+                choosingLocation = false;
+                itemsScroller.ScrollToAsync(storeRequiItem, ScrollToPosition.Center, true);
+            });
+        }
+        #endregion
+
+        #region Buttons Pressed
+        private void Add_Clicked(object sender, EventArgs e)
+        {
+            if (GlobalVariables.ProductsLoaded)
+            {
+                ProductList products = new ProductList(GlobalVariables.ProductShells, true);
+                products.OnProductSelected += ()=> 
+                {
+                    Tuple<string, double> tuple = new Tuple<string, double>(products.SelectedProduct.Code, 1);
+
+                    LoadProduct(new Tuple<string, double>(products.SelectedProduct.Code, 1), products.SelectedProduct.Code, new RequisitionItem());
+                };
+                Navigation.PushAsync(products);
+            }
+        }
+
+        private void NextBtn_Clicked(object sender, EventArgs e)
+        {
+            if (RequiItems.NewRequisitionRows.Count > 0)
+                RequiItems.NewRequisitionRows.Clear();
+
+            if (idItemRowPairs.Count > 0)
+            {
+                //RequiItems.NewRequisitionRows = idItemRowPairs.Values.ToList();
+                //StoreRequiConfirmationPage storeRequiConfirmationPage = new StoreRequiConfirmationPage(requisition);
+                //storeRequiConfirmationPage.OnSaveSelected += () => { Navigation.PopAsync(); };
+                //Navigation.PushAsync(storeRequiConfirmationPage);
+            }
+            else
+            {
+                DisplayAlert("Alert", "Please add items", "Cancel");
+            }
+        }
+        private async void StoreRequiItem_OnZeroSelected(int ID)
+        {
+            string chosenOption = await DisplayActionSheet("Remove Item?", "Cancel", null, "Yes", "No");
+            switch (chosenOption)
+            {
+                case "Cancel":
+                    return;
+                    break;
+                case "No":
+                    return;
+                    break;
+                case "Yes":
+                    break;
+                default:
+                    return;
+                    break;
+            }
+            StoreRequiItem storeRequiItem = idItemRowPairs[ID];
+            itemRowsFlexlayout.Children.Remove(storeRequiItem);
+            idItemRowPairs.Remove(ID);
+            itemRowScannerRawResultPairs.Remove(storeRequiItem);
+            itemRowScannerProcessedResultPairs.Remove(storeRequiItem);
+            itemsCount--;
+            Title = "Store Requis (" + itemsCount + ")";
+        }
+
+        private async void OnParseError()
+        {
+            DisplayAlert("Error", "Enter only numbers", "OK");
+        }
+
+        #endregion
+
+
+
+    }
+}

+ 6 - 6
prs.server/Engines/Database/DatabaseEngine.cs

@@ -12,9 +12,9 @@ using InABox.Core;
 using InABox.Database;
 using InABox.Database.SQLite;
 using InABox.DeviceIdentifier;
+using InABox.IPC;
 using InABox.Server;
 using InABox.Wpf.Reports;
-using Piping;
 using PRS.Shared;
 using Timer = System.Timers.Timer;
 
@@ -25,7 +25,7 @@ namespace PRSServer
         private Timer? CertificateRefreshTimer;
         private Timer? CertificateHaltTimer;
         private string PipeName;
-        private PipeIPCServer? PipeServer;
+        private IPCServer? PipeServer;
 
         public override void Configure(Server server)
         {
@@ -90,7 +90,7 @@ namespace PRSServer
             var user = CredentialsCache.Validate(session);
             if (user == null)
                 return Array.Empty<Notification>();
-            var store = DbFactory.FindStore<Notification>(user.ID, user.UserID, "", "");
+            var store = DbFactory.FindStore<Notification>(user.ID, user.UserID, default(Platform), "");
             return store.Query(
                 new Filter<Notification>(x => x.Employee.UserLink.ID).IsEqualTo(user.ID)
                     .And(x => x.Closed).IsEqualTo(DateTime.MinValue),
@@ -117,7 +117,7 @@ namespace PRSServer
 
         private void ConfigureNotifier()
         {
-            Notify.Notifier?.AddPollHandler(PollNotifications);
+            Notify.AddPollHandler(PollNotifications);
         }
 
         public override void Run()
@@ -197,7 +197,7 @@ namespace PRSServer
             Logger.Send(LogType.Information, "", string.Format("Rest Server Started listening on port {0}", Properties.Port));
 
             Logger.Send(LogType.Information, "", $"Starting Pipe Listener with pipe name {PipeName}");
-            PipeServer = new PipeIPCServer(PipeName);
+            PipeServer = new IPCServer(PipeName);
             PipeServer.Start();
 
             Logger.Send(LogType.Information, "", "Pipe Listener started");
@@ -279,7 +279,7 @@ namespace PRSServer
                     notification.Employee.ID = employee.ID;
                     notification.Title = "HTTPS Certificate expires soon";
                     notification.Description = message;
-                    DbFactory.FindStore<Notification>(employee.UserLink.ID, employee.UserLink.UserID, "", "").Save(notification, "");
+                    DbFactory.FindStore<Notification>(employee.UserLink.ID, employee.UserLink.UserID, default(Platform), "").Save(notification, "");
                 }
                 else
                 {

+ 2 - 1
prs.server/Engines/GPS/GPSEngine.cs

@@ -12,6 +12,7 @@ using InABox.Client.IPC;
 using InABox.Clients;
 using InABox.Core;
 using InABox.DigitalMatter;
+using InABox.IPC;
 using PRSServer.Engines;
 
 namespace PRSServer
@@ -226,7 +227,7 @@ namespace PRSServer
             CoreUtils.RegisterClasses();
             ComalUtils.RegisterClasses();
 
-            ClientFactory.SetClientType(typeof(PipeIPCClient<>), "GPSServer", Version, DatabaseServerProperties.GetPipeName(Properties.Server));
+            ClientFactory.SetClientType(typeof(IPCClient<>), Platform.GPSEngine, Version, DatabaseServerProperties.GetPipeName(Properties.Server));
             CheckConnection();
 
             DMFactory.Initialise(Properties.DumpFormat, Properties.DumpFile);

+ 2 - 1
prs.server/Engines/Scheduler/ScheduleEngine.cs

@@ -4,6 +4,7 @@ using Comal.TaskScheduler.Shared;
 using InABox.Client.IPC;
 using InABox.Clients;
 using InABox.Core;
+using InABox.IPC;
 
 namespace PRSServer
 {
@@ -33,7 +34,7 @@ namespace PRSServer
                     Logger.Send(LogType.Error, "", "Server is blank!");
                     return;
                 }
-                ClientFactory.SetClientType(typeof(PipeIPCClient<>), "Scheduler", Version, DatabaseServerProperties.GetPipeName(Properties.Server));
+                ClientFactory.SetClientType(typeof(IPCClient<>), Platform.SchedulerEngine, Version, DatabaseServerProperties.GetPipeName(Properties.Server));
                 CheckConnection();
 
                 Logger.Send(LogType.Information, "", "Starting Scheduler: ");

+ 2 - 1
prs.server/Engines/WebEngine/WebEngine.cs

@@ -6,6 +6,7 @@ using InABox.Client.IPC;
 using InABox.Clients;
 using InABox.Configuration;
 using InABox.Core;
+using InABox.IPC;
 using InABox.Wpf.Reports;
 
 namespace PRSServer
@@ -29,7 +30,7 @@ namespace PRSServer
                 Logger.Send(LogType.Error, "", "Server is blank!");
                 return;
             }
-            ClientFactory.SetClientType(typeof(PipeIPCClient<>), "WebEngine", Version, DatabaseServerProperties.GetPipeName(Properties.Server));
+            ClientFactory.SetClientType(typeof(IPCClient<>), Platform.WebEngine, Version, DatabaseServerProperties.GetPipeName(Properties.Server));
 
             Logger.Send(LogType.Information, "", "Registering Classes");
 

+ 5 - 5
prs.server/Forms/ServerGrid.cs

@@ -27,7 +27,7 @@ using InABox.Database;
 using InABox.Database.SQLite;
 using InABox.DeviceIdentifier;
 using InABox.DynamicGrid;
-using InABox.IPC.Shared;
+using InABox.IPC;
 using InABox.Scripting;
 using InABox.Wpf.Editors;
 using InABox.WPF;
@@ -372,7 +372,7 @@ namespace PRSServer
                                 {
                                     var client = IPCClientFactory.GetClient(DatabaseServerProperties.GetPipeName(key));
 
-                                    var response = client.Send(PipeRequest.Info(new InfoRequest()), 10_000).GetResponse<InfoResponse>();
+                                    var response = client.Send(IPCMessage.Info(new InfoRequest()), 10_000).GetResponse<InfoResponse>();
                                     if (response.Status != StatusCode.Error)
                                     {
                                         SecureConnections[key] = response.Info.IsHTTPS;
@@ -1028,7 +1028,7 @@ namespace PRSServer
                         currentServerURL = url;
                         currentServerPort = port;
                         currentServerName = null;
-                        ClientFactory.SetClientType(typeof(RestClient<>), "PRSServer", CoreUtils.GetVersion(), url, port, true);
+                        ClientFactory.SetClientType(typeof(RestClient<>), Platform.Server, CoreUtils.GetVersion(), url, port, true);;
 
                         // override the need to provide credentials when configuring the database
                         ClientFactory.SetBypass();
@@ -1088,7 +1088,7 @@ namespace PRSServer
                         currentServerPort = null;
                         currentServerURL = null;
                         currentServerName = pipeName;
-                        ClientFactory.SetClientType(typeof(PipeIPCClient<>), "PRSServer", CoreUtils.GetVersion(), pipeName);
+                        ClientFactory.SetClientType(typeof(IPCClient<>), Platform.Server, CoreUtils.GetVersion(), pipeName);
                         using (new WaitCursor())
                         {
                             if (!Client.Ping())
@@ -1145,7 +1145,7 @@ namespace PRSServer
 
                 if (!DbFactory.IsProviderSet || DbFactory.Provider is not SQLiteProvider sql || sql.URL != properties.FileName)
                 {
-                    ClientFactory.SetClientType(typeof(LocalClient<>), "PRSServer", CoreUtils.GetVersion(), "");
+                    ClientFactory.SetClientType(typeof(LocalClient<>), Platform.Server, CoreUtils.GetVersion(), "");
                     Progress.ShowModal("Configuring database", (progress) =>
                     {
                         DbFactory.Stores = CoreUtils.TypeList(

+ 39 - 0
prs.shared/DatabaseUpdateScripts.cs

@@ -10,6 +10,7 @@ using System.Threading.Tasks;
 using Syncfusion.Windows.Tools.Controls;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
+using FastReport.Utils;
 
 namespace PRS.Shared
 {
@@ -24,6 +25,7 @@ namespace PRS.Shared
             DataUpdater.RegisterUpdateScript("6.43", Update_6_43);
             DataUpdater.RegisterUpdateScript("7.00", Update_7_00);
             DataUpdater.RegisterUpdateScript("7.06", Update_7_06);
+            DataUpdater.RegisterUpdateScript("7.14", Update_7_14);
         }
 
         private static Dictionary<string, Tuple<string, string>> _6_31_module_map = new()
@@ -531,5 +533,42 @@ namespace PRS.Shared
             Logger.Send(LogType.Information, "", "Finished updating Deletions");
             return true;
         }
+
+        /// <summary>
+        /// Updating Wpf and Timebench fields to use Platform.DesktopVersion and Platform.MobileVersion
+        /// </summary>
+        /// <returns></returns>
+        private static bool Update_7_14()
+        {
+            Logger.Send(LogType.Information, "", "Converting User.Wpf, User.Timebench -> User.Platform.DesktopVersion, User.Platform.MobileVersion");
+
+            Logger.Send(LogType.Information, "", "Loading Wpf, Timebench properties");
+            var props = DbFactory.Provider.Query<CustomProperty>(new Filter<CustomProperty>(x => x.Name).InList("Wpf", "TimeBench"))
+                .Rows.Select(x => x.ToObject<CustomProperty>()).ToArray();
+            DatabaseSchema.Load(props);
+
+            var columns = new Columns<User>(x => x.ID);
+            columns.Add("Wpf", "TimeBench");
+
+            var users = DbFactory.Provider.Query<User>(
+                new Filter<User>().All(),
+                columns).ToObjects<User>().ToList();
+            foreach(var user in users)
+            {
+                if(user.UserProperties.Dictionary.TryGetValue("Wpf", out var wpf))
+                {
+                    user.Platform.DesktopVersion = wpf?.Value?.ToString() ?? "";
+                }
+                if (user.UserProperties.Dictionary.TryGetValue("TimeBench", out var timebench))
+                {
+                    user.Platform.MobileVersion = timebench?.Value?.ToString() ?? "";
+                }
+            }
+            DbFactory.Provider.Save<User>(users);
+
+            Logger.Send(LogType.Information, "", "Finished updating user versions");
+
+            return true;
+        }
     }
 }

+ 21 - 8
prs.stores/BaseStore.cs

@@ -12,7 +12,7 @@ namespace Comal.Stores
     public class UserPlatform
     {
         public string UserID { get; set; }
-        public string Platform { get; set; }
+        public Platform Platform { get; set; }
         public string Version { get; set; }
     }
 
@@ -49,7 +49,7 @@ namespace Comal.Stores
 
         private void CheckPlatformVersion()
         {
-            if (string.IsNullOrEmpty(UserID) || string.IsNullOrEmpty(Platform) || string.IsNullOrEmpty(Version))
+            if (string.IsNullOrEmpty(UserID) || string.IsNullOrEmpty(Version))
                 return;
 
             var platform = PlatformCache.Platforms.FirstOrDefault(x => x.UserID.Equals(UserID) && x.Platform.Equals(Platform));
@@ -62,16 +62,29 @@ namespace Comal.Stores
             if (!platform.Version.Equals(Version))
             {
                 platform.Version = Version;
-                var prop = DatabaseSchema.Property(typeof(User), Platform);
-                if (prop != null && prop.Getter() != null && prop.Setter() != null)
+
+                if(Platform == Platform.Desktop || Platform == Platform.Mobile)
                 {
                     var user = Provider.Load(new Filter<User>(x => x.UserID).IsEqualTo(UserID)).FirstOrDefault();
-                    if (user != null)
+                    if(user is not null)
                     {
-                        var value = prop.Getter().Invoke(user);
-                        if (!Version.Equals(value))
+                        var current = Platform switch
+                        {
+                            Platform.Desktop => user.Platform.DesktopVersion,
+                            Platform.Mobile => user.Platform.MobileVersion,
+                            _ => ""
+                        };
+                        if (!Version.Equals(current))
                         {
-                            prop.Setter().Invoke(user, Version);
+                            switch (Platform)
+                            {
+                                case Platform.Desktop:
+                                    user.Platform.DesktopVersion = Version;
+                                    break;
+                                case Platform.Mobile:
+                                    user.Platform.MobileVersion = Version;
+                                    break;
+                            }
                             Provider.Save(user);
                         }
                     }