using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; namespace PRSDesktop.Panels.Requisitions; /// /// Interaction logic for RequisitionItemEditor.xaml /// public partial class RequisitionItemEditor : INotifyPropertyChanged { private readonly RequisitionItem Item; public RequisitionItem[]? Result { get; private set; } private bool _loadedAllocations; private bool _loadedStockHoldings; private bool _loadedProductInstances; private bool _loadedBOMItems; private bool _loadedJobRequisitionItems; private bool _loadedCustom; private bool _allowActual; private bool _canSave; public bool CanSave { get => _canSave; private set { _canSave = value; OnPropertyChanged(); } } public RequisitionItemEditor(Func> products, RequisitionItem requisitionItem, bool allowActual) { _allowActual = allowActual; Item = requisitionItem; InitializeComponent(); StockHoldingProducts.ProductsFunction = products; ProductInstancesProducts.ProductsFunction = products; BOMItems.Item = requisitionItem; JobRequisitionItemsGrid.Item = requisitionItem; AllocationsPage.Visibility = _allowActual && requisitionItem.Product.ID != Guid.Empty ? Visibility.Visible : Visibility.Collapsed; HoldingsPage.Visibility = _allowActual && requisitionItem.SourceJRI.ID == Guid.Empty ? Visibility.Visible : Visibility.Collapsed; InstancesPage.Visibility = _allowActual ? Visibility.Collapsed : Visibility.Visible; BOMPage.Visibility = _allowActual ? Visibility.Collapsed : Visibility.Visible; RequisitionPage.Visibility = _allowActual ? Visibility.Collapsed : Visibility.Visible; TabControl.SelectedItem = requisitionItem.EditType == RequisitionItemEditType.Normal ? _allowActual ? requisitionItem.Product.ID != Guid.Empty ? AllocationsPage : HoldingsPage : requisitionItem.JobRequisitionItem.ID == Guid.Empty ? BOMPage : RequisitionPage : CustomPage; CanSave = false; UpdateState(); } private void UpdateState() { if (TabControl.SelectedTab == AllocationsPage) { if (!_loadedAllocations) { LoadAllocations(); _loadedAllocations = true; } } else if(TabControl.SelectedTab == HoldingsPage) { if (!_loadedStockHoldings) { LoadStockHoldings(); _loadedStockHoldings = true; } } else if(TabControl.SelectedTab == BOMPage) { if (!_loadedBOMItems) { LoadBOMItems(); _loadedBOMItems = true; } } else if(TabControl.SelectedTab == RequisitionPage) { if (!_loadedJobRequisitionItems) { LoadJobRequisitionItems(); _loadedJobRequisitionItems = true; } } else if(TabControl.SelectedTab == InstancesPage) { if (!_loadedProductInstances) { LoadProductInstances(); _loadedProductInstances = true; } } else if (TabControl.SelectedTab == CustomPage) { if (!_loadedCustom) { LoadCustom(); _loadedCustom = true; } } CheckCanSave(); } private void CheckCanSave() { if (TabControl.SelectedTab == AllocationsPage) CanSave = CanSaveAllocations(); else if (TabControl.SelectedTab == HoldingsPage) CanSave = CanSaveStockHoldings(); else if (TabControl.SelectedTab == BOMPage) CanSave = CanSaveBOMItems(); else if (TabControl.SelectedTab == RequisitionPage) CanSave = CanSaveJobRequisitionItems(); else if (TabControl.SelectedTab == InstancesPage) CanSave = CanSaveProductInstances(); else if (TabControl.SelectedTab == CustomPage) CanSave = true; } private void DoSave() { if (TabControl.SelectedTab == AllocationsPage) SaveAllocations(); else if (TabControl.SelectedTab == HoldingsPage) SaveStockHoldings(); else if (TabControl.SelectedTab == BOMPage) SaveBOMItems(); else if (TabControl.SelectedTab == RequisitionPage) SaveJobRequisitionItems(); else if (TabControl.SelectedTab == InstancesPage) SaveProductInstances(); else if (TabControl.SelectedTab == CustomPage) SaveCustom(); } private void OKButton_Click(object sender, RoutedEventArgs e) { DoSave(); DialogResult = true; Close(); } private void CancelButton_Click(object sender, RoutedEventArgs e) { Item.CancelChanges(); DialogResult = false; Close(); } private void TabChanged(object sender, SelectionChangedEventArgs e) { if (e.OriginalSource == sender) UpdateState(); } #region Allocations private IEnumerable CreateStockSelectionItems(IEnumerable> groups, Func description) { var _result = new List(); foreach (var _group in groups) { var _units = _group.Sum(x => x.Units); if (!_units.IsEffectivelyEqual(0.0)) { var _holding = new StockHolding(); _holding.Location.ID = _group.Key; _holding.Location.Synchronise(_group.FirstOrDefault()?.Location ?? new StockLocationLink()); _holding.Product.ID = Item.Product.ID; _holding.Product.Synchronise(Item.Product); _holding.Style.ID = Item.Style.ID; _holding.Style.Synchronise(Item.Style); _holding.Dimensions.CopyFrom(Item.Dimensions); _holding.Location.Area.Code = description(_group.FirstOrDefault() ?? new StockMovement()); _holding.Units = _units; var _jri = new JobRequisitionItem() { ID = _group.FirstOrDefault()?.JobRequisitionItem.ID ?? Guid.Empty }; _result.Add(new StockSelectionItem() { Holding = _holding, MaxValue = Item.Quantity, JRI = _jri }); } } return _result; } private void LoadAllocations() { var movements = Client.Query( new Filter(x=>x.Product.ID).IsEqualTo(Item.Product.ID) .And(x=>x.Style.ID).IsEqualTo(Item.Style.ID) .And(x=>x.Dimensions).DimensionEquals(Item.Dimensions) .And( new Filter(x => x.JobRequisitionItem.ID).IsEqualTo(Item.SourceJRI.ID) .Or(new Filter(x => x.Job.ID).IsEqualTo(Item.RequisitionLink.JobLink.ID) .And(x => x.JobRequisitionItem.ID).IsEqualTo(Guid.Empty)) .Or(new Filter(x => x.Job.ID).IsEqualTo(Guid.Empty) .And(x => x.JobRequisitionItem.ID).IsEqualTo(Guid.Empty)) ), Columns.None() .Add(x => x.Location.ID) .Add(x => x.Location.Code) .Add(x => x.Location.Description) .Add(x => x.Job.ID) .Add(x=>x.Job.JobNumber) .Add(x=>x.Job.Name) .Add(x => x.JobRequisitionItem.ID) .Add(x=>x.JobRequisitionItem.Requisition.Number) .Add(x=>x.JobRequisitionItem.Requisition.Description) .Add(x => x.Units) ).Rows.Select(x=>x.ToObject()).ToArray(); List items = new(); var allocations = movements.Where(x => x.JobRequisitionItem.ID == Item.SourceJRI.ID) .GroupBy(x => x.Location.ID); items.AddRange( CreateStockSelectionItems( allocations, (m) => $"{m.Job.JobNumber}:{m.JobRequisitionItem.Requisition.Number} {m.JobRequisitionItem.Requisition.Description}" ) ); // if the pickinglist does not have a job, skip this bit var reserves = movements.Where(x => x.JobRequisitionItem.ID != Guid.Empty && x.Job.ID == Item.RequisitionLink.JobLink.ID && x.JobRequisitionItem.ID == Guid.Empty) .GroupBy(x => x.Location.ID); items.AddRange( CreateStockSelectionItems( reserves, (m) => $"{m.Job.JobNumber} (Unallocated Stock)" ) ); var freeitems = movements.Where(x => x.Job.ID == Guid.Empty && x.JobRequisitionItem.ID == Guid.Empty) .GroupBy(x => x.Location.ID); items.AddRange( CreateStockSelectionItems( freeitems, (m) => $"General Stock" ) ); Allocations.Items = items; Allocations.Refresh(true,true); } private void Allocations_OnAfterRefresh(object sender, AfterRefreshEventArgs args) { UpdateMaximums(); } private void UpdateMaximums() { foreach (var item in Allocations.Items) item.MaxValue = Item.Quantity + item.Issued - Allocations.Items.Sum(x=>x.Issued); } private void Allocations_OnPropertyChanged(object? sender, PropertyChangedEventArgs e) { UpdateMaximums(); CheckCanSave(); } private bool CanSaveAllocations() { return Allocations.Items.Sum(x=>x.Issued).IsEffectivelyGreaterThan(0.0); } private RequisitionItem CopyRequisitionItem() { var newItem = new RequisitionItem(); newItem.Done = Item.Done; newItem.Image.CopyFrom(Item.Image); newItem.RequisitionLink.ID = Item.RequisitionLink.ID; newItem.SourceJRI.ID = Item.SourceJRI.ID; newItem.Product.CopyFrom(Item.Product); newItem.Style.CopyFrom(Item.Style); newItem.Location.CopyFrom(Item.Location); newItem.JobRequisitionItem.ID = Item.JobRequisitionItem.ID; newItem.Dimensions.CopyFrom(Item.Dimensions, true); newItem.JobLink.CopyFrom(Item.JobLink); newItem.Description = Item.Description; newItem.Quantity = Item.Quantity; newItem.EditType = RequisitionItemEditType.Normal; return newItem; } private void SaveAllocations() { var remainder = Item.Quantity; var newItems = new List(); foreach(var item in Allocations.Items.Where(x=>!x.Issued.IsEffectivelyEqual(0.0))) { var newItem = newItems.Count == 0 ? Item : CopyRequisitionItem(); newItem.Location.CopyFrom(item.Holding.Location); newItem.JobRequisitionItem.CopyFrom(item.JRI); newItem.JobLink.CopyFrom(item.Holding.Job); // Automagically attach to Requisition if it seems appropriate // ie selected from a JRI allocation, and current source jri is blank if (newItem.SourceJRI.ID == Guid.Empty && newItem.RequisitionLink.JobLink.ID == item.Holding.Job.ID && item.JRI.ID != Guid.Empty) newItem.SourceJRI.ID = item.JRI.ID; newItem.Quantity = item.Issued; newItem.ActualQuantity = item.Issued; newItem.Done = true; remainder -= item.Issued; newItems.Add(newItem); } if(!remainder.IsEffectivelyEqual(0.0)) { var newItem = CopyRequisitionItem(); newItem.Location.CopyFrom(new StockLocationLink()); newItem.JobRequisitionItem.CopyFrom(new JobRequisitionItemLink()); newItem.JobLink.CopyFrom(new JobLink()); newItem.Quantity = remainder; newItem.Done = false; newItems.Add(newItem); } Client.Save(newItems, "Updated from RequisitionItemEditor(Allocations)"); Result = newItems.ToArray(); } #endregion #region StockHoldings private void LoadStockHoldings() { StockHoldingProducts.Refresh(true, true); foreach(var column in StockHolding.Columns) { StockHoldings.HiddenColumns.Add(column); } foreach(var column in Columns.None() .AddSubColumns(x => x.Product, LookupFactory.DefineLookupColumns(x => x.Product).CastIntersection()) .AddSubColumns(x => x.Style, LookupFactory.DefineLookupColumns(x => x.Style).CastIntersection()) .AddSubColumns(x => x.Location, LookupFactory.DefineLookupColumns(x => x.Location).CastIntersection()) .AddSubColumns(x => x.Job, LookupFactory.DefineLookupColumns(x => x.JobLink).CastIntersection()) ) { StockHoldings.HiddenColumns.Add(column); } StockHoldings.HiddenColumns.Add(x => x.Available); StockHoldings.Refresh(true, false); if(Item.Product.ID != Guid.Empty) { // Delaying, cause otherwise it doesn't work. Dispatcher.BeginInvoke(() => { StockHoldingProducts.SelectedRows = StockHoldingProducts.Data.Rows.Where(x => x.Get(x => x.ID) == Item.Product.ID).ToArray(); StockHoldingProducts.ScrollIntoView(StockHoldingProducts.SelectedRows[0]); }); } } private void StockHoldingProducts_OnSelectItem(object sender, InABox.DynamicGrid.DynamicGridSelectionEventArgs e) { StockHoldings.Product = e.Rows is not null && e.Rows.Length > 0 ? e.Rows[0].ToObject() : null; StockHoldings.Refresh(false, true); CheckCanSave(); } private void StockHoldings_OnAfterRefresh(object sender, AfterRefreshEventArgs args) { var row = StockHoldings.Data.Rows.FirstOrDefault(); if (row != null) { StockHoldings.SelectedRows = new CoreRow[] { row }; SelectHolding(new CoreRow[] { row }); } } private void StockHoldings_OnSelectItem(object sender, InABox.DynamicGrid.DynamicGridSelectionEventArgs e) { SelectHolding(e.Rows); } private void SelectHolding(CoreRow[]? rows) { StockHolding = rows?.FirstOrDefault()?.ToObject(); RefreshJobRequisitionItems(); JRIRow.Height = _allowActual && StockHolding is not null ? new GridLength(1, GridUnitType.Star) : new GridLength(0); TotalRow.Height = _allowActual && StockHolding is not null ? new GridLength(40) : new GridLength(0); RequiredEditor.SetValue(Grid.ColumnProperty, _allowActual && StockHolding is not null ? 4 : 6); CheckCanSave(); } private bool CanSaveStockHoldings() { return StockHolding is not null && TotalTaken > 0; } private void SaveStockHoldings() { if (StockHolding is null) return; var items = JRIItems.Where(x => x.Taken > 0).ToArray(); if (items.Length == 0) return; Item.Product.CopyFrom(StockHolding.Product); Item.Style.CopyFrom(StockHolding.Style); Item.Location.CopyFrom(StockHolding.Location); Item.Dimensions.CopyFrom(StockHolding.Dimensions, true); Item.JobLink.CopyFrom(StockHolding.Job); Item.EditType = RequisitionItemEditType.Normal; var remainder = Item.Quantity; var newItems = new List(); foreach(var item in items) { RequisitionItem newItem; if(newItems.Count == 0) { newItem = Item; } else { newItem = new RequisitionItem(); newItem.Done = Item.Done; newItem.Description = Item.Description; newItem.Image.CopyFrom(Item.Image); newItem.RequisitionLink.ID = Item.RequisitionLink.ID; newItem.JobRequisitionItem.ID = item.ID; newItem.Product.CopyFrom(Item.Product); newItem.Style.CopyFrom(Item.Style); newItem.Location.CopyFrom(Item.Location); newItem.Dimensions.CopyFrom(Item.Dimensions, true); newItem.JobLink.CopyFrom(Item.JobLink); newItem.EditType = RequisitionItemEditType.Normal; } newItem.JobRequisitionItem.ID = item.ID; newItem.Quantity = item.Taken; newItem.ActualQuantity = item.Taken; remainder -= item.Taken; newItems.Add(newItem); } if(remainder > 0) { var newItem = new RequisitionItem(); newItem.Done = false; newItem.Description = Item.Description; newItem.Image.CopyFrom(Item.Image); newItem.RequisitionLink.ID = Item.RequisitionLink.ID; newItem.JobRequisitionItem.ID = Guid.Empty; newItem.Product.CopyFrom(Item.Product); newItem.Style.CopyFrom(Item.Style); newItem.Dimensions.CopyFrom(Item.Dimensions, true); newItem.Quantity = remainder; newItems.Add(newItem); } Client.Save(newItems, "Edited by user."); Result = newItems.ToArray(); } #region JobRequisitionItem Selection public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private bool _observing = false; public void SetObserving(bool observing) { if(_observing != observing) { _observing = observing; if (_observing) { Recalculate(); } } } private StockHolding? StockHolding; private List _jriItems; public List JRIItems { get => _jriItems; set { _jriItems = value; OnPropertyChanged(); } } private void RefreshJobRequisitionItems() { SetObserving(false); if(StockHolding is null) { JRIItems = []; } else { var jris = StockHolding.LoadRequisitionItems(true); JRIItems = jris.Select(x => { var newItem = new RequisitionItemStockHoldingItem { Quantity = x.Qty, Taken = 0, ID = x.ID, Text = x.ID == Guid.Empty ? "General Stock" : $"{x.Job.JobNumber}:{x.Requisition.Number} {x.Requisition.Description} ({x.Qty})", }; newItem.PropertyChanged += (o, e) => Recalculate(); return newItem; }).ToList(); } SetObserving(true); } private void Recalculate() { if (!_observing) return; TotalTaken = JRIItems.Sum(x => x.Taken); } private double _totalTaken; public double TotalTaken { get => _totalTaken; set { _totalTaken = value; OnPropertyChanged(); CheckCanSave(); } } public double RequiredQuantity { get => Item.Quantity; set { Item.Quantity = value; CheckCanSave(); OnPropertyChanged(); } } private void None_Click(object sender, RoutedEventArgs e) { if (sender is not FrameworkElement element || element.Tag is not RequisitionItemStockHoldingItem item) return; item.Taken = 0; } private void Minus_Click(object sender, RoutedEventArgs e) { if (sender is not FrameworkElement element || element.Tag is not RequisitionItemStockHoldingItem item) return; item.Taken = Math.Max(0, item.Taken - 1); } private void Plus_Click(object sender, RoutedEventArgs e) { if (sender is not FrameworkElement element || element.Tag is not RequisitionItemStockHoldingItem item) return; item.Taken = Math.Min(item.Taken + 1, item.Quantity); } private void All_Click(object sender, RoutedEventArgs e) { if (sender is not FrameworkElement element || element.Tag is not RequisitionItemStockHoldingItem item) return; item.Taken = Math.Min(Math.Max(RequiredQuantity - (TotalTaken - item.Taken), 0), item.Quantity); } #endregion #endregion #region BOM Items private void LoadBOMItems() { BOMItems.Refresh(true,true); } private bool CanSaveBOMItems() { return BOMItems.Data.Rows.Any(r =>!r.Get(x=>x.Quantity).IsEffectivelyEqual(0.0)); } private void SaveBOMItems() { var result = new List() { Item }; var bomItems = BOMItems.Data.Rows .Where(r => !r.Get(x => x.Quantity).IsEffectivelyEqual(0.0)) .Select(r => r.ToObject()).ToArray(); var first = bomItems.FirstOrDefault(); if (first is null) return; Item.Product.CopyFrom(first.Product); Item.Style.CopyFrom(first.Style); Item.Dimensions.CopyFrom(first.Dimensions, true); Item.EditType = RequisitionItemEditType.Normal; Item.Quantity = first.Quantity; foreach (var item in bomItems.Skip(1).ToArray()) { var newItem = new RequisitionItem(); newItem.RequisitionLink.ID = Item.RequisitionLink.ID; newItem.Done = false; newItem.Description = item.Product.Name; newItem.Product.CopyFrom(item.Product); newItem.Style.CopyFrom(item.Style); newItem.Dimensions.CopyFrom(item.Dimensions, true); newItem.EditType = RequisitionItemEditType.Normal; newItem.Quantity = item.Quantity; result.Add(newItem); } Client.Save(result, "Edited by user."); Result = result.ToArray(); } private void BOMItems_OnOnChanged(object? sender, EventArgs e) { CheckCanSave(); } #endregion #region Job Requisition Items private void LoadJobRequisitionItems() { JobRequisitionItemsGrid.Refresh(true,true); } private bool CanSaveJobRequisitionItems() { return JobRequisitionItemsGrid.Data.Rows.Any(r =>!r.Get(x=>x.Quantity).IsEffectivelyEqual(0.0)); } private void SaveJobRequisitionItems() { var result = new List() { Item }; var items = JobRequisitionItemsGrid.Data.Rows .Where(r => !r.Get(x => x.Quantity).IsEffectivelyEqual(0.0)) .Select(r => r.ToObject()).ToArray(); var first = items.FirstOrDefault(); if (first is null) return; Item.Product.CopyFrom(first.JRI.Product); Item.Style.CopyFrom(first.JRI.Style); Item.Dimensions.CopyFrom(first.JRI.Dimensions, true); Item.JobRequisitionItem.CopyFrom(first.JRI); Item.EditType = RequisitionItemEditType.Normal; Item.Quantity = first.Quantity; foreach (var item in items.Skip(1).ToArray()) { var newItem = new RequisitionItem(); newItem.RequisitionLink.ID = Item.RequisitionLink.ID; newItem.Done = false; newItem.Description = item.JRI.Product.Name; newItem.Product.CopyFrom(item.JRI.Product); newItem.Style.CopyFrom(item.JRI.Style); newItem.Dimensions.CopyFrom(item.JRI.Dimensions, true); newItem.JobRequisitionItem.CopyFrom(item.JRI); newItem.EditType = RequisitionItemEditType.Normal; newItem.Quantity = item.Quantity; result.Add(newItem); } Client.Save(result, "Edited by user."); Result = result.ToArray(); } private void JobRequisitionItems_OnOnChanged(object? sender, EventArgs e) { CheckCanSave(); } #endregion #region Product Instances private ProductInstance? SelectedProductInstance; private void LoadProductInstances() { ProductInstancesProducts.Refresh(true, true); ProductInstances.Refresh(true, false); foreach(var column in Columns.None().Add(x => x.Style.ID).AddDimensionsColumns(x => x.Dimensions)) { ProductInstances.HiddenColumns.Add(column); } if(Item.Product.ID != Guid.Empty) { // Delaying, cause otherwise it doesn't work. Dispatcher.BeginInvoke(() => { ProductInstancesProducts.SelectedRows = ProductInstancesProducts.Data.Rows.Where(x => x.Get(x => x.ID) == Item.Product.ID).ToArray(); if (ProductInstancesProducts.SelectedRows.Any()) ProductInstancesProducts.ScrollIntoView(ProductInstancesProducts.SelectedRows[0]); }); } } private bool CanSaveProductInstances() { return ProductInstances.Product is not null && SelectedProductInstance is not null && RequiredQuantity > 0; } private void SaveProductInstances() { if (ProductInstances.Product is null || SelectedProductInstance is null) return; Item.Product.CopyFrom(ProductInstances.Product); Item.Style.CopyFrom(SelectedProductInstance.Style); Item.Dimensions.CopyFrom(SelectedProductInstance.Dimensions, true); Item.Location.ID = Guid.Empty; Item.EditType = RequisitionItemEditType.Normal; Client.Save(Item, "Edited by user."); Result = [Item]; } private void ProductInstancesProducts_OnSelectItem(object sender, InABox.DynamicGrid.DynamicGridSelectionEventArgs e) { ProductInstances.Product = e.Rows is not null && e.Rows.Length > 0 ? e.Rows[0].ToObject() : null; ProductInstances.Refresh(false, true); CheckCanSave(); } private void ProductInstances_OnSelectItem(object sender, DynamicGridSelectionEventArgs e) { SelectedProductInstance = e.Rows is not null && e.Rows.Length > 0 ? e.Rows[0].ToObject() : null; CheckCanSave(); } #endregion #region Custom private void LoadCustom() { CustomEditorGrid.Setup(typeof(RequisitionItem)); CustomEditorGrid.Items = new RequisitionItem[] { Item }; } private static readonly Columns ActualCustomColumns = Columns.None() .Add(x => x.Product.ID) .Add(x => x.Style.ID) .Add(x => x.Dimensions) .Add(x => x.Description) .Add(x => x.Location.ID) .Add(x => x.Quantity) .Add(x => x.ActualQuantity); private static readonly Columns RequstedCustomColumns = Columns.None() .Add(x => x.Product.ID) .Add(x => x.Style.ID) .Add(x => x.Dimensions) .Add(x => x.Description) .Add(x => x.Location.ID) .Add(x => x.Quantity) .Add(x => x.ActualQuantity); // private static readonly Columns ProductInstanceCustomColumns = new Columns() // .Add(x => x.Product.ID) // .Add(x => x.Style.ID) // .Add(x => x.Dimensions) // .Add(x => x.Description) // .Add(x => x.Quantity); private void CustomEditorGrid_OnFormCustomiseEditor(IDynamicEditorForm sender, object items, DynamicGridColumn column, BaseEditor editor) { var columns = _allowActual ? ActualCustomColumns : RequstedCustomColumns; editor.Editable = columns.Contains(column.ColumnName) ? Editable.Enabled : Editable.Hidden; } /* private void LoadCustom() { var host = new DefaultDynamicEditorHost { Items = [Item] }; var editors = new Dictionary(); BaseDynamicEditorControl? CreateControl(Expression> exp, int row) { var property = DatabaseSchema.Property(exp)!; BaseEditor? editor; if (property.HasParentEditor()) { property = property.GetParentWithEditor(); } if (property is null) return null; editor = property.Editor; editor = editor.CloneEditor(); var control = DynamicEditorControlFactory.CreateControl(editor, host)!; Grid.SetRow(control, row); Grid.SetColumn(control, 1); control.Margin = new Thickness(5, 2.5, 5, 2.5); CustomEditorGrid.Children.Add(control); editors[property.Name] = control; control.IsEnabled = true; control.ColumnName = property.Name; control.Color = editor is UniqueCodeEditor ? Color.FromArgb(0xFF, 0xF6, 0xC9, 0xE8) : Colors.LightYellow; control.Configure(); control.Loaded = true; foreach(var (k, v) in control.GetValues()) { var value = DatabaseSchema.Property(typeof(RequisitionItem), k)!.Getter()(Item); if(!Equals(v, value)) { bool bLoaded = control.Loaded; control.Loaded = false; control.SetValue(k, value); control.Loaded = bLoaded; } } control.OnEditorValueChanged += (sender, values) => { var changededitors = new Dictionary(); void ExtractChanged(Dictionary? columns) { if (columns != null) foreach (var (change, value) in columns) if (!changededitors.ContainsKey(change) && !change.Equals(sender.ColumnName)) changededitors[change] = value; } var name = sender.ColumnName; var resetAll = false; if(values.Remove(name, out var value)) { var changedcolumns = DynamicGridUtils.UpdateEditorValue([Item], sender.ColumnName, value); resetAll = changedcolumns?.ContainsKey(name) ?? false; ExtractChanged(changedcolumns); } foreach(var (k, v) in values) { var changes = new Dictionary(); if (resetAll) { var prop = DatabaseSchema.Property(typeof(RequisitionItem), k); if(prop is not null) { var def = CoreUtils.GetDefault(prop.PropertyType); var resetChanges = DynamicGridUtils.UpdateEditorValue([Item], k, def); if(resetChanges is not null) { changes = resetChanges; } } else { } } var changedOtherColumns = DynamicGridUtils.UpdateEditorValue([Item], k, v); if(changedOtherColumns is not null) { foreach (var (k1, v1) in changedOtherColumns) { changes[k1] = v1; } } ExtractChanged(changes); } if (changededitors.Count != 0) { foreach(var (k, v) in changededitors) { var editor = editors.GetValueOrDefault(k) ?? editors.FirstOrDefault(x => k.StartsWith(x.Key + ".")).Value; if(editor is not null) { editor.Loaded = false; editor.SetValue(k, v); editor.Loaded = true; } } } }; return control; } var productEditor = CreateControl(x => x.Product.ID, 0); var styleEditor = CreateControl(x => x.Style.ID, 1); var dimensionsEditor = CreateControl(x => x.Dimensions.Quantity, 2); var descriptionEditor = CreateControl(x => x.Description, 3); var locationEditor = CreateControl(x => x.Location.ID, 4); var quantityEditor = CreateControl(x => x.Quantity, 5); } */ private void SaveCustom() { Item.EditType = RequisitionItemEditType.Custom; Client.Save(Item, "Custom edited by user."); Result = [Item]; } #endregion }