Forráskód Böngészése

- Added DeliveryEquipment system.
- Provided default set of columns for equipment.

Kenric Nugteren 6 hónapja
szülő
commit
8899c91fca

+ 1 - 0
prs.classes/Entities/DeliveryItem/Delivery.cs

@@ -42,6 +42,7 @@ namespace Comal.Classes
         public DeliveryTypeLink Type { get; set; }
 
         [EditorSequence(3)]
+        [RequiredColumn]
         public JobLink Job { get; set; }
         
         [EditorSequence(4)]

+ 27 - 0
prs.classes/Entities/DeliveryItem/DeliveryEquipment.cs

@@ -0,0 +1,27 @@
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Comal.Classes
+{
+    public class DeliveryEquipment : Entity, IRemotable, IPersistent, ILicense<LogisticsLicense>, IManyToMany<Delivery, Equipment>
+    {
+        [EntityRelationship(DeleteAction.Cascade)]
+        [EditorSequence(1)]
+        public DeliveryLink Delivery { get; set; }
+
+        [EntityRelationship(DeleteAction.Cascade)]
+        [EditorSequence(2)]
+        public EquipmentLink Equipment { get; set; }
+
+        [EditorSequence(3)]
+        public DeliveryEquipmentType Type { get; set; }
+    }
+
+    public enum DeliveryEquipmentType
+    {
+        DropOff,
+        PickUp
+    }
+}

+ 10 - 0
prs.classes/Entities/Equipment/Equipment.cs

@@ -136,6 +136,16 @@ namespace Comal.Classes
             LinkedProperties.Register<Equipment, GPSTrackerLink, double>(x => x.TrackerLink, x => x.Counter3, x => x.Counter3);
             LinkedProperties.Register<Equipment, GPSTrackerLink, double>(x => x.TrackerLink, x => x.Counter4, x => x.Counter4);
             LinkedProperties.Register<Equipment, GPSTrackerLink, double>(x => x.TrackerLink, x => x.Counter5, x => x.Counter5);
+
+            DefaultColumns.Add<Equipment>(x => x.Code);
+            DefaultColumns.Add<Equipment>(x => x.Description);
+            DefaultColumns.Add<Equipment>(x => x.Type, width: 120, alignment: Alignment.MiddleCenter);
+            DefaultColumns.Add<Equipment>(x => x.GroupLink.Code, caption: "Group");
+            DefaultColumns.Add<Equipment>(x => x.SerialNumber);
+            DefaultColumns.Add<Equipment>(x => x.TrackerLink.DeviceID, caption: "GPS Tracker");
+            DefaultColumns.Add<Equipment>(x => x.TrackerLink.Location.Address, caption: "Address");
+            DefaultColumns.Add<Equipment>(x => x.TrackerLink.Location.Latitude, caption: "Latitude");
+            DefaultColumns.Add<Equipment>(x => x.TrackerLink.Location.Longitude, caption: "Longitude");
         }
     }
 }

+ 3 - 0
prs.classes/Entities/Job/JobEquipment.cs

@@ -17,5 +17,8 @@ namespace Comal.Classes
         [EditorSequence(3)]
         [DateEditor]
         public DateTime Certified { get; set; }
+
+        [EditorSequence(4)]
+        public bool OnSite { get; set; }
     }
 }

+ 95 - 0
prs.desktop/Panels/Delivery/DeliveryEquipmentList.cs

