< Summary

Class:WinSolutions.Sveta.Server.Services.Implements.MovementService
Assembly:WinSolutions.Sveta.Server
File(s):/opt/dev/sveta_api_build/WinSolutions.Sveta.Server/Services/Implements/MovementService.cs
Covered lines:511
Uncovered lines:30
Coverable lines:541
Total lines:830
Line coverage:94.4% (511 of 541)
Covered branches:93
Total branches:154
Branch coverage:60.3% (93 of 154)

Metrics

MethodLine coverage Branch coverage
.ctor(...)100%100%
GetFilterMovements()96%70%
FillMovementsItems(...)100%100%
GetFilterMovementsWithoutPagination(...)100%100%
GetMovementsByParent()100%100%
GetLiteMovementByParent()100%100%
GetDraft()100%100%
GetLightMovement()100%100%
GetMovement()100%100%
GetMovementReadonly()100%75%
GetMovement()100%100%
GetMovementForItems()100%100%
GetMovementForItems()100%100%
GetMovementForNextStatus()100%100%
GetMovementForNextStatus()100%100%
GetMovementWithAttachment()0%0%
GetMovement()100%100%
GetCountMovements()100%100%
GetMovementStatus(...)100%100%
CreateMovement()84.21%80%
UpdateMovement()90.47%75%
DeleteMovementItem()100%100%
DeleteMovement()85.71%50%
DeleteMovement()100%100%
MovementExists()100%100%
SortMovement(...)100%48.68%
PrepareAttachmentMovement()0%100%
GetMovementItemsByMovements(...)100%100%
PrepareMovementForNextStatus()100%100%
PrepareForMovementItem()100%100%
PrepareListMovement(...)100%100%
PrepareMovement(...)100%100%
FilterMovement(...)100%100%
PrepareOnlyMovement()100%100%
PrepareForSingle()100%100%

File(s)

