Browse Source

Added update functionality to Logikal BOM import
Added "IgnoredStyles" to LogikalSettings

frogsoftware 1 week ago
parent
commit
b28faa6b43

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

@@ -1,3 +1,4 @@
+using System;
 using InABox.Core;
 
 namespace Comal.Classes
@@ -9,12 +10,15 @@ namespace Comal.Classes
         string? Description { get; set; }
         IEntityLink Entity { get; } 
         IntegrationSourceType Source { get; set; }
+        
     }
     
     public abstract class BaseIntegrationSource<TEntity,TLink> : Entity, IRemotable, IPersistent, IOneToMany<TEntity>, IBaseIntegrationSource
         where TEntity : Entity
         where TLink : IEntityLink<TEntity>
     {
+        public Type EntityType() => typeof(TEntity);
+        
         [NullEditor]
         [EntityRelationship(DeleteAction.Cascade)]
         [RequiredColumn]

+ 76 - 10
prs.classes/Integrations/Logikal/LogikalSettings.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Linq;
 using InABox.Configuration;
 using InABox.Core;
 
@@ -56,6 +57,13 @@ namespace Comal.Classes
         ElevationWithSectionLines,
         SectionLine,
     }
+
+    public enum LogikalUpdateType
+    {
+        None,
+        NewOnly,
+        AlwaysUpdate,
+    }
     
     public class LogikalSettings : BaseObject, IGlobalConfigurationSettings
     {
@@ -112,13 +120,76 @@ namespace Comal.Classes
         [CheckBoxEditor]
         [EditorSequence(11)]
         public bool SaveFiles { get; set; }
-        
+
+        [EnumLookupEditor(typeof(LogikalUpdateType))]
+        [EditorSequence("0. Sections", 1)]
+        [Caption("Finishes")]
+        public LogikalUpdateType UpdateStyles { get; set; } = LogikalUpdateType.NewOnly;
+        
+        [EnumLookupEditor(typeof(LogikalUpdateType))]
+        [EditorSequence("0. Sections", 2)]
+        [Caption("Groups")]
+        public LogikalUpdateType UpdateGroups { get; set; } = LogikalUpdateType.NewOnly;
+        
+        [EnumLookupEditor(typeof(LogikalUpdateType))]
+        [EditorSequence("0. Sections", 3)]
+        [Caption("Suppliers")]
+        public LogikalUpdateType UpdateSuppliers { get; set; } = LogikalUpdateType.NewOnly;
+        
+        [EnumLookupEditor(typeof(LogikalUpdateType))]
+        [EditorSequence("0. Sections", 4)]
+        [Caption("Discounts")]
+        public LogikalUpdateType UpdateDiscounts { get; set; } = LogikalUpdateType.NewOnly;
+        
+        [EnumLookupEditor(typeof(LogikalUpdateType))]
+        [EditorSequence("0. Sections", 5)]
+        [Caption("Profiles")]
+        public LogikalUpdateType UpdateProfiles { get; set; } = LogikalUpdateType.NewOnly;
+        
+        [EnumLookupEditor(typeof(LogikalUpdateType))]
+        [EditorSequence("0. Sections", 6)]
+        [Caption("Gaskets")]
+        public LogikalUpdateType UpdateGaskets { get; set; } = LogikalUpdateType.NewOnly;
+        
+        [EnumLookupEditor(typeof(LogikalUpdateType))]
+        [EditorSequence("0. Sections", 7)]
+        [Caption("Components")]
+        public LogikalUpdateType UpdateComponents { get; set; } = LogikalUpdateType.NewOnly;
+        
+        [EnumLookupEditor(typeof(LogikalUpdateType))]
+        [EditorSequence("0. Sections", 8)]
+        [Caption("Glass")]
+        public LogikalUpdateType UpdateGlass { get; set; } = LogikalUpdateType.NewOnly;
+        
+        [EnumLookupEditor(typeof(LogikalUpdateType))]
+        [EditorSequence("0. Sections", 9)]
+        [Caption("Labour")]
+        public LogikalUpdateType UpdateLabour { get; set; } = LogikalUpdateType.NewOnly;
         
         [MemoEditor]
         [EditorSequence("1. Groups", 1)]
         [Caption("Query")]
         public String GroupSQL { get; set; }
         
+
+        
+        [MemoEditor(Height=400)]
+        [EditorSequence("2. Finishes", 1)]
+        [Caption("Query")]
+        public String FinishSQL { get; set; }
+        
+        [TextBoxEditor]
+        [EditorSequence("2. Finishes", 2)]
+        [Caption("Ignore Styles")]
+        public string IgnoredStyles { get; set; }
+
+        public string IgnoreStyles(string value)
+        {
+            if (IgnoredStyles.Split(';', ',', '|').Select(x => x.Trim().ToUpper()).Contains(value.ToUpper()) == true)
+                return "";
+            return value;
+        }
+        
         private class TreatmentTypeLookup : LookupDefinitionGenerator<TreatmentType, LogikalSettings>
         {
             public override Filter<TreatmentType> DefineFilter(LogikalSettings[] items) 
@@ -126,30 +197,25 @@ namespace Comal.Classes
         }
         
         [LookupDefinition(typeof(TreatmentTypeLookup))]
-        [EditorSequence("2. Finishes", 1)]
+        [EditorSequence("2. Finishes", 3)]
         [Caption("Powdercoated")]
         public TreatmentTypeLink PowdercoatedType { get; set; }
         
         [LookupDefinition(typeof(TreatmentTypeLookup))]
-        [EditorSequence("2. Finishes", 2)]
+        [EditorSequence("2. Finishes", 4)]
         [Caption("Anodised")]
         public TreatmentTypeLink AnodisedType { get; set; }
         
         [LookupDefinition(typeof(TreatmentTypeLookup))]
-        [EditorSequence("2. Finishes", 3)]
+        [EditorSequence("2. Finishes", 5)]
         [Caption("Varnished")]
         public TreatmentTypeLink VarnishedType { get; set; }
         
         [LookupDefinition(typeof(TreatmentTypeLookup))]
-        [EditorSequence("2. Finishes", 4)]
+        [EditorSequence("2. Finishes", 6)]
         [Caption("Taped")]
         public TreatmentTypeLink TapedType { get; set; }
         
-        [MemoEditor]
-        [EditorSequence("2. Finishes", 5)]
-        [Caption("Query")]
-        public String FinishSQL { get; set; }
-        
         private class ProfileUomLookup : LookupDefinitionGenerator<ProductDimensionUnit, LogikalSettings>
         {
             public override Filter<ProductDimensionUnit> DefineFilter(LogikalSettings[] items)

+ 1 - 1
prs.desktop/Integrations/Common/AWGMappingWindow.xaml

@@ -112,7 +112,7 @@
             Visibility="{Binding SelectedSection, Converter={StaticResource DiscountsGridVisible}}"
             ItemsSource="{Binding DiscountMappings}"
             CreateEntity="{Binding CreateDiscount}"
-            AfterCreateEntity="{Binding AfterCreateDiscount}"
+            AfterCreateEntity="{Binding AfterCreateDiscountGroup}"
             SourceType="{Binding SourceType}"
             OnChanged="BaseDynamicGrid_OnOnChanged"/>
         

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

@@ -57,6 +57,7 @@ public partial class AWGMappingWindow : Window
 
     private void OKClick(object sender, RoutedEventArgs e)
     {
+        ViewModel.CheckUpdates();
         ViewModel.GetDiscounts(_discountCallback);
         ViewModel.GetParts(_partsCallback,_labourCallback);
         DialogResult = true;

+ 372 - 190
prs.desktop/Integrations/Common/AWGMappingWindowViewModel.cs

@@ -11,9 +11,11 @@ using Comal.Classes;
 using InABox.Clients;
 using InABox.Configuration;
 using InABox.Core;
+using InABox.Integration;
 using InABox.Integration.Awg;
 using InABox.WPF;
 using Inflector;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
 using Microsoft.Xaml.Behaviors.Core;
 using PRSDimensionUtils;
 using sun.text.resources.ro;
@@ -29,6 +31,15 @@ public class AWGMappingWindowViewModel : DependencyObject
     public AWGMappingWindowViewModel() : base()
     {
         _settings = new GlobalConfiguration<LogikalSettings>().Load();
+        StylesChecked = _settings.UpdateStyles != LogikalUpdateType.None; 
+        GroupsChecked = _settings.UpdateGroups != LogikalUpdateType.None;
+        SuppliersChecked = _settings.UpdateSuppliers != LogikalUpdateType.None;
+        DiscountsChecked = _settings.UpdateDiscounts != LogikalUpdateType.None;
+        ProfilesChecked = _settings.UpdateProfiles != LogikalUpdateType.None;
+        GasketsChecked = _settings.UpdateGaskets !=  LogikalUpdateType.None;
+        ComponentsChecked = _settings.UpdateComponents != LogikalUpdateType.None;
+        GlassChecked = _settings.UpdateGlass != LogikalUpdateType.None;
+        LabourChecked = _settings.UpdateLabour != LogikalUpdateType.None;
     }
     
     private static readonly DependencyProperty SourceTypeProperty = DependencyProperty.Register(
@@ -238,6 +249,7 @@ public class AWGMappingWindowViewModel : DependencyObject
             {
                 mapping.Group = logikal.Group;
                 mapping.Supplier = logikal.Supplier;
+                mapping.Style = logikal.Finish;
                 mapping.Dimensions.Unit.CopyFrom(model.Settings.GasketUom);
                 mapping.Dimensions.Length = logikal.Length;
                 mapping.Cost = logikal.Cost;
@@ -280,6 +292,7 @@ public class AWGMappingWindowViewModel : DependencyObject
                 mapping.Cost = logikal.Cost;
                 mapping.Group = logikal.Group;
                 mapping.Supplier = logikal.Supplier;
+                mapping.Style = logikal.Finish;
             },
             x => x.Code
         );
@@ -785,139 +798,166 @@ public class AWGMappingWindowViewModel : DependencyObject
         {
             if (o is IntegrationGridCreateEntityArgs<ProductStyle, ProductStyleIntegrationSource> args)
             {
-                args.Entity.Code = args.Mapping.Code ?? "";
-                args.Entity.Description = args.Mapping.Description ?? "";
+                PopulateStyle(args.Entity, args.Mapping);
                 args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
-
-                TreatmentTypeLink? type = args.Mapping.StyleType == AwgStyleType.Powdercoated
-                    ? _settings.PowdercoatedType
-                    : args.Mapping.StyleType == AwgStyleType.Anodised
-                        ? _settings.AnodisedType
-                        : args.Mapping.StyleType == AwgStyleType.Varnished
-                            ? _settings.VarnishedType
-                            : args.Mapping.StyleType == AwgStyleType.Taped
-                                ? _settings.TapedType
-                                : null;
-                if (type != null)
-                {
-                    args.Entity.TreatmentType.CopyFrom(type);
-
-                    var product = new Client<Product>().Query(
-                        new Filter<Product>(x => x.Code).IsEqualTo(args.Entity.Code),
-                        Columns.Local<Product>()
-                    ).ToArray<Product>().FirstOrDefault();
-                    if (product == null)
-                    {
-                        product = new Product();
-                        product.Code = args.Entity.Code;
-                        product.Name = args.Entity.Description;
-                        product.Problem.Notes = ["Created from BOM Integration Window"];
-                        product.TreatmentType.CopyFrom(type);
-                        Client.Save(product, "Created from AWG Mapping Window");
-                    }
-
-                    args.Entity.StockTreatmentProduct.CopyFrom(product);
-                    args.Entity.ManufacturingTreatmentProduct.CopyFrom(product);
-
-                }
-
             }
         }
     );
 
+    private void PopulateStyle(ProductStyle entity, ProductStyleIntegrationSource mapping)
+    {
+        entity.Code = mapping.Code ?? "";
+        entity.Description = mapping.Description ?? "";
+        
+        TreatmentTypeLink? type = mapping.StyleType == AwgStyleType.Powdercoated
+            ? _settings.PowdercoatedType
+            : mapping.StyleType == AwgStyleType.Anodised
+                ? _settings.AnodisedType
+                : mapping.StyleType == AwgStyleType.Varnished
+                    ? _settings.VarnishedType
+                    : mapping.StyleType == AwgStyleType.Taped
+                        ? _settings.TapedType
+                        : new TreatmentTypeLink();
+
+        entity.TreatmentType.ID = type.ID;
+        if (entity.TreatmentType.ID != Guid.Empty)
+        {
+            var product = new Client<Product>().Query(
+                new Filter<Product>(x => x.Code).IsEqualTo(entity.Code),
+                Columns.Local<Product>()
+            ).ToArray<Product>().FirstOrDefault();
+            
+            if (product == null)
+            {
+                product = new Product();
+                product.Problem.Notes = ["Created from BOM Integration Window"];
+            }
+
+            product.Code = entity.Code;
+            product.Name = entity.Description;
+            product.TreatmentType.ID = entity.TreatmentType.ID;
+            if (product.IsChanged())
+                Client.Save(product, "Created from AWG Mapping Window");
+            
+            entity.StockTreatmentProduct.ID = product.ID;
+            entity.ManufacturingTreatmentProduct.ID = product.ID;
 
+        }
+    }
+    
     public ICommand CreateGroup => new ActionCommand(
         o =>
         {
             if (o is IntegrationGridCreateEntityArgs<ProductGroup, ProductGroupIntegrationSource> args)
             {
-                Guid parentid = Guid.Empty;
-                var parentcodes = args.Mapping.Parent
-                    .Split('/')
-                    .Select(x => x.Trim().ToUpper())
-                    .Where(x=>!string.IsNullOrWhiteSpace(x))
-                    .ToArray();
-                var parents = Client.Query(
-                    new Filter<ProductGroup>(x => x.Code).InList(parentcodes),
-                    Columns.None<ProductGroup>().Add(x => x.ID).Add(x => x.Code)
-                ).ToArray<ProductGroup>();
-
-                foreach (var parentcode in parentcodes)
-                {
-                    var parent = parents.FirstOrDefault(x => Equals(x.Code,parentcode));
-                    if (parent == null)
-                    {
-                        parent = new ProductGroup();
-                        parent.Code = parentcode;
-                        parent.Description = !string.IsNullOrWhiteSpace(parentcode) ? parentcode.Titleize() : "";
-                        parent.Parent.ID = parentid;
-                        parent.Problem.Notes = ["Created from BOM Integration Window"];
-                        Client.Save(parent, "Created from AWG Mapping Window");
-                    }
-                    parentid = parent.ID;
-                }
-                
-                args.Entity.Parent.ID = parentid;
-                args.Entity.Code = args.Mapping.Code ?? "";
-                args.Entity.Description = args.Mapping.Description ?? "";
+                PopulateGroup(args.Entity, args.Mapping);
                 args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
             }
         }
     );
+    
+    public void PopulateGroup(ProductGroup entity, ProductGroupIntegrationSource mapping)
+    {
+        Guid parentid = Guid.Empty;
+        var parentcodes = mapping.Parent
+            .Split('/')
+            .Select(x => x.Trim().ToUpper())
+            .Where(x=>!string.IsNullOrWhiteSpace(x))
+            .ToArray();
+        var parents = Client.Query(
+            new Filter<ProductGroup>(x => x.Code).InList(parentcodes),
+            Columns.None<ProductGroup>().Add(x => x.ID).Add(x => x.Code)
+        ).ToArray<ProductGroup>();
+
+        foreach (var parentcode in parentcodes)
+        {
+            var parent = parents.FirstOrDefault(x => Equals(x.Code,parentcode));
+            if (parent == null)
+            {
+                parent = new ProductGroup();
+                parent.Code = parentcode;
+                parent.Description = !string.IsNullOrWhiteSpace(parentcode) ? parentcode.Titleize() : "";
+                parent.Parent.ID = parentid;
+                parent.Problem.Notes = ["Created from BOM Integration Window"];
+                Client.Save(parent, "Created from AWG Mapping Window");
+            }
+            parentid = parent.ID;
+        }
+                
+        entity.Parent.ID = parentid;
+        entity.Code = mapping.Code ?? "";
+        entity.Description = mapping.Description ?? "";
+
+    }
 
     public ICommand CreateSupplier => new ActionCommand(
         o =>
         {
             if (o is IntegrationGridCreateEntityArgs<Supplier, SupplierIntegrationSource> args)
             {
-                args.Entity.Code = args.Mapping.Code ?? "";
-                args.Entity.Name = args.Mapping.Description ?? "";
+                PopulateSupplier(args.Entity, args.Mapping);
                 args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
             }
         }
     );
-    
+
+    private void PopulateSupplier(Supplier entity, SupplierIntegrationSource mapping)
+    {
+        entity.Code = mapping.Code ?? "";
+        entity.Name = mapping.Description ?? "";
+    }
+
     public ICommand CreateDiscount => new ActionCommand(
         o =>
         {
             if (o is IntegrationGridCreateEntityArgs<SupplierDiscountGroup, SupplierDiscountGroupIntegrationSource> args)
             {
-                args.Entity.Code = args.Mapping.Code ?? "";
-                args.Entity.Description = args.Mapping.Description ?? "";
-                args.Entity.Type = SupplierDiscountGroupType.Discount;
-                var supplier = SupplierMappings.FirstOrDefault(x => string.Equals(x.Code, args.Mapping.Supplier))?.Entity.ID;
-                if (supplier == null)
-                    supplier = new Client<Supplier>()
-                        .Query(new Filter<Supplier>(x => x.Code).IsEqualTo(args.Entity.Code),
-                            Columns.None<Supplier>().Add(x => x.ID))
-                        .ToArray<Supplier>()
-                        .FirstOrDefault()?.ID;
-                if (supplier == null)
-                    throw new Exception($"Unable to locate Supplier [{args.Mapping.Supplier}]");       
-                args.Entity.Supplier.ID = supplier.Value;
+                PopulateDiscountGroup(args.Entity, args.Mapping);
                 args.Entity.Problem.Notes = ["Created from BOM Integration Window"]; 
-
             }
         }
     );
-    
-    public ICommand AfterCreateDiscount => new ActionCommand(
+
+    private void PopulateDiscountGroup(SupplierDiscountGroup entity, SupplierDiscountGroupIntegrationSource mapping)
+    {
+        entity.Code = mapping.Code ?? "";
+        entity.Description = mapping.Description ?? "";
+        entity.Type = SupplierDiscountGroupType.Discount;
+        var _supplier = SupplierMappings?.FirstOrDefault(x => string.Equals(x.Code, mapping.Supplier))?.Entity.ID;
+        if (_supplier == null)
+            _supplier = new Client<Supplier>()
+                .Query(new Filter<Supplier>(x => x.Code).IsEqualTo(entity.Code),
+                    Columns.None<Supplier>().Add(x => x.ID))
+                .ToArray<Supplier>()
+                .FirstOrDefault()?.ID ?? Guid.Empty;
+        entity.Supplier.ID = _supplier.Value;
+    }
+
+    public ICommand AfterCreateDiscountGroup => new ActionCommand(
         o =>
         {
             if (o is IntegrationGridCreateEntityArgs<SupplierDiscountGroup, SupplierDiscountGroupIntegrationSource> args)
             {
-                CreateSupplierDiscountInstance(args);
+                PopulateDiscount(args.Entity,args.Mapping);
             }
         }
     );
-    
-    private void CreateSupplierDiscountInstance(IntegrationGridCreateEntityArgs<SupplierDiscountGroup, SupplierDiscountGroupIntegrationSource> args)
+
+    private void PopulateDiscount(SupplierDiscountGroup entity, SupplierDiscountGroupIntegrationSource mapping)
     {
-        var discount = new SupplierDiscount();
-        discount.Group.CopyFrom(args.Entity);
-        discount.Value = args.Mapping.Discount;
-        discount.Job.ID = Guid.Empty;
-        Client.Save(discount, "Created from AWG Mapping Window");
+        var _discount = new Client<SupplierDiscount>().Query(
+            new Filter<SupplierDiscount>(x => x.Group.ID).IsEqualTo(entity.ID),
+            Columns.Local<SupplierDiscount>()
+        ).ToObjects<SupplierDiscount>().FirstOrDefault();
+        if (_discount == null)
+        {
+            _discount = new();
+            _discount.Group.ID = entity.ID;
+        }
+        _discount.Value = mapping.Discount;
+        
+        if (_discount.IsChanged()) 
+            Client.Save(_discount, "Updated from AWG Mapping Window");
     }
 
     public ICommand CreateProfile => new ActionCommand(
@@ -925,118 +965,154 @@ public class AWGMappingWindowViewModel : DependencyObject
         {
             if (o is IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
             {
-                args.Entity.Code = args.Mapping.Code ?? "";
-                args.Entity.Name = args.Mapping.Description ?? "";
-                args.Entity.UnitOfMeasure.CopyFrom(_settings.ProfileUom);
-                var group = GroupMappings?.FirstOrDefault(x => Equals(x.Code, args.Mapping.Group));
-                if (group != null)
-                    args.Entity.Group.CopyFrom(group.Entity);
+                PopulateProfile(args.Entity,args.Mapping);
                 args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
-                CreateImage(args);
+                //CreateImage(args);
             }
         }
     );
 
+    private void PopulateProfile(Product entity, ProductIntegrationSource mapping)
+    {
+        entity.Code = mapping.Code ?? "";
+        entity.Name = mapping.Description ?? "";
+        entity.UnitOfMeasure.ID = _settings.ProfileUom.ID;
+        var _group = GroupMappings?.FirstOrDefault(x => Equals(x.Code, mapping.Group));
+        if (_group != null)
+            entity.Group.ID = _group.Entity.ID;
+    }
+
     public ICommand AfterCreateProduct => new ActionCommand(
         o =>
         {
             if (o is IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
             {
-                CreateTreatmentParams(args, AwgStyleType.Powdercoated, Settings.PowdercoatedType);
-                CreateTreatmentParams(args, AwgStyleType.Anodised, Settings.AnodisedType);
-                CreateTreatmentParams(args, AwgStyleType.Taped, Settings.TapedType);
-                CreateTreatmentParams(args, AwgStyleType.Varnished, Settings.VarnishedType);
-                CreateSupplierProducts(args);
+                PopulateProduct(args.Entity,args.Mapping);
             }
         }
     );
 
-    private void CreateImage(IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
+    private void PopulateProduct(Product entity, ProductIntegrationSource mapping)
     {
-        if (!string.IsNullOrWhiteSpace(_settings.ImageUrl))
-        {
-            var model = new LogikalCodeModel() { Code = args.Mapping.Code };
-            var _expression = new CoreExpression<LogikalCodeModel, string>(_settings.ImageUrl);
-            if (_expression.Evaluate(model).Get(out var eval, out var e))
-            {
-                var tcs = new TaskCompletionSource<byte[]>(); 
-                new HttpClient().GetByteArrayAsync(eval)
-                    .ContinueWith(
-                        t =>
-                        {
-                            if (t.IsFaulted) 
-                                tcs.SetException(t.Exception); 
-                            else 
-                                tcs.SetResult(t.Result);
-                        });
-                try
-                {
-                    var data = tcs.Task.Result;
-                    var document = new Document()
-                    {
-                        Data = tcs.Task.Result,
-                        CRC = CoreUtils.CalculateCRC(data),
-                        FileName = args.Mapping.Code
-                    };
-                    Client.Save(document,"Created from AWG Mapping Window");
-                    args.Entity.Image.ID = document.ID;
-                }
-                catch (Exception exception)
-                {
-                    Logger.Send(LogType.Error,"",$"Exception in CreateImage(): {exception.Message}");
-                }
-            }
-        }
+        CreateTreatmentParams(entity, mapping, AwgStyleType.Powdercoated, Settings.PowdercoatedType);
+        CreateTreatmentParams(entity, mapping, AwgStyleType.Anodised, Settings.AnodisedType);
+        CreateTreatmentParams(entity, mapping, AwgStyleType.Taped, Settings.TapedType);
+        CreateTreatmentParams(entity, mapping, AwgStyleType.Varnished, Settings.VarnishedType);
+        CreateSupplierProducts(entity, mapping);
     }
 
-    private void CreateTreatmentParams(IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args, AwgStyleType type, TreatmentTypeLink link)
+    // private void CreateImage(IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
+    // {
+    //     if (!string.IsNullOrWhiteSpace(_settings.ImageUrl))
+    //     {
+    //         var model = new LogikalCodeModel() { Code = args.Mapping.Code };
+    //         var _expression = new CoreExpression<LogikalCodeModel, string>(_settings.ImageUrl);
+    //         if (_expression.Evaluate(model).Get(out var eval, out var e))
+    //         {
+    //             var tcs = new TaskCompletionSource<byte[]>(); 
+    //             new HttpClient().GetByteArrayAsync(eval)
+    //                 .ContinueWith(
+    //                     t =>
+    //                     {
+    //                         if (t.IsFaulted) 
+    //                             tcs.SetException(t.Exception); 
+    //                         else 
+    //                             tcs.SetResult(t.Result);
+    //                     });
+    //             try
+    //             {
+    //                 var data = tcs.Task.Result;
+    //                 var document = new Document()
+    //                 {
+    //                     Data = tcs.Task.Result,
+    //                     CRC = CoreUtils.CalculateCRC(data),
+    //                     FileName = args.Mapping.Code
+    //                 };
+    //                 Client.Save(document,"Created from AWG Mapping Window");
+    //                 args.Entity.Image.ID = document.ID;
+    //             }
+    //             catch (Exception exception)
+    //             {
+    //                 Logger.Send(LogType.Error,"",$"Exception in CreateImage(): {exception.Message}");
+    //             }
+    //         }
+    //     }
+    // }
+
+    private void CreateTreatmentParams(Product entity, ProductIntegrationSource mapping, AwgStyleType type, TreatmentTypeLink link)
     {
-        if (args.Mapping.TreatmentParameters.ContainsKey(type) && !Equals(link.ID,Guid.Empty))
+        if (mapping.TreatmentParameters.ContainsKey(type) && !Equals(link.ID,Guid.Empty))
         {
-            ProductTreatment treatment = new();
-            treatment.Product.CopyFrom(args.Entity);
-            treatment.TreatmentType.CopyFrom(link);
-            treatment.Parameter = args.Mapping.TreatmentParameters[type];
-            Client.Save(treatment,"Created from AWG Mapping Window");
+            var _treatment = new Client<ProductTreatment>().Query(
+                new Filter<ProductTreatment>(x=>x.Product.ID).IsEqualTo(entity.ID)
+                    .And(x=>x.TreatmentType.ID).IsEqualTo(link.ID),
+                Columns.Local<ProductTreatment>()
+                ).ToObjects<ProductTreatment>().FirstOrDefault();
+            if (_treatment == null)
+            {
+                _treatment = new();
+                _treatment.Product.ID = entity.ID;
+                _treatment.TreatmentType.ID = link.ID;
+            }
+            
+            _treatment.Parameter = mapping.TreatmentParameters[type];
+            if (_treatment.IsChanged())
+                Client.Save(_treatment,"Updated from AWG Mapping Window");
         }
     }
 
 
-    private void CreateSupplierProducts(IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
+    private void CreateSupplierProducts(Product entity, ProductIntegrationSource mapping)
     {
-        var supplier = SupplierMappings?.FirstOrDefault(x => Equals(x.Code, args.Mapping.Supplier));
+        var supplier = SupplierMappings?.FirstOrDefault(x => Equals(x.Code, mapping.Supplier));
         if (supplier != null)
         {
             List<SupplierProduct> products = new();
             
-            if (!args.Mapping.MillCost.IsEffectivelyEqual(args.Mapping.Cost))
+            if (!mapping.MillCost.IsEffectivelyEqual(mapping.Cost))
             {
-                var _mill = CreateSupplierProduct(args, supplier, "", args.Mapping.MillCost);
+                var _mill = CreateSupplierProduct(entity, mapping, supplier, "", mapping.MillCost, false);
                 products.Add(_mill);
             }
             
-            var _treated = CreateSupplierProduct(args, supplier, args.Mapping.Style, args.Mapping.Cost);
+            var _treated = CreateSupplierProduct(entity, mapping, supplier, mapping.Style, mapping.Cost, false);
             products.Add(_treated);
-            
-            Client.Save(products,"Created from AWG Mapping Window");
+
+            var _updates = products.Where(x => x.IsChanged()).ToArray();
+            if (_updates.Any())
+                Client.Save(_updates,"Updated from AWG Mapping Window");
         }
     }
 
-    private SupplierProduct CreateSupplierProduct(IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args, SupplierIntegrationSource supplier, string stylecode, double cost)
+    private SupplierProduct CreateSupplierProduct(Product entity, ProductIntegrationSource mapping, SupplierIntegrationSource supplier, 
+        string stylecode, double cost, bool save)
     {
-        SupplierProduct sp = new SupplierProduct();
-        sp.Product.ID = args.Entity.ID;
-        sp.SupplierLink.ID = supplier.Entity.ID;
-        sp.SupplierCode = args.Mapping.Code;
-        sp.SupplierDescription = args.Mapping.Description;
-        sp.Dimensions.CopyFrom(args.Mapping.Dimensions);
-                    
-        var style = StyleMappings?.FirstOrDefault(x => Equals(x.Code, stylecode));
-        if (style != null)
-            sp.Style.CopyFrom(style.Entity);
-
-        sp.TradePrice = cost; 
-        return sp;
+        var _sp = new Client<SupplierProduct>().Query(
+            new Filter<SupplierProduct>(x => x.Product.ID).IsEqualTo(entity.ID).And(x=>x.SupplierLink.ID).IsEqualTo(supplier.Entity.ID),
+            Columns.Local<SupplierProduct>()
+        ).ToObjects<SupplierProduct>().FirstOrDefault();
+        if (_sp == null)
+        {
+            _sp = new();
+            _sp.Product.ID = entity.ID;
+            _sp.SupplierLink.ID = supplier.Entity.ID;
+        }
+        _sp.SupplierCode = mapping.Code ?? "";
+        _sp.SupplierDescription = mapping.Description ?? "";
+        _sp.Dimensions.Unit.ID = mapping.Dimensions.Unit.ID;
+        _sp.Dimensions.Height = mapping.Dimensions.Height;
+        _sp.Dimensions.Width = mapping.Dimensions.Width;
+        _sp.Dimensions.Length = mapping.Dimensions.Length;
+        _sp.Dimensions.Quantity = mapping.Dimensions.Quantity;
+        _sp.Dimensions.Weight = mapping.Dimensions.Weight;
+        _sp.Dimensions.Value = mapping.Dimensions.Value;
+        _sp.Dimensions.UnitSize = mapping.Dimensions.UnitSize;
+        var _style = StyleMappings?.FirstOrDefault(x => Equals(x.Code, stylecode));
+        _sp.Style.ID = _style?.Entity.ID ?? Guid.Empty;
+        _sp.TradePrice = cost;
+        if (save && _sp.IsChanged())
+            new Client<SupplierProduct>().Save(_sp, "Updated by Awg Mapping Window");
+        return _sp;
     }
 
     public ICommand CreateGasket => new ActionCommand(
@@ -1044,61 +1120,82 @@ public class AWGMappingWindowViewModel : DependencyObject
         {
             if (o is IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
             {
-                args.Entity.Code = args.Mapping.Code ?? "";
-                args.Entity.Name = args.Mapping.Description ?? "";
-                args.Entity.UnitOfMeasure.CopyFrom(_settings.GasketUom);
-                var group = GroupMappings?.FirstOrDefault(x => Equals(x.Code, args.Mapping.Group));
-                if (group != null)
-                    args.Entity.Group.CopyFrom(group.Entity);
+                PopulateGasket(args.Entity, args.Mapping);
                 args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
             }
         }
     );
-    
+
+    private void PopulateGasket(Product argsEntity, ProductIntegrationSource argsMapping)
+    {
+        argsEntity.Code = argsMapping.Code ?? "";
+        argsEntity.Name = argsMapping.Description ?? "";
+        argsEntity.UnitOfMeasure.ID = _settings.GasketUom.ID;
+        var _group = GroupMappings?.FirstOrDefault(x => Equals(x.Code, argsMapping.Group));
+        if (_group != null)
+            argsEntity.Group.ID = _group.Entity.ID;
+    }
+
     public ICommand CreateComponent => new ActionCommand(
         o =>
         {
             if (o is IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
             {
-                args.Entity.Code = args.Mapping.Code ?? "";
-                args.Entity.Name = args.Mapping.Description ?? "";
-                args.Entity.UnitOfMeasure.CopyFrom(_settings.ComponentUom);
-                var group = GroupMappings?.FirstOrDefault(x => Equals(x.Code, args.Mapping.Group));
-                if (group != null)
-                    args.Entity.Group.CopyFrom(group.Entity);
+                PopulateComponent(args.Entity, args.Mapping);
                 args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
             }
         }
     );
-    
+
+    private void PopulateComponent(Product entity, ProductIntegrationSource mapping)
+    {
+        entity.Code = mapping.Code ?? "";
+        entity.Name = mapping.Description ?? "";
+        entity.UnitOfMeasure.ID = _settings.ComponentUom.ID;
+        var _group = GroupMappings?.FirstOrDefault(x => Equals(x.Code, mapping.Group));
+        if (_group != null)
+            entity.Group.ID = _group.Entity.ID;
+    }
+
     public ICommand CreateGlass => new ActionCommand(
         o =>
         {
             if (o is IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
             {
-                args.Entity.Code = args.Mapping.Code ?? "";
-                args.Entity.Name = args.Mapping.Description ?? "";
-                args.Entity.UnitOfMeasure.CopyFrom(_settings.GlassUom);
-                var group = GroupMappings?.FirstOrDefault(x => Equals(x.Code, args.Mapping.Group));
-                if (group != null)
-                    args.Entity.Group.CopyFrom(group.Entity);
+                PopulateGlass(args.Entity, args.Mapping);
                 args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
             }
         }
     );
-    
+
+    private void PopulateGlass(Product entity, ProductIntegrationSource mapping)
+    {
+        entity.Code = mapping.Code ?? "";
+        entity.Name = mapping.Description ?? "";
+        entity.UnitOfMeasure.ID = _settings.GlassUom.ID;
+        var _group = GroupMappings?.FirstOrDefault(x => Equals(x.Code, mapping.Group));
+        if (_group != null)
+            entity.Group.ID = _group.Entity.ID;
+    }
+
     public ICommand CreateActivity => new ActionCommand(
         o =>
         {
             if (o is IntegrationGridCreateEntityArgs<Activity, ActivityIntegrationSource> args)
             {
-                args.Entity.Code = args.Mapping.Code ?? "";
-                args.Entity.Description = args.Mapping.Description ?? "";
+                PopulateActivity(args.Entity, args.Mapping);
+                
                 args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
             }
         }
     );
-    
+
+    private void PopulateActivity(Activity entity, ActivityIntegrationSource mapping)
+    {
+        entity.Code = mapping.Code ?? "";
+        entity.Description = mapping.Description ?? "";
+    }
+
     private class RawDimensions : IBaseDimensions
     {
         public double Quantity { get; set; }
@@ -1245,5 +1342,90 @@ public class AWGMappingWindowViewModel : DependencyObject
         }
 
     }
+
+    private void CheckUpdate<TEntity,TItem,TMapping>(bool isChecked, LogikalUpdateType updateType, IEnumerable<TItem>? items, 
+        IEnumerable<TMapping>? mappings, 
+        Action<TEntity,TMapping> callback)
+        where TEntity : Entity, IRemotable, IPersistent, new()
+        where TItem : IIntegrationItem
+        where TMapping : class, IBaseIntegrationSource
+    {
+        var _items = items as TItem[] ?? items?.ToArray();
+        var _mappings = mappings as TMapping[] ?? mappings?.ToArray();
+        if (isChecked && updateType.Equals(LogikalUpdateType.AlwaysUpdate) && _items?.Any()==true && _mappings?.Any() == true)
+        {
+            var _ids = _mappings?.Select(x => x.Entity.ID).Distinct().ToArray() ?? [];
+            var _entities = new Client<TEntity>().Query(
+                new Filter<TEntity>(x=>x.ID).InList(_ids), 
+                Columns.Local<TEntity>().Add(Columns.Required<TEntity>())
+            ).ToArray<TEntity>();
+            
+            foreach (var _item in _items)
+            {
+                var _mapping = _mappings?.FirstOrDefault(x => string.Equals(x.Code, _item.Code));
+                if (_mapping != null)
+                {
+                    var _entity = _entities.FirstOrDefault(x => x.ID == _mapping.Entity.ID);
+                    if (_entity != null)
+                        callback(_entity, _mapping);
+                }
+            }
+
+            var _updates = _entities.Where(x => x.IsChanged()).ToArray();
+            if (_updates.Any())
+                new Client<TEntity>().Save(_updates,"Updated by AWG Mapping Window");
+        }
+    }
+
+    public void CheckUpdates()
+    {
+        CheckUpdate<ProductStyle,IAwgStyle,ProductStyleIntegrationSource>(StylesChecked, Settings.UpdateStyles, Styles, 
+            StyleMappings, PopulateStyle);
+        
+        CheckUpdate<ProductGroup,IAwgGroup,ProductGroupIntegrationSource>(GroupsChecked, Settings.UpdateGroups, Groups, 
+            GroupMappings, PopulateGroup);
+        
+        CheckUpdate<Supplier, IAwgSupplier, SupplierIntegrationSource>(SuppliersChecked, Settings.UpdateSuppliers,
+            Suppliers, SupplierMappings, PopulateSupplier);
+        
+        CheckUpdate<SupplierDiscountGroup, IAwgDiscount,SupplierDiscountGroupIntegrationSource>(DiscountsChecked,
+            Settings.UpdateDiscounts, Discounts, DiscountMappings, (entity,mapping) =>
+            {
+                PopulateDiscountGroup(entity,mapping);
+                PopulateDiscount(entity,mapping);
+            });
+
+        CheckUpdate<Product, IAwgProfile, ProductIntegrationSource>(ProfilesChecked, Settings.UpdateProfiles,
+            Profiles, ProfileMappings, (entity, mapping) =>
+            {
+                PopulateProfile(entity,mapping);
+                PopulateProduct(entity,mapping);
+            });
+        
+        CheckUpdate<Product, IAwgGasket, ProductIntegrationSource>(GasketsChecked, Settings.UpdateGaskets,
+            Gaskets, GasketMappings, (entity, mapping) =>
+            {
+                PopulateGasket(entity,mapping);
+                PopulateProduct(entity,mapping);
+            });
+        
+        CheckUpdate<Product, IAwgComponent, ProductIntegrationSource>(ComponentsChecked, Settings.UpdateComponents,
+            Components, ComponentMappings, (entity, mapping) =>
+            {
+                PopulateComponent(entity,mapping);
+                PopulateProduct(entity,mapping);
+            });
+        
+        CheckUpdate<Product, IAwgGlass, ProductIntegrationSource>(GlassChecked, Settings.UpdateGlass,
+            Glass, GlassMappings, (entity, mapping) =>
+            {
+                PopulateGlass(entity,mapping);
+                PopulateProduct(entity,mapping);
+            });
+        
+        CheckUpdate<Activity, IAwgLabour, ActivityIntegrationSource>(LabourChecked, Settings.UpdateLabour,
+            Labour, LabourMappings, PopulateActivity);
+
+    }
     
 }

+ 0 - 2
prs.desktop/Integrations/Logikal/Classes/LogikalGasket.cs

@@ -59,8 +59,6 @@ left outer join
     suppliers s on a.[LK_SupplierID] = s.[SupplierID]
 left outer join colors c2
     on trim(c2.[colorname]) = trim(Substr(a.[articlecode_ordercode],Instr(a.[articlecode_ordercode], '.') + 1))
-left outer join colors c2
-    on trim(c2.[colorname]) = trim(Substr(a.[articlecode_ordercode],Instr(a.[articlecode_ordercode], '.') + 1))
 left outer join colors c
     on c.[colorid] = coalesce(c2.[ColorID],a.[lk_ColorID])
 where

+ 4 - 2
prs.desktop/Integrations/Logikal/Classes/LogikalProfile.cs

@@ -90,8 +90,10 @@ $@"select
     p.[PriceGross]*p.[Length] as [{nameof(Cost)}]
 from
     profilebars p 
-left outer join
-    colors c on p.[lk_colorid] = c.[colorid]      
+left outer join colors c2
+    on trim(c2.[colorname]) = trim(Substr(p.[articlecode_ordercode],Instr(p.[articlecode_ordercode], '.') + 1))
+left outer join colors c
+    on c.[colorid] = coalesce(c2.[ColorID],p.[lk_ColorID])        
 left outer join
     suppliers s on p.[SupplierID] = s.[SupplierID]
 group by

+ 0 - 1
prs.desktop/Integrations/Logikal/Classes/LogikalStyle.cs

@@ -29,7 +29,6 @@ namespace PRSDesktop.Integrations.Logikal
 
         public static String SQL = $@"select distinct 
     
-   
     upper(case c.[ColorTypeSupplier]
         when -1 then 
             case coalesce(c.[PowderID],'')

+ 14 - 7
prs.desktop/Integrations/Logikal/LogikalClient.cs

@@ -780,6 +780,7 @@ public class LogikalClient : IDisposable
                         _component.PackSize = CheckValue<double>(row[nameof(LogikalComponent.PackSize)]);
                         _component.Group = (CheckValue<string>(row[nameof(LogikalComponent.Group)]) ?? "").ToUpper();
                         _component.Supplier = (CheckValue<string>(row[nameof(LogikalComponent.Supplier)]) ?? "").ToUpper();
+                        _component.Finish = Settings.IgnoreStyles(CheckValue<string>(row[nameof(LogikalComponent.Finish)]) ?? "").ToUpper();
                         components.Add(_component);
                     }
 
@@ -817,6 +818,7 @@ public class LogikalClient : IDisposable
                         _gasket.Length =  GetScale(Settings.GasketMeasurement, CheckValue<double>(row[nameof(LogikalGasket.Length)]));
                         _gasket.Group = (CheckValue<string>(row[nameof(LogikalGasket.Group)]) ?? "").ToUpper();
                         _gasket.Supplier = (CheckValue<string>(row[nameof(LogikalGasket.Supplier)]) ?? "").ToUpper();
+                        _gasket.Finish = Settings.IgnoreStyles(CheckValue<string>(row[nameof(LogikalGasket.Finish)]) ?? "").ToUpper();
                         gaskets.Add(_gasket);
                     }
 
@@ -852,7 +854,7 @@ public class LogikalClient : IDisposable
                         _profile.Description = CheckValue<string>(row[nameof(LogikalProfile.Description)]);
                         _profile.Quantity = CheckValue<Int64>(row[nameof(LogikalProfile.Quantity)]);
                         _profile.Cost = CheckValue<double>(row[nameof(LogikalProfile.Cost)]);
-                        _profile.Finish = (CheckValue<string>(row[nameof(LogikalProfile.Finish)]) ?? "").ToUpper();
+                        _profile.Finish = Settings.IgnoreStyles(CheckValue<string>(row[nameof(LogikalProfile.Finish)]) ?? "").ToUpper();
                         _profile.Length =  GetScale(Settings.ProfileMeasurement, CheckValue<double>(row[nameof(LogikalProfile.Length)]));
                         _profile.Group = (CheckValue<string>(row[nameof(LogikalProfile.Group)]) ?? "").ToUpper();
                         _profile.Supplier = (CheckValue<string>(row[nameof(LogikalProfile.Supplier)]) ?? "").ToUpper();
@@ -985,12 +987,17 @@ public class LogikalClient : IDisposable
                     List<LogikalStyle> finishes = new List<LogikalStyle>();
                     foreach (DataRow row in _dt.Rows)
                     {
-                        var style = new LogikalStyle();
-                        style.Code = CheckValue<string>(row[nameof(LogikalStyle.Code)])?.ToUpper() ?? "";
-                        style.Description = CheckValue<string>(row[nameof(LogikalStyle.Description)]) ?? "";
-                        style.Cost = CheckValue<double>(row[nameof(LogikalStyle.Cost)]);
-                        style.StyleType = (AwgStyleType)CheckValue<int>(row[nameof(LogikalStyle.StyleType)]);
-                        finishes.Add(style);
+                        var _code = Settings.IgnoreStyles(
+                            CheckValue<string>(row[nameof(LogikalStyle.Code)])?.ToUpper() ?? "");
+                        if (!string.IsNullOrWhiteSpace(_code))
+                        {
+                            var style = new LogikalStyle();
+                            style.Code = _code;
+                            style.Description = CheckValue<string>(row[nameof(LogikalStyle.Description)]) ?? "";
+                            style.Cost = CheckValue<double>(row[nameof(LogikalStyle.Cost)]);
+                            style.StyleType = (AwgStyleType)CheckValue<int>(row[nameof(LogikalStyle.StyleType)]);
+                            finishes.Add(style);
+                        }
                     }
 
                     elevation.Styles = finishes;

+ 21 - 6
prs.desktop/Panels/Jobs/BillOfMaterials/JobBillOfMaterialsGrid.cs

@@ -195,12 +195,27 @@ namespace PRSDesktop
                     exceldata = bom.ExcelData;
                     if (_logikalSettings.SaveFiles)
                     {
-                        File.WriteAllBytes(
-                            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
-                                "BillOfMaterials.xlsx"), bom.ExcelData);
-                        File.WriteAllBytes(
-                            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
-                                "BillOfMaterials.sqlite"), bom.SQLiteData);
+                        var _ssFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
+                            "BillOfMaterials.xlsx");
+                        try
+                        {
+                            File.WriteAllBytes(_ssFile, bom.ExcelData);
+                        }
+                        catch (Exception e)
+                        {
+                            MessageWindow.ShowError($"Unable to Save File: {_ssFile}",e);
+                        }
+                        
+                        var _sqlFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
+                            "BillOfMaterials.sqlite");
+                        try
+                        {
+                            File.WriteAllBytes(_sqlFile, bom.SQLiteData);
+                        }
+                        catch (Exception e)
+                        {
+                            MessageWindow.ShowError($"Unable to Save File: {_sqlFile}",e);
+                        }
                     }
 
                     AWGMappingWindow window = new AWGMappingWindow(