@@ -0,0 +1,95 @@
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Core;
+using InABox.DynamicGrid;
+using InABox.Wpf;
+using InABox.WPF;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace PRSDesktop;
+
+internal class DeliveryEquipmentList : DynamicDataGrid<DeliveryEquipment>, IMasterDetailControl<Delivery, DeliveryEquipment>
+{
+    public Delivery? Master { get; set; }
+
+    public Filter<DeliveryEquipment> MasterDetailFilter => Master != null && Master.ID != Guid.Empty
+        ? new Filter<DeliveryEquipment>(x => x.Delivery.ID).IsEqualTo(Master.ID)
+        : new Filter<DeliveryEquipment>().None();
+    
+    public DeliveryEquipmentList()
+    {
+        HiddenColumns.Add(x => x.Delivery.ID);
+        HiddenColumns.Add(x => x.Equipment.ID);
+    }
+
+    protected override void DoReconfigure(DynamicGridOptions options)
+    {
+        base.DoReconfigure(options);
+        options.RecordCount = true;
+        options.AddRows = true;
+        options.DeleteRows = true;
+        options.SelectColumns = true;
+    }
+
+    protected override DynamicGridColumns LoadColumns()
+    {
+        var columns = new DynamicGridColumns<DeliveryEquipment>();
+        columns.Add(x => x.Equipment.Code, caption: "Eq.", alignment: Alignment.MiddleCenter);
+        columns.Add(x => x.Equipment.Description, width: 0, caption: "Equipment");
+        columns.Add(x => x.Type, width: 70);
+        return columns;
+    }
+
+    protected override void DoAdd(bool openEditorOnDirectEdit = false)
+    {
+        if ((Master?.ID ?? Guid.Empty) == Guid.Empty)
+        {
+            MessageWindow.ShowMessage("Please select a delivery first", "No Delivery Selected");
+            return;
+        }
+
+        if (!Master!.Completed.IsEmpty())
+        {
+            MessageWindow.ShowMessage("You cannot modify a completed delivery!", "Delivery Complete");
+            return;
+        }
+
+
+        if (MultiSelectDialog.SelectItems(out var equipmentList,
+            new Filter<Equipment>(x => x.ID).NotInList(ExtractValues(x => x.Equipment.ID, Selection.All).ToArray()),
+            Columns.None<Equipment>().Add(x => x.ID)))
+        {
+            Progress.Show("Adding Equipment to Delivery");
+
+            var newEquipmentList = new List<DeliveryEquipment>();
+            foreach (var equipment in equipmentList)
+            {
+                var newEquipment = new DeliveryEquipment();
+                newEquipment.Delivery.CopyFrom(Master);
+                newEquipment.Equipment.CopyFrom(equipment);
+                newEquipmentList.Add(newEquipment);
+            }
+
+            Client.Save(newEquipmentList, "Added to Delivery");
+
+            DoChanged();
+
+            Progress.Close();
+        }
+    }
+
+
+    protected override void Reload(
+    	Filters<DeliveryEquipment> criteria, Columns<DeliveryEquipment> columns, ref SortOrder<DeliveryEquipment>? sort,
+    	CancellationToken token, Action<CoreTable?, Exception?> action)
+    {
+        
+        criteria.Add(MasterDetailFilter);
+        base.Reload(criteria, columns, ref sort, token, action);
+    }
+}

+ 5 - 4
prs.desktop/Panels/Delivery/DeliveryPanel.xaml

@@ -30,17 +30,18 @@
                         <Grid.ColumnDefinitions>
                             <ColumnDefinition Width="2*" />
                             <ColumnDefinition Width="*" />
-                            <ColumnDefinition Width="*" />
                         </Grid.ColumnDefinitions>
                         <Grid.RowDefinitions>
                             <RowDefinition Height="*" />
-                            <RowDefinition Height="2*" />
+                            <RowDefinition Height="*" />
+                            <RowDefinition Height="*" />
                         </Grid.RowDefinitions>
                         <TextBox Grid.Row="0" Grid.Column="0" x:Name="Notes" Margin="0,0,0,2" IsReadOnly="True"
                                  Background="LightYellow" />
                         <local:DeliveryRackList Grid.Row="0" Grid.Column="1" x:Name="Racks" Margin="2,0,0,2" />
-                        <local:DeliveryRequiList Grid.Row="0" Grid.Column="2" x:Name="Requis" Margin="2,0,0,2" />
-                        <local:DeliveryItemsList Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" x:Name="Items" />
+                        <local:DeliveryRequiList Grid.Row="1" Grid.Column="1" x:Name="Requis" Margin="2,0,0,2" />
+                        <local:DeliveryEquipmentList Grid.Row="2" Grid.Column="1" x:Name="Equipment" Margin="2,0,0,2" />
+                        <local:DeliveryItemsList Grid.Row="1" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="1" x:Name="Items" />
                     </Grid>
                 </dynamicgrid:DynamicTabItem>
 

+ 6 - 2
prs.desktop/Panels/Delivery/DeliveryPanel.xaml.cs

