using Distribution.Data; using Distribution.Models; using Microsoft.EntityFrameworkCore; namespace Distribution.Features.Vehicle; public class VehicleDashboardRepository(AppDBContext db) : IVehicleDashboardRepository { public Task> GetStockOnBoardAsync(Guid vehicleId) => db.StockBalances.AsNoTracking() .Where(b => b.LocationType == LocationType.Vehicle && b.LocationId == vehicleId && b.Quantity > 0) .Select(b => new VehicleBalanceRow( b.FK_ItemId, b.Item.Sku, b.Item.Name, b.Item.Category.Name, b.Item.BaseUom.Name, b.Item.SalePrice, b.Quantity, b.UpdatedAt)) .ToListAsync(); public Task> GetMovementsAsync(Guid vehicleId, DateTime from, DateTime toExclusive) => db.StockMovements.AsNoTracking() .Where(m => (m.FromLocationId == vehicleId || m.ToLocationId == vehicleId) && m.CreatedAt >= from && m.CreatedAt < toExclusive) .OrderByDescending(m => m.CreatedAt) .Select(m => new VehicleMovementRow( m.Id, m.FK_ItemId, m.Item.Name, m.Item.Sku, m.FromLocationId, m.FromLocationType, m.ToLocationId, m.ToLocationType, m.Quantity, m.Type, m.ReasonCode, m.ReferenceId, m.CreatedAt, m.CreatedBy)) .ToListAsync(); public Task> GetRidesAsync(Guid vehicleId, DateTime from, DateTime toExclusive) => db.DeliveryRuns.AsNoTracking() .Where(r => r.FK_VehicleId == vehicleId && (r.StartedAt ?? r.CreatedAt) >= from && (r.StartedAt ?? r.CreatedAt) < toExclusive) .OrderByDescending(r => r.StartedAt ?? r.CreatedAt) .Select(r => new VehicleRideRow( r.Id, r.Status, r.FK_DriverProfileId, r.DriverProfile.User.FullName, r.Meterstart, r.Meterend, r.StartedAt, r.ClosedAt, r.CreatedAt, r.CancelTypeCode, r.CancelReason, db.DeliveryTasks.Count(t => t.FK_RideId == r.Id), db.DeliveryTasks.Count(t => t.FK_RideId == r.Id && (t.Status == DeliveryTaskStatus.Delivered || t.Status == DeliveryTaskStatus.PartiallyDelivered)), db.DeliveryTasks.Count(t => t.FK_RideId == r.Id && t.Status == DeliveryTaskStatus.Failed))) .ToListAsync(); public Task GetOpenRideAsync(Guid vehicleId) => db.DeliveryRuns.AsNoTracking() .Where(r => r.FK_VehicleId == vehicleId && r.Status == RideStatus.Open) .Select(r => new VehicleRideRow( r.Id, r.Status, r.FK_DriverProfileId, r.DriverProfile.User.FullName, r.Meterstart, r.Meterend, r.StartedAt, r.ClosedAt, r.CreatedAt, r.CancelTypeCode, r.CancelReason, db.DeliveryTasks.Count(t => t.FK_RideId == r.Id), db.DeliveryTasks.Count(t => t.FK_RideId == r.Id && (t.Status == DeliveryTaskStatus.Delivered || t.Status == DeliveryTaskStatus.PartiallyDelivered)), db.DeliveryTasks.Count(t => t.FK_RideId == r.Id && t.Status == DeliveryTaskStatus.Failed))) .FirstOrDefaultAsync(); public Task> GetTasksAsync(Guid vehicleId, DateTime from, DateTime toExclusive) => db.DeliveryTasks.AsNoTracking() .Where(t => t.Ride.FK_VehicleId == vehicleId && (t.Ride.StartedAt ?? t.Ride.CreatedAt) >= from && (t.Ride.StartedAt ?? t.Ride.CreatedAt) < toExclusive) .Select(t => new VehicleTaskRow( t.FK_RideId, t.Status, t.ShopPO.FK_StoreId, t.ShopPO.Store.StoreName, t.ShopPO.Items.Sum(i => i.QuantityFulfilled * i.UnitPrice), t.Ride.StartedAt ?? t.Ride.CreatedAt)) .ToListAsync(); public Task> GetCollectionsAsync(Guid vehicleId, DateTime from, DateTime toExclusive) => db.Payments.AsNoTracking() .Where(p => p.CollectedAt >= from && p.CollectedAt < toExclusive && db.DeliveryTasks.Any(t => t.Ride.FK_VehicleId == vehicleId && t.FK_ShopPOId == p.Invoice.FK_ShopPOId)) .GroupBy(p => p.Method) .Select(g => new VehicleCollectionRow(g.Key, g.Sum(p => p.Amount), g.Count())) .ToListAsync(); public async Task GetReturnTotalsAsync(Guid vehicleId, DateTime from, DateTime toExclusive) { var returns = db.SaleReturns.AsNoTracking() .Where(r => r.CreatedAt >= from && r.CreatedAt < toExclusive && db.DeliveryTasks.Any(t => t.Ride.FK_VehicleId == vehicleId && t.FK_ShopPOId == r.FK_ShopPOId)); var count = await returns.CountAsync(); var credit = await returns.SumAsync(r => (decimal?)r.TotalCreditAmount) ?? 0m; var units = await returns.SelectMany(r => r.Items).SumAsync(i => (decimal?)i.Quantity) ?? 0m; return new VehicleReturnTotals(count, credit, units); } public Task> GetDriversAsync(IEnumerable driverProfileIds) { var ids = driverProfileIds.Distinct().ToList(); return db.DriverProfiles.AsNoTracking() .Where(d => ids.Contains(d.Id)) .Select(d => new VehicleDriverRow( d.Id, d.User.FullName, d.LicenseNumber, d.LicenseExpiry, d.MedicalClearanceExpiry, d.IsActive)) .ToListAsync(); } public Task GetWarehouseRefAsync(Guid? warehouseId) => warehouseId is null ? Task.FromResult(null) : db.Warehouses.AsNoTracking() .Where(w => w.Id == warehouseId) .Select(w => new VehicleNamedRef(w.Id, w.Name)) .FirstOrDefaultAsync(); public Task GetMobileWarehouseAsync(Guid vehicleId) => db.Warehouses.AsNoTracking() .Where(w => w.VehicleId == vehicleId) .Select(w => new VehicleNamedRef(w.Id, w.Name)) .FirstOrDefaultAsync(); public async Task> GetLocationNamesAsync(IEnumerable ids) { var idList = ids.Distinct().ToList(); var names = new Dictionary(); if (idList.Count == 0) return names; foreach (var w in await db.Warehouses.AsNoTracking().Where(w => idList.Contains(w.Id)).Select(w => new { w.Id, w.Name }).ToListAsync()) names[w.Id] = w.Name; foreach (var v in await db.Vehicles.AsNoTracking().Where(v => idList.Contains(v.Id)).Select(v => new { v.Id, v.RegNumber }).ToListAsync()) names[v.Id] = v.RegNumber; return names; } public async Task> GetUserNamesAsync(IEnumerable ids) { var idList = ids.Distinct().ToList(); if (idList.Count == 0) return []; return await db.Users.AsNoTracking() .Where(u => idList.Contains(u.Id)) .ToDictionaryAsync(u => u.Id, u => u.FullName); } }