Quellcode durchsuchen

Added UseLogikalProjectNumber to LogikalSettings
Added UseV6QuoteNumber to V6Settings
Fixed mssing IntegrationSourceType in mappings screen
Added JobStageType to JobStage class
Added Month View as default project Planner
Tweaked Meeting Panel layout

frankvandenbos vor 9 Monaten
Ursprung
Commit
ce151cd0d1

+ 67 - 8
prs.classes/Entities/Job/JobStage.cs

@@ -1,7 +1,9 @@
 using System;
+using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
 using InABox.Core;
+using Newtonsoft.Json;
 
 namespace Comal.Classes
 {
@@ -17,7 +19,61 @@ namespace Comal.Classes
         public override Columns<Job> DefineFilterColumns()
             => Columns.None<Job>().Add(x => x.ID);
     }
+    
+    
+    [UserTracking("Gantt Charts")]
+    public class JobStageType : Entity, IRemotable, IPersistent, ILicense<ProjectManagementLicense>
+    {
+        [EditorSequence(1)]
+        [UniqueCodeEditor(Visible = Visible.Default, Editable = Editable.Enabled)]
+        public string Code { get; set; }
+
+        [EditorSequence(2)]
+        public string Description { get; set; }
+
+        [EditorSequence(3)]
+        [ColorEditor]
+        public string Color { get; set; }
+    }
+    
+    public class JobStageTypeLink : EntityLink<JobStageType>
+    {
+        [CodePopupEditor(typeof(JobStageType), CanAdd = true)]
+        public override Guid ID { get; set; }
+        
+        [EditorSequence(1)]
+        [CodeEditor(Visible = Visible.Default, Editable = Editable.Hidden)]
+        public string Code { get; set; }
 
+        [EditorSequence(2)]
+        [CodeEditor(Visible = Visible.Optional, Editable = Editable.Hidden)]
+        public string Description { get; set; }
+
+        [EditorSequence(3)]
+        [ColorEditor(Visible = Visible.Optional, Editable = Editable.Hidden)]
+        public string Color { get; set; }
+    }
+    
+    public class JobStageTypeLookups : EntityLookup<JobStageType>
+    {
+        public override Filter<JobStageType> DefineFilter() 
+            => new Filter<JobStageType>().All();
+        
+        public override Columns<JobStageType> DefineColumns() 
+            => Columns.None<JobStageType>()
+                .Add(x => x.ID)
+                .Add(x => x.Code)
+                .Add(x => x.Description)
+                .Add(x=>x.Color);
+        
+        public override SortOrder<JobStageType>? DefineSortOrder() 
+            => new SortOrder<JobStageType>(x=>x.Code, SortDirection.Ascending);
+
+        public override string FormatLookup(Dictionary<string, object?> values, IEnumerable<string> exclude) 
+            => values.TryGetValue("Description", out var description) ? description?.ToString() ?? "" : "";
+    }
+    
+    
     [UserTracking("Gantt Charts")]
     public class JobStage : Entity, IRemotable, IPersistent, ISequenceable, INotifyPropertyChanged, ILicense<ProjectManagementLicense>, IOneToMany<Job>,
         IChildEntityLookup<Job, JobStage, JobStageJobLookup>
@@ -46,36 +102,39 @@ namespace Comal.Classes
         [LookupDefinition(typeof(JobStageLookup))]
         [NullEditor]
         public JobStageLink Parent { get; set; }
-
+        
         [EditorSequence(1)]
-        public string Name { get; set; }
+        public JobStageTypeLink Type { get; set; }
 
         [EditorSequence(2)]
+        public string Name { get; set; }
+
+        [EditorSequence(3)]
         [NullEditor]
         public JobStageCalendarLink Calendar { get; set; }
 
         [DateEditor]
-        [EditorSequence(3)]
+        [EditorSequence(4)]
         public DateTime StartDate { get; set; }
 
         [IntegerEditor]
-        [EditorSequence(4)]
+        [EditorSequence(5)]
         public int WorkDays { get; set; }
 
         [DateEditor]