@@ -29,6 +29,7 @@ namespace PRSDesktop
             Deliveries.OnSelectItem += Deliveries_OnSelectItem;
             Requis.OnChanged += OnChanged;
             Racks.OnChanged += OnChanged;
+            Equipment.OnChanged += OnChanged;
             Bookings.LoadSettings += (sender) => new UserConfiguration<CalendarSettings>("Deliveries").Load();
             Bookings.SaveSettings += (sender, properties) => new UserConfiguration<CalendarSettings>("Deliveries").Save(properties);
             Bookings.HeaderVisibility = Visibility.Visible;
@@ -64,6 +65,7 @@ namespace PRSDesktop
             Items.Refresh(true, false);
             Racks.Refresh(true, false);
             Requis.Refresh(true, false);
+            Equipment.Refresh(true, false);
         }
 
         public void Shutdown(CancelEventArgs? cancel)
@@ -72,7 +74,6 @@ namespace PRSDesktop
 
         public void CreateToolbarButtons(IPanelHost host)
         {
-            
             if (Security.CanView<DeliveryType>())
             {
                 host.CreateSetupAction(new PanelAction
@@ -118,7 +119,7 @@ namespace PRSDesktop
         {
         }
 
-        private void OnChanged(object sender, EventArgs args)
+        private void OnChanged(object? sender, EventArgs args)
         {
             Deliveries.Refresh(false, false);
         }
@@ -234,6 +235,9 @@ namespace PRSDesktop
 
             Requis.Master = _delivery;
             Requis.Refresh(false, true);
+
+            Equipment.Master = _delivery;
+            Equipment.Refresh(false, true);
             
             Bookings.Refresh();
         }

+ 163 - 124
prs.stores/DeliveryStore.cs

@@ -5,153 +5,192 @@ using Comal.Classes;
 using InABox.Core;
 using System;
 
-namespace Comal.Stores
+namespace Comal.Stores;
+
+internal class DeliveryStore : BaseStore<Delivery>
 {
-    internal class DeliveryStore : BaseStore<Delivery>
+    private void SendNotifications(Delivery delivery)
     {
-        private void SendNotifications(Delivery delivery)
-        {
-            if (delivery == null)
-                return;
-            var delnumber = delivery.Number;
-            var jobid = delivery.Job.ID;
-            var delemp = delivery.Employee.ID;
-
-            // Get Job Details
-            var job = jobid.Equals(Guid.Empty)
-                ? null
-                : Provider.Query(
-                    new Filter<Job>(x => x.ID).IsEqualTo(jobid),
-                    Columns.None<Job>().Add(
-                        x => x.JobNumber,
-                        x => x.Name,
-                        x => x.SiteLead.ID,
-                        x => x.ProjectLead.ID
-                    )
-                ).Rows.FirstOrDefault();
-            var jobnumber = job != null ? job.Get<Job, string>(x => x.JobNumber) : "";
-            var jobname = job != null ? job.Get<Job, string>(x => x.Name) : "";
-            var sitelead = job != null ? job.Get<Job, Guid>(x => x.SiteLead.ID) : Guid.Empty;
-            var projlead = job != null ? job.Get<Job, Guid>(x => x.ProjectLead.ID) : Guid.Empty;
-
-            // Scan for Delivery Items and Build the List to Send
-            var items = Provider.Query(
-                new Filter<DeliveryItem>(x => x.Delivery.ID).IsEqualTo(delivery.ID),
-                Columns.None<DeliveryItem>().Add(
-                    x => x.ID,
-                    x => x.ShipmentLink.ID,
-                    x => x.ShipmentLink.Code,
-                    x => x.RequisitionLink.ID,
-                    x => x.RequisitionLink.Number,
-                    x => x.Piece,
-                    x => x.Title,
-                    x => x.Barcode,
-                    x => x.ManufacturingPacketLink.Serial,
-                    x => x.Description
+        if (delivery == null)
+            return;
+        var delnumber = delivery.Number;
+        var jobid = delivery.Job.ID;
+        var delemp = delivery.Employee.ID;
+
+        // Get Job Details
+        var job = jobid.Equals(Guid.Empty)
+            ? null
+            : Provider.Query(
+                new Filter<Job>(x => x.ID).IsEqualTo(jobid),
+                Columns.None<Job>().Add(
+                    x => x.JobNumber,
+                    x => x.Name,
+                    x => x.SiteLead.ID,
+                    x => x.ProjectLead.ID
                 )
-            );
+            ).Rows.FirstOrDefault();
+        var jobnumber = job != null ? job.Get<Job, string>(x => x.JobNumber) : "";
+        var jobname = job != null ? job.Get<Job, string>(x => x.Name) : "";
+        var sitelead = job != null ? job.Get<Job, Guid>(x => x.SiteLead.ID) : Guid.Empty;
+        var projlead = job != null ? job.Get<Job, Guid>(x => x.ProjectLead.ID) : Guid.Empty;
 
-            var lines = new Dictionary<string, List<string>>();
-            foreach (var row in items.Rows)
+        // Scan for Delivery Items and Build the List to Send
+        var items = Provider.Query(
+            new Filter<DeliveryItem>(x => x.Delivery.ID).IsEqualTo(delivery.ID),
+            Columns.None<DeliveryItem>().Add(
+                x => x.ID,
+                x => x.ShipmentLink.ID,
+                x => x.ShipmentLink.Code,
+                x => x.RequisitionLink.ID,
+                x => x.RequisitionLink.Number,
+                x => x.Piece,
+                x => x.Title,
+                x => x.Barcode,
+                x => x.ManufacturingPacketLink.Serial,
+                x => x.Description
+            )
+        );
+
+        var lines = new Dictionary<string, List<string>>();
+        foreach (var row in items.Rows)
+        {
+            var tag = "";
+            if (row.IsEntityLinkValid<DeliveryItem, ShipmentLink>(x => x.ShipmentLink))
+                tag = string.Format("Rack #{0}", row.Get<DeliveryItem, string>(c => c.ShipmentLink.Code));
+            else if (row.IsEntityLinkValid<DeliveryItem, RequisitionLink>(x => x.RequisitionLink))
+                tag = string.Format("Requisition #{0}", row.Get<DeliveryItem, int>(c => c.RequisitionLink.Number));
+            if (!string.IsNullOrWhiteSpace(tag))
             {
-                var tag = "";
-                if (row.IsEntityLinkValid<DeliveryItem, ShipmentLink>(x => x.ShipmentLink))
-                    tag = string.Format("Rack #{0}", row.Get<DeliveryItem, string>(c => c.ShipmentLink.Code));
-                else if (row.IsEntityLinkValid<DeliveryItem, RequisitionLink>(x => x.RequisitionLink))
-                    tag = string.Format("Requisition #{0}", row.Get<DeliveryItem, int>(c => c.RequisitionLink.Number));
-                if (!string.IsNullOrWhiteSpace(tag))
-                {
-                    if (!lines.ContainsKey(tag))
-                        lines[tag] = new List<string>();
-                    if (row.IsEntityLinkValid<DeliveryItem, RequisitionLink>(x => x.RequisitionLink))
-                        lines[tag].Add(string.Format("{0}: {1}", row.Get<DeliveryItem, string>(c => c.Piece),
-                            row.Get<DeliveryItem, string>(c => c.Description)));
-
-                    else
-                        lines[tag].Add(string.Format("{0}: {1} - {2}", row.Get<DeliveryItem, string>(c => c.Barcode),
-                            row.Get<DeliveryItem, string>(c => c.ManufacturingPacketLink.Serial), row.Get<DeliveryItem, string>(c => c.Description)));
-                }
+                if (!lines.ContainsKey(tag))
+                    lines[tag] = new List<string>();
+                if (row.IsEntityLinkValid<DeliveryItem, RequisitionLink>(x => x.RequisitionLink))
+                    lines[tag].Add(string.Format("{0}: {1}", row.Get<DeliveryItem, string>(c => c.Piece),
+                        row.Get<DeliveryItem, string>(c => c.Description)));
+
+                else
+                    lines[tag].Add(string.Format("{0}: {1} - {2}", row.Get<DeliveryItem, string>(c => c.Barcode),
+                        row.Get<DeliveryItem, string>(c => c.ManufacturingPacketLink.Serial), row.Get<DeliveryItem, string>(c => c.Description)));
             }
+        }
 
-            var sb = new StringBuilder();
-            sb.AppendLine("The following items have been delivered:");
-            foreach (var key in lines.Keys)
+        var sb = new StringBuilder();
+        sb.AppendLine("The following items have been delivered:");
+        foreach (var key in lines.Keys)
+        {
+            sb.AppendLine(" ");
+            sb.AppendLine(key);
+            sb.AppendLine(new string('=', key.Length));
+            foreach (var line in lines[key])
+                sb.AppendLine(line);
+        }
+
+        // Send the notifications
+        var emps = Provider.Query(
+            new Filter<Employee>(x => x.UserLink.UserID).IsEqualTo(UserID)
+                .Or(x => x.ID).IsEqualTo(delemp)
+                .Or(x => x.ID).IsEqualTo(sitelead)
+                .Or(x => x.ID).IsEqualTo(projlead)
+            ,
+            Columns.None<Employee>().Add(
+                x => x.ID,
+                x => x.UserLink.ID
+            )
+        );
+
+        var me = emps.Rows.FirstOrDefault(r => r.Get<Employee, Guid>(c => c.UserLink.ID) == UserGuid);
+
+        var notifications = new List<Notification>();
+        foreach (var emp in emps.Rows)
+            if (emp.Get<Employee, Guid>(x => x.UserLink.ID) != UserGuid)
             {
-                sb.AppendLine(" ");
-                sb.AppendLine(key);
-                sb.AppendLine(new string('=', key.Length));
-                foreach (var line in lines[key])
-                    sb.AppendLine(line);
+                var notification = new Notification();
+                notification.Employee.ID = emp.Get<Employee, Guid>(x => x.ID);
+                notification.Title = string.Format("Delivery #{0} ({1}: {2}) has been Delivered", delnumber, jobnumber, jobname);
+                notification.Description = sb.ToString();
+                notification.Job.ID = jobid;
+                notification.EntityType = typeof(Delivery).EntityName();
+                notification.EntityID = delivery.ID;
+                notification.Sender.ID = me != null ? me.Get<Employee, Guid>(x => x.ID) : Guid.Empty;
+                notifications.Add(notification);
             }
 
-            // Send the notifications
-            var emps = Provider.Query(
-                new Filter<Employee>(x => x.UserLink.UserID).IsEqualTo(UserID)
-                    .Or(x => x.ID).IsEqualTo(delemp)
-                    .Or(x => x.ID).IsEqualTo(sitelead)
-                    .Or(x => x.ID).IsEqualTo(projlead)
-                ,
-                Columns.None<Employee>().Add(
-                    x => x.ID,
-                    x => x.UserLink.ID
-                )
-            );
+        if (notifications.Any())
+            FindSubStore<Notification>().Save(notifications, "");
+    }
 
-            var me = emps.Rows.FirstOrDefault(r => r.Get<Employee, Guid>(c => c.UserLink.ID) == UserGuid);
+    private void UpdateEquipment(Delivery delivery)
+    {
+        var deliveryEquipments = Provider.Query(
+            new Filter<DeliveryEquipment>(x => x.Delivery.ID).IsEqualTo(delivery.ID),
+            Columns.None<DeliveryEquipment>()
+                .Add(x => x.Equipment.ID).Add(x => x.Type))
+            .ToObjects<DeliveryEquipment>();
+        var jobEquipments = Provider.Query(
+            new Filter<JobEquipment>(x => x.JobLink.ID).IsEqualTo(delivery.Job.ID),
+            Columns.Required<JobEquipment>()
+                .Add(x => x.ID)
+                .Add(x => x.EquipmentLink.ID))
+            .ToObjects<JobEquipment>()
+            .ToDictionary(x => x.EquipmentLink.ID);
 
-            var notifications = new List<Notification>();
-            foreach (var emp in emps.Rows)
-                if (emp.Get<Employee, Guid>(x => x.UserLink.ID) != UserGuid)
-                {
-                    var notification = new Notification();
-                    notification.Employee.ID = emp.Get<Employee, Guid>(x => x.ID);
-                    notification.Title = string.Format("Delivery #{0} ({1}: {2}) has been Delivered", delnumber, jobnumber, jobname);
-                    notification.Description = sb.ToString();
-                    notification.Job.ID = jobid;
-                    notification.EntityType = typeof(Delivery).EntityName();
-                    notification.EntityID = delivery.ID;
-                    notification.Sender.ID = me != null ? me.Get<Employee, Guid>(x => x.ID) : Guid.Empty;
-                    notifications.Add(notification);
-                }
+        var jobEquipmentStore = FindSubStore<JobEquipment>();
 
-            if (notifications.Any())
-                FindSubStore<Notification>().Save(notifications, "");
+        var toSave = new List<JobEquipment>();
+        foreach(var deliveryEquipment in deliveryEquipments)
+        {
+            if(jobEquipments.TryGetValue(deliveryEquipment.Equipment.ID, out var jobEquipment))
+            {
+                jobEquipment.OnSite = deliveryEquipment.Type == DeliveryEquipmentType.DropOff;
+                toSave.Add(jobEquipment);
+            }
+            else
+            {
+                var newJobEquipment = new JobEquipment();
+                newJobEquipment.EquipmentLink.CopyFrom(deliveryEquipment.Equipment);
+                newJobEquipment.JobLink.CopyFrom(delivery.Job);
+                newJobEquipment.OnSite = deliveryEquipment.Type == DeliveryEquipmentType.DropOff;
+                toSave.Add(newJobEquipment);
+            }
         }
+        jobEquipmentStore.Save(toSave, "Updated by delivery.");
+    }
+
+    protected override void AfterSave(Delivery entity)
+    {
+        base.AfterSave(entity);
 
-        protected override void AfterSave(Delivery entity)
+        if (!entity.Delivered.IsEmpty() && entity.HasOriginalValue(x => x.Delivered) && entity.GetOriginalValue(x => x.Delivered).IsEmpty())
         {
-            base.AfterSave(entity);
+            SendNotifications(entity);
+            UpdateEquipment(entity);
+        }
 
-            if (!entity.Delivered.IsEmpty() && entity.HasOriginalValue(x => x.Delivered) && entity.GetOriginalValue(x => x.Delivered).IsEmpty())
-                SendNotifications(entity);
+        UpdateTrackingKanban<DeliveryKanban, Delivery, DeliveryLink>(entity, d =>
+        {
+            return !entity.Delivered.IsEmpty()
+                ? KanbanStatus.Complete
+                : entity.Assignment.IsValid()
+                    ? KanbanStatus.InProgress
+                    : KanbanStatus.Open;
+        });
 
-            UpdateTrackingKanban<DeliveryKanban, Delivery, DeliveryLink>(entity, d =>
-            {
-                return !entity.Delivered.IsEmpty()
-                    ? KanbanStatus.Complete
-                    : entity.Assignment.IsValid()
-                        ? KanbanStatus.InProgress
-                        : KanbanStatus.Open;
-            });
-
-            //requi tracking kanban is usually updated in RequisitionStore, the requi is not necessarily saved when a delivery is completed
-            if (entity.Delivered != DateTime.MinValue)
+        //requi tracking kanban is usually updated in RequisitionStore, the requi is not necessarily saved when a delivery is completed
+        if (entity.Delivered != DateTime.MinValue)
+        {
+            CoreTable table = Provider.Query<Requisition>(new Filter<Requisition>(x => x.Delivery.ID).IsEqualTo(entity.ID));
+            if (table.Rows.Any())
             {
-                CoreTable table = Provider.Query<Requisition>(new Filter<Requisition>(x => x.Delivery.ID).IsEqualTo(entity.ID));
-                if (table.Rows.Any())
+                foreach (CoreRow row in table.Rows)
                 {
-                    foreach (CoreRow row in table.Rows)
-                    {
-                        Requisition requisition = row.ToObject<Requisition>();
-                        UpdateTrackingKanban<RequisitionKanban, Requisition, RequisitionLink>(requisition, e => { return KanbanStatus.Complete; });
-                    }
+                    Requisition requisition = row.ToObject<Requisition>();
+                    UpdateTrackingKanban<RequisitionKanban, Requisition, RequisitionLink>(requisition, e => { return KanbanStatus.Complete; });
                 }
             }
         }
+    }
 
-        protected override void BeforeDelete(Delivery entity)
-        {
-            UnlinkTrackingKanban<DeliveryKanban, Delivery, DeliveryLink>(entity);
-        }
+    protected override void BeforeDelete(Delivery entity)
+    {
+        UnlinkTrackingKanban<DeliveryKanban, Delivery, DeliveryLink>(entity);
     }
 }