| | | 1 | | using Microsoft.EntityFrameworkCore; |
| | | 2 | | using Microsoft.Extensions.Logging; |
| | | 3 | | using System; |
| | | 4 | | using System.Collections.Generic; |
| | | 5 | | using System.Linq; |
| | | 6 | | using System.Text.Json; |
| | | 7 | | using System.Threading.Tasks; |
| | | 8 | | using WinSolutions.Sveta.Server.Domain; |
| | | 9 | | using WinSolutions.Sveta.Server.Data.DataModel.Contexts; |
| | | 10 | | using WinSolutions.Sveta.Server.Data.DataModel.Entities; |
| | | 11 | | using WinSolutions.Sveta.Server.Data.DataModel.Kinds; |
| | | 12 | | using WinSolutions.Sveta.Server.Services.Interfaces; |
| | | 13 | | using WinSolutions.Sveta.Common; |
| | | 14 | | using SVETA.Api.Helpers; |
| | | 15 | | |
| | | 16 | | |
| | | 17 | | namespace WinSolutions.Sveta.Server.Services.Implements |
| | | 18 | | { |
| | | 19 | | public class WorkScheduleService : SvetaServiceBase, IWorkScheduleService |
| | | 20 | | { |
| | | 21 | | private readonly SvetaDbContext _db; |
| | | 22 | | private readonly ILogger<WorkScheduleService> _logger; |
| | | 23 | | public WorkScheduleService(SvetaDbContext db, ILogger<WorkScheduleService> logger, IAuthenticationService authen |
| | 185 | 24 | | : base(authenticationService) |
| | 185 | 25 | | { |
| | 185 | 26 | | _db = db; |
| | 185 | 27 | | _logger = logger; |
| | 185 | 28 | | } |
| | | 29 | | |
| | | 30 | | /// <summary> |
| | | 31 | | /// получить записи с пагинацией |
| | | 32 | | /// </summary> |
| | | 33 | | /// <param name="page">номер страницы</param> |
| | | 34 | | /// <param name="limit">размер страницы</param> |
| | | 35 | | /// <param name="date">дата начала рабочего дня</param> |
| | | 36 | | /// <param name="workingDay">true, если только рабочие дни</param> |
| | | 37 | | /// <param name="sortByDateDesc">сортировать по дате начала дня</param> |
| | | 38 | | /// <param name="departmentId">id склада</param> |
| | | 39 | | /// <returns></returns> |
| | | 40 | | public async Task<PaginatedData<List<WorkSchedule>>> GetSchedules(int page, int limit, DateTime? date, bool work |
| | 10 | 41 | | { |
| | 10 | 42 | | var schedules = PrepareSchedules().AsNoTracking(); |
| | 10 | 43 | | int total = await schedules?.CountAsync(); |
| | 10 | 44 | | schedules = schedules.Where(x => !workingDay || x.IsWorkingDay == true) |
| | 10 | 45 | | .Where(x=> departmentId == 0 || x.WarehouseId == departmentId) |
| | 10 | 46 | | .Where(x => date.IsNullOrMinValue() || x.BeginTime.Date == date.Value.Date); |
| | 10 | 47 | | schedules = !sortByDateDesc ? schedules.OrderBy(x => x.BeginTime) : schedules.OrderByDescending(x => x.Begin |
| | 10 | 48 | | int filtered = await schedules?.CountAsync(); |
| | 10 | 49 | | return new PaginatedData<List<WorkSchedule>> |
| | 10 | 50 | | { |
| | 10 | 51 | | Result = await schedules.Skip(page * limit).Take(limit).ToListAsync(), |
| | 10 | 52 | | TotalCount = total, |
| | 10 | 53 | | TotalFilteredCount = filtered |
| | 10 | 54 | | }; |
| | 10 | 55 | | } |
| | | 56 | | |
| | | 57 | | /// <summary> |
| | | 58 | | /// возвращает следующий после текущего рабочий день |
| | | 59 | | /// </summary> |
| | | 60 | | /// <param name="warehouseId">id склада</param> |
| | | 61 | | /// <returns></returns> |
| | 29 | 62 | | public async Task<WorkSchedule> GetNextWorkDay(long warehouseId) => await PrepareSchedules() |
| | 29 | 63 | | .Where(x => x.WarehouseId == warehouseId) |
| | 29 | 64 | | .OrderBy(x => x.BeginTime) |
| | 29 | 65 | | .FirstOrDefaultAsync(x => x.BeginTime > DateTime.UtcNow && x.IsWorkingDay); |
| | | 66 | | |
| | | 67 | | /// <summary> |
| | | 68 | | /// получить все записи |
| | | 69 | | /// </summary> |
| | | 70 | | /// <returns></returns> |
| | 187 | 71 | | public async Task<List<WorkSchedule>> GetSchedules() => await PrepareSchedules().ToListAsync(); |
| | | 72 | | |
| | | 73 | | /// <summary> |
| | | 74 | | /// получить запись |
| | | 75 | | /// </summary> |
| | | 76 | | /// <param name="id">id записи</param> |
| | | 77 | | /// <returns></returns> |
| | 1 | 78 | | public Task<WorkSchedule> GetSchedule(long id) => PrepareSchedules().FirstOrDefaultAsync(e => e.Id == id); |
| | | 79 | | |
| | | 80 | | /// <summary> |
| | | 81 | | /// предварительная выборка записей |
| | | 82 | | /// </summary> |
| | | 83 | | /// <returns></returns> |
| | 230 | 84 | | private IQueryable<WorkSchedule> PrepareSchedules() => _db.WorkScheduler |
| | 230 | 85 | | .Include(x=>x.Warehouse).ThenInclude(x=>x.Contragent) |
| | 230 | 86 | | .Where(e => !e.IsDeleted) |
| | 230 | 87 | | .AsQueryable(); |
| | | 88 | | |
| | | 89 | | /// <summary> |
| | | 90 | | /// создает запись |
| | | 91 | | /// </summary> |
| | | 92 | | /// <param name="data">объект WorkSchedule</param> |
| | | 93 | | /// <returns></returns> |
| | | 94 | | public async Task Create(WorkSchedule data) |
| | 1 | 95 | | { |
| | 1 | 96 | | var existSchedule = PrepareSchedules().Any(x => x.BeginTime == data.BeginTime && x.EndTime == data.EndTime & |
| | 1 | 97 | | if (existSchedule) |
| | 0 | 98 | | throw new ArgumentException("Запись с указанной датой начала и окончания рабочего дня уже существует"); |
| | 1 | 99 | | _db.Entry(data.Warehouse).State = data.Warehouse.Id == 0 ? EntityState.Detached : EntityState.Unchanged; |
| | 1 | 100 | | await _db.WorkScheduler.AddAsync(data); |
| | 1 | 101 | | await _db.SaveChangesAsync(CurrentUserId); |
| | 1 | 102 | | } |
| | | 103 | | |
| | | 104 | | /// <summary> |
| | | 105 | | /// создает список записей |
| | | 106 | | /// </summary> |
| | | 107 | | /// <param name="data">список записей WorkSchedule</param> |
| | | 108 | | /// <returns></returns> |
| | | 109 | | public async Task Create(List<WorkSchedule> data) |
| | 187 | 110 | | { |
| | 187 | 111 | | if (data.Count == 0) |
| | 0 | 112 | | return; |
| | 187 | 113 | | data.ForEach(x => |
| | 23811 | 114 | | { |
| | 23811 | 115 | | _db.Entry(x.Warehouse).State = x.Warehouse.Id == 0 ? EntityState.Detached : EntityState.Unchanged; |
| | 23811 | 116 | | }); |
| | 187 | 117 | | await _db.WorkScheduler.AddRangeAsync(data); |
| | 187 | 118 | | await _db.SaveChangesAsync(CurrentUserId); |
| | 187 | 119 | | } |
| | | 120 | | |
| | | 121 | | /// <summary> |
| | | 122 | | /// проверяет существование записи |
| | | 123 | | /// </summary> |
| | | 124 | | /// <param name="id">id записи</param> |
| | | 125 | | /// <returns></returns> |
| | 2 | 126 | | public async Task<bool> ScheduleExists(long id) => await _db.WorkScheduler.AsNoTracking().Where(e => e.Id == id |
| | | 127 | | |
| | | 128 | | /// <summary> |
| | | 129 | | /// обновляет запись |
| | | 130 | | /// </summary> |
| | | 131 | | /// <param name="data">объект WorkSchedule</param> |
| | | 132 | | /// <returns></returns> |
| | | 133 | | public async Task Update(WorkSchedule data) |
| | 1 | 134 | | { |
| | 1 | 135 | | if (!(await ScheduleExists(data.Id))) |
| | 0 | 136 | | { |
| | 0 | 137 | | throw new ArgumentException($"Расписание с id={data.Id} не найдено"); |
| | | 138 | | } |
| | 1 | 139 | | var existSchedule = PrepareSchedules().Any(x => x.BeginTime == data.BeginTime && x.EndTime == data.EndTime & |
| | 1 | 140 | | if (existSchedule) |
| | 0 | 141 | | throw new ArgumentException("Запись с указанной датой начала и окончания рабочего дня уже существует"); |
| | 1 | 142 | | _db.WorkScheduler.Update(data); |
| | 1 | 143 | | await _db.SaveChangesAsync(CurrentUserId); |
| | 1 | 144 | | } |
| | | 145 | | |
| | | 146 | | /// <summary> |
| | | 147 | | /// обновляет список записей |
| | | 148 | | /// </summary> |
| | | 149 | | /// <param name="data">список записей WorkSchedule</param> |
| | | 150 | | /// <returns></returns> |
| | | 151 | | public async Task Update(List<WorkSchedule> data) |
| | 1 | 152 | | { |
| | 1 | 153 | | if (data.Count == 0) |
| | 0 | 154 | | return; |
| | 1 | 155 | | _db.WorkScheduler.UpdateRange(data); |
| | 1 | 156 | | await _db.SaveChangesAsync(CurrentUserId); |
| | 1 | 157 | | } |
| | | 158 | | |
| | | 159 | | /// <summary> |
| | | 160 | | /// удаляет запись |
| | | 161 | | /// </summary> |
| | | 162 | | /// <param name="id">id записи</param> |
| | | 163 | | /// <returns></returns> |
| | | 164 | | public async Task Delete(long id) |
| | 1 | 165 | | { |
| | 1 | 166 | | WorkSchedule rec = await _db.WorkScheduler.FindAsync(id); |
| | 1 | 167 | | if (rec == null) |
| | 0 | 168 | | throw new KeyNotFoundException($"Расписание с id={id} не найдено"); |
| | 1 | 169 | | rec.IsDeleted = true; |
| | 1 | 170 | | await _db.SaveChangesAsync(CurrentUserId); |
| | 1 | 171 | | } |
| | | 172 | | |
| | | 173 | | /// <summary> |
| | | 174 | | /// Подсчитывает рабочее время в минутах между двумя датами на основе расписания работы склада |
| | | 175 | | /// </summary> |
| | | 176 | | /// <remarks>author i.rebenok</remarks> |
| | | 177 | | /// <param name="dateFrom">Дата-время с</param> |
| | | 178 | | /// <param name="dateTo">Дата-время по</param> |
| | | 179 | | /// <param name="warehouseId">id склада</param> |
| | | 180 | | public async Task<double> CalcPeriodFromSchedule(DateTime dateFrom, DateTime dateTo, long warehouseId) |
| | 1 | 181 | | { |
| | | 182 | | //выбираем промежуток для расчетов. Увеличиваем диапазон на 1 день для начала и конца. Так как могут быть см |
| | | 183 | | //НАпример смена с 01.06 22:00 до 02.06 06:00. Мы передали даты расчета с 02.06 02:00 до 02.06 05:00. Чтобы |
| | 1 | 184 | | var schedule = await PrepareSchedules().AsNoTracking().Where(x => x.IsWorkingDay && x.BeginTime.Date >= date |
| | 1 | 185 | | double result = 0; |
| | 5 | 186 | | foreach (var item in schedule) |
| | 1 | 187 | | { |
| | | 188 | | //или если datefrom < времени начала работы и dateto >= даты-времени окончания дня, то считаем весь день |
| | 1 | 189 | | if (dateFrom <= item.BeginTime && dateTo >= item.EndTime) |
| | 0 | 190 | | result += item.EndTime.Subtract(item.BeginTime).TotalMinutes; |
| | | 191 | | //если внутри одного рабочего дня, считаем разницу между датами переданного периода |
| | 1 | 192 | | else if (dateFrom >= item.BeginTime && dateTo <= item.EndTime) |
| | 1 | 193 | | result += dateTo.Subtract(dateFrom).TotalMinutes; |
| | | 194 | | //если datefrom <= времени начала работы и dateto находится в рабочем периоде этого дня, считаем разницу |
| | 0 | 195 | | else if (dateFrom <= item.BeginTime && dateTo <= item.EndTime && dateTo >= item.BeginTime) |
| | 0 | 196 | | result += dateTo.Subtract(item.BeginTime).TotalMinutes; |
| | | 197 | | //если datefrom находится в рабочем периоде этого дня и dateto >= времени окончания дня, считаем разницу |
| | 0 | 198 | | else if (dateFrom >= item.BeginTime && dateFrom <= item.EndTime && dateTo >= item.EndTime) |
| | 0 | 199 | | result += item.EndTime.Subtract(dateFrom).TotalMinutes; |
| | 1 | 200 | | } |
| | 1 | 201 | | return result; |
| | 1 | 202 | | } |
| | | 203 | | } |
| | | 204 | | } |