-        [EditorSequence(5)]
+        [EditorSequence(6)]
         public DateTime EndDate { get; set; }
 
         [DoubleEditor]
-        [EditorSequence(9)]
+        [EditorSequence(7)]
         public double SupervisionHours { get; set; }
 
         [DoubleEditor]
-        [EditorSequence(10)]
+        [EditorSequence(8)]
         public double TradesHours { get; set; }
 
         [DoubleEditor]
-        [EditorSequence(11)]
+        [EditorSequence(9)]
         public double ApprenticeHours { get; set; }
 
         [DoubleEditor(Editable = Editable.Disabled)]

+ 1 - 0
prs.classes/Integrations/BaseIntegrationSource.cs

@@ -8,6 +8,7 @@ namespace Comal.Classes
         string? Code { get; set; }
         string? Description { get; set; }
         IEntityLink Entity { get; } 
+        IntegrationSourceType Source { get; set; }
     }
     
     public abstract class BaseIntegrationSource<TEntity,TLink> : Entity, IRemotable, IPersistent, IOneToMany<TEntity>, IBaseIntegrationSource

+ 4 - 0
prs.classes/Integrations/Logikal/LogikalSettings.cs

@@ -45,6 +45,10 @@ namespace Comal.Classes
         [EditorSequence(4)]
         [Caption("Import Projects")]
         public LogikalProjectType ImportProjects { get; set; }
+        
+        [CheckBoxEditor]
+        [EditorSequence(6)]
+        public bool UseLogikalProjectNumber { get; set; }
 
         [EnumLookupEditor(typeof(LogikalCostType))]
         [EditorSequence(5)]

+ 6 - 2
prs.classes/Integrations/V6/V6Settings.cs

@@ -52,14 +52,18 @@ namespace Comal.Classes
         [EditorSequence(5)]
         [Caption("Import Projects")]
         public V6ProjectType ImportProjects { get; set; }
+        
+        [CheckBoxEditor]
+        [EditorSequence(6)]
+        public bool UseV6QuoteNumber { get; set; }
 
         [EnumLookupEditor(typeof(V6CostType))]
-        [EditorSequence(6)]
+        [EditorSequence(7)]
         [Caption("Import Costs")]
         public V6CostType ImportCosts { get; set; }
 
         [EnumLookupEditor(typeof(V6DesignType))]
-        [EditorSequence(7)]
+        [EditorSequence(8)]
         [Caption("Import Designs")]
         public V6DesignType ImportDesigns { get; set; }
         

+ 3 - 8
prs.desktop/Components/Calendar/Calendar.xaml.cs

@@ -1259,13 +1259,8 @@ namespace PRSDesktop
         }
         
         
-        private Dictionary<CalendarMenuName, MenuItem> _menuitems = new Dictionary<CalendarMenuName, MenuItem>();
-
-        public MenuItem? FindMenu(CalendarMenuName name)
-        {
-            _menuitems.TryGetValue(name, out MenuItem result);
-            return result;
-        }
+        //private Dictionary<CalendarMenuName, MenuItem> _menuitems = new Dictionary<CalendarMenuName, MenuItem>();
+        
         
         MenuItem CreateMenu<T>(ItemsControl menu, CalendarMenuName name, string header, Action<T?>? action = null, T? data = null) where T : class
         {
@@ -1276,7 +1271,7 @@ namespace PRSDesktop
                 item.Click += (o,args) => action(data);
             item.IsEnabled = data != null;
             menu.Items.Add(item);
-            _menuitems[name] = item;
+            //_menuitems[name] = item;
             return item;
         }
         

+ 12 - 7
prs.desktop/Integrations/Common/AWGMappingWindow.xaml

@@ -77,7 +77,8 @@
             Margin="5,0,0,0"
             Visibility="{Binding SelectedSection, Converter={StaticResource StylesGridVisible}}"
             ItemsSource="{Binding FinishMappings}"
