diff --git a/BeWo/BeWo.csproj b/BeWo/BeWo.csproj index 4dc230d1c..f407d6ba5 100644 --- a/BeWo/BeWo.csproj +++ b/BeWo/BeWo.csproj @@ -1062,6 +1062,9 @@ DebugMessageLogView.xaml + + + ServiceRecordCreationView.xaml diff --git a/BeWo/View/Controls/ValueListEntryEditControl.xaml.cs b/BeWo/View/Controls/ValueListEntryEditControl.xaml.cs index 74871ca7a..0833e85ce 100644 --- a/BeWo/View/Controls/ValueListEntryEditControl.xaml.cs +++ b/BeWo/View/Controls/ValueListEntryEditControl.xaml.cs @@ -56,7 +56,6 @@ namespace BeWo.View.Controls private void UpdateControls() { bool zeigeKatalogAuswahl = RBtnKatalog.IsChecked.HasValue && RBtnKatalog.IsChecked.Value; - LabelKatalog.Visibility = zeigeKatalogAuswahl ? Visibility.Visible : Visibility.Collapsed; CboCatalog.Visibility = zeigeKatalogAuswahl ? Visibility.Visible : Visibility.Collapsed; diff --git a/BeWo/View/Detail/MassnahmenHistory.cs b/BeWo/View/Detail/MassnahmenHistory.cs new file mode 100644 index 000000000..8780a9505 --- /dev/null +++ b/BeWo/View/Detail/MassnahmenHistory.cs @@ -0,0 +1,136 @@ +using BeWo.Core; +using BeWo.ViewModel.ListViewModel; +using BS.Shared.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static BeWo.View.Detail.MassnahmenHistoryRecord; + +namespace BeWo.View.Detail +{ + //public class MassnahmenTreeViewHistoryManager + //{ + // public MassnahmenTreeViewHistoryManager() + // { + // History = new Stack(); + // } + + // public Stack History { get; set; } + + // internal void RecordAction(MassnahmenActionParameters param) + // { + // History.Push(param); + // } + + // internal void UndoAction() + // { + + // } + + // internal void UndoAddAction() + // { + + // } + + // internal void UndoDeleteAction() + // { + + // } + + // internal void UndoMoveAction() + // { + + // } + + // internal void UndoCopyAction() + // { + + // } + //} + + public class MassnahmenHistory + { + public Action RefreshTree { get; set; } + public Action RefreshButton { get; set; } + + public MassnahmenHistoryRecordOrigin Origin { get; set; } + public LinkedList History { get; set; } + public LinkedListNode LastChange { get; set; } + + public MassnahmenTreeTrimmer TreeTrimmer { get; set; } + + public MassnahmenHistory(MassnahmenTreeTrimmer trimmer, Action refresh, Action button) + { + Origin = new MassnahmenHistoryRecordOrigin(); + History = new LinkedList(); + History.AddLast(Origin); + + LastChange = History.Last; + + TreeTrimmer = trimmer; + RefreshTree = refresh; + RefreshButton = button; + } + + public bool GotPrevAction => LastChange is object && LastChange.Value != Origin; + public bool GotNextAction => LastChange is object && LastChange.Next is object; + public bool GotChanges => LastChange is object && (GotNextAction || GotPrevAction); + + public void RecordAdd(NodeRelation param) => RecordAction(new MassnahmenHistoryRecordAdd(param)); + public void RecordMove(MassnahmenHistoryRecordMove move) => RecordAction(move); + public void RecordImport(List param) => RecordAction(new MassnahmenHistoryRecordImport(param)); + public void RecordDelete(MassnahmenHistoryRecordDelete del) => RecordAction(del); + public void RecordCopy(NodeRelation root, List param) => RecordAction(new MassnahmenHistoryRecordCopy(root, param)); + public void RecordEdit(MassnahmenHistoryRecordEdit edit) => RecordAction(edit); + + private void RecordAction(MassnahmenHistoryRecord val) + { + while (LastChange != History.Last) + { + History.RemoveLast(); + } + + History.AddLast(val); + + LastChange = History.Last; + + RefreshTree(); + RefreshButton(); + } + + public void ClearHistory() + { + while(History.Last != History.First) + { + History.RemoveLast(); + } + + LastChange = History.Last; + + RefreshButton(); + } + + public void UndoPrevAction() + { + var action = LastChange.Value; + + TreeTrimmer.UndoAction(action); + + LastChange = LastChange.Previous; + RefreshButton(); + RefreshTree(); + } + public void RedoNextAction() + { + var action = LastChange.Next.Value; + + TreeTrimmer.RedoAction(action); + + LastChange = LastChange.Next; + RefreshButton(); + RefreshTree(); + } + } +} diff --git a/BeWo/View/Detail/MassnahmenHistoryRecord.cs b/BeWo/View/Detail/MassnahmenHistoryRecord.cs new file mode 100644 index 000000000..c9b0f4ad0 --- /dev/null +++ b/BeWo/View/Detail/MassnahmenHistoryRecord.cs @@ -0,0 +1,129 @@ +using BeWo.Core; +using BeWo.ViewModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BeWo.View.Detail +{ + public abstract class MassnahmenHistoryRecord + { + public struct NodeRelation + { + public SupportConceptGoalTreeItem Current { get; set; } + public SupportConceptGoalTreeItem Parent { get; set; } + + public NodeRelation(SupportConceptGoalTreeItem newitem, SupportConceptGoalTreeItem parent) + { + Current = newitem; + Parent = parent; + } + public NodeRelation(SupportConceptGoalTreeItem newitem) + { + Current = newitem; + Parent = newitem.ParentGoal as SupportConceptGoalTreeItem; + } + } + + public struct NodeEditInformation + { + public string Abbreviation { get; set; } + public string Description { get; set; } + public string Notice { get; set; } + } + } + + public class MassnahmenHistoryRecordOrigin : MassnahmenHistoryRecord + { + + } + + public class MassnahmenHistoryRecordAdd : MassnahmenHistoryRecord + { + public NodeRelation Parameter { get; set; } + + public MassnahmenHistoryRecordAdd(SupportConceptGoalTreeItem newitem, SupportConceptGoalTreeItem parent) + { + Parameter = new NodeRelation(newitem, parent); + } + public MassnahmenHistoryRecordAdd(NodeRelation param) + { + Parameter = param; + } + } + + public class MassnahmenHistoryRecordImport : MassnahmenHistoryRecord + { + public List Parameters { get; set; } + public List ParametersReversed { get; set; } + + public MassnahmenHistoryRecordImport(List list) + { + Parameters = list; + ParametersReversed = new Stack(list).ToList(); + } + } + + public class MassnahmenHistoryRecordDelete : MassnahmenHistoryRecord + { + public NodeRelation ExNode { get; set; } + public List Parameters { get; set; } + + public MassnahmenHistoryRecordDelete(NodeRelation exnode, List list) + { + Parameters = list; + ExNode = exnode; + } + } + + public class MassnahmenHistoryRecordMove : MassnahmenHistoryRecord + { + public SupportConceptGoalTreeItem Child { get; set; } + public SupportConceptGoalTreeItem ExParent { get; set; } + public SupportConceptGoalTreeItem Parent { get; set; } + + public MassnahmenHistoryRecordMove(SupportConceptGoalTreeItem child, SupportConceptGoalTreeItem exparent, SupportConceptGoalTreeItem parent) + { + Child = child; + ExParent = exparent; + Parent = parent; + } + } + + public class MassnahmenHistoryRecordCopy : MassnahmenHistoryRecord + { + public NodeRelation CurrentNode { get; set; } + public List Parameters { get; set; } + + public MassnahmenHistoryRecordCopy(NodeRelation root, List list) + { + Parameters = list; + CurrentNode = root; + } + } + + public class MassnahmenHistoryRecordEdit : MassnahmenHistoryRecord + { + public ValueListEntryVM VM { get; set; } + + public NodeEditInformation Before { get; set; } + public NodeEditInformation After { get; set; } + + public MassnahmenHistoryRecordEdit(ValueListEntryVM vm, NodeEditInformation b, NodeEditInformation a) + { + Before = b; + After = a; + VM = vm; + } + + public void SetVM(NodeEditInformation info) + { + VM.Abbreviation = info.Abbreviation; + VM.Description = info.Description; + VM.Notice = info.Notice; + VM.CommitToDataContract(); + } + } +} diff --git a/BeWo/View/Detail/MassnahmenTreeTrimmer.cs b/BeWo/View/Detail/MassnahmenTreeTrimmer.cs new file mode 100644 index 000000000..7b824b139 --- /dev/null +++ b/BeWo/View/Detail/MassnahmenTreeTrimmer.cs @@ -0,0 +1,342 @@ +using BeWo.Core; +using BeWo.ViewModel; +using BeWo.ViewModel.ListViewModel; +using BS.Shared; +using BS.Shared.Core; +using BS.Shared.DataContracts; +using BS.Shared.DataContracts.ClientPartials; +using System; +using System.Collections.Generic; +using System.Linq; +using static BeWo.View.Detail.MassnahmenHistoryRecord; + +namespace BeWo.View.Detail +{ + public class MassnahmenTreeTrimmer + { + public Dictionary, Action>> Type2Actions { get; set; } + + public MassnahmenTreeViewView View { get; set; } + + public GenericValueListEntryListVM ViewModel => View.ViewModel; + public ObservableSortCollection GoalTree => View.GoalTree; + + public MassnahmenTreeTrimmer(MassnahmenTreeViewView view) + { + View = view; + + Type2Actions = new Dictionary, Action>> + { + { typeof(MassnahmenHistoryRecordAdd), GetTuple(UndoAddAction, RedoAddAction) }, + { typeof(MassnahmenHistoryRecordCopy), GetTuple(UndoCopyAction, RedoCopyAction) }, + { typeof(MassnahmenHistoryRecordDelete), GetTuple(UndoDeleteAction, RedoDeleteAction) }, + { typeof(MassnahmenHistoryRecordImport), GetTuple(UndoImportAction, RedoImportAction) }, + { typeof(MassnahmenHistoryRecordMove), GetTuple(UndoMoveAction, RedoMoveAction) }, + { typeof(MassnahmenHistoryRecordEdit), GetTuple(UndoEditAction, RedoEditAction) }, + }; + } + + public void UndoAction(MassnahmenHistoryRecord action) + { + Type2Actions[action.GetType()].Item1(action); + } + public void RedoAction(MassnahmenHistoryRecord action) + { + Type2Actions[action.GetType()].Item2(action); + } + + public NodeRelation DoAddAction(SupportConceptGoalTreeItem parent, ValueListEntryType valueListEntryType, + string description, string abbreviation, string notice) + { + if (!valueListEntryType.HasFlag(ValueListEntryType.SupportConceptGoalCategoryType) && + !valueListEntryType.HasFlag(ValueListEntryType.SupportConceptGoalType)) + throw new ArgumentOutOfRangeException(); + + var vm = ViewModel.NewVM; + vm.Type = valueListEntryType; + + if (description is object) vm.Description = description; + if (abbreviation is object) vm.Abbreviation = abbreviation; + if (notice is object) vm.Notice = notice; + + var newItem = new SupportConceptGoalTreeItem + { + GoalEntryVM = vm, + GoalEntryDC = vm.CommitToDataContract(), + ParentGoal = parent + }; + + var rtn = new NodeRelation(newItem, parent); + + ViewModel.AddNewVMToList(); + + if (parent == null) + { + GoalTree.Add(newItem); + return rtn; + } + + + vm.ParentEntry = parent.GoalEntryDC ?? parent.GoalEntryVM.CommitToDataContract(); + + parent.Items.Add(newItem); + parent.IsExpanded = true; + + return rtn; + } + private void UndoAddAction(MassnahmenHistoryRecordAdd record) + { + DeactivateNode(record.Parameter); + } + private void RedoAddAction(MassnahmenHistoryRecordAdd param) => RedoAddAction(param.Parameter); + private void RedoAddAction(NodeRelation addParam) + { + ActivateNode(addParam); + } + + public List DoCopyAction(SupportConceptGoalTreeItem parent, SupportConceptGoalTreeItem source, + MassnahmenTreeCopyType copyType, bool force_deep_copy, + out NodeRelation current, Stack records = null) + { + if (records is null) + records = new Stack(); + + if (source == null || parent == null) + throw new NotImplementedException("#43298467891364987"); + + var wm = source.GoalEntryVM; + var childs = source.Items.ToList(); + var param = DoAddAction(parent, wm.Type, wm.Description, wm.Abbreviation, wm.Notice); + var node = param.Current; + var parameter = new NodeRelation(node, parent); + + current = parameter; + + records.Push(parameter); + + if (copyType == MassnahmenTreeCopyType.Subtree || force_deep_copy) + { + foreach (var child in childs) + { + DoCopyAction(node, child, copyType, force_deep_copy, out var item, records); + } + } + + return records.ToList(); + } + private void UndoCopyAction(MassnahmenHistoryRecordCopy record) + { + DeactivateSubtree(record.CurrentNode, record.Parameters); + } + private void RedoCopyAction(MassnahmenHistoryRecordCopy record) + { + ActivateSubtree(record.CurrentNode, record.Parameters); + } + + public MassnahmenHistoryRecordDelete DoDeleteAction(NodeRelation param) + { + // Delete Full Subtree + var stack = new Stack(); + var queue = new Queue(); + + DoDeleteActionRec(param.Current, ref stack, ref queue); + + // Queue löschen + DeactivateSubtree(param, queue); + + //var list = stack.Select(x => new MassnahmenHistoryNodeParameter(x, View.GetParent(x))).ToList(); + var node = new NodeRelation(param.Current, param.Parent); + + return new MassnahmenHistoryRecordDelete(node, stack.ToList()); + } + public void DoDeleteActionRec(SupportConceptGoalTreeItem item, ref Stack stack, ref Queue queue) + { + foreach (var child in item.Items) + { + DoDeleteActionRec(child, ref stack, ref queue); + } + + var param = new NodeRelation(item); + stack.Push(param); + queue.Enqueue(param); + } + private void UndoDeleteAction(MassnahmenHistoryRecordDelete param) + { + ActivateSubtree(param.ExNode, param.Parameters); + } + private void RedoDeleteAction(MassnahmenHistoryRecordDelete param) + { + DeactivateSubtree(param.ExNode, param.Parameters); + } + + public List DoImportAction(SupportConceptGoalDC goal, List records = null) + { + if (goal == null) + return null; + + if (records == null) + records = new List(); + + goal.Children?.ForEach(x => DoImportAction(x, records)); + + if (goal.IsChecked) + AddIcf(goal, records); + + return records; + } + private void UndoImportAction(MassnahmenHistoryRecordImport param) + { + foreach (var item in param.ParametersReversed) + { + DeactivateNode(item); + } + } + private void RedoImportAction(MassnahmenHistoryRecordImport param) + { + foreach (var item in param.Parameters) + { + ActivateNode(item); + } + } + + public MassnahmenHistoryRecordMove DoMoveAction(SupportConceptGoalTreeItem child, SupportConceptGoalTreeItem ex_parent, SupportConceptGoalTreeItem parent) + { + // Entferne child aus Ex-Parent + if (ex_parent is object) + ex_parent.Items.Remove(child); + else + GoalTree.Remove(child); + + // Füge child parent hinzu + if (parent is object) + parent.Items.Add(child); + else + GoalTree.Add(child); + + // Setze child Parent Entry + child.GoalEntryVM.ParentEntry = parent?.GoalEntryDC; + child.ParentGoal = parent; + + return new MassnahmenHistoryRecordMove(child, ex_parent, parent); + } + private void UndoMoveAction(MassnahmenHistoryRecordMove move) + { + DoMoveAction(move.Child, move.Parent, move.ExParent); + } + private void RedoMoveAction(MassnahmenHistoryRecordMove move) + { + DoMoveAction(move.Child, move.ExParent, move.Parent); + } + + public MassnahmenHistoryRecordEdit DoEditAction(ValueListEntryDC b, ValueListEntryVM a) + { + var before = new NodeEditInformation { Abbreviation = b.Abbreviation, Description = b.TypeDescription, Notice = b.Notice }; + var after = new NodeEditInformation { Abbreviation = a.Abbreviation, Description = a.Description, Notice = a.Notice }; + + return new MassnahmenHistoryRecordEdit(a, before, after); + } + private void UndoEditAction(MassnahmenHistoryRecordEdit edit) + { + edit.SetVM(edit.Before); + } + private void RedoEditAction(MassnahmenHistoryRecordEdit edit) + { + edit.SetVM(edit.After); + } + + private Tuple, Action> GetTuple(Action a, Action b) where T1 : MassnahmenHistoryRecord + => new Tuple, Action>(x => a(x as T1), x => b(x as T1)); + private SupportConceptGoalTreeItem AddIcf(SupportConceptGoalDC goal, List records) + { + if (goal == null) return null; + + // Schaut, ob das Child bereits existiert + var similarGoal = GoalTree.FirstOrDefault(x => MassnahmenTreeViewView.SearchGoalTreeItem(x, goal.GoalEntryDC.TypeDescription, goal.GoalEntryDC.Abbreviation, goal.GoalEntryDC.Notice) != null); + if (similarGoal != null) + return similarGoal; + + // Schaut, ob das Goal ein Parent hat + if (goal.ParentGoal == null) + { + // Ist Root Element + var record = DoAddAction(null, ValueListEntryType.SupportConceptGoalCategoryType, + goal.GoalEntryDC.TypeDescription, goal.GoalEntryDC.Abbreviation, goal.GoalEntryDC.Notice); + + records.Add(record); + + //GoalTree.Sort((x, y) => x.GoalEntryVM?.DisplayName?.CompareTo(y.GoalEntryVM?.DisplayName) ?? string.Compare(string.Empty, y.GoalEntryVM?.DisplayName, StringComparison.Ordinal)); + + return record.Current; + } + + // Schaut, ob das ParentGoal bereits existiert + foreach (var goalTreeItem in GoalTree) + { + var similarParentGoal = MassnahmenTreeViewView.SearchGoalTreeItem(goalTreeItem, goal.ParentGoal.GoalEntryDC.TypeDescription, goal.ParentGoal.GoalEntryDC.Abbreviation, goal.ParentGoal.GoalEntryDC.Notice); + if (similarParentGoal == null) + continue; + + var record = DoAddAction(similarParentGoal, ValueListEntryType.SupportConceptGoalCategoryType, goal.GoalEntryDC.TypeDescription, goal.GoalEntryDC.Abbreviation, goal.GoalEntryDC.Notice); + records.Add(record); + return record.Current; + } + + // Fügt den Parent hinzu + var parent = AddIcf(goal.ParentGoal, records); + var record2 = DoAddAction(parent, ValueListEntryType.SupportConceptGoalCategoryType, + goal.GoalEntryDC.TypeDescription, goal.GoalEntryDC.Abbreviation, goal.GoalEntryDC.Notice); + records.Add(record2); + return record2.Current; + } + + private void ActivateSubtree(NodeRelation currentNode, List param) + { + foreach (var item in param) + { + ViewModel.VMList.Add(item.Current.GoalEntryVM); + } + + if (currentNode.Parent is object) + currentNode.Parent.Items.Add(currentNode.Current); + else + GoalTree.Add(currentNode.Current); + } + private void ActivateNode(NodeRelation currentNode) + { + var newItem = currentNode.Current; + var parent = currentNode.Parent; + + ViewModel.VMList.Add(newItem.GoalEntryVM); + + if (parent != null) + parent.Items.Add(newItem); + else + GoalTree.Add(newItem); + } + + private void DeactivateSubtree(NodeRelation currentNode, IEnumerable param) + { + foreach (var item in param) + { + ViewModel.VMList.Remove(item.Current.GoalEntryVM); + } + + if (currentNode.Parent is object) + currentNode.Parent.Items.Remove(currentNode.Current); + else + GoalTree.Remove(currentNode.Current); + } + private void DeactivateNode(NodeRelation currentNode) + { + var newItem = currentNode.Current; + var parent = currentNode.Parent; + + ViewModel.VMList.Remove(newItem.GoalEntryVM); + + if (parent != null) + parent.Items.Remove(newItem); + else + GoalTree.Remove(newItem); + } + } +} diff --git a/BeWo/View/Detail/MassnahmenTreeViewView.xaml b/BeWo/View/Detail/MassnahmenTreeViewView.xaml index 22cdbd327..7ac7401ca 100644 --- a/BeWo/View/Detail/MassnahmenTreeViewView.xaml +++ b/BeWo/View/Detail/MassnahmenTreeViewView.xaml @@ -11,7 +11,7 @@ - + @@ -70,13 +70,23 @@ + - + + + + +