| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 | using Comal.Classes;using InABox.Clients;using InABox.Core;using InABox.DynamicGrid;using InABox.Scripting;using InABox.Wpf.Reports;using InABox.WPF;using PRSDesktop.Configuration;using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Threading;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Media;using System.Windows.Media.Imaging;namespace PRSDesktop;public class DigitalFormDockGrid : DynamicGrid<DigitalFormDockModel>{    private static readonly CoreFieldMap<IDigitalFormInstance, DigitalFormDockModel> _mappings =        new CoreFieldMap<IDigitalFormInstance, DigitalFormDockModel>()            .Add(x => x.ID, x => x.ID)            .Add(x => x.Form.ID, x => x.FormID)            .Add(x => x.Number, x => x.Number)            .Add(x => x.Description, x => x.FormName)            .Add(x => x.FormCompleted, x => x.Completed)            .Add(x => x.FormCompletedBy.UserID, x => x.CompletedBy)            .Add(x => x.FormProcessed, x => x.Processed)            .Add(x => x.Parent.ID, x => x.ParentID);    public static readonly Dictionary<Type, System.Drawing.Bitmap> Images = new()        {            { typeof(AssignmentForm), PRSDesktop.Resources.assignments },            { typeof(KanbanForm), PRSDesktop.Resources.kanban },            { typeof(JobForm), PRSDesktop.Resources.project },            { typeof(JobITPForm), PRSDesktop.Resources.checklist },            { typeof(EmployeeForm), PRSDesktop.Resources.employees },            { typeof(LeaveRequestForm), PRSDesktop.Resources.leave },            { typeof(ManufacturingPacketStage), PRSDesktop.Resources.factory },            { typeof(TimeSheetForm), PRSDesktop.Resources.time },            { typeof(PurchaseOrderItemForm), PRSDesktop.Resources.purchase },            { typeof(DeliveryForm), PRSDesktop.Resources.truck },        };    public List<Type> ExcludedTypes { get; private set; }    public DateTime StartDate { get; set; }    public DigitalFormDockGrid()    {        StartDate = DateTime.Today;        ExcludedTypes = new List<Type>();        ActionColumns.Add(new DynamicImageColumn(TypeImage)        {            Position = DynamicActionColumnPosition.Start,            GetFilter = () => new StaticColumnFilter<Type>(                FormType,                Images.Keys.Select(x => new Tuple<string, Type>(x.EntityName().Split('.').Last().SplitCamelCase(), x)).ToArray())        });        ActionColumns.Add(new DynamicMenuColumn(MenuBuild, MenuStatus) { Position = DynamicActionColumnPosition.End });    }    protected override void DoReconfigure(DynamicGridOptions options)    {        options.Clear();        options.FilterRows = true;    }    protected override DynamicGridStyle GetRowStyle(CoreRow row, DynamicGridStyle style)    {        var result = base.GetRowStyle(row, style);        if (!row.Get<DigitalFormDockModel, DateTime>(x => x.Processed).IsEmpty())            result = new DynamicGridRowStyle(result)            {                Background = new SolidColorBrush(Colors.LightGray),            };        return result;    }    private Type FormType(CoreRow row)    {        return row.Get<DigitalFormDockModel, Type>(x => x.FormType);    }    private BitmapImage? TypeImage(CoreRow? arg)    {        if (arg is null)            return null;        var type = arg.Get<DigitalFormDockModel, Type>(x => x.FormType);        return Images.GetValueOrDefault(type)?.AsBitmapImage();    }    private static readonly MethodInfo MenuBuildGenericMethod = typeof(DigitalFormDockGrid)        .GetMethod(nameof(MenuBuildGeneric), BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)!;    private void MenuBuildGeneric<TEntityForm, TEntity, TEntityLink>(ContextMenu menu, DigitalFormDockModel formModel)            where TEntityForm : Entity, IDigitalFormInstance<TEntityLink>, IRemotable, IPersistent, new()            where TEntity : Entity, IRemotable, IPersistent, new()            where TEntityLink : IEntityLink<TEntity>, new()    {        var filter = new Filter<TEntityForm>(x => x.ID).IsEqualTo(formModel.ID);        var model = new DigitalFormReportDataModel<TEntityForm>(filter, formModel.FormID);        if (Security.CanView<TEntity>())        {            menu.AddItem($"View {typeof(TEntity).Name}", Images.GetValueOrDefault(typeof(TEntityForm)), formModel, ViewEntity_Click<TEntity>);        }        var moduleTask = Task.Run(() =>        {            return new Client<CustomModule>().Query(                new Filter<CustomModule>(x => x.DataModel).IsEqualTo(model.Name)                    .And(x => x.Section).IsEqualTo(formModel.FormID.ToString())                    .And(x => x.Visible).IsEqualTo(true)                ).ToObjects<CustomModule>();        });        var modulesSeparator = menu.AddSeparatorIfNeeded();        var modulesItem = menu.AddItem("Loading...", null, null, enabled: false);        moduleTask.ContinueWith((task) =>        {            try            {                var index = menu.Items.IndexOf(modulesItem);                menu.Items.Remove(modulesItem);                var any = false;                foreach (var module in task.Result)                {                    any = true;                    menu.AddItem(                        module.Name,                        PRSDesktop.Resources.edit,                        () =>                        {                            try                            {                                if (ScriptDocument.RunCustomModule(model, new Dictionary<string, object[]>(), module.Script))                                {                                    Refresh(false, true);                                }                            }                            catch (CompileException c)                            {                                MessageBox.Show(c.Message);                            }                            catch (Exception e)                            {                                MessageBox.Show(CoreUtils.FormatException(e));                            }                        },                        enabled: formModel.Processed.IsEmpty(),                        index: index);                    ++index;                }                if (!any && modulesSeparator is not null)                {                    menu.Items.Remove(modulesSeparator);                }            }            catch (Exception ex)            {                Logger.Send(LogType.Error, ClientFactory.UserID, $"Error in digital form dock while loading custom modules: {CoreUtils.FormatException(ex)}");                MessageBox.Show($"Error: {ex.Message}", "Error");            }        }, TaskScheduler.FromCurrentSynchronizationContext());        menu.AddSeparator();        if (formModel.Processed.IsEmpty())        {            menu.AddItem(                "Mark as Processed",                PRSDesktop.Resources.lock_sml,                () =>                {                    var form = new TEntityForm                    {                        FormProcessed = formModel.Processed                    }.SetID(formModel.ID);                    form.CommitChanges();                    form.FormProcessed = DateTime.Now;                    using (new WaitCursor())                    {                        Client.Save(form, "Marked As Processed");                        Refresh(false, true);                    }                });        }        else        {            menu.AddItem(                "Clear Processed Flag",                PRSDesktop.Resources.lock_sml,                () =>                {                    var form = new TEntityForm                    {                        FormProcessed = formModel.Processed                    }.SetID(formModel.ID);                    form.CommitChanges();                    form.FormProcessed = DateTime.MinValue;                    using (new WaitCursor())                    {                        Client.Save(form, "Processed Flag Cleared");                        Refresh(false, true);                    }                });        }        if (typeof(TEntity).HasInterface<IJobScopedItem>())        {            menu.AddSeparatorIfNeeded();            menu.AddItem("Set Job", PRSDesktop.Resources.project, formModel,                SetJob_ClickMethod.MakeGenericMethod(typeof(TEntity)).CreateDelegate<Action<DigitalFormDockModel>>());            menu.AddItem("Set Job Scope", PRSDesktop.Resources.project, formModel,                SetJobScope_ClickMethod.MakeGenericMethod(typeof(TEntity)).CreateDelegate<Action<DigitalFormDockModel>>());        }        else if (typeof(TEntity) == typeof(Job) && typeof(TEntityForm) == typeof(JobForm))        {            menu.AddSeparatorIfNeeded();            var scopeheader = menu.AddItem("Set Job Scope", PRSDesktop.Resources.project, formModel, dockModel => { });            var scopes = Client.Query<JobScope>(                new Filter<JobScope>(x => x.Job.ID).IsEqualTo(formModel.ParentID),                Columns.None<JobScope>()                    .Add(x => x.ID)                    .Add(x => x.Number)                    .Add(x => x.Description)            ).ToObjects<JobScope>();            foreach (var scope in scopes)            {                var existingscope = new MenuItem() { Header = $"{scope.Number}: {scope.Description}", DataContext = formModel, Tag = scope };                existingscope.Click += SetJobScopeFromMenu_Click;                scopeheader.Items.Add(existingscope);            }            if (scopes.Any())                scopeheader.Items.Add(new Separator());                        var newscope = new MenuItem() { Header = $"Create New Scope", DataContext = formModel };            newscope.Click += CreateJobScopeFromMenu_Click;            scopeheader.Items.Add(newscope);        }        if (Security.IsAllowed<CanCustomiseModules>())        {            menu.AddSeparatorIfNeeded();            menu.AddItem("Customise Modules", PRSDesktop.Resources.script, () =>            {                var manager = new CustomModuleManager                {                    Section = formModel.FormID.ToString(),                    DataModel = model                };                manager.ShowDialog();            });        }        if (Security.IsAllowed<CanPrintReports>())        {            menu.AddSeparatorIfNeeded();            var printItem = menu.AddItem("Print", PRSDesktop.Resources.printer, null);            ReportUtils.PopulateMenu(printItem, formModel.FormID.ToString(), model, Security.IsAllowed<CanDesignReports>(), true);        }    }        private static void UpdateScopeID(DigitalFormDockModel formModel, Guid scopeid)    {        var instance = Client.Query(                new Filter<JobForm>(x => x.ID).IsEqualTo(formModel.ID),                Columns.Required<JobForm>()            ).ToObjects<JobForm>().First();                instance.JobScope.ID = scopeid;        Client.Save(instance, "Linked scope set by user from Digital forms dock.");    }    private void CreateJobScopeFromMenu_Click(object sender, RoutedEventArgs e)    {        if ((sender as MenuItem)?.DataContext is DigitalFormDockModel model)        {            var scope = new JobScope();            scope.Job.ID = model.ParentID;            scope.Description = model.FormName;            scope.Type = JobScopeType.Variation;            if (new DynamicDataGrid<JobScope>().EditItems(new[] { scope }))            {                UpdateScopeID(model, scope.ID);                MessageBox.Show($"Form has been assigned to scope {scope.Number}.");                // Update the form scope here (from the SetScope) stuff            }        }            }    private void SetJobScopeFromMenu_Click(object sender, RoutedEventArgs e)    {        if (sender is MenuItem mi && mi.DataContext is DigitalFormDockModel model && mi.Tag is JobScope scope)        {            UpdateScopeID(model, scope.ID);            MessageBox.Show($"Form has been assigned to scope {scope.Number}.");            // Update the form scope here (from the SetScope) stuff        }    }    private static void ViewEntity_Click<TEntity>(DigitalFormDockModel model)        where TEntity : Entity, IRemotable, IPersistent, new()    {        var entity = Client.Query(            new Filter<TEntity>(x => x.ID).IsEqualTo(model.ParentID),            DynamicGridUtils.LoadEditorColumns<TEntity>(Columns.None<TEntity>()))            .ToObjects<TEntity>().First();        var grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicGrid<>), typeof(TEntity)) as DynamicGrid<TEntity>)!;        grid.EditItems(new TEntity[] { entity });    }    private static void SetJobScopeFromJob_Click(DigitalFormDockModel formModel)    {        var instance = Client.Query(            new Filter<JobForm>(x => x.ID).IsEqualTo(formModel.ID),            LookupFactory.DefineLookupFilterColumns<JobForm, JobScope, JobScopeLink>(x => x.JobScope)                .Add(x => x.ID)                .Add(x => x.Parent.ID))            .ToObjects<JobForm>().First();        var job = Client.Query(            new Filter<Job>(x => x.ID).IsEqualTo(instance.Parent.ID),            LookupFactory.DefineChildFilterColumns<Job, JobScope>()).ToObjects<Job>().First();        var window = new MultiSelectDialog<JobScope>(            new Filters<JobScope>()                .Add(LookupFactory.DefineChildFilter<Job, JobScope>(new Job[] { job }))                .Add(new Filter<JobScope>(x => x.Job.ID).IsEqualTo(instance.Parent.ID))                .Combine(),            Columns.None<JobScope>().Add(x => x.ID).Add(x => x.Number), multiselect: false);        if (!window.ShowDialog())        {            return;        }        var scope = window.Data().ToObjects<JobScope>().First();        instance.JobScope.ID = scope.ID;        Client.Save(instance, "Linked scope set by user from Digital forms dock.");        MessageBox.Show($"Form has been assigned to scope {scope.Number}.");    }    private static readonly MethodInfo SetJobScope_ClickMethod = typeof(DigitalFormDockGrid).GetMethod(nameof(SetJobScope_Click), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)!;    private static void SetJobScope_Click<TEntity>(DigitalFormDockModel formModel)        where TEntity : Entity, IJobScopedItem, IRemotable, IPersistent, new()    {        var entity = Client.Query(            new Filter<TEntity>(x => x.ID).IsEqualTo(formModel.ParentID),            LookupFactory.DefineLookupFilterColumns<TEntity, JobScope, JobScopeLink>(x => x.JobScope)                .Add(x => x.ID)                .Add(x => x.JobLink.ID)                .Add(x => x.JobScope.ID)                .Add(x => x.JobScope.Number))            .ToObjects<TEntity>().First();        if (entity.JobLink.ID == Guid.Empty)        {            MessageBox.Show($"{typeof(TEntity).Name} is not linked to a job. Please select a job first.");            return;        }        var window = new MultiSelectDialog<JobScope>(            new Filters<JobScope>()                .Add(LookupFactory.DefineLookupFilter<TEntity, JobScope, JobScopeLink>(x => x.JobScope, new TEntity[] { entity }))                .Add(new Filter<JobScope>(x => x.Job.ID).IsEqualTo(entity.JobLink.ID))                .Combine(),            Columns.None<JobScope>().Add(x => x.ID).Add(x => x.Number),            multiselect: false);        if (!window.ShowDialog(nameof(JobScope.Number), entity.JobScope.Number, Syncfusion.Data.FilterType.Equals))        {            return;        }        var scope = window.Data().ToObjects<JobScope>().First();        entity.JobScope.ID = scope.ID;        Client.Save(entity, "Linked scope set by user from Digital forms dock.");        MessageBox.Show($"{typeof(TEntity).Name} has been assigned to scope {scope.Number}.");    }    private static readonly MethodInfo SetJob_ClickMethod = typeof(DigitalFormDockGrid).GetMethod(nameof(SetJob_Click), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)!;    private static void SetJob_Click<TEntity>(DigitalFormDockModel formModel)        where TEntity: Entity, IJobScopedItem, IRemotable, IPersistent, new()    {        var entity = Client.Query(            new Filter<TEntity>(x => x.ID).IsEqualTo(formModel.ParentID),            LookupFactory.DefineLookupFilterColumns<TEntity, Job, JobLink>(x => x.JobLink)                .Add(x => x.ID)                .Add(x => x.JobLink.ID)                .Add(x => x.JobLink.JobNumber))            .ToObjects<TEntity>().First();        var window = new MultiSelectDialog<Job>(            LookupFactory.DefineLookupFilter<TEntity, Job, JobLink>(x => x.JobLink, new TEntity[] { entity }),            Columns.None<Job>().Add(x => x.DefaultScope.ID).Add(x => x.JobNumber),            multiselect: false);        if (!window.ShowDialog(nameof(Job.JobNumber), entity.JobLink.JobNumber, Syncfusion.Data.FilterType.Equals))        {            return;        }        var job = window.Data().ToObjects<Job>().First();        entity.JobLink.ID = job.ID;        entity.JobLink.Synchronise(job);        Client.Save(entity, "Linked job set by user from Digital forms dock.");        MessageBox.Show($"{typeof(TEntity).Name} has been assigned to job {job.JobNumber}.");    }    private void MenuBuild(DynamicMenuColumn column, CoreRow? row)    {        if (row is null) return;        var form = row.ToObject<DigitalFormDockModel>();        var linkType = DFUtils.FormEntityLinkType(form.FormType);        var entityType = DFUtils.FormEntityType(form.FormType);        MenuBuildGenericMethod.MakeGenericMethod(form.FormType, entityType, linkType).Invoke(this, new object?[] { column.GetMenu(), form });    }    private DynamicMenuStatus MenuStatus(CoreRow row)    {        if (row == null) return DynamicMenuStatus.Hidden;        return DynamicMenuStatus.Enabled;    }    protected override void Reload(    	Filters<DigitalFormDockModel> criteria, Columns<DigitalFormDockModel> columns, ref SortOrder<DigitalFormDockModel>? sort,    	CancellationToken token, Action<CoreTable?, Exception?> action)    {        var queryDefs = new Dictionary<string, IQueryDef>();        var types = DFUtils.GetFormInstanceTypes().Except(ExcludedTypes).ToList();        foreach (var type in types)        {            var filter = Filter.Create<IDigitalFormInstance>(type, x => x.FormCompleted).IsGreaterThanOrEqualTo(StartDate)                .And<IDigitalFormInstance>(x => x.FormCancelled).IsEqualTo(DateTime.MinValue);            var cols = Columns.Create<IDigitalFormInstance>(type, ColumnTypeFlags.None)                .Add<IDigitalFormInstance>(c => c.ID)                .Add<IDigitalFormInstance>(c => c.Parent.ID)                .Add<IDigitalFormInstance>(c => c.Form.ID)                .Add<IDigitalFormInstance>(c => c.Number)                .Add<IDigitalFormInstance>(c => c.Description)                .Add<IDigitalFormInstance>(c => c.FormCompleted)                .Add<IDigitalFormInstance>(c => c.FormCompletedBy.UserID)                .Add<IDigitalFormInstance>(c => c.FormProcessed);            var sorts = SortOrder.Create<IDigitalFormInstance>(type, x => x.FormCompleted, SortDirection.Descending);            queryDefs.Add(                type.ToString(),                new QueryDef(type)                {                    Filter = filter,                    Columns = cols,                    SortOrder = sorts                });        }        Client.QueryMultiple(            (results, e) =>            {                if(results is not null)                {                    var data = new CoreTable();                    data.LoadColumns(typeof(DigitalFormDockModel));                    foreach (var type in types)                        data.LoadFrom(                            results[type.ToString()],                            _mappings,                            (r) => r.Set<DigitalFormDockModel, Type>(x => x.FormType, type)                        );                    action.Invoke(data, null);                }                else if(e is not null)                {                    Logger.Send(LogType.Error, ClientFactory.UserID, CoreUtils.FormatException(e));                    MessageBox.Show($"Error: {e.Message}");                }            },            queryDefs);    }    public override DigitalFormDockModel LoadItem(CoreRow row)    {        return row.ToObject<DigitalFormDockModel>();    }    public override void SaveItem(DigitalFormDockModel item)    {    }    public override void DeleteItems(params CoreRow[] rows)    {    }    protected override void DoDoubleClick(object sender, DynamicGridCellClickEventArgs args)    {        base.DoDoubleClick(sender, args);        var row = SelectedRows.FirstOrDefault();        if (row is null)            return;        var instanceID = row.Get<DigitalFormDockModel, Guid>(x => x.ID);        var formID = row.Get<DigitalFormDockModel, Guid>(x => x.FormID);        var formType = row.Get<DigitalFormDockModel, Type>(x => x.FormType);        var formInstance = Client.Create(formType)            .Query(                Filter.Create<IDigitalFormInstance>(formType, x => x.ID).IsEqualTo(instanceID),                DynamicFormEditWindow.FormColumns(formType))            .Rows.FirstOrDefault()            ?.ToObject(formType) as IDigitalFormInstance;        if (formInstance is null)        {            return;        }        if (formID == Guid.Empty)        {            var window = new DeletedFormWindow();            window.FormData = formInstance?.FormData ?? "";            window.ShowDialog();            return;        }        if (DynamicFormEditWindow.EditDigitalForm(formInstance, out var model))        {            model.Update(null);        }    }}
 |