/opt/dev/sveta_api_build/WinSolutions.Sveta.Server/Services/Implements/MovementService.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using Microsoft.Extensions.Logging;
 3using System;
 4using System.Collections.Generic;
 5using System.Linq;
 6using System.Linq.Expressions;
 7using System.Reflection;
 8using System.Text;
 9using System.Threading.Tasks;
 10using SVETA.Api.Helpers;
 11using WinSolutions.Sveta.Server.Data.DataModel.Contexts;
 12using WinSolutions.Sveta.Server.Data.DataModel.Entities;
 13using WinSolutions.Sveta.Server.Data.DataModel.Kinds;
 14using WinSolutions.Sveta.Server.Domain;
 15using WinSolutions.Sveta.Server.Services.Interfaces;
 16using WinSolutions.Sveta.Common;
 17
 18namespace WinSolutions.Sveta.Server.Services.Implements
 19{
 20    public class MovementService : SvetaServiceBase, IMovementService
 21    {
 22        private readonly SvetaDbContext _db;
 23        private readonly ILogger<MovementService> _logger;
 24        public MovementService(SvetaDbContext db, ILogger<MovementService> logger, IAuthenticationService authentication
 14425            : base(authenticationService)
 14426        {
 14427            _db = db;
 14428            _logger = logger;
 14429        }
 30
 31        /// <summary>
 32        /// Возвращает отфильтрованный и отсортированный список документов
 33        /// </summary>
 34        /// <param name="state"></param>
 35        /// <param name="documentNumber">Номер документа</param>
 36        /// <param name="customerId">Идентификтор заказчика</param>
 37        /// <param name="receiverId">Идентификтор получателя</param>
 38        /// <param name="supplierId">Идентификтор поставщика</param>
 39        /// <param name="fromDate">Дата начала отбора </param>
 40        /// <param name="toDate">Дата окончания отбора</param>
 41        /// <param name="kind">Тип документа</param>
 42        /// <param name="contragentBuyerFilter">Поиск по контрагенту покупателю</param>
 43        /// <param name="page">Страница</param>
 44        /// <param name="limit">Количество для отбора - макс 100, если null то вернет все записи</param>
 45        /// <param name="sort">сортировка по умолчанию по id - id|desc,created_on,created_on|desc,state,state|desc,statu
 46        /// <param name="excludedStatuses"></param>
 47        /// <param name="status"></param>
 48        /// <returns>Task<PaginatedData<List<Movement>>></returns>
 49        public async Task<PaginatedData<List<Movement>>> GetFilterMovements(long customerId = 0, long receiverId = 0, lo
 50            DateTime toDate = default, MovementType kind = null, MovementStatus status = null, RecordsState state = null
 51            string sort = default, long[] excludedStatuses = null, string contragentBuyerFilter = default, long senderId
 452        {
 453            var movements = PrepareListMovement(kind);
 454            if (movements == null)
 055                return await Task.FromResult(new PaginatedData<List<Movement>>());
 56
 457            int total = customerId != 0 || supplierId != 0 || excludedStatuses != null
 458                ? await FilterMovement(movements, customerId, 0, supplierId, excludedStatuses: excludedStatuses)
 459                    .CountAsync()
 460                : await movements?.CountAsync();
 61
 462            movements = FilterMovement(movements, customerId, receiverId, supplierId, fromDate, toDate, status,
 463                state, documentNumber, excludedStatuses, contragentBuyerFilter, senderId: senderId);
 64
 465            int filteredCount = movements != null
 466                ? await movements?.CountAsync()
 467                : 0;
 468            movements = SortMovement(movements, sort?.ToLower());
 469            if(limit != null)
 470                movements = movements.Skip((page < 2 ? 0 : page-1) * (int)limit).Take((int)limit);
 471            PaginatedData<List<Movement>> paginated = new PaginatedData<List<Movement>>
 472            {
 473                TotalCount = total,
 474                TotalFilteredCount = filteredCount,
 475                Result = await movements.ToListAsync()
 476            };
 77            // позиции документов заполняем уже для конечного результата, чтобы побыстрее
 478            FillMovementsItems(paginated.Result);
 79
 480            return await Task.FromResult(paginated);
 481        }
 82
 83        /// <summary>
 84        /// Для переданных документов заполняет их позиции. Используется для ускорения работы с базой
 85        /// </summary>
 86        /// <param name="result"></param>
 87        public void FillMovementsItems(List<Movement> result)
 588        {
 889            var items = GetMovementItemsByMovements(result.Select(x => x.Id).ToList());
 2190            foreach(var mov in result)
 391            {
 392                mov.Items = items[mov.Id];
 393            }
 594        }
 95
 96        /// <summary>
 97        /// Возвращает список документов без пагинации
 98        /// </summary>
 99        /// <param name="customerId">идентификатор контрагента заказчика - по умолчанию 0, поиск по всем</param>
 100        /// <param name="receiverId">идентификатор департамента магазина заказчика- по умолчанию 0, поиск по всем</param
 101        /// <param name="supplierId">идентификатор контрагента поставщика- по умолчанию 0, поиск по всем</param>
 102        /// <param name="fromDate">дата начала отбора по умолчанию все</param>
 103        /// <param name="toDate">дата окончания отбора по умолчанию все</param>
 104        /// <param name="kind">тип документа для поиска </param>
 105        /// <param name="status">статус документа для поиска</param>
 106        /// <param name="state">состояние записи документа</param>
 107        /// <param name="sort">тип сортировки - сортировка по умолчанию по id - id|desc,created_on,created_on|desc,state
 108        /// <param name="excludedStatuses">массив с идентификаторами статусов для исключения из выдачи</param>
 109        /// <returns>IQueryable<Movement></returns>
 110        public IQueryable<Movement> GetFilterMovementsWithoutPagination(long customerId = 0,
 111            long receiverId = 0,
 112            long supplierId = 0, DateTime fromDate = default,
 113            DateTime toDate = default, MovementType kind = null, MovementStatus status = null,
 114            RecordsState state = null, string sort = default, long[] excludedStatuses = null) =>
 45115            FilterMovement(PrepareListMovement(kind).AsNoTracking(), customerId, receiverId, supplierId, fromDate,
 45116                toDate, status, state, sort, excludedStatuses);
 117
 118        /// <summary>
 119        /// Возвращает заявки созданные на основании идентификатора переданного в параметре
 120        /// </summary>
 121        /// <param name="parentId">идентификатор документа родителя</param>
 122        /// <param name="kind">Тип возращаемых документов</param>
 123        /// <param name="statusId">Статус для поиска - 0 для всех</param>
 124        /// <returns>Task<List<Movement>></returns>
 125        public async Task<List<Movement>> GetMovementsByParent(long parentId, MovementType kind, long statusId) =>
 82126            await PrepareMovement(kind)
 82127                .Where(d => d.ParentId == parentId)
 82128                .Where(d => statusId == 0 || d.MovementStatus.Id == statusId)
 82129                .ToListAsync();
 130
 131        /// <summary>
 132        /// Возвращает документы по родителю с минимальным количеством инклюдов
 133        /// </summary>
 134        /// <param name="parentId">Идентификатор родительского документа</param>
 135        /// <param name="kind">Тип возвращаемых документов</param>
 136        /// <param name="statusId">Статус документов для поиска</param>
 137        /// <returns>Task<List<Movement>></returns>
 138        public async Task<List<Movement>> GetLiteMovementByParent(long parentId, MovementType kind, long statusId) =>
 2139            await _db.Movements.AsNoTracking().Include(d => d.MovementStatus)
 2140                .Include(d => d.MovementType)
 2141                .Where(movement => !movement.IsDeleted && movement.MovementType.Id == kind.Id)
 2142                .Where(movement => statusId == 0 || movement.MovementStatus.Id == statusId)
 2143                .Where(movement => movement.ParentId == parentId)
 2144                .ToListAsync();
 145
 146        /// <summary>
 147        /// Возвращает документ в статусе черновик для заказчика и магазина
 148        /// </summary>
 149        /// <param name="contragentId">Идентификатор контрагента заказчика</param>
 150        /// <param name="departmentId">Идентификатор департамента получателя - магазина</param>
 151        /// <param name="type">Тип документа для поиска</param>
 152        /// <returns>Task<Movement></returns>
 153        public async Task<Movement> GetDraft(long contragentId, long departmentId, long senderId)
 63154        {
 63155            var type = _db.refMovementType.FirstOrDefault(d => d.Id == (long)MovementKind.Order);
 63156            return await PrepareMovement(type).FirstOrDefaultAsync(movement => movement.Receiver.Id == departmentId
 63157                                                                                                   && movement.Customer.
 63158                                                                                                   && movement.Sender.Id
 63159                                                                                                   && movement.MovementS
 63160        }
 161
 162        /// <summary>
 163        /// Возвращает документ с минимальным количеством Include
 164        /// </summary>
 165        /// <param name="movementId">Идентификатор документа</param>
 166        /// <returns>Task<Movement></returns>
 167        public async Task<Movement> GetLightMovement(long movementId) =>
 42168            await PrepareOnlyMovement().FirstOrDefaultAsync(d => d.Id == movementId);
 169
 170        /// <summary>
 171        /// Возвращает документ по его Id и типу документа
 172        /// </summary>
 173        /// <param name="id">идентификатор документа</param>
 174        /// <param name="type">Тип документа</param>
 175        /// <remarks>auth: aabelentsov</remarks>
 176        /// <returns>Task<Movement></returns>
 1177        public async Task<Movement> GetMovement(long id, MovementType type) => await PrepareForSingle()
 1178            .FirstOrDefaultAsync(d => d.Id == id && d.MovementType.Id == type.Id);
 179
 180        public async Task<Movement> GetMovementReadonly(long id, MovementType type)
 58181        {
 58182            var mvt = await _db.Movements
 58183                .Include(d => d.Parent)
 58184                    .ThenInclude(parent => parent.MovementStatus)
 58185                .Include(d => d.Customer)
 58186                    .ThenInclude(Customer => Customer.JuridicAddress)
 58187                .Include(d => d.Customer)
 58188                    .ThenInclude(Customer => Customer.PhysicAddress)
 58189                .Include(d => d.Customer)
 58190                    .ThenInclude(customer => customer.ContractsAsBuyer)
 58191                        .ThenInclude(contract => contract.Seller)
 58192                .Include(d => d.Supplier)
 58193                    .ThenInclude(Supplier => Supplier.JuridicAddress)
 58194                .Include(d => d.Receiver)
 58195                    .ThenInclude(Receiver => Receiver.ActualAddress)
 58196                .Include(d => d.Receiver)
 58197                    .ThenInclude(receiver => receiver.Cluster)
 58198                        .ThenInclude(Cluster => Cluster.ClusterDeliveryTypes)
 58199                            .ThenInclude(ClusterDeliveryTypes => ClusterDeliveryTypes.DeliveryType)
 58200                .Include(d => d.MovementStatus)
 58201                    .ThenInclude(d => d.StatusOwner)
 58202                .Include(d => d.MovementStatus)
 58203                    .ThenInclude(d => d.MovementType)
 58204                .Include(d => d.MovementType)
 58205                .Include(d => d.RecState)
 58206                .Include(d => d.Sender)
 58207                    .ThenInclude(Sender => Sender.ActualAddress)
 58208                .Include(d => d.Notes)
 58209                    .ThenInclude(Note => Note.CreatedByUser)
 58210                .Include(d => d.Children)
 58211                    .ThenInclude(c => c.Items)
 58212                        .ThenInclude(i => i.Good)
 58213                .Include(d => d.MovementStatusJournals)
 58214                    .ThenInclude(m => m.StatusCurrent)
 58215                .Include(d => d.DeliveryType)
 58216                .AsNoTracking()
 58217                .FirstOrDefaultAsync(e => !e.IsDeleted && e.Id == id);
 58218            if(mvt != null)
 58219            {
 58220                mvt.Items = await _db.MovementItems
 58221                    .Include(Item => Item.Good)
 58222                        .ThenInclude(Good => Good.DepartmentGoodSettings)
 58223                            .ThenInclude(DepartmentGoodSetting => DepartmentGoodSetting.Department)
 58224                    .Include(Items => Items.Good)
 58225                        .ThenInclude(good => good.Brand)
 58226                    .Include(Items => Items.Good)
 58227                        .ThenInclude(x => x.Prices)
 58228                            .ThenInclude(x => x.PriceTrend)
 58229                                .ThenInclude(x => x.SupplierDepartment)
 58230                    .Include(Item => Item.Good)
 58231                        .ThenInclude(Good => Good.VatsKind)
 58232                    .Include(Item => Item.Good)
 58233                        .ThenInclude(Good => Good.Country)
 58234                    .Include(Item => Item.Good)
 58235                        .ThenInclude(Good => Good.UnitsKind)
 58236                    .Include(Items => Items.Good)
 58237                        .ThenInclude(Good => Good.DefaultBarCode)
 58238                    .Include(Items => Items.Good)
 58239                        .ThenInclude(Good => Good.GoodBarcodes)
 58240                            .ThenInclude(barcode => barcode.BarCode)
 58241                    .Include(Items => Items.Good)
 58242                        .ThenInclude(good => good.Category)
 58243                            .ThenInclude(category => category.DepartmentCategoryRatios)
 58244                    .Include(Item => Item.Good)
 58245                        .ThenInclude(good => good.Rests)
 58246                    .Include(Item => Item.Good)
 58247                        .ThenInclude(Good => Good.Photos)
 58248                    .AsNoTracking()
 58249                    .Where(x => !x.IsDeleted && x.MovementId == id)
 58250                    .ToListAsync();
 58251            }
 58252            return mvt;
 58253        }
 254
 255        /// <summary>
 256        /// Возвращает документ по его Id
 257        /// </summary>
 258        /// <param name="id">Идентификатор документа</param>
 259        /// <returns>Task<Movement></returns>
 14260        public async Task<Movement> GetMovement(long id) => await PrepareForSingle()
 14261            .FirstOrDefaultAsync(d => d.Id == id);
 262
 263        /// <summary>
 264        /// Возвращает документ по его ID для работы с вложениями (удаление, обновление)
 265        /// </summary>
 266        /// <param name="id">Идентификатор документа</param>
 267        /// <returns></returns>
 60268        public async Task<Movement> GetMovementForItems(long id) => await PrepareForMovementItem()
 60269            .FirstOrDefaultAsync(d => d.Id == id);
 270
 271        /// <summary>
 272        /// Возвращает документ по его ID для работы с вложениями (удаление, обновление)
 273        /// </summary>
 274        /// <param name="id">Идентификатор документа</param>
 275        /// <returns></returns>
 1276        public async Task<Movement> GetMovementForItems(Guid guid) => await PrepareForMovementItem()
 1277            .FirstOrDefaultAsync(d => d.GUID == guid);
 278        /// <summary>
 279        /// Возвравщает документ для установки следующего статуса
 280        /// </summary>
 281        /// <param name="id">Идентификатор документа</param>
 282        /// <returns></returns>
 225283        public async Task<Movement> GetMovementForNextStatus(long id) => await PrepareMovementForNextStatus()
 225284            .FirstOrDefaultAsync(d => d.Id == id);
 285
 286        /// <summary>
 287        /// Возвравщает документ для установки следующего статуса
 288        /// </summary>
 289        /// <param name="guid">Внешний идентификатор документа</param>
 290        /// <returns></returns>
 23291        public async Task<Movement> GetMovementForNextStatus(Guid guid) => await PrepareMovementForNextStatus()
 23292            .FirstOrDefaultAsync(d => d.GUID == guid);
 293
 294        /// <summary>
 295        /// Возвращает документ с вложениями
 296        /// </summary>
 297        /// <param name="id">Идентифактор документа</param>
 298        /// <returns></returns>
 299        public async Task<Movement> GetMovementWithAttachment(long id)
 0300        {
 0301            var movement = await PrepareAttachmentMovement()
 0302                .FirstOrDefaultAsync(d => d.Id == id);
 0303            if (movement != null)
 0304            {
 0305                movement.MovementAttachments = movement.MovementAttachments
 0306                    .Where(d => !d.IsDeleted).ToList();
 0307            }
 308
 0309            return movement;
 0310        }
 311
 312        /// <summary>
 313        /// Возвращает документ по его GUID
 314        /// </summary>
 315        /// <param name="guid">Глобальный идентификатор документа</param>
 316        /// <returns>Task<Movement></returns>
 1317        public async Task<Movement> GetMovement(Guid guid) => await PrepareForSingle()
 1318            .FirstOrDefaultAsync(d => d.GUID.Equals(guid));
 319
 320        /// <summary>
 321        /// Возвращает количество отфильтрованных документов
 322        /// </summary>
 323        /// <param name="documentNumber">Номер документа</param>
 324        /// <param name="customerId">Идентификтор заказчика</param>
 325        /// <param name="receiverId">Идентификтор получателя</param>
 326        /// <param name="supplierId">Идентификтор поставщика</param>
 327        /// <param name="fromDate">Дата начала отбора </param>
 328        /// <param name="toDate">Дата окончания отбора</param>
 329        /// <param name="kind">Тип документа</param>
 330        /// <param name="status">Статус документа - 0 для всех</param>
 331        /// <param name="state">Состояние документа - 0 для всех</param>
 332        /// <param name="excludedStatuses">Массив идентификаторов статусов для исключения из выдачи</param>
 333        /// <returns>Task<int></returns>
 334        public async Task<int> GetCountMovements(long customerId = 0, long receiverId = 0, long supplierId = 0, DateTime
 335            DateTime toDate = default, MovementType kind = null, MovementStatus status = null, RecordsState state = null
 2336        {
 2337            var movements = PrepareListMovement(kind);
 2338            movements = FilterMovement(movements, customerId, receiverId, supplierId, fromDate, toDate, status, state, d
 2339            return await Task.FromResult(movements.Count());
 2340        }
 341
 342        /// <summary>
 343        /// Возвращает запрос для получения текущего статуса по отгрузкам поставщика
 344        /// </summary>
 345        /// <param name="supplierId">идентификато контрагента поставщика</param>
 346        /// <param name="dateFrom">Дата начала выборки по умолчанию все</param>
 347        /// <param name="dateTo">Дата окончания выборки по умолчанию все</param>
 348        /// <param name="docNumber">Номер документа для поиска</param>
 349        /// <returns>IQueryable<Movement></returns>
 350        public IQueryable<Movement> GetMovementStatus(long supplierId, DateTime dateFrom, DateTime dateTo,
 2351            Guid docNumber) => _db.Movements
 2352            .Include(movement => movement.MovementType)
 2353            .Include(movement => movement.MovementStatus)
 2354            .Include(movement => movement.Supplier)
 2355            .Include(movement => movement.DeliveryType)
 2356            .Where(movement => !movement.IsDeleted)
 2357            .Where(movement => movement.MovementType.Id == (long)MovementKind.Shipment)
 2358            .Where(movement => movement.Supplier.Id == supplierId)
 2359            .Where(movement => dateFrom == DateTime.MinValue || movement.CreationDateTime >= dateFrom)
 2360            .Where(movement => dateTo == DateTime.MinValue || movement.CreationDateTime <= dateTo)
 2361            .Where(movement => docNumber == default || movement.GUID.Equals(docNumber))
 2362            .AsQueryable();
 363
 364        /// <summary>
 365        /// Создание документа
 366        /// </summary>
 367        /// <param name="movement">Документ</param>
 368        /// <returns>Task</returns>
 369        public async Task CreateMovement(Movement movement, string envName)
 167370        {
 167371            if (movement.Parent != null)
 71372                _db.Entry(movement.Parent).State = EntityState.Unchanged;
 167373            _db.Entry(movement.MovementStatus).State = EntityState.Unchanged;
 167374            _db.Entry(movement.MovementType).State = EntityState.Unchanged;
 167375            _db.Entry(movement.Supplier).State = EntityState.Unchanged;
 167376            if (movement.Sender != null)
 167377                _db.Entry(movement.Sender).State = EntityState.Unchanged;
 167378            if (movement.Receiver.Id != movement.Customer.Id)
 0379            {
 0380                _db.Entry(movement.Receiver).State = EntityState.Unchanged;
 0381            }
 167382            if (movement.RecState != null)
 134383                _db.Entry(movement.RecState).State = EntityState.Unchanged;
 384
 167385            await _db.Movements.AddAsync(movement);
 167386            await _db.SaveChangesAsync(CurrentUserId);
 167387            movement.CreateDocumentNumber(envName);
 167388            await _db.SaveChangesAsync(CurrentUserId);
 167389        }
 390
 391        /// <summary>
 392        /// Обновление документа
 393        /// </summary>
 394        /// <param name="movement"></param>
 395        /// <returns>Task</returns>
 396        public async Task UpdateMovement(Movement data)
 340397        {
 340398            if (!(await MovementExists(data.Id)))
 0399            {
 0400                throw new ArgumentException($"Документ с идентификатором #{data.Id} не найден");
 401            }
 402
 340403            if (data.Items != null && data.Items.Count > 0)
 339404            {
 1368405                for (int i = 0; i < data?.Items.Count; i++)
 345406                {
 345407                    _db.Entry(data?.Items[i]?.RecState).State = EntityState.Unchanged;
 345408                }
 339409            }
 410
 340411            if (data.Notes != null && data.Notes.Count > 0)
 34412            {
 136413                for (int i = 0; i < data.Notes.Count; i++)
 34414                {
 34415                    _db.Entry(data.Notes[i]).State = data.Notes[i].Id == 0 ? EntityState.Added : EntityState.Unchanged;
 34416                }
 34417            }
 340418            _db.Movements.Update(data);
 340419            await _db.SaveChangesAsync(CurrentUserId);
 340420        }
 421
 422        /// <summary>
 423        /// Удаление элемента из документа
 424        /// </summary>
 425        /// <param name="item">Объект элемента</param>
 426        /// <returns>Task</returns>
 427        public async Task DeleteMovementItem(MovementItem item)
 3428        {
 3429            _db.Entry(item).State = EntityState.Deleted;
 3430            await _db.SaveChangesAsync(CurrentUserId);
 3431        }
 432
 433        /// <summary>
 434        /// Удаление документа или его пометка на удаление
 435        /// </summary>
 436        /// <param name="id">Идентификатор документа</param>
 437        /// <returns>Task</returns>
 438        public async Task DeleteMovement(long id)
 1439        {
 1440            Movement movement = await _db.Movements.FindAsync(id);
 1441            if (movement == null)
 0442                throw new KeyNotFoundException($"Record #{id} not found");
 1443            movement.IsDeleted = true;
 1444            await _db.SaveChangesAsync(CurrentUserId);
 1445        }
 446        /// <summary>
 447        /// Удаление документа или его пометка на удаление
 448        /// </summary>
 449        /// <param name="id">Идентификатор документа</param>
 450        /// <returns>Task</returns>
 451        public async Task DeleteMovement(Movement movement)
 5452        {
 5453            movement.IsDeleted = true;
 5454            await _db.SaveChangesAsync(CurrentUserId);
 5455        }
 456
 457        /// <summary>
 458        /// Проверка на существование записи в бд с таким идентификатором
 459        /// </summary>
 460        /// <param name="id">Идентификатор документа</param>
 461        /// <returns>Task<bool></returns>
 341462        public async Task<bool> MovementExists(long id) => await _db.Movements
 341463            .Where(d => d.Id == id)
 341464            .Where(e => !e.IsDeleted)
 341465            .AnyAsync();
 466
 467        /// <summary>
 468        /// Сортирует документы по полям
 469        /// </summary>
 470        /// <param name="movements"></param>
 471        /// <param name="sort">сортировка по умолчанию по id - id|desc,created_on,created_on|desc,state,state|desc,statu
 472        /// <returns></returns>
 6473        public IQueryable<Movement> SortMovement(IQueryable<Movement> movements, string sort = default) => (sort ?? "").
 6474        {
 6475            "inqueuetransferdate" => movements.OrderBy(s => s.MovementStatusJournals.Count > 0  && s.MovementStatusJourn
 6476                .Any(d => d.StatusCurrent.Id == (long)MovementsStatus.InQueue)
 6477                ? s.MovementStatusJournals
 6478                    .FirstOrDefault(d => d.StatusCurrent.Id == (long)MovementsStatus.InQueue)
 6479                    .CreationDateTime
 6480                : s.CreationDateTime),
 6481            "inqueuetransferdate|desc" => movements.OrderByDescending(s => s.MovementStatusJournals.Count > 0  && s.Move
 6482                    .Any(d => d.StatusCurrent.Id == (long)MovementsStatus.InQueue)
 6483                    ? s.MovementStatusJournals
 6484                        .FirstOrDefault(d => d.StatusCurrent.Id == (long)MovementsStatus.InQueue)
 6485                        .CreationDateTime
 6486                    : s.CreationDateTime),
 6487            "created_on" => movements.OrderBy(d => d.CreationDateTime),
 6488            "created_on|desc" => movements.OrderByDescending(d => d.CreationDateTime),
 6489            "state" => movements.OrderBy(d => d.RecState.Id),
 6490            "state|desc" => movements.OrderByDescending(d => d.RecState.Id),
 6491            "status" => movements.OrderBy(d => d.MovementStatus.Id),
 6492            "status|desc" => movements.OrderByDescending(d => d.MovementStatus.Id),
 6493            "customer" => movements.OrderBy(d => d.Customer.ShortName),
 6494            "customer|desc" => movements.OrderByDescending(d => d.Customer.ShortName),
 6495            "supplier" => movements.OrderBy(d => d.Supplier.ShortName),
 6496            "supplier|desc" => movements.OrderByDescending(d => d.Supplier.ShortName),
 6497            "receiver" => movements.OrderBy(d => d.Receiver.Name),
 6498            "receiver|desc" => movements.OrderByDescending(d => d.Receiver.Name),
 6499            "id|desc" => movements.OrderByDescending(d => d.Id),
 6500            _ => movements.OrderBy(d => d.Id),
 6501        };
 502
 503        /// <summary>
 504        /// Строит запрос для получения списка вложений в документ
 505        /// </summary>
 506        /// <param name="id"></param>
 507        /// <returns></returns>
 0508        private IQueryable<Movement> PrepareAttachmentMovement() => _db.Movements
 0509            .Include(d => d.Customer)
 0510            .ThenInclude(customer => customer.ContractsAsBuyer)
 0511            .ThenInclude(contract => contract.Seller)
 0512            .Include(d => d.Supplier)
 0513            .Include(d => d.Receiver)
 0514            .Include(d => d.MovementStatus)
 0515            .Include(d => d.MovementType)
 0516            .Include(d => d.RecState)
 0517            .Include(d => d.Sender)
 0518            .Include(d => d.MovementAttachments)
 0519            .Where(e => !e.IsDeleted)
 0520            .AsQueryable();
 521        public Dictionary<long, List<MovementItem>> GetMovementItemsByMovements(List<long> movementIds)
 5522        {
 5523            return _db.MovementItems
 5524                .Include(Items => Items.Good)
 5525                    .ThenInclude(Good => Good.DefaultBarCode)
 5526                .Include(Item => Item.Good)
 5527                    .ThenInclude(Good => Good.DepartmentGoodSettings)
 5528                        .ThenInclude(DepartmentGoodSetting => DepartmentGoodSetting.Department)
 5529                .Include(d => d.Good)
 5530                    .ThenInclude(Good => Good.GoodBarcodes)
 5531                        .ThenInclude(barcode => barcode.BarCode)
 5532                .Include(Items => Items.Good)
 5533                    .ThenInclude(Good => Good.VatsKind)
 5534                .Where(x => !x.IsDeleted && movementIds.Contains(x.MovementId.Value))
 5535                .AsNoTracking()
 5536                .ToList()
 8537                .GroupBy(x => x.MovementId.Value)
 11538                .ToDictionary(x => x.Key, x => x.ToList());
 5539        }
 540
 248541        private IQueryable<Movement> PrepareMovementForNextStatus() => _db.Movements
 248542            .Include(d => d.Items)
 248543            .Include(d => d.DeliveryType)
 248544            .Include(d => d.Customer)
 248545            .ThenInclude(customer => customer.ContractsAsBuyer)
 248546            .ThenInclude(contract => contract.Seller)
 248547            .Include(d => d.Supplier)
 248548            .Include(d => d.Receiver)
 248549            .ThenInclude(receiver => receiver.Cluster)
 248550            .Include(d => d.MovementStatus)
 248551            .Include(d => d.MovementType)
 248552            .Include(d => d.RecState)
 248553            .Include(d => d.Sender)
 248554            .Include(d => d.Notes)
 248555            .Where(e => !e.IsDeleted)
 248556            .AsQueryable();
 557
 61558        private IQueryable<Movement> PrepareForMovementItem() =>  _db.Movements
 61559            .Include(d => d.Items)
 61560            .ThenInclude(Item => Item.Good)
 61561            .ThenInclude(Good => Good.DepartmentGoodSettings)
 61562            .ThenInclude(DepartmentGoodSetting => DepartmentGoodSetting.Department)
 61563            .Include(d => d.Items)
 61564            .ThenInclude(Items => Items.Good)
 61565            .ThenInclude(x => x.Prices)
 61566            .ThenInclude(x => x.PriceTrend)
 61567            .ThenInclude(x => x.SupplierDepartment)
 61568            .Include(d => d.Items)
 61569            .ThenInclude(Item => Item.Good)
 61570            .ThenInclude(Good => Good.VatsKind)
 61571            .Include(d => d.Items)
 61572            .ThenInclude(Items => Items.Good)
 61573            .ThenInclude(Good => Good.DefaultBarCode)
 61574            .Include(d => d.Items)
 61575            .ThenInclude(Items => Items.Good)
 61576            .ThenInclude(Good => Good.GoodBarcodes)
 61577            .ThenInclude(barcode =>  barcode.BarCode)
 61578            .Include(d => d.Items)
 61579            .ThenInclude(Items => Items.Good)
 61580            .ThenInclude(good => good.Category)
 61581            .ThenInclude(category => category.DepartmentCategoryRatios)
 61582            .Include(d => d.Items)
 61583            .ThenInclude(Item => Item.Good)
 61584            .ThenInclude(Good => Good.Photos)
 61585            .Include(d => d.Customer)
 61586            .ThenInclude(customer => customer.ContractsAsBuyer)
 61587            .ThenInclude(contract => contract.Seller)
 61588            .Include(d => d.Supplier)
 61589            .Include(d => d.Receiver)
 61590            .ThenInclude(receiver => receiver.Cluster)
 61591            .Include(d => d.MovementStatus)
 61592            .Include(d => d.MovementType)
 61593            .Include(d => d.RecState)
 61594            .Include(d => d.Sender)
 61595            .Include(d => d.Notes)
 61596            .Where(e => !e.IsDeleted)
 61597            .AsQueryable();
 598
 599
 600
 601        /// <summary>
 602        /// Возвращает запрос с документами и всеми их полями кроме Items. НЕ ДОБАВЛЯЙТЕ СЮДА ЕЩЕ И ВОЗВРАЩЕНИЕ ITEMS, В
 603        /// ВМЕСТО ЭТОГО ИСПОЛЬЗУЙТЕ МЕТОД FillMovementsItems
 604        /// </summary>
 605        /// <param name="kind"></param>
 606        /// <returns></returns>
 51607        private IQueryable<Movement> PrepareListMovement(MovementType kind) => _db.Movements
 51608            .Include(d => d.Items)
 51609            .Include(d => d.Children)
 51610                .ThenInclude(children => children.MovementStatus)
 51611            .Include(d => d.Parent)
 51612                .ThenInclude(parent => parent.MovementStatus)
 51613            .Include(d => d.Customer)
 51614                .ThenInclude(customer => customer.JuridicAddress)
 51615            .Include(d => d.Customer)
 51616                .ThenInclude(Customer => Customer.PhysicAddress)
 51617            .Include(d => d.Supplier)
 51618                .ThenInclude(supplier => supplier.JuridicAddress)
 51619            .Include(d => d.Receiver)
 51620                .ThenInclude(receiver => receiver.ActualAddress)
 51621            .Include(d => d.Receiver)
 51622                .ThenInclude(receiver => receiver.Cluster)
 51623            .Include(d => d.Receiver)
 51624                .ThenInclude(receiver => receiver.Contragent)
 51625                    .ThenInclude(supplier => supplier.JuridicAddress)
 51626            .Include(d => d.MovementStatus)
 51627            .Include(d => d.MovementStatus)
 51628                .ThenInclude(d => d.MovementType)
 51629            .Include(d => d.Sender)
 51630                .ThenInclude(Sender => Sender.ActualAddress)
 51631            .Include(d => d.MovementType)
 51632            .Include(d => d.RecState)
 51633            .Include(d => d.Notes)
 51634                .ThenInclude(Notes => Notes.CreatedByUser)
 51635            .Include(d => d.Children)
 51636            .Include(d => d.MovementStatusJournals)
 51637                .ThenInclude(m => m.StatusCurrent)
 51638            .Include(d => d.DeliveryType)
 51639            .Include(d => d.MovementAttachments)
 51640            .Where(d => !d.IsDeleted && (kind == null || d.MovementType.Id == kind.Id))
 51641            .Where(d => d.Items.Count > 0)
 51642            .AsNoTracking();
 643
 644        /// <summary>
 645        /// Подоготовка записей к выдаче
 646        /// </summary>
 647        /// <param name="kind">Тип документа</param>
 648        /// <returns></returns>
 145649        private IQueryable<Movement> PrepareMovement(MovementType kind) => _db.Movements
 145650            .Include(d => d.Items)
 145651            .ThenInclude(Items => Items.Good)
 145652            .ThenInclude(Good => Good.DefaultBarCode)
 145653            .Include(d => d.Items)
 145654            .ThenInclude(Items => Items.Good)
 145655            .ThenInclude(Good => Good.GoodBarcodes)
 145656            .ThenInclude(barcode =>  barcode.BarCode)
 145657            .Include(d => d.Items)
 145658            .ThenInclude(Items => Items.Good)
 145659            .ThenInclude(Good => Good.VatsKind)
 145660            .Include(d => d.Items)
 145661            .ThenInclude(Item => Item.Good)
 145662            .ThenInclude(Good => Good.DepartmentGoodSettings)
 145663            .ThenInclude(DepartmentGoodSetting => DepartmentGoodSetting.Department)
 145664            .Include(d => d.Items)
 145665            .ThenInclude(Items => Items.Good)
 145666            .ThenInclude(x => x.Prices)
 145667            .ThenInclude(x => x.PriceTrend)
 145668            .ThenInclude(x => x.SupplierDepartment)
 145669            .Include(d => d.Items)
 145670            .ThenInclude(Items => Items.Good)
 145671            .ThenInclude(good => good.Category)
 145672            .ThenInclude(category => category.DepartmentCategoryRatios)
 145673            .Include(d => d.Customer)
 145674            .ThenInclude(customer => customer.JuridicAddress)
 145675            .Include(d => d.Customer)
 145676            .ThenInclude(customer => customer.ContractsAsBuyer)
 145677            .Include(d => d.Customer)
 145678            .ThenInclude(Customer => Customer.PhysicAddress)
 145679            .Include(d => d.Supplier)
 145680            .ThenInclude(supplier => supplier.JuridicAddress)
 145681            .Include(d => d.Receiver)
 145682            .ThenInclude(receiver => receiver.ActualAddress)
 145683            .Include(d => d.Receiver)
 145684            .ThenInclude(receiver => receiver.Cluster)
 145685            .Include(d => d.MovementStatus)
 145686            .Include(d => d.MovementStatus)
 145687            .ThenInclude(d => d.MovementType)
 145688            .Include(d => d.Sender)
 145689            .ThenInclude(Sender => Sender.ActualAddress)
 145690            .Include(d => d.MovementType)
 145691            .Include(d => d.RecState)
 145692            .Include(d => d.Notes)
 145693            .ThenInclude(Notes => Notes.CreatedByUser)
 145694            .Include(d => d.Children)
 145695            .Include(d => d.MovementStatusJournals)
 145696            .ThenInclude(m => m.StatusCurrent)
 145697            .Include(d => d.DeliveryType)
 145698            .Where(d => !d.IsDeleted && (kind == null || d.MovementType.Id == kind.Id));
 699
 700        /// <summary>
 701        /// Фильтрует документы по полям
 702        /// </summary>
 703        /// <param name="movements"></param>
 704        /// <param name="customerId">Идентификтор заказчика</param>
 705        /// <param name="receiverId">Идентификтор получателя</param>
 706        /// <param name="supplierId">Идентификтор поставщика</param>
 707        /// <param name="fromDate">Дата начала отбора </param>
 708        /// <param name="toDate">Дата окончания отбора</param>
 709        /// <param name="status">Статус документа - 0 для всех</param>
 710        /// <param name="state">Состояние документа - 0 для всех</param>
 711        /// <param name="documentNumber"></param>
 712        /// <param name="excludedStatuses"></param>
 713        /// <returns></returns>
 714        private IQueryable<Movement> FilterMovement(IQueryable<Movement> movements, long customerId = 0,
 715            long receiverId = 0, long supplierId = 0, DateTime fromDate = default,
 716            DateTime toDate = default, MovementStatus status = null, RecordsState state = null,
 717            string documentNumber = null, long[] excludedStatuses = null, string contragentBuyerFilter = default, long s
 53718            movements
 53719                .Where(e => !e.IsDeleted)
 53720                .Where(d => state == null || d.RecState.Id == state.Id)
 53721                .Where(d => status == null || d.MovementStatus.Id == status.Id)
 53722                .Where(d => excludedStatuses == null || !excludedStatuses.Contains(d.MovementStatus.Id))
 53723                .Where(d => customerId == 0 || d.Customer.Id == customerId)
 53724                .Where(d => receiverId == 0 || d.Receiver.Id == receiverId)
 53725                .Where(d => supplierId == 0 || d.Supplier.Id == supplierId)
 53726                .Where(d => senderId == 0 || d.Sender.Id == senderId)
 53727                .Where(d => fromDate == DateTime.MinValue || d.CreationDateTime >= fromDate)
 53728                .Where(d => toDate == DateTime.MinValue || d.CreationDateTime <= toDate)
 53729                .Where(x => string.IsNullOrWhiteSpace(documentNumber) ||
 53730                            x.DocumentNumber.ToLower().Contains(documentNumber.ToLower()))
 53731                .Where(d => string.IsNullOrEmpty(contragentBuyerFilter) ||
 53732                            (d.Customer.ShortName.ToLower().Contains(contragentBuyerFilter.ToLower()) ||
 53733                             d.Customer.FullName.ToLower().Contains(contragentBuyerFilter)));
 734
 42735        private IQueryable<Movement> PrepareOnlyMovement() => _db.Movements
 42736            .Include(d => d.Items)
 42737            .ThenInclude(item => item.Good)
 42738            .Include(d => d.Parent)
 42739            .Include(d => d.Customer)
 42740            .ThenInclude(customer => customer.PhysicAddress)
 42741            .Include(d => d.Supplier)
 42742            .Include(d => d.Sender)
 42743            .Include(d => d.Receiver)
 42744            .Include(d => d.RecState)
 42745            .Include(d => d.MovementStatus)
 42746            .ThenInclude(status => status.StatusOwner)
 42747            .Include(d => d.MovementType)
 42748            .Include(d => d.Children)
 42749            .Include(d => d.DeliveryType)
 42750            .Where(d => !d.IsDeleted)
 42751            .AsQueryable();
 752
 16753        private IQueryable<Movement> PrepareForSingle() => _db.Movements
 16754            .Include(d => d.Items)
 16755            .ThenInclude(Item => Item.Good)
 16756            .ThenInclude(Good => Good.DepartmentGoodSettings)
 16757            .ThenInclude(DepartmentGoodSetting => DepartmentGoodSetting.Department)
 16758            .Include(d => d.Items)
 16759            .ThenInclude(Items => Items.Good)
 16760            .ThenInclude(good => good.Brand)
 16761            .Include(d => d.Items)
 16762            .ThenInclude(Items => Items.Good)
 16763            .ThenInclude(x => x.Prices)
 16764            .ThenInclude(x => x.PriceTrend)
 16765            .ThenInclude(x => x.SupplierDepartment)
 16766            .Include(d => d.Items)
 16767            .ThenInclude(Item => Item.Good)
 16768            .ThenInclude(Good => Good.VatsKind)
 16769            .Include(d => d.Items)
 16770            .ThenInclude(Item => Item.Good)
 16771            .ThenInclude(Good => Good.Country)
 16772            .Include(d => d.Items)
 16773            .ThenInclude(Item => Item.Good)
 16774            .ThenInclude(Good => Good.UnitsKind)
 16775            .Include(d => d.Items)
 16776            .ThenInclude(Items => Items.Good)
 16777            .ThenInclude(Good => Good.DefaultBarCode)
 16778            .Include(d => d.Items)
 16779            .ThenInclude(Items => Items.Good)
 16780            .ThenInclude(Good => Good.GoodBarcodes)
 16781            .ThenInclude(barcode =>  barcode.BarCode)
 16782            .Include(d => d.Items)
 16783            .ThenInclude(Items => Items.Good)
 16784            .ThenInclude(good => good.Category)
 16785            .ThenInclude(category => category.DepartmentCategoryRatios)
 16786            .Include(d => d.Items)
 16787            .ThenInclude(Item => Item.Good)
 16788            .ThenInclude(good => good.Rests)
 16789            .Include(d => d.Items)
 16790            .ThenInclude(Item => Item.Good)
 16791            .ThenInclude(Good => Good.Photos)
 16792            .Include(d => d.Parent)
 16793            .ThenInclude(parent => parent.MovementStatus)
 16794            .Include(d => d.Customer)
 16795            .ThenInclude(Customer => Customer.JuridicAddress)
 16796            .Include(d => d.Customer)
 16797            .ThenInclude(Customer => Customer.PhysicAddress)
 16798            .Include(d => d.Customer)
 16799            .ThenInclude(Customer => Customer.Owner)
 16800            .Include(d => d.Customer)
 16801            .ThenInclude(customer => customer.ContractsAsBuyer)
 16802            .ThenInclude(contract => contract.Seller)
 16803            .Include(d => d.Supplier)
 16804            .ThenInclude(Supplier => Supplier.JuridicAddress)
 16805            .Include(d => d.Receiver)
 16806            .ThenInclude(Receiver => Receiver.ActualAddress)
 16807            .Include(d => d.Receiver)
 16808            .ThenInclude(receiver => receiver.Cluster)
 16809            .ThenInclude(Cluster=>Cluster.ClusterDeliveryTypes)
 16810            .ThenInclude(ClusterDeliveryTypes => ClusterDeliveryTypes.DeliveryType)
 16811            .Include(d => d.MovementStatus)
 16812            .ThenInclude(d => d.StatusOwner)
 16813            .Include(d => d.MovementStatus)
 16814            .ThenInclude(d => d.MovementType)
 16815            .Include(d => d.MovementType)
 16816            .Include(d => d.RecState)
 16817            .Include(d => d.Sender)
 16818            .ThenInclude(Sender => Sender.ActualAddress)
 16819            .Include(d => d.Notes)
 16820            .ThenInclude(Note => Note.CreatedByUser)
 16821            .Include(d => d.Children)
 16822            .ThenInclude(c => c.Items)
 16823            .ThenInclude(i => i.Good)
 16824            .Include(d => d.MovementStatusJournals)
 16825            .ThenInclude(m => m.StatusCurrent)
 16826            .Include(d => d.DeliveryType)
 16827            .Where(e => !e.IsDeleted)
 16828            .AsQueryable();
 829    }
 830}