-            CreateEntity="{Binding CreateStyle}"/>
+            CreateEntity="{Binding CreateStyle}"
+            SourceType="{Binding SourceType}"/>
         
         <local:ProductIntegrationGrid
             Grid.Row="0"
@@ -85,7 +86,8 @@
             Margin="5,0,0,0"
             Visibility="{Binding SelectedSection, Converter={StaticResource ProfilesGridVisible}}"
             ItemsSource="{Binding ProfileMappings}"
-            CreateEntity="{Binding CreateProfile}"/>
+            CreateEntity="{Binding CreateProfile}"
+            SourceType="{Binding SourceType}"/>
                 
         <local:ProductIntegrationGrid
             Grid.Row="0"
@@ -93,7 +95,8 @@
             Margin="5,0,0,0"
             Visibility="{Binding SelectedSection, Converter={StaticResource GasketsGridVisible}}"
             ItemsSource="{Binding GasketMappings}"
-            CreateEntity="{Binding CreateGasket}"/>
+            CreateEntity="{Binding CreateGasket}"
+            SourceType="{Binding SourceType}"/>
         
         <local:ProductIntegrationGrid
             Grid.Row="0"
@@ -101,8 +104,8 @@
             Margin="5,0,0,0"
             Visibility="{Binding SelectedSection, Converter={StaticResource ComponentsGridVisible}}"
             ItemsSource="{Binding ComponentMappings}"
-            CreateEntity="{Binding CreateComponent}"/>
-        
+            CreateEntity="{Binding CreateComponent}"
+            SourceType="{Binding SourceType}"/>
         
         <local:ProductIntegrationGrid
             Grid.Row="0"
@@ -110,7 +113,8 @@
             Margin="5,0,0,0"
             Visibility="{Binding SelectedSection, Converter={StaticResource GlassGridVisible}}"
             ItemsSource="{Binding GlassMappings}"
-            CreateEntity="{Binding CreateGlass}"/>
+            CreateEntity="{Binding CreateGlass}"
+            SourceType="{Binding SourceType}"/>
         
         <local:ActivityIntegrationGrid
             Grid.Row="0"
@@ -118,7 +122,8 @@
             Margin="5,0,0,0"
             Visibility="{Binding SelectedSection, Converter={StaticResource LabourGridVisible}}" 
             ItemsSource="{Binding LabourMappings}"
-            CreateEntity="{Binding CreateActivity}"/>
+            CreateEntity="{Binding CreateActivity}"
+            SourceType="{Binding SourceType}"/>
         
         <DockPanel
             Grid.Row="1"

+ 2 - 0
prs.desktop/Integrations/Common/AWGMappingWindow.xaml.cs

@@ -19,6 +19,7 @@ public partial class AWGMappingWindow : Window
     private readonly Action<ActivityLink,TimeSpan, double>? _labourCallback;
     
     public AWGMappingWindow(
+        IntegrationSourceType sourceType,
         //Guid jobid, 
         IEnumerable<IAwgFinish> finishes, 
         IEnumerable<IAwgProfile> profiles, 
@@ -33,6 +34,7 @@ public partial class AWGMappingWindow : Window
         _partsCallback = partsCallback;
         _labourCallback = labourCallback;
         //ViewModel.JobID = jobid;
+        ViewModel.SourceType = sourceType;
         ViewModel.Finishes = finishes;
         ViewModel.Profiles = profiles;
         ViewModel.Gaskets = gaskets;

+ 12 - 0
prs.desktop/Integrations/Common/AWGMappingWindowViewModel.cs

@@ -21,6 +21,18 @@ namespace PRSDesktop.Integrations.Common;
 public class AWGMappingWindowViewModel : DependencyObject
 {
     
+    private static readonly DependencyProperty SourceTypeProperty = DependencyProperty.Register(
+        nameof(SourceType), 
+        typeof(IntegrationSourceType), 
+        typeof(AWGMappingWindowViewModel)
+    );
+
+    public IntegrationSourceType SourceType
+    {
+        get => (IntegrationSourceType)GetValue(SourceTypeProperty);
+        set => SetValue(SourceTypeProperty, value);
+    } 
+    
     // public static DependencyProperty JobIDProperty = DependencyProperty.Register(
     //     nameof(JobID), 
     //     typeof(Guid), 

+ 13 - 0
prs.desktop/Integrations/Common/BOM/BaseIntegrationGrid.cs

@@ -26,6 +26,18 @@ public abstract class BaseIntegrationGrid<TType, TEntity,TLink> : DynamicItemsLi
     where TLink : EntityLink<TEntity>
 {
     
+    private static readonly DependencyProperty SourceTypeProperty = DependencyProperty.Register(
+        nameof(SourceType), 
+        typeof(IntegrationSourceType), 
+        typeof(BaseIntegrationGrid<TType, TEntity,TLink>)
+    );
+
+    public IntegrationSourceType SourceType
+    {
+        get => (IntegrationSourceType)GetValue(SourceTypeProperty);
+        set => SetValue(SourceTypeProperty, value);
+    } 
+    
     private static readonly DependencyProperty CreateEntityProperty = DependencyProperty.Register(
         nameof(CreateEntity), 
         typeof(ICommand), 
@@ -58,6 +70,7 @@ public abstract class BaseIntegrationGrid<TType, TEntity,TLink> : DynamicItemsLi
             CreateEntity?.Execute(args);
             if (!args.Cancel)
             {
+                item.Source = SourceType;
                 new Client<TEntity>().Save(entity, "Created by Integration Window");
                 item.Entity.CopyFrom(entity);
             }

+ 65 - 26
prs.desktop/Panels/JobPlanner/JobResourcePlanner.xaml

@@ -182,41 +182,79 @@
         </ControlTemplate>
 
     </UserControl.Resources>
-
+    
     <Grid>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="*" />
-            <ColumnDefinition Width="250" />
+            <ColumnDefinition Width="Auto" />
         </Grid.ColumnDefinitions>
 
         <Grid.RowDefinitions>
             <RowDefinition Height="*" />
         </Grid.RowDefinitions>
+        
+        <dynamicGrid:DynamicTabControl
+            x:Name="Data"
+            TabStripPlacement="Bottom" 
+            Grid.Column="0" 
+            Margin="5,0,0,0"
+            SelectionChanged="Data_OnSelectionChanged">
+            
+            <dynamicGrid:DynamicTabItem x:Name="MonthView" Header="Month View">
+                
+                <Syncfusion:SfScheduler x:Name="Schedule"
+                                        ViewType="Month" 
+                                        AppointmentEditorOpening="Schedule_AppointmentEditorOpening"
+                                        SchedulerContextMenuOpening="Schedule_OnSchedulerContextMenuOpening"
+                                        AppointmentDropping="Schedule_OnAppointmentDropping"
+                                        QueryAppointments="Schedule_OnQueryAppointments">
+                    <Syncfusion:SfScheduler.CellContextMenu>
+                        <ContextMenu x:Name="CellContextMenu" />
+                    </Syncfusion:SfScheduler.CellContextMenu>
+                    <Syncfusion:SfScheduler.AppointmentContextMenu>
+                        <ContextMenu x:Name="AppointmentContextMenu" />
+                    </Syncfusion:SfScheduler.AppointmentContextMenu>
+                    <Syncfusion:SfScheduler.MonthViewSettings>
+                        <Syncfusion:MonthViewSettings 
+                            AppointmentDisplayMode="Appointment"/>
+                    </Syncfusion:SfScheduler.MonthViewSettings>
+                </Syncfusion:SfScheduler>
+                
+            </dynamicGrid:DynamicTabItem>
+            
+            <dynamicGrid:DynamicTabItem x:Name="JobView" Header="Job View">
+                <Syncfusion:SfDataGrid
+                    x:Name="dataGrid"
+                    Grid.Row="0"
+                    Grid.Column="0"
+                    AutoGenerateColumns="True"
+                    AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
+                    RowHeight="30"
+                    AllowSorting="False"
+                    HeaderRowHeight="200"
+                    SelectionUnit="Cell"
+                    NavigationMode="Cell"
+                    FrozenColumnCount="2"
+                    CanMaintainScrollPosition="True"
+                    SelectionMode="Multiple"
+                    SelectionForegroundBrush="Yellow"
+                    RowSelectionBrush="Red"
+                    SelectionChanging="DataGrid_OnSelectionChanging"
+                    CurrentCellActivating="DataGrid_OnCurrentCellActivating"
+                    PreviewMouseDown="DataGrid_OnPreviewMouseDown"
+                    PreviewMouseUp="DataGrid_OnPreviewMouseUp"
+                    MouseUp="DataGrid_OnMouseUp">
+                </Syncfusion:SfDataGrid>
+            </dynamicGrid:DynamicTabItem>
+            
+        </dynamicGrid:DynamicTabControl>
 
-        <Syncfusion:SfDataGrid
-            x:Name="dataGrid"
-            Grid.Row="0"
-            Grid.Column="0"
-            AutoGenerateColumns="True"
-            AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
-            RowHeight="30"
-            AllowSorting="False"
-            HeaderRowHeight="200"
-            SelectionUnit="Cell"
-            NavigationMode="Cell"
-            FrozenColumnCount="2"
-            CanMaintainScrollPosition="True"
-            SelectionMode="Multiple"
-            SelectionForegroundBrush="Yellow"
-            RowSelectionBrush="Red"
-            SelectionChanging="DataGrid_OnSelectionChanging"
-            CurrentCellActivating="DataGrid_OnCurrentCellActivating"
-            PreviewMouseDown="DataGrid_OnPreviewMouseDown"
-            PreviewMouseUp="DataGrid_OnPreviewMouseUp"
-            MouseUp="DataGrid_OnMouseUp">
-        </Syncfusion:SfDataGrid>
-
-        <dynamicGrid:DynamicTabControl TabStripPlacement="Bottom" Grid.Column="1" Margin="5,0,0,0">
+        <dynamicGrid:DynamicTabControl
+            x:Name="Settings"
+            TabStripPlacement="Bottom" 
+            Grid.Column="1" 
+            Margin="5,0,0,0"
+            Width="250">
 
             <dynamicGrid:DynamicTabItem Header="Assignments">
                 <Grid Margin="0,0,0,2">
@@ -392,4 +430,5 @@
 
     </Grid>
 
+
 </UserControl>

+ 225 - 0
prs.desktop/Panels/JobPlanner/JobResourcePlanner.xaml.cs

@@ -24,8 +24,11 @@ using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventA
 using PRS.Shared;
 using Columns = InABox.Core.Columns;
 using InABox.Wpf;
+using MYOB.AccountRight.SDK.Extensions;
 using Syncfusion.Data.Extensions;
 using NPOI.OpenXmlFormats.Spreadsheet;
+using Syncfusion.UI.Xaml.Scheduler;
+using Color = System.Drawing.Color;
 
 namespace PRSDesktop;
 
@@ -1233,4 +1236,226 @@ public partial class JobResourcePlanner : UserControl
         AssignedEmployees.Items = AssignedEmployees.Items.OrderBy(x => x.Name).ToList();
     }
 
+    private void Data_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
+    {
+        Settings.Visibility = Data.SelectedTab == JobView 
+            ? Visibility.Visible 
+            : Visibility.Collapsed;
+    }
+
+    ScheduleAppointmentCollection appointments = new();
+    
+    private void Schedule_OnQueryAppointments(object? sender, QueryAppointmentsEventArgs e)
+    {
+        appointments.Clear();
+        var cols = Columns.Required<JobStage>()
+            .Add(x => x.ID)
+            .Add(x => x.StartDate)
+            .Add(x => x.EndDate)
+            .Add(x=>x.Name)
+            .Add(x => x.Job.ID)
+            .Add(x => x.Job.JobNumber)
+            .Add(x => x.Job.Name)
+            .Add(x => x.Type.ID)
+            .Add(x => x.Type.Color)
+            .Add(x => x.Type.Description)
+            .Add(x => x.Type.Color)
+            .Add(x=>x.Calendar.ID)
+            .Add(x=>x.Calendar.Name)
+            .Add(x=>x.Calendar.Monday)
+            .Add(x=>x.Calendar.Tuesday)
+            .Add(x=>x.Calendar.Wednesday)
+            .Add(x=>x.Calendar.Thursday)
+            .Add(x=>x.Calendar.Friday)
+            .Add(x=>x.Calendar.Saturday)
+            .Add(x=>x.Calendar.Sunday)
+            .Add(x => x.Apprentices)
+            .Add(x=>x.ApprenticeHours)
+            .Add(x=>x.Supervisors)
+            .Add(x=>x.SupervisionHours)
+            .Add(x=>x.Tradespersons)
+            .Add(x=>x.TradesHours);
+        
+        var stages =  Client.Query<JobStage>(
+            new Filter<JobStage>(x => x.StartDate).IsLessThanOrEqualTo(e.VisibleDateRange.ActualEndDate)
+                .And(x => x.EndDate).IsGreaterThanOrEqualTo(e.VisibleDateRange.ActualStartDate),
+            cols
+        ).ToObjects<JobStage>()
+           .ToArray();
+        
+       foreach (var stage in stages)
+           CreateAppointment(stage);
+       Schedule.ItemsSource = appointments;
+    }
+
+    private void CreateAppointment(JobStage stage)
+    {
+        var model = new StageModel(stage);
+        appointments.Add(model);
+    }
+    
+    private class StageModel : ScheduleAppointment
+    {
+
+        public StageModel(JobStage stage)
+        {
+            Stage = stage;
+        }
+        
+        private JobStage _stage;
+        public JobStage Stage
+        {
+            get => _stage;
+            set
+            {
+                _stage = value;
+                Reload(); 
+            }
+        }
+
+        public void Reload()
+        {
+            var color = !string.IsNullOrWhiteSpace(_stage.Type.Color)
+                ? (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(_stage.Type.Color)
+                : Colors.Transparent;
+                
+            StartTime = _stage.StartDate;
+            EndTime = _stage.EndDate;
+            IsAllDay = true;
+            AppointmentBackground = new SolidColorBrush(color);
+            Foreground = new SolidColorBrush(color.GetForegroundColor());
+            Subject = $"{_stage.Job.JobNumber}: {_stage.Job.Name}";
+        }
+    }
+    
+    private void Schedule_AppointmentEditorOpening(object? sender, AppointmentEditorOpeningEventArgs e)
+    {
+        
+    }
+
+    void CreateMenu<T>(ItemsControl menu, CalendarMenuName name, string header, Action<T?>? action, T data)
+    {
+        var item = new MenuItem();
+        item.Name = name.ToString();
+        item.Header = header;
+        if (action != null)
+            item.Click += (o,args) => action(data);
+        menu.Items.Add(item);
+    }
+
+    private StageModel? _copiedModel;
+    
+    
+    private void CreateJobStage(JobStageType type)
+    {
+        if (Schedule.SelectedDate is null)
+            return;
+        
+        var stage = new JobStage
+        {
+            StartDate = Schedule.SelectedDate.Value,
+            EndDate = Schedule.SelectedDate.Value,
+            Name = type.Description
+        };
+        stage.Type.CopyFrom(type);
+        if (new JobStagesGrid().EditItems([stage]))
+        {
+            CreateAppointment(stage);
+            _copiedModel = null;
+        }
+        if (!types?.Any(x => x.ID == stage.Type.ID) == false)
+            types = null;
+    }
+
+    private void EditJobStage(StageModel? stage)
+    {
+        if (stage is null)
+            return;
+
+        if (new JobStagesGrid().EditItems([stage.Stage]))
+        {
+            stage.Reload();
+            _copiedModel = null;
+        }
+    }
+    
+    private void DeleteJobStage(StageModel? stage)
+    {
+        if (stage is null)
+            return;
+        //Schedule.ItemsSource = null;
+        appointments.Remove(stage);
+        new Client<JobStage>().Delete(stage.Stage,"Deleted from Project Planner");
+        _copiedModel = null;
+    }
+
+    private void CopyJobStage(StageModel? stage)
+    {
+        _copiedModel = stage;
+    }
+
+    
+    private void PasteJobStage(StageModel? stage)
+    {
+        if (stage is null || Schedule.SelectedDate is null)
+            return;
+        var newstage = stage.Stage.Clone();
+        newstage.ID = Guid.Empty;
+        newstage.OriginalValues?.TryRemove("ID", out _);
+        
+        var span = newstage.EndDate - newstage.StartDate;
+        newstage.StartDate = Schedule.SelectedDate.Value;
+        newstage.EndDate = newstage.StartDate + span;
+        
+        new Client<JobStage>().Save(newstage,"Created from Project Planner");
+        CreateAppointment(newstage);
+        _copiedModel = null;
+    }
+    
+    private List<JobStageType>? types;
+
+    private void Schedule_OnSchedulerContextMenuOpening(object? sender, SchedulerContextMenuOpeningEventArgs e)
+    {
+        e.ContextMenu.Items.Clear();
+        if ((e.MenuType == SchedulerContextMenuType.Appointment) && (e.MenuInfo.Appointment  is StageModel model))
+        {
+            CreateMenu(e.ContextMenu, CalendarMenuName.Edit, "Edit Booking", EditJobStage, model);
+            CreateMenu(e.ContextMenu, CalendarMenuName.Copy, "Copy Booking", CopyJobStage, model);
+            e.ContextMenu.Items.Add(new Separator());
+            CreateMenu(e.ContextMenu, CalendarMenuName.Edit, "Delete Booking", DeleteJobStage, model);
+        }
+        else if (e.MenuType == SchedulerContextMenuType.MonthCell)
+        {
+            types ??= new Client<JobStageType>().Query(null, Columns.All<JobStageType>()).ToList<JobStageType>();
+            if (types.Any())
+            {
+                var header = new MenuItem() { Header = "Create Booking..." };
+                foreach (var type in types)
+                    CreateMenu(header, CalendarMenuName.CreateNew, type.Description, CreateJobStage, type);
+                e.ContextMenu.Items.Add(header);
+
+            }
+            else
+                CreateMenu(e.ContextMenu, CalendarMenuName.CreateNew, "Create Booking", CreateJobStage, new JobStageType());
+        
+            if (_copiedModel != null)
+            {
+                e.ContextMenu.Items.Add(new Separator());
+                CreateMenu(e.ContextMenu, CalendarMenuName.Paste, "Paste Booking", PasteJobStage, _copiedModel);
+            }
+        }
+    }
+
+
+    private void Schedule_OnAppointmentDropping(object? sender, AppointmentDroppingEventArgs e)
+    {
+        if (e.Appointment is StageModel model)
+        {
+            var duration = model.Stage.EndDate - model.Stage.StartDate;
+            model.Stage.StartDate = e.DropTime;
+            model.Stage.EndDate = e.DropTime + duration;
+            model.Reload();
+            new Client<JobStage>().Save(model.Stage,"Reallocated By Project Planner", (o,e) => { });
+        }
+    }
 }

+ 2 - 0
prs.desktop/Panels/Jobs/BillOfMaterials/JobBillOfMaterialsGrid.cs

@@ -174,6 +174,7 @@ namespace PRSDesktop
                         Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                             "BillOfMaterials.sqlite"), bom.SQLiteData);
                     AWGMappingWindow window = new AWGMappingWindow(
+                        IntegrationSourceType.Logikal,
                         bom.Finishes,
                         bom.Profiles,
                         bom.Gaskets,
@@ -264,6 +265,7 @@ namespace PRSDesktop
             {
                 var bom = _client.GetBOM(project, elevations.Select(x => x.ID));
                 AWGMappingWindow window = new AWGMappingWindow(
+                    IntegrationSourceType.V6,
                     bom.Finishes, 
                     bom.Profiles,
                     bom.Gaskets, 

+ 16 - 8
prs.desktop/Panels/Jobs/ProjectsGrid.cs

@@ -297,14 +297,17 @@ public class ProjectsGrid : DynamicDataGrid<Job>
     }
     
     private List<string>? _logikalSources;
+    
     private bool FilterLogikalProjects(LogikalProject project)
     {
-        _logikalSources ??= Client.Query(
-                new Filter<Job>(x => x.SourceRef).BeginsWith("Logikal:"),
-                Columns.None<Job>().Add(x => x.SourceRef)
-            ).Rows
-            .Select(r => r.Get<Job, string>(c => c.SourceRef))
-            .ToList();
+
+        _logikalSources ??=
+            Client.Query(
+                    new Filter<Job>(x => x.SourceRef).BeginsWith("Logikal:"),
+                    Columns.None<Job>().Add(x => x.SourceRef)
+                ).Rows
+                .Select(r => r.Get<Job, string>(c => c.SourceRef))
+                .ToList();
         return !_logikalSources.Contains(project.GetReference());
     }
     
@@ -314,8 +317,10 @@ public class ProjectsGrid : DynamicDataGrid<Job>
         
         var _job = new Job();
         //var _customer = new Customer();
-        
-        _job.Name = project.Title;
+
+        if (_logikalSettings.UseLogikalProjectNumber)
+            _job.JobNumber = $"{project.JobNumber}";
+        _job.Name = project?.Title ?? "New Logikal Project";
         //_job.Customer.CopyFrom(_customer);
         //_job.Account.CopyFrom(_customer.Account);
         // _job.SiteAddress.Street = project.Street;
@@ -398,6 +403,9 @@ public class ProjectsGrid : DynamicDataGrid<Job>
                 Client.Save(_customer, "Imported From V6");
             }
         }
+
+        if (_v6Settings.UseV6QuoteNumber)
+            _job.JobNumber = $"{project.Number}";
         
         _job.Name = project.Title;
         _job.Customer.CopyFrom(_customer);

+ 9 - 0
prs.desktop/Panels/Jobs/Stages/JobStagesGrid.cs

@@ -1,4 +1,6 @@
 using System;
+using System.Collections.Generic;
+using System.Linq;
 using System.Threading;
 using Comal.Classes;
 using InABox.Clients;
@@ -31,4 +33,11 @@ public class JobStagesGrid : DynamicGrid<JobStage>
     {
         new Client<JobStage>().Save(item, ""); //, (o, e) => { });
     }
+
+    protected override void DoValidate(JobStage[] items, List<string> errors)
+    {
+        base.DoValidate(items, errors);
+        if (items.Any(x=>x.Job.ID == Guid.Empty))
+            errors.Add("Please select a job before continuing!");
+    }
 }

+ 1 - 1
prs.desktop/Panels/Meeting/MeetingPanel.xaml

@@ -58,7 +58,7 @@
                                 ButtonsVisible="False"
                                 TabsVisible="False"
                                 DockPanel.Dock="Left"
-                                Margin="-5,-5,-5,0"
+                                Margin="0"
                                 OnBeforeLoad="MeetingDetailsForm_OnBeforeLoad"
                                 />
 

+ 2 - 0
prs.desktop/Panels/Staging/Setouts/StagingSetoutGrid.cs

@@ -587,6 +587,7 @@ public class StagingSetoutGrid : DynamicDataGrid<StagingSetout>
             }
 
             AWGMappingWindow window = new AWGMappingWindow(
+                IntegrationSourceType.Logikal,
                 finishes,
                 profiles,
                 gaskets,
@@ -729,6 +730,7 @@ public class StagingSetoutGrid : DynamicDataGrid<StagingSetout>
             var elevationids = elevations.Select(x => x.ID).ToArray();
             var bom = _client.GetBOM(project, elevationids);
             AWGMappingWindow window = new AWGMappingWindow(
+                IntegrationSourceType.V6,
                 bom.Finishes,
                 bom.Profiles,
                 bom.Gaskets,