| | | 1 | | using AutoMapper; |
| | | 2 | | using Microsoft.AspNetCore.Http; |
| | | 3 | | using Microsoft.Extensions.Logging; |
| | | 4 | | using SVETA.Api.Data.Domain; |
| | | 5 | | using SVETA.Api.Data.DTO; |
| | | 6 | | using SVETA.Api.Data.DTO.Movements; |
| | | 7 | | using SVETA.Api.Helpers.Authorize; |
| | | 8 | | using SVETA.Api.Services.Interfaces; |
| | | 9 | | using System; |
| | | 10 | | using System.Collections.Concurrent; |
| | | 11 | | using System.Collections.Generic; |
| | | 12 | | using System.Diagnostics; |
| | | 13 | | using System.Globalization; |
| | | 14 | | using System.IO; |
| | | 15 | | using System.IO.Compression; |
| | | 16 | | using System.Linq; |
| | | 17 | | using System.Linq.Expressions; |
| | | 18 | | using System.Text; |
| | | 19 | | using System.Threading; |
| | | 20 | | using System.Threading.Tasks; |
| | | 21 | | using CrmVtbc; |
| | | 22 | | using DocumentFormat.OpenXml.Wordprocessing; |
| | | 23 | | using IdentityServer4.Extensions; |
| | | 24 | | using Microsoft.AspNetCore.Mvc; |
| | | 25 | | using Microsoft.AspNetCore.Routing; |
| | | 26 | | using Microsoft.EntityFrameworkCore; |
| | | 27 | | using Microsoft.EntityFrameworkCore.Internal; |
| | | 28 | | using Microsoft.Extensions.Hosting; |
| | | 29 | | using Microsoft.Extensions.Options; |
| | | 30 | | using Newtonsoft.Json; |
| | | 31 | | using SVETA.Api.Data.DTO.Cluster; |
| | | 32 | | using System.Text.Json; |
| | | 33 | | using Microsoft.Extensions.Configuration; |
| | | 34 | | using SkiaSharp; |
| | | 35 | | using SVETA.Api.Data.DTO.DepartmentDTO; |
| | | 36 | | using SVETA.Api.Helpers; |
| | | 37 | | using TidVtbc; |
| | | 38 | | using WinSolutions.Sveta.Server.Data.DataModel.Entities; |
| | | 39 | | using WinSolutions.Sveta.Server.Data.DataModel.Kinds; |
| | | 40 | | using WinSolutions.Sveta.Server.Domain; |
| | | 41 | | using WinSolutions.Sveta.Server.Services.Interfaces; |
| | | 42 | | using Wkhtmltopdf.NetCore; |
| | | 43 | | using WinSolutions.Sveta.Common; |
| | | 44 | | using WinSolutions.Sveta.Common.Extensions; |
| | | 45 | | using WinSolutions.Sveta.Server.Data.DataModel.Extensions; |
| | | 46 | | |
| | | 47 | | namespace SVETA.Api.Services.Implements |
| | | 48 | | { |
| | | 49 | | public class MovementWorker: IMovementWorker |
| | | 50 | | { |
| | | 51 | | private readonly ILogger<MovementWorker> _logger; |
| | | 52 | | private readonly IMovementService _movementService; |
| | | 53 | | private readonly IMovementTypeStatusService _movementTypeStatusService; |
| | | 54 | | private readonly IDepartmentService _departmentService; |
| | | 55 | | private readonly IDirectoriesService _dirService; |
| | | 56 | | private readonly ISupplyContractService _suplContractService; |
| | | 57 | | private readonly IUserService _userService; |
| | | 58 | | private readonly IGoodService _goodService; |
| | | 59 | | private readonly IContragentService _contragentService; |
| | | 60 | | private readonly IMovementStatusJournalService _movementStatusJournalService; |
| | | 61 | | private readonly IAuthenticationService _authUserService; |
| | | 62 | | private readonly IGeneratePdf _generatePdf; |
| | | 63 | | private readonly IEventService _eventService; |
| | | 64 | | private readonly INotificationWorker _notificationWorker; |
| | | 65 | | private readonly IExchangeTokenService _exchangeTokenService; |
| | | 66 | | private readonly IDiskStorageService _diskStorageService; |
| | | 67 | | private readonly CommonSettings _commonSettings; |
| | | 68 | | private readonly IMovementRouteActionsService _movementRouteActionsService; |
| | | 69 | | List<DiscountColor> discountColors; |
| | | 70 | | private Movement _currentMovement; |
| | | 71 | | private readonly IHostEnvironment _env; |
| | | 72 | | private readonly ImagesSettings _imagesSettings; |
| | | 73 | | private readonly IAnonymousMovementService _anonymousMovementService; |
| | | 74 | | private readonly ConfigurationsSettings _confSettings; |
| | 112 | 75 | | private long annonymContragentId = (long) -1; |
| | 1 | 76 | | public static SemaphoreSlim _createAnonymMovementSlim = new SemaphoreSlim(1); |
| | 1 | 77 | | public static SemaphoreSlim _createMovementSlim = new SemaphoreSlim(1); |
| | 1 | 78 | | public static SemaphoreSlim _addItemMovementSlim = new SemaphoreSlim(1); |
| | 1 | 79 | | public static SemaphoreSlim _changeQuantityMovementSlim = new SemaphoreSlim(1); |
| | | 80 | | |
| | 112 | 81 | | public MovementWorker(IMovementService movementService, |
| | 112 | 82 | | IAuthenticationService authUserService, |
| | 112 | 83 | | IMovementTypeStatusService movementTypeStatusService, |
| | 112 | 84 | | IDepartmentService departmentService, |
| | 112 | 85 | | ISupplyContractService suplContractService, |
| | 112 | 86 | | IGoodService goodService, |
| | 112 | 87 | | IUserService userService, |
| | 112 | 88 | | IDirectoriesService dirService, |
| | 112 | 89 | | IContragentService contragentService, |
| | 112 | 90 | | IMovementStatusJournalService movementStatusJournalService, |
| | 112 | 91 | | IGeneratePdf generatePdf, |
| | 112 | 92 | | INotificationWorker notificationWorker, |
| | 112 | 93 | | IExchangeTokenService exchangeTokenService, |
| | 112 | 94 | | IDiscountColorService discountColorService, |
| | 112 | 95 | | IDiskStorageService diskStorageService, |
| | 112 | 96 | | IRestHoldService restHoldService, |
| | 112 | 97 | | IHostEnvironment env, |
| | 112 | 98 | | IOptions<CommonSettings> options, |
| | 112 | 99 | | IOptions<ImagesSettings> imageOptions, |
| | 112 | 100 | | IMovementRouteActionsService movementRouteActionsService, |
| | 112 | 101 | | IAnonymousMovementService anonymousMovementService, |
| | 112 | 102 | | IEventService eventService, |
| | 112 | 103 | | IOptions<ConfigurationsSettings> confSettings, |
| | 112 | 104 | | ILogger<MovementWorker> logger) |
| | 112 | 105 | | { |
| | 112 | 106 | | _env = env; |
| | 112 | 107 | | _eventService = eventService; |
| | 112 | 108 | | _movementService = movementService; |
| | 112 | 109 | | _authUserService = authUserService; |
| | 112 | 110 | | _movementTypeStatusService = movementTypeStatusService; |
| | 112 | 111 | | _suplContractService = suplContractService; |
| | 112 | 112 | | _departmentService = departmentService; |
| | 112 | 113 | | _goodService = goodService; |
| | 112 | 114 | | _dirService = dirService; |
| | 112 | 115 | | _userService = userService; |
| | 112 | 116 | | _contragentService = contragentService; |
| | 112 | 117 | | _logger = logger; |
| | 112 | 118 | | _movementStatusJournalService = movementStatusJournalService; |
| | 112 | 119 | | _notificationWorker = notificationWorker; |
| | 112 | 120 | | _exchangeTokenService = exchangeTokenService; |
| | 112 | 121 | | _generatePdf = generatePdf; |
| | 112 | 122 | | _diskStorageService = diskStorageService; |
| | 112 | 123 | | discountColors = discountColorService.GetDiscountColors(0, int.MaxValue, null, null).Result.Result; |
| | 112 | 124 | | _commonSettings = options.Value; |
| | 112 | 125 | | _imagesSettings = imageOptions.Value; |
| | 112 | 126 | | _movementRouteActionsService = movementRouteActionsService; |
| | 112 | 127 | | _anonymousMovementService = anonymousMovementService; |
| | 112 | 128 | | _confSettings = confSettings.Value; |
| | 112 | 129 | | } |
| | | 130 | | |
| | | 131 | | /// <summary> |
| | | 132 | | /// Возвращает отфильтрованный и отсортированный список документов |
| | | 133 | | /// </summary> |
| | | 134 | | /// <param name="inParam">Объект с параметрами для фильтрации</param> |
| | | 135 | | /// <returns></returns> |
| | | 136 | | public async Task<PaginatedData<List<MovementDTO>>> GetMovements(MovementParam inParam) |
| | | 137 | | { |
| | | 138 | | //Проверим вошедшие параметры на возможность работы с ними, если что поменяем на пользовательские или ругнем |
| | | 139 | | inParam = await SetUserParam(inParam); |
| | | 140 | | |
| | | 141 | | var state = await GetRecordState(inParam.StateId); |
| | | 142 | | var status = await _movementTypeStatusService.GetMovementStatus(inParam.StatusId); |
| | | 143 | | var movementType = await GetMovementType((long)inParam.Kind); |
| | | 144 | | |
| | | 145 | | int total = 0, totalFiltered = 0; |
| | | 146 | | PaginatedData<List<Movement>> orders; |
| | | 147 | | IMapper mapper = ToDtoMapper(); |
| | | 148 | | |
| | | 149 | | var result = new PaginatedData<List<MovementDTO>> |
| | | 150 | | { |
| | | 151 | | TotalCount = 0, |
| | | 152 | | TotalFilteredCount = 0, |
| | | 153 | | Result = new List<MovementDTO>() |
| | | 154 | | }; |
| | | 155 | | |
| | | 156 | | var allActions = await _movementRouteActionsService.GetActions(); |
| | | 157 | | switch (inParam.ShowAnonym.ToLower()) |
| | | 158 | | { |
| | | 159 | | case "all": |
| | | 160 | | { |
| | | 161 | | var localLimit = inParam.Limit; |
| | | 162 | | var annonymList = await _anonymousMovementService.GetAnonymMovements(default, inParam.ReceiverId, |
| | | 163 | | inParam.SupplierId, inParam.SenderId, inParam.FromDate, inParam.ToDate, inParam.Sort, |
| | | 164 | | inParam.Page >= 1 ? inParam.Page - 1 : inParam.Page, inParam.Limit); |
| | | 165 | | if (annonymList.TotalFilteredCount < inParam.Limit) |
| | | 166 | | { |
| | | 167 | | localLimit += inParam.Limit - annonymList.TotalFilteredCount; |
| | | 168 | | } |
| | | 169 | | orders = await _movementService.GetFilterMovements(inParam.CustomerId, inParam.ReceiverId, |
| | | 170 | | inParam.SupplierId, inParam.FromDate, inParam.ToDate, movementType, status, state, inParam.Docum |
| | | 171 | | inParam.Page >= 1 ? inParam.Page - 1 : inParam.Page, |
| | | 172 | | localLimit, inParam.Sort, inParam.ExcludedStatuses, inParam.СontragentBuyerFilter, inParam.Sende |
| | | 173 | | |
| | | 174 | | total += orders.TotalCount + annonymList.TotalCount; |
| | | 175 | | totalFiltered += orders.TotalFilteredCount + annonymList.TotalFilteredCount; |
| | | 176 | | if (!inParam.GenerateExcel) |
| | | 177 | | { |
| | 0 | 178 | | orders.Result.ForEach(d => d.FillActions(allActions, |
| | 0 | 179 | | _authUserService.ContragentKindId, |
| | 0 | 180 | | _authUserService.Roles)); |
| | | 181 | | orders.Result = await PrepareStatusList(orders.Result, movementType); |
| | | 182 | | } |
| | | 183 | | |
| | | 184 | | var list = mapper.Map<List<Movement>, List<MovementDTO>>(orders.Result); |
| | | 185 | | var anonymMovementList = new List<Movement>(); |
| | 0 | 186 | | annonymList.Result.ForEach(d => anonymMovementList.Add((Movement)d)); |
| | | 187 | | var anonymMapper = ToDtoMapper(true); |
| | | 188 | | list.AddRange(anonymMapper.Map<List<Movement>, List<MovementDTO>>(anonymMovementList)); |
| | | 189 | | var sortedList = SortMovement(list.AsQueryable(), inParam.Sort); |
| | | 190 | | result.TotalCount = total; |
| | | 191 | | result.TotalFilteredCount = totalFiltered; |
| | | 192 | | result.Result = sortedList.Skip((inParam.Page >= 1 ? inParam.Page - 1 : inParam.Page) * inParam.Limi |
| | | 193 | | .Take(inParam.Limit).ToList(); |
| | | 194 | | break; |
| | | 195 | | } |
| | | 196 | | case "anonymousonly": |
| | | 197 | | { |
| | | 198 | | var userId = _authUserService.IsAnonym() ? _authUserService.AnonymousUuid : default; |
| | | 199 | | if (_authUserService.IsAnonym() && userId == default) |
| | | 200 | | return result; |
| | | 201 | | var annonymList = await _anonymousMovementService.GetAnonymMovements(userId, inParam.ReceiverId, |
| | | 202 | | inParam.SupplierId, inParam.SenderId, inParam.FromDate, inParam.ToDate, inParam.Sort, |
| | | 203 | | inParam.Page, |
| | | 204 | | inParam.Limit); |
| | | 205 | | var anonymResult = new List<Movement>(); |
| | 0 | 206 | | annonymList.Result.ForEach(d => anonymResult.Add((Movement)d)); |
| | | 207 | | var anonymMapper = ToDtoMapper(true); |
| | | 208 | | var anonymMovementDto = anonymMapper.Map<List<Movement>, List<MovementDTO>>(anonymResult); |
| | | 209 | | result.Result = |
| | | 210 | | anonymMovementDto; |
| | | 211 | | result.TotalCount = annonymList.TotalCount; |
| | | 212 | | result.TotalFilteredCount = annonymList.TotalFilteredCount; |
| | | 213 | | break; |
| | | 214 | | } |
| | | 215 | | default: |
| | | 216 | | { |
| | | 217 | | orders = await _movementService.GetFilterMovements(inParam.CustomerId, inParam.ReceiverId, |
| | | 218 | | inParam.SupplierId, inParam.FromDate, inParam.ToDate, movementType, status, state, inParam.Docum |
| | | 219 | | inParam.Limit, inParam.Sort, inParam.ExcludedStatuses, inParam.СontragentBuyerFilter, inParam.Se |
| | | 220 | | result.TotalCount = orders.TotalCount; |
| | | 221 | | result.TotalFilteredCount = orders.TotalFilteredCount; |
| | | 222 | | if (orders.TotalFilteredCount > 0) |
| | | 223 | | { |
| | | 224 | | if (_authUserService.ContragentKindId == (long) ContragentKind.Retailer) |
| | | 225 | | { |
| | | 226 | | if (orders.Result.Any(d => d.MovementStatus.Id == (long) MovementsStatus.OrderDraft)) |
| | | 227 | | { |
| | | 228 | | var unionsMovement = new List<Movement>(); |
| | | 229 | | for (int i = 0; i < orders.Result.Count; i++) |
| | | 230 | | { |
| | | 231 | | var draft = orders.Result[i]; |
| | | 232 | | if (draft.MovementStatus.Id == (long) MovementsStatus.OrderDraft) |
| | | 233 | | { |
| | | 234 | | if (draft.Items.Count > 0) |
| | | 235 | | { |
| | | 236 | | var inner = (_movementService.GetMovementReadonly(draft.Id, movementType)).R |
| | | 237 | | try |
| | | 238 | | { |
| | | 239 | | inner.UpdatePriceInDraft(); |
| | | 240 | | } |
| | | 241 | | catch (Exception e) |
| | | 242 | | { |
| | | 243 | | _logger.LogError(e.Message); |
| | | 244 | | _logger.LogError($"Номер документа: {draft.Id}"); |
| | | 245 | | break; |
| | | 246 | | } |
| | | 247 | | |
| | | 248 | | inner.ReCalculate(); |
| | | 249 | | } |
| | | 250 | | } |
| | | 251 | | |
| | | 252 | | unionsMovement.Add(draft); |
| | | 253 | | } |
| | | 254 | | orders.Result = _movementService.SortMovement(unionsMovement.AsQueryable(), inParam.Sort |
| | | 255 | | .ToList(); |
| | | 256 | | } |
| | | 257 | | } |
| | | 258 | | |
| | | 259 | | if (!inParam.GenerateExcel) |
| | | 260 | | { |
| | 0 | 261 | | orders.Result.ForEach(d => d.FillActions(allActions, |
| | 0 | 262 | | _authUserService.ContragentKindId, |
| | 0 | 263 | | _authUserService.Roles)); |
| | | 264 | | orders.Result = await PrepareStatusList(orders.Result, movementType); |
| | | 265 | | } |
| | | 266 | | result.Result = mapper.Map<List<Movement>, List<MovementDTO>>(orders.Result); |
| | | 267 | | } |
| | | 268 | | break; |
| | | 269 | | } |
| | | 270 | | } |
| | | 271 | | return result; |
| | | 272 | | } |
| | | 273 | | |
| | | 274 | | /// <summary> |
| | | 275 | | /// Возвращает список анонимных заявок |
| | | 276 | | /// </summary> |
| | | 277 | | /// <param name="inParam">Параметры запроса</param> |
| | | 278 | | /// <returns></returns> |
| | | 279 | | public async Task<PaginatedData<List<MovementDTO>>> GetAnonymousMovements(MovementParam inParam) |
| | 0 | 280 | | { |
| | 0 | 281 | | inParam = await SetUserParam(inParam); |
| | | 282 | | PaginatedData<List<Movement>> orders; |
| | 0 | 283 | | IMapper mapper = ToDtoMapper(); |
| | 0 | 284 | | var movements = await _anonymousMovementService.GetAnonymMovements(_authUserService.AnonymousUuid, |
| | 0 | 285 | | inParam.ReceiverId, inParam.SupplierId, inParam.SenderId, inParam.FromDate, inParam.ToDate, inParam.Sort |
| | 0 | 286 | | inParam.Page, inParam.Limit); |
| | 0 | 287 | | if (movements.TotalCount == 0) |
| | 0 | 288 | | return new PaginatedData<List<MovementDTO>> {Result = new List<MovementDTO>()}; |
| | | 289 | | |
| | 0 | 290 | | var data = new List<Movement>(); |
| | 0 | 291 | | var allActions = await _movementRouteActionsService.GetActions(); |
| | 0 | 292 | | movements.Result.ForEach(d => |
| | 0 | 293 | | { |
| | 0 | 294 | | var movement = (Movement) d; |
| | 0 | 295 | | movement.FillActions(allActions, _authUserService.ContragentKindId, |
| | 0 | 296 | | _authUserService.Roles); |
| | 0 | 297 | | data.Add(movement); |
| | 0 | 298 | | }); |
| | | 299 | | |
| | 0 | 300 | | return new PaginatedData<List<MovementDTO>> |
| | 0 | 301 | | { |
| | 0 | 302 | | TotalCount = movements.TotalCount, |
| | 0 | 303 | | TotalFilteredCount = movements.TotalFilteredCount, |
| | 0 | 304 | | Result = mapper.Map<List<Movement>, List<MovementDTO>>(data) |
| | 0 | 305 | | }; |
| | 0 | 306 | | } |
| | | 307 | | |
| | | 308 | | /// <summary> |
| | | 309 | | /// Возвращает кол-во корзин анонима с указанием товарного состава, складов и возможных магазинов для слияния |
| | | 310 | | /// </summary> |
| | | 311 | | /// <returns></returns> |
| | | 312 | | public async Task<AnonymousMovementMergeDto> GetAnonymousCache() |
| | | 313 | | { |
| | | 314 | | var list = await _anonymousMovementService.GetAnonymMovements(_authUserService.AnonymousUuid); |
| | | 315 | | var result = new AnonymousMovementMergeDto |
| | | 316 | | { |
| | | 317 | | Total = 0, |
| | | 318 | | Cache = new List<AnonymousCache>() |
| | | 319 | | }; |
| | | 320 | | if (list.TotalFilteredCount == 0 || list.Result.All(d => d.Items.Count == 0)) |
| | | 321 | | return result; |
| | | 322 | | |
| | | 323 | | var departments = |
| | | 324 | | (await _departmentService.GetDepartmentsByUserAndKind(_authUserService.UserId, DepartmentKind.Shop, |
| | | 325 | | default)) |
| | | 326 | | .Where(d => d.Cluster != null) |
| | | 327 | | .Where(d => d.Contragent.ContractsAsBuyer |
| | | 328 | | .Where(s => s.BeginDate <= DateTime.UtcNow && s.EndDate >= DateTime.UtcNow) |
| | | 329 | | ?.Any() |
| | | 330 | | ?? false) |
| | | 331 | | .ToList(); |
| | | 332 | | |
| | | 333 | | var listWithoutEmptyCache = list.Result.Where(x => x.Items.Count > 0).ToList(); |
| | | 334 | | |
| | | 335 | | result.Cache = listWithoutEmptyCache.Select(item => |
| | 2 | 336 | | { |
| | 2 | 337 | | var itemMapper = ToItemDTOMapper(item.Sender.Id, discountColors); |
| | 3 | 338 | | item.Items.ForEach(g => g.Good.Photos.SetPhotoUrl(_imagesSettings)); |
| | 2 | 339 | | var elem = new AnonymousCache |
| | 2 | 340 | | { |
| | 2 | 341 | | |
| | 2 | 342 | | Id = item.Id, |
| | 2 | 343 | | Items = itemMapper.Map<List<MovementItem>, List<MovementItemResponseDTO>>(item.Items), |
| | 2 | 344 | | Receiver = new DepartmentShortDTO(item.Receiver), |
| | 2 | 345 | | Sender = new DepartmentShortDTO(item.Sender), |
| | 2 | 346 | | AvailableReceiver = departments |
| | 4 | 347 | | .Where(d => d.Cluster.WarehouseId == item.Sender.Id && d.Contragent.ContractsAsBuyer.FirstOrDefa |
| | | 348 | | .Select(d => new DepartmentShortDTO(d)).ToList(), |
| | 2 | 349 | | Reason = departments |
| | 4 | 350 | | .Where(d => d.Cluster.WarehouseId == item.Sender.Id && d.Contragent.ContractsAsBuyer.FirstOrDefa |
| | 2 | 351 | | ? "Товары из корзины не могут быть добавлены в заявку. Поставщик не обслуживает ваши подразделен |
| | 2 | 352 | | : default, |
| | 2 | 353 | | CanMerge = departments |
| | 4 | 354 | | .Where(d => d.Cluster.WarehouseId == item.Sender.Id && d.Contragent.ContractsAsBuyer.FirstOrDefa |
| | 2 | 355 | | }; |
| | 2 | 356 | | return elem; |
| | 2 | 357 | | }).ToList(); |
| | | 358 | | result.Total = listWithoutEmptyCache.Count(); |
| | | 359 | | return result; |
| | | 360 | | } |
| | | 361 | | |
| | | 362 | | /// <summary> |
| | | 363 | | /// Добавляет товар из анонимной заявки в заявку магазина |
| | | 364 | | /// </summary> |
| | | 365 | | /// <param name="orderId">Идентификатор анонимной заявки</param> |
| | | 366 | | /// <param name="goodId">Идентификатор товара для добавления</param> |
| | | 367 | | /// <param name="receiverId">Идентификатор магазина для добавления</param> |
| | | 368 | | /// <returns></returns> |
| | | 369 | | public async Task<MessageDto> AddAnonymousGoodToOrder(long orderId, long goodId, long receiverId) |
| | | 370 | | { |
| | | 371 | | var anonymMovement = await _anonymousMovementService.GetAnonymMovement(orderId) ?? |
| | | 372 | | throw new ArgumentException($"Не найдена анонимная заявка {orderId}"); |
| | | 373 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | | 374 | | if (((Movement) anonymMovement).CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | | 375 | | _authUserService.ContragentId, _authUserService.IsOwner(), userDeps, false, _authUserService.AnonymousUu |
| | | 376 | | { |
| | | 377 | | throw new ForbidException(); |
| | | 378 | | } |
| | | 379 | | var movement = await CreateMovement((long) MovementKind.Order, receiverId, 0); |
| | 2 | 380 | | var item = anonymMovement.Items.FirstOrDefault(d => d.Good.Id == goodId) |
| | | 381 | | ?? throw new ArgumentException($"Не найден товар {goodId} в анонимной заявке"); |
| | | 382 | | var activeState = await GetRecordState((long) RecordState.Active); |
| | | 383 | | movement.AddItem(item, activeState); |
| | | 384 | | movement.UpdatePriceInDraft(); |
| | | 385 | | movement.ReCalculate(); |
| | | 386 | | await _movementService.UpdateMovement(movement); |
| | 2 | 387 | | var addedItem = movement.Items.FirstOrDefault(d => d.Good.Id == goodId); |
| | | 388 | | if (item.Price != addedItem.Price) |
| | | 389 | | return new MessageDto { Body = $"Цена для товара \"{addedItem.Good.Name}\" изменилась." }; |
| | | 390 | | return default; |
| | | 391 | | } |
| | | 392 | | |
| | | 393 | | /// <summary> |
| | | 394 | | /// Очищает анонимную заявку |
| | | 395 | | /// </summary> |
| | | 396 | | /// <param name="movementId">Идентификатор анонимной заявки</param> |
| | | 397 | | /// <returns></returns> |
| | | 398 | | /// <exception cref="ForbidException"></exception> |
| | | 399 | | public async Task ClearAnonymousMovement(long movementId) |
| | 1 | 400 | | { |
| | 1 | 401 | | var anonymMovement = await _anonymousMovementService.GetAnonymMovement(movementId) |
| | 1 | 402 | | ?? throw new ArgumentException($"Не найдена анонимная заявка {movementId}"); |
| | 1 | 403 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 1 | 404 | | if (((Movement) anonymMovement).CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 1 | 405 | | _authUserService.ContragentId, _authUserService.IsOwner(), userDeps, false, _authUserService.AnonymousUu |
| | 0 | 406 | | { |
| | 0 | 407 | | throw new ForbidException(); |
| | | 408 | | } |
| | 1 | 409 | | anonymMovement.Items = new List<MovementItem>(); |
| | 1 | 410 | | anonymMovement.PrepaimentSum = 0; |
| | 1 | 411 | | await _anonymousMovementService.UpdateAnonymMovement(anonymMovement); |
| | 1 | 412 | | } |
| | | 413 | | |
| | | 414 | | /// <summary> |
| | | 415 | | /// Возвращает список документов со статусами для обмена с 1С |
| | | 416 | | /// </summary> |
| | | 417 | | /// <param name="tnx">Токен департамента в системе</param> |
| | | 418 | | /// <param name="from">Дата начала отбора - по умолчанию все</param> |
| | | 419 | | /// <param name="to">Дата окончания отбора - по умолчанию все</param> |
| | | 420 | | /// <param name="docNum">Номер документа для фильтрации</param> |
| | | 421 | | /// <returns></returns> |
| | | 422 | | public async Task<List<ExchangeMovementStatus>> GetShipmentStatusToExchange(Guid tnx, DateTime from, DateTime to |
| | 2 | 423 | | { |
| | 2 | 424 | | var token = await _exchangeTokenService.GetToken(tnx) |
| | 2 | 425 | | ?? throw new ArgumentException("Токен не найден"); |
| | 1 | 426 | | return await _movementService.GetMovementStatus(token.DepartmentId, from, to, docNum) |
| | 1 | 427 | | .Select(movement => new ExchangeMovementStatus |
| | 1 | 428 | | { |
| | 1 | 429 | | GUID = movement.GUID, |
| | 1 | 430 | | DocumentNumber = movement.DocumentNumber, |
| | 1 | 431 | | DtCreated = movement.CreationDateTime, |
| | 1 | 432 | | DtModified = movement.ModificationDateTime ?? DateTime.MinValue, |
| | 1 | 433 | | Status = movement.MovementStatus.Code, |
| | 1 | 434 | | DeliveryType = movement.DeliveryType == null ? default : movement.DeliveryType.Code |
| | 1 | 435 | | }) |
| | 1 | 436 | | .OrderBy(movement => movement.DtCreated) |
| | 1 | 437 | | .ToListAsync(); |
| | 1 | 438 | | } |
| | | 439 | | |
| | | 440 | | /// <summary> |
| | | 441 | | /// Возвращает список документов для обмена с 1С |
| | | 442 | | /// </summary> |
| | | 443 | | /// <param name="tnx">Токен департамента в системе</param> |
| | | 444 | | /// <param name="from">Дата начала отбора - по умолчанию сутки назад</param> |
| | | 445 | | /// <param name="to">Дата окончания отбора - по умолчанию текущий день</param> |
| | | 446 | | /// <param name="kind">Тип документов</param> |
| | | 447 | | /// <returns>Task<List<MovementExchnageResponseDto>></returns> |
| | | 448 | | /// <exception cref="ArgumentException"></exception> |
| | | 449 | | public async Task<List<MovementExchnageResponseDto>> GetMovementListToExchange(Guid tnx, DateTime from, DateTime |
| | 2 | 450 | | { |
| | 2 | 451 | | var token = await _exchangeTokenService.GetToken(tnx) |
| | 2 | 452 | | ?? throw new ArgumentException("Токен не найден"); |
| | 1 | 453 | | int diff = (to - from).Days; |
| | 1 | 454 | | if (diff > 10) |
| | 0 | 455 | | throw new ArgumentException("Интервал запроса слишком большой"); |
| | 1 | 456 | | var type = await GetMovementType((long) kind) ?? |
| | 1 | 457 | | throw new ArgumentException($"Не найден тип документа {kind}"); |
| | 1 | 458 | | var movements = |
| | 1 | 459 | | _movementService.GetFilterMovementsWithoutPagination(supplierId:token.Department.Contragent.Id, fromDate |
| | 1 | 460 | | _movementService.FillMovementsItems(movements); |
| | 1 | 461 | | var config = new MapperConfiguration(cfg => |
| | | 462 | | { |
| | | 463 | | cfg.CreateMap<Movement, MovementExchnageResponseDto>() |
| | | 464 | | .ForMember(d => d.DtModified, e => e.MapFrom(s => s.ModificationDateTime)) |
| | | 465 | | .ForMember(d => d.Status, e => e.MapFrom(s => s.MovementStatus.Code)) |
| | | 466 | | .ForMember(d => d.DtCreated, e => e.MapFrom(s => s.CreationDateTime)) |
| | | 467 | | .ForMember(d => d.Customer, e => e.MapFrom(s => s.Receiver)) |
| | | 468 | | .ForMember(d => d.Notes, e => e.MapFrom(s => s.Notes)) |
| | | 469 | | .ForMember(d => d.DeliveryType, e => e.MapFrom(s => s.DeliveryType.Code)); |
| | | 470 | | cfg.CreateMap<MovementItem, MovementItemExchangeResponseDto>() |
| | | 471 | | .ForMember(d => d.BarCode, e => e.MapFrom(s => s.Good.GetActualBarCode())) |
| | | 472 | | .ForMember(d => d.VendorCode, e => e.MapFrom(s => s.Good.GetActualVendorCode(s.Movement.Sender.Id))) |
| | | 473 | | .ForMember(d => d.UniqueCode, e => e.MapFrom(s => s.Good.UniqueCode)); |
| | | 474 | | cfg.CreateMap<Department, DepartmentExchangeResponseDto>() |
| | | 475 | | .ForMember(d => d.Inn, e => e.MapFrom(s => s.Contragent.Inn)) |
| | | 476 | | .ForMember(d => d.FullName, e => e.MapFrom(s => s.Name)) |
| | | 477 | | .ForMember(d => d.Phone, e => e.MapFrom(s => s.PhoneNumber)) |
| | | 478 | | .ForMember(d => d.Address, e => e.MapFrom(s => s.ActualAddress.FullAddress)) |
| | | 479 | | .ForMember(d => d.Kpp, e => e.MapFrom(s => s.Contragent.Kpp)) |
| | | 480 | | .ForMember(d => d.Ogrn, e => e.MapFrom(s => s.Contragent.Ogrn)) |
| | | 481 | | .ForMember(d => d.Okato, e => e.MapFrom(s => s.Contragent.Okato)) |
| | | 482 | | .ForMember(d => d.Okved, e => e.MapFrom(s => s.Contragent.Okved)) |
| | | 483 | | .ForMember(d => d.Okpo, e => e.MapFrom(s => s.Contragent.Okpo)) |
| | | 484 | | .ForMember(d => d.Taxsystem, e => e.MapFrom(s => s.Contragent.TaxSystemCRM)) |
| | | 485 | | .ForMember(d => d.Ownership, e => e.MapFrom(s => s.Contragent.Ownership)) |
| | | 486 | | .ForMember(d => d.JuridicAddress, e => e.MapFrom(s => s.Contragent.JuridicAddress.FullAddress)); |
| | | 487 | | cfg.CreateMap<MovementNote, MovementNoteExchangeDto>() |
| | | 488 | | .ForMember(d => d.DateTime, e => e.MapFrom(s => s.CreationDateTime)) |
| | | 489 | | .ForMember(d => d.User, e => e.MapFrom(s => s.CreatedByUser.Email)); |
| | | 490 | | }); |
| | 1 | 491 | | var mapper = config.CreateMapper(); |
| | 1 | 492 | | var mp = mapper.Map<List<Movement>, List<MovementExchnageResponseDto>>(movements); |
| | 1 | 493 | | return await Task.FromResult(mp); |
| | 1 | 494 | | } |
| | | 495 | | |
| | | 496 | | /// <summary> |
| | | 497 | | /// Возвращает количество документов определенного вида с фильтрацией |
| | | 498 | | /// </summary> |
| | | 499 | | /// <param name="inParam">Объект с параметрами для фильтрации</param> |
| | | 500 | | /// <returns></returns> |
| | | 501 | | public async Task<int> GetCountMovement(MovementParam inParam) |
| | 0 | 502 | | { |
| | 0 | 503 | | inParam = await SetUserParam(inParam); |
| | | 504 | | |
| | 0 | 505 | | var state = await GetRecordState(inParam.StateId); |
| | 0 | 506 | | var status = await _movementTypeStatusService.GetMovementStatus(inParam.StatusId); |
| | 0 | 507 | | var movementType = await GetMovementType((long)inParam.Kind); |
| | | 508 | | |
| | 0 | 509 | | return await _movementService.GetCountMovements(inParam.CustomerId, inParam.ReceiverId, inParam.SupplierId, |
| | 0 | 510 | | inParam.FromDate, inParam.ToDate, movementType, status, state, inParam.DocumentNumber, inParam.ExcludedS |
| | 0 | 511 | | } |
| | | 512 | | |
| | | 513 | | /// <summary> |
| | | 514 | | /// Возвращает документ по его id проверяет возможность работы с этим документом |
| | | 515 | | /// </summary> |
| | | 516 | | /// <param name="id">Идентфикатор документа</param> |
| | | 517 | | /// <param name="kind">Тип документа</param> |
| | | 518 | | /// <returns>Task<MovementWithNotesDTO></returns> |
| | | 519 | | public async Task<MovementDTO> GetMovement(long id, MovementKind kind, bool isAnonym = false) |
| | 52 | 520 | | { |
| | 52 | 521 | | var movementType = await GetMovementType((long) kind) ?? |
| | 52 | 522 | | throw new ArgumentException($"Не найден тип документа {(long) kind}"); |
| | 52 | 523 | | Movement movement = _authUserService.IsAnonym() || isAnonym |
| | 52 | 524 | | ? await _anonymousMovementService.GetAnonymMovement(id) |
| | 52 | 525 | | : await _movementService.GetMovementReadonly(id, movementType) |
| | 52 | 526 | | ?? throw new KeyNotFoundException($"Документ не найден"); |
| | | 527 | | |
| | 52 | 528 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 52 | 529 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 52 | 530 | | _authUserService.ContragentId, |
| | 52 | 531 | | _authUserService.IsOwner(), |
| | 52 | 532 | | userDeps, _authUserService.IsAnonym(), _authUserService.AnonymousUuid)) |
| | 0 | 533 | | throw new ForbidException(); |
| | | 534 | | |
| | 51 | 535 | | if (movement.MovementStatus.Id == (long) MovementsStatus.OrderDraft && _authUserService.IsUserRetailer()) |
| | 0 | 536 | | { |
| | 0 | 537 | | movement.UpdatePriceInDraft(); |
| | 0 | 538 | | movement.ReCalculate(); |
| | 0 | 539 | | } |
| | 51 | 540 | | var allActions = await _movementRouteActionsService.GetActions(); |
| | 51 | 541 | | movement.FillActions(allActions, _authUserService.ContragentKindId, _authUserService.Roles); |
| | 51 | 542 | | var result = ToDtoMapper(isAnonym).Map<Movement, MovementDTO>(movement); |
| | | 543 | | |
| | 51 | 544 | | return await Task.FromResult(result); |
| | 51 | 545 | | } |
| | | 546 | | |
| | | 547 | | /// <summary> |
| | | 548 | | /// Возвращает заказанные товары документа |
| | | 549 | | /// </summary> |
| | | 550 | | /// <param name="movementId">Идентификатор документа</param> |
| | | 551 | | /// <param name="sort">Сортировка элементов по умолчания по Id, name, name|desc, price, price|desc, created_in, |
| | | 552 | | /// <param name="page">Номер страницы</param> |
| | | 553 | | /// <param name="limit">Количество для отбора</param> |
| | | 554 | | /// <param name="kind"></param> |
| | | 555 | | /// <param name="filter"></param> |
| | | 556 | | /// <returns>Task<PaginatedData<List<MovementItemResponseDTO>>></returns> |
| | | 557 | | /// <exception cref="ForbidException">Нет прав для работы с эти документом</exception> |
| | | 558 | | public async Task<PaginatedData<List<MovementItemResponseDTO>>> GetMovementItems(long movementId, MovementKind k |
| | | 559 | | string filter, string sort, int page, int limit = 10) |
| | 6 | 560 | | { |
| | | 561 | | |
| | 6 | 562 | | MovementType type = await GetMovementType((long) kind) ?? |
| | 6 | 563 | | throw new ArgumentException($"Тип документа {kind} не найден"); |
| | 6 | 564 | | Movement movement = _authUserService.IsAnonym() ? await _anonymousMovementService.GetAnonymMovement(movement |
| | 6 | 565 | | await _movementService.GetMovementReadonly(movementId, type) |
| | 6 | 566 | | ?? throw new ArgumentException($"Документ {movementId} не найден"); |
| | | 567 | | |
| | 6 | 568 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 6 | 569 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 6 | 570 | | _authUserService.ContragentId, |
| | 6 | 571 | | _authUserService.IsOwner(), |
| | 6 | 572 | | userDeps)) |
| | 0 | 573 | | throw new ForbidException(); |
| | 6 | 574 | | _currentMovement = movement; |
| | 6 | 575 | | if (movement.Items == null || movement.Items.Count == 0) |
| | 0 | 576 | | { |
| | 0 | 577 | | return await Task.FromResult(new PaginatedData<List<MovementItemResponseDTO>> {Result = new List<Movemen |
| | | 578 | | } |
| | | 579 | | |
| | 6 | 580 | | if (movement.MovementStatus.Id == (long) MovementsStatus.OrderDraft && _authUserService.IsUserRetailer()) |
| | 0 | 581 | | { |
| | 0 | 582 | | movement.UpdatePriceInDraft(); |
| | 0 | 583 | | } |
| | | 584 | | |
| | 12 | 585 | | IQueryable<MovementItem> PrepareLocalItem() => movement.Items.Where(d => |
| | 32 | 586 | | string.IsNullOrWhiteSpace(filter) || d.Good.Name.ToLower().Contains(filter.ToLower())) |
| | 12 | 587 | | .AsQueryable(); |
| | | 588 | | |
| | 6 | 589 | | var slice = PrepareLocalItem().Select(d => new SliceMovementItem |
| | 6 | 590 | | { |
| | 6 | 591 | | Id = d.Id, |
| | 6 | 592 | | Good = d.Good, |
| | 6 | 593 | | Price = d.Price, |
| | 6 | 594 | | Quantity = d.Quantity, |
| | 6 | 595 | | Comment = d.Comment, |
| | 6 | 596 | | Sum = d.Price * d.Quantity, |
| | 6 | 597 | | RecState = d.RecState, |
| | 6 | 598 | | CreationDateTime = d.CreationDateTime |
| | 6 | 599 | | }); |
| | | 600 | | |
| | 6 | 601 | | Expression<Func<SliceMovementItem, object>> sortExpression = (sort?.Split("|").ElementAtOrDefault(0) ?? ""). |
| | 6 | 602 | | { |
| | 6 | 603 | | "name" => e => e.Good.Name, |
| | 6 | 604 | | "price" => e => e.Good.Prices.Actual(movement.Sender.Id).PriceNew, |
| | 6 | 605 | | "created_on" => e => e.CreationDateTime, |
| | 6 | 606 | | "vendorcode" => e => e.Good.GetActualVendorCode(movement.Sender.Id), |
| | 6 | 607 | | "quantity" => e => e.Quantity, |
| | 6 | 608 | | "totalndssum" => e => e.Sum, |
| | 6 | 609 | | _ => e => e.Id |
| | 6 | 610 | | }; |
| | 6 | 611 | | int totalCount = movement.Items.Count; |
| | | 612 | | |
| | | 613 | | |
| | 6 | 614 | | int filteredCount = PrepareLocalItem().Count(); |
| | | 615 | | |
| | 6 | 616 | | var result = (sort?.Split("|").ElementAtOrDefault(1) ?? "").ToLower() switch |
| | 6 | 617 | | { |
| | 6 | 618 | | "desc" => slice.OrderByDescending(sortExpression), |
| | 6 | 619 | | _ => slice.OrderBy(sortExpression) |
| | 6 | 620 | | }; |
| | | 621 | | |
| | 6 | 622 | | var itemsList = result.Skip((page < 2 ? 0 : page - 1) * limit).Take(limit).ToList(); |
| | 16 | 623 | | itemsList.ForEach(g => g.Good.Photos.SetPhotoUrl(_imagesSettings)); |
| | | 624 | | |
| | 6 | 625 | | var paginatedData = new PaginatedData<List<MovementItemResponseDTO>> |
| | 6 | 626 | | { |
| | 6 | 627 | | TotalCount = totalCount, |
| | 6 | 628 | | TotalFilteredCount = filteredCount, |
| | 6 | 629 | | Result = null |
| | 6 | 630 | | }; |
| | 6 | 631 | | var items = new List<MovementItem>(); |
| | 16 | 632 | | itemsList.ForEach(d => items.Add((MovementItem)d)); |
| | 6 | 633 | | var dtoItems = ToItemDTOMapper(movement.Sender.Id, discountColors).Map<List<MovementItemResponseDTO>>(items) |
| | 6 | 634 | | if (movement.Children != null) |
| | 0 | 635 | | { |
| | 0 | 636 | | Parallel.ForEach(dtoItems, item => |
| | 0 | 637 | | { |
| | 0 | 638 | | item.ShipmentQuantity = |
| | 0 | 639 | | movement.Children.Items.FirstOrDefault(d => d.Good.Id == item.Good.Id)?.Quantity ?? 0; |
| | 0 | 640 | | }); |
| | 0 | 641 | | } |
| | | 642 | | |
| | 6 | 643 | | paginatedData.Result = dtoItems; |
| | 6 | 644 | | return await Task.FromResult(paginatedData); |
| | 6 | 645 | | } |
| | | 646 | | |
| | | 647 | | /// <summary> |
| | | 648 | | /// Создание пустого документа |
| | | 649 | | /// </summary> |
| | | 650 | | /// <param name="mKind">Тип документа</param> |
| | | 651 | | /// <param name="departmentId">Подразделение на которое создается заявка</param> |
| | | 652 | | /// <param name="parentId">Родительский документ (например, для отгрузки это заявка)</param> |
| | | 653 | | /// <returns></returns> |
| | | 654 | | public async Task<MovementDTO> CreateEmptyMovement(long mKind, long departmentId, long parentId = 0) |
| | 60 | 655 | | { |
| | 60 | 656 | | await _createMovementSlim.WaitAsync(); |
| | | 657 | | try |
| | 60 | 658 | | { |
| | 60 | 659 | | var movement = await CreateMovement(mKind, departmentId, parentId); |
| | 60 | 660 | | var allActions = await _movementRouteActionsService.GetActions(); |
| | 60 | 661 | | movement.FillActions(allActions, _authUserService.ContragentKindId, _authUserService.Roles); |
| | 60 | 662 | | var result = ToDtoMapper().Map<Movement, MovementDTO>(movement); |
| | 60 | 663 | | return await Task.FromResult(result); |
| | | 664 | | } |
| | 0 | 665 | | catch (Exception e) |
| | 0 | 666 | | { |
| | 0 | 667 | | _logger.LogError(e.Message + " " + e.StackTrace); |
| | 0 | 668 | | throw; |
| | | 669 | | } finally |
| | 60 | 670 | | { |
| | 60 | 671 | | _createMovementSlim.Release(); |
| | 60 | 672 | | } |
| | | 673 | | |
| | 60 | 674 | | } |
| | | 675 | | |
| | | 676 | | /// <summary> |
| | | 677 | | /// Создание документа |
| | | 678 | | /// </summary> |
| | | 679 | | /// <param name="mKind">Тип документа</param> |
| | | 680 | | /// <param name="departmentId">идентификатор подразделения для которого создается документ</param> |
| | | 681 | | /// <param name="parentId">Идентификатор родителя</param> |
| | | 682 | | /// <returns></returns> |
| | | 683 | | /// <exception cref="ArgumentException"></exception> |
| | | 684 | | private async Task<Movement> CreateMovement(long mKind, long departmentId, long parentId = 0) |
| | 61 | 685 | | { |
| | 61 | 686 | | Contragent customer = await _contragentService.GetContragent(_authUserService.ContragentId); |
| | | 687 | | |
| | 61 | 688 | | var department = await _departmentService.GetDepartment(departmentId) ?? |
| | 61 | 689 | | throw new ArgumentException($"Подразделение #{departmentId} не найдено"); |
| | | 690 | | |
| | 61 | 691 | | var cluster = department.Cluster; |
| | 61 | 692 | | if (cluster == null || cluster.IsDeleted) |
| | 0 | 693 | | throw new ArgumentException($"Подразделение {department.Name} не включено в кластер"); |
| | 61 | 694 | | if (cluster.Warehouse == null || cluster.Warehouse.IsDeleted) |
| | 0 | 695 | | throw new ArgumentException($"У кластера {cluster.Name} отсутствует склад владелец"); |
| | | 696 | | |
| | 61 | 697 | | if (department.Contragent.Id != customer.Id) |
| | 0 | 698 | | throw new ArgumentException($"Подразделение #{department.Name} не подчиняется контрагенту #{customer.Sho |
| | | 699 | | |
| | 61 | 700 | | var movementType = await GetMovementType(mKind); |
| | 61 | 701 | | var mStatus = await GetDraft(movementType) ?? |
| | 61 | 702 | | throw new ArgumentException($"Не найден статус черновик для типа {movementType.Name}"); |
| | | 703 | | |
| | 61 | 704 | | var draft = await _movementService.GetDraft(_authUserService.ContragentId, department.Id, cluster.WarehouseI |
| | | 705 | | |
| | 61 | 706 | | if (draft != null) |
| | 0 | 707 | | { |
| | 0 | 708 | | return draft; |
| | | 709 | | } |
| | | 710 | | |
| | 61 | 711 | | var contracts = await _suplContractService.GetSupplyContracts(customer.Id, true); |
| | 61 | 712 | | if (contracts.Count == 0) |
| | 0 | 713 | | throw new ArgumentException("Не найдено активных контрактов"); |
| | | 714 | | |
| | 61 | 715 | | var supply = contracts |
| | | 716 | | .Where(d => d.EndDate >= DateTime.UtcNow) |
| | | 717 | | .Where(d => d.BeginDate <= DateTime.UtcNow) |
| | 122 | 718 | | .FirstOrDefault(x=>x.Seller.Id == cluster.Warehouse.Contragent.Id) ?? |
| | 61 | 719 | | throw new ArgumentException("Не найдено активных контрактов"); |
| | | 720 | | |
| | 61 | 721 | | decimal prepaymentPercent = supply.PrepaimentPercent; |
| | | 722 | | |
| | 61 | 723 | | Movement movement = new Movement() |
| | 61 | 724 | | { |
| | 61 | 725 | | DtCreated = DateTime.UtcNow, |
| | 61 | 726 | | MovementType = movementType, |
| | 61 | 727 | | RecState = await GetRecordState((long)RecordState.Active), |
| | 61 | 728 | | Customer = customer, |
| | 61 | 729 | | Supplier = supply.Seller, |
| | 61 | 730 | | Receiver = department, |
| | 61 | 731 | | SupplierTransferDate = null, |
| | 61 | 732 | | MovementStatus = mStatus, |
| | 61 | 733 | | PrepaimentPercent = prepaymentPercent, |
| | 61 | 734 | | Sender = cluster.Warehouse, |
| | 61 | 735 | | DocumentNumber = "" |
| | 61 | 736 | | }; |
| | 61 | 737 | | if(parentId != 0) |
| | 0 | 738 | | { |
| | 0 | 739 | | Movement parent = await _movementService.GetMovement(parentId); |
| | 0 | 740 | | if (parent == null && mKind != (long)MovementKind.Order) |
| | 0 | 741 | | { |
| | 0 | 742 | | throw new ArgumentException($"Родительский документ #{movementType.Name} не может быть найден"); |
| | | 743 | | } |
| | 0 | 744 | | movement.ParentId = parentId; |
| | 0 | 745 | | movement.Parent = parent; |
| | 0 | 746 | | } |
| | 61 | 747 | | await _movementService.CreateMovement(movement, _env.EnvironmentName); |
| | 61 | 748 | | await SaveJournal(movement, movement.MovementStatus); |
| | 61 | 749 | | return movement; |
| | 61 | 750 | | } |
| | | 751 | | |
| | | 752 | | |
| | | 753 | | /// <summary> |
| | | 754 | | /// Создание или возврат анонимной заявки |
| | | 755 | | /// </summary> |
| | | 756 | | /// <param name="userId">Идентификатор анонимного пользователя</param> |
| | | 757 | | /// <param name="receiverId">Идентификатор магазина</param> |
| | | 758 | | /// <returns></returns> |
| | | 759 | | public async Task<MovementDTO> CreateAnnonymMovement(Guid userId, long receiverId) |
| | 12 | 760 | | { |
| | 12 | 761 | | await _createAnonymMovementSlim.WaitAsync(); |
| | | 762 | | AnonymousMovement existsMovement; |
| | | 763 | | try |
| | 12 | 764 | | { |
| | 12 | 765 | | existsMovement = await _anonymousMovementService.GetAnonymMovement(userId, receiverId); |
| | 12 | 766 | | var mapper = ToDtoMapper(); |
| | 12 | 767 | | if (existsMovement != null) |
| | 0 | 768 | | return mapper.Map<Movement, MovementDTO>(existsMovement); |
| | | 769 | | |
| | 12 | 770 | | var receiver = await _departmentService.GetDepartment(receiverId) ?? throw |
| | 12 | 771 | | new ArgumentException($"Не найдено подразделение #{receiverId}"); |
| | | 772 | | |
| | 12 | 773 | | var sender = receiver.Cluster.Warehouse; |
| | 12 | 774 | | var contracts = await _suplContractService.GetSupplyContracts(receiver.Contragent.Id, true); |
| | 12 | 775 | | if (contracts.Count == 0) |
| | 0 | 776 | | throw new ArgumentException($"Не найдено активных контрактов"); |
| | | 777 | | |
| | 12 | 778 | | var supply = contracts |
| | | 779 | | .Where(d => d.EndDate >= DateTime.UtcNow) |
| | | 780 | | .Where(d => d.BeginDate <= DateTime.UtcNow) |
| | 24 | 781 | | .FirstOrDefault(x => x.Seller.Id == receiver.Cluster.Warehouse.Contragent.Id) ?? |
| | 12 | 782 | | throw new ArgumentException($"Не найдено активных контрактов"); |
| | | 783 | | |
| | 12 | 784 | | var annonym = new AnonymousMovement |
| | 12 | 785 | | { |
| | 12 | 786 | | AnonymousKey = userId, |
| | 12 | 787 | | Items = new List<MovementItem>(), |
| | 12 | 788 | | Receiver = receiver, |
| | 12 | 789 | | Supplier = sender.Contragent, |
| | 12 | 790 | | Sender = sender, |
| | 12 | 791 | | Customer = await _contragentService.GetContragent(annonymContragentId), |
| | 12 | 792 | | PrepaimentPercent = supply.PrepaimentPercent |
| | 12 | 793 | | }; |
| | 12 | 794 | | await _anonymousMovementService.CreateAnonymMovement(annonym); |
| | 12 | 795 | | return mapper.Map<Movement, MovementDTO>(annonym); |
| | | 796 | | } |
| | 0 | 797 | | catch (Exception e) |
| | 0 | 798 | | { |
| | 0 | 799 | | _logger.LogError($"Ошибка поиска анонимной заявки для userId {userId} и магазина {receiverId}"); |
| | 0 | 800 | | _logger.LogError(e.Message + " " + e.StackTrace); |
| | 0 | 801 | | throw; |
| | | 802 | | } |
| | | 803 | | finally |
| | 12 | 804 | | { |
| | 12 | 805 | | _createAnonymMovementSlim.Release(); |
| | 12 | 806 | | } |
| | 12 | 807 | | } |
| | | 808 | | |
| | | 809 | | /// <summary> |
| | | 810 | | /// Удаление документа |
| | | 811 | | /// </summary> |
| | | 812 | | /// <param name="id">Идентификатор документа</param> |
| | | 813 | | /// <returns>Task</returns> |
| | | 814 | | public async Task DeleteMovement(long id) |
| | | 815 | | { |
| | | 816 | | Movement movement = await _movementService.GetMovement(id) ?? |
| | | 817 | | throw new KeyNotFoundException("Документ не найден"); |
| | | 818 | | var draftStatus = await GetDraft(movement.MovementType); |
| | | 819 | | if (movement.MovementStatus.Id != draftStatus.Id) |
| | | 820 | | throw new ArgumentException($"Документ невозможно удалить из этого статуса"); |
| | | 821 | | |
| | | 822 | | if (!_authUserService.IsUserPlatform() && !(movement.MovementType.Id switch |
| | | 823 | | { |
| | | 824 | | (long)MovementKind.Order => movement.Customer.Id == _authUserService.ContragentId, |
| | | 825 | | (long)MovementKind.Shipment => movement.Supplier.Id == _authUserService.ContragentId, |
| | | 826 | | _ => false |
| | | 827 | | })) |
| | | 828 | | { |
| | | 829 | | throw new ForbidException(); |
| | | 830 | | } |
| | | 831 | | |
| | | 832 | | var listJournals = await _movementStatusJournalService.GetMovementStatusJournal(movement.Id); |
| | | 833 | | if (listJournals.Any(d => d.RecState.Id == (long) RecordState.Active)) |
| | | 834 | | { |
| | | 835 | | listJournals.Where(d => d.RecState.Id == (long) RecordState.Active) |
| | | 836 | | .Select(async d => await _movementStatusJournalService.SetRecordInactive(d)); |
| | | 837 | | } |
| | | 838 | | await _movementService.DeleteMovement(movement); |
| | | 839 | | } |
| | | 840 | | |
| | | 841 | | /// <summary> |
| | | 842 | | /// Создание черновика отгрузки |
| | | 843 | | /// </summary> |
| | | 844 | | /// <param name="parent">Объект родительской заявки</param> |
| | | 845 | | /// <returns></returns> |
| | | 846 | | /// <exception cref="ArgumentException"></exception> |
| | | 847 | | public async Task<Movement> CreateShipmentDraft(Movement parent) |
| | | 848 | | { |
| | | 849 | | try |
| | | 850 | | { |
| | | 851 | | var movementType = await GetMovementType((long) MovementKind.Shipment) ?? |
| | | 852 | | throw new ArgumentException($"Не найден тип документа Отгрузка"); |
| | | 853 | | var status = await GetDraft(movementType) ?? |
| | | 854 | | throw new ArgumentException($"Не найден статус черновик для типа {movementType.Name}"); |
| | | 855 | | var state = await _dirService.GetRecordState((int) RecordState.Active); |
| | | 856 | | var existsShipments = await _movementService.GetMovementsByParent(parent.Id, movementType, 0); |
| | | 857 | | if (existsShipments.Count > 0 && existsShipments.Any(d => !d.IsDeleted)) |
| | | 858 | | throw new ArgumentException($"Найдена существующая отгрузка для заявки {parent.Id}"); |
| | | 859 | | Movement movement = parent.CreateBaseOn(movementType, status, state); |
| | | 860 | | movement.DocumentNumber = ""; |
| | | 861 | | await _movementService.CreateMovement(movement, _env.EnvironmentName); |
| | | 862 | | await SaveJournal(movement, status); |
| | | 863 | | return movement; |
| | | 864 | | } |
| | | 865 | | catch (Exception e) |
| | | 866 | | { |
| | | 867 | | _logger.LogError($"Ошибка создания черновика отгрузки для заявки {parent.DocumentNumber}"); |
| | | 868 | | _logger.LogError(e.Message, e); |
| | | 869 | | throw; |
| | | 870 | | } |
| | | 871 | | } |
| | | 872 | | |
| | | 873 | | /// <summary> |
| | | 874 | | /// Создание ссылки на документ |
| | | 875 | | /// </summary> |
| | | 876 | | /// <param name="movement">объект документа</param> |
| | | 877 | | /// <returns></returns> |
| | | 878 | | private string CreateLink(Movement movement) |
| | 1 | 879 | | { |
| | 1 | 880 | | string type = CreatePostfixLink(movement.MovementType); |
| | 1 | 881 | | return $"<a class='notification-link' href='{_commonSettings.BaseFrontUrl}/{type}/{movement.Id}'>{movement.D |
| | 1 | 882 | | } |
| | | 883 | | |
| | | 884 | | /// <summary> |
| | | 885 | | /// Создание окончания ссылки |
| | | 886 | | /// </summary> |
| | | 887 | | /// <param name="type">Тип документа</param> |
| | | 888 | | /// <returns></returns> |
| | 1 | 889 | | private string CreatePostfixLink(MovementType type) => type.Id switch |
| | 1 | 890 | | { |
| | 1 | 891 | | (long) MovementKind.Order => $"orders", |
| | 1 | 892 | | (long) MovementKind.Shipment => $"shipments", |
| | 1 | 893 | | _ => default |
| | 1 | 894 | | }; |
| | | 895 | | |
| | | 896 | | |
| | | 897 | | /// <summary> |
| | | 898 | | /// Создание документа на основании |
| | | 899 | | /// </summary> |
| | | 900 | | /// <param name="id">идентификатор документа</param> |
| | | 901 | | /// <param name="nextKind">Идентификатор Типа документа для создания - либо этот параметр либо объект типа докум |
| | | 902 | | /// <param name="nextType">Объект документа для создания - либо этот параметр либо идентификатор из перечисления |
| | | 903 | | /// <returns></returns> |
| | | 904 | | public async Task<MovementDTO> CreateMovementOnBase(long id, MovementKind nextKind, MovementType nextType = null |
| | 1 | 905 | | { |
| | 1 | 906 | | Movement parent = await _movementService.GetMovement(id) ?? |
| | 1 | 907 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 908 | | |
| | 1 | 909 | | if (_authUserService.ContragentKindId != (long) ContragentKind.Platform && parent.MovementType.Id switch |
| | 1 | 910 | | { |
| | 1 | 911 | | (long)MovementKind.Order => parent.Supplier.Id != _authUserService.ContragentId, |
| | 1 | 912 | | (long)MovementKind.Shipment => parent.Customer.Id != _authUserService.ContragentId, |
| | 1 | 913 | | _ => true |
| | 1 | 914 | | }) |
| | 0 | 915 | | { |
| | 0 | 916 | | throw new ForbidException(); |
| | | 917 | | } |
| | | 918 | | |
| | 1 | 919 | | var movementType = nextType ?? await _dirService.GetMovementType((int) nextKind); |
| | 1 | 920 | | var status = await GetDraft(movementType) ?? |
| | 1 | 921 | | throw new ArgumentException($"Не найден статус черновик для типа {movementType?.Name}"); |
| | 1 | 922 | | var state = await _dirService.GetRecordState((int)RecordState.Active); |
| | | 923 | | |
| | 1 | 924 | | Movement movement = parent.CreateBaseOn(movementType, status, state); |
| | 1 | 925 | | if (movement.MovementType.Id == (long) MovementKind.Order) |
| | 1 | 926 | | { |
| | 1 | 927 | | movement.Parent = null; |
| | 1 | 928 | | movement.ParentId = null; |
| | 1 | 929 | | var contracts = await _suplContractService.GetSupplyContracts(movement.Customer.Id, true); |
| | 1 | 930 | | if (contracts.Count == 0) |
| | 0 | 931 | | throw new ArgumentException("Не найдено активных контрактов"); |
| | | 932 | | |
| | 1 | 933 | | var receiver = await _departmentService.GetDepartment(movement.Receiver.Id) ?? |
| | 1 | 934 | | throw new ArgumentException($"Подразделение #{movement.Receiver.Id} не найдено"); |
| | | 935 | | |
| | 1 | 936 | | var supply = contracts |
| | | 937 | | .Where(d => d.EndDate >= DateTime.UtcNow) |
| | | 938 | | .Where(d => d.BeginDate <= DateTime.UtcNow) |
| | 2 | 939 | | .FirstOrDefault(x=>x.Seller.Id == receiver.Cluster.Warehouse.Contragent.Id) ?? |
| | 1 | 940 | | throw new ArgumentException("Не найдено активных контрактов"); |
| | | 941 | | |
| | 1 | 942 | | movement.PrepaimentPercent = supply.PrepaimentPercent; |
| | 1 | 943 | | movement.UpdatePriceInDraft(); |
| | 1 | 944 | | movement.ReCalculate(); |
| | 1 | 945 | | } |
| | | 946 | | |
| | 1 | 947 | | movement.DocumentNumber = ""; |
| | 1 | 948 | | await _movementService.CreateMovement(movement, _env.EnvironmentName); |
| | 1 | 949 | | await SaveJournal(movement, status); |
| | 1 | 950 | | await _notificationWorker.CreateNotification(movement, "Создан новый документ", $"Создан новый документ {Cre |
| | 1 | 951 | | var allActions = await _movementRouteActionsService.GetActions(); |
| | 1 | 952 | | movement.FillActions(allActions, _authUserService.ContragentKindId, _authUserService.Roles); |
| | 1 | 953 | | var result = ToDtoMapper().Map<Movement, MovementDTO>(movement); |
| | 1 | 954 | | return await Task.FromResult(result); |
| | 1 | 955 | | } |
| | | 956 | | |
| | | 957 | | /// <summary> |
| | | 958 | | /// Клонирование документа |
| | | 959 | | /// </summary> |
| | | 960 | | /// <param name="id">Идентификтор документа донора</param> |
| | | 961 | | /// <param name="departmentId">Идентификтор магазина</param> |
| | | 962 | | /// <returns></returns> |
| | | 963 | | public async Task<MovementDTO> CloneMovement(long id, long departmentId) |
| | | 964 | | { |
| | | 965 | | Movement parent = await _movementService.GetMovementForItems(id) ?? |
| | | 966 | | throw new KeyNotFoundException("Документ не найден"); |
| | | 967 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | | 968 | | if(!parent.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | | 969 | | _authUserService.ContragentId, |
| | | 970 | | _authUserService.IsOwner(), |
| | | 971 | | userDeps)) |
| | | 972 | | throw new ForbidException(); |
| | | 973 | | |
| | | 974 | | if (parent.Customer.Id != _authUserService.ContragentId) |
| | | 975 | | throw new ForbidException(); |
| | | 976 | | if (parent.MovementType.Id != (long) MovementKind.Order) |
| | | 977 | | throw new ArgumentException("Документ этого типа невозможно клонировать"); |
| | | 978 | | var status = await GetDraft(parent.MovementType) ?? |
| | | 979 | | throw new ArgumentException($"Не найден статус черновик для типа {parent.MovementType?.Name}"); |
| | | 980 | | if (parent.MovementStatus.Id == status.Id) |
| | | 981 | | throw new ArgumentException("Документ в статусе черновик невозможно клонировать"); |
| | | 982 | | |
| | | 983 | | var department = await _departmentService.GetDepartment(departmentId) ?? |
| | | 984 | | throw new ArgumentException($"Подразделение #{departmentId} не найдено"); |
| | | 985 | | |
| | | 986 | | var cluster = department.Cluster; |
| | | 987 | | |
| | | 988 | | var currentDraft = await _movementService.GetDraft(_authUserService.ContragentId, departmentId, cluster.Ware |
| | | 989 | | Movement movement; |
| | 2 | 990 | | SupplyContract contract = parent.Customer.ContractsAsBuyer.FirstOrDefault(d => !d.IsDeleted |
| | 2 | 991 | | && d.Seller.Id == |
| | 2 | 992 | | parent.Supplier.Id |
| | 2 | 993 | | && d.BeginDate <= |
| | 2 | 994 | | DateTime.UtcNow |
| | 2 | 995 | | && d.EndDate >= |
| | 2 | 996 | | DateTime.UtcNow) |
| | | 997 | | ?? throw new ArgumentException( |
| | | 998 | | $"Документ невозможно клонировать. Не найден контракт для покупателя {parent.C |
| | | 999 | | if (currentDraft != null) |
| | | 1000 | | { |
| | | 1001 | | movement = currentDraft; |
| | | 1002 | | if (parent.Items != null && parent.Items.Count > 0) |
| | | 1003 | | { |
| | | 1004 | | var activeRecord = await GetRecordState((long) RecordState.Active); |
| | | 1005 | | foreach (var item in parent.Items) |
| | | 1006 | | { |
| | | 1007 | | movement.AddItem(item, activeRecord); |
| | | 1008 | | } |
| | | 1009 | | } |
| | | 1010 | | movement.UpdatePriceInDraft(); |
| | | 1011 | | movement.ReCalculate(); |
| | | 1012 | | await _movementService.UpdateMovement(movement); |
| | | 1013 | | } |
| | | 1014 | | else |
| | | 1015 | | { |
| | | 1016 | | var state = await _dirService.GetRecordState((int)RecordState.Active); |
| | | 1017 | | var receiver = await _departmentService.GetDepartment(departmentId) ?? |
| | | 1018 | | throw new ArgumentException($"Подразделение {departmentId} не найдено"); |
| | | 1019 | | if (receiver.Contragent.Id != _authUserService.ContragentId) |
| | | 1020 | | throw new ForbidException(); |
| | | 1021 | | movement = parent.CloneThis(status, state); |
| | | 1022 | | movement.PrepaimentSum = 0; |
| | | 1023 | | movement.Receiver = receiver; |
| | | 1024 | | movement.DocumentNumber = ""; |
| | | 1025 | | movement.UpdatePriceInDraft(); |
| | | 1026 | | movement.ReCalculate(); |
| | | 1027 | | await _movementService.CreateMovement(movement, _env.EnvironmentName); |
| | | 1028 | | } |
| | | 1029 | | await SaveJournal(movement, movement.MovementStatus); |
| | | 1030 | | |
| | | 1031 | | var allActions = await _movementRouteActionsService.GetActions(); |
| | | 1032 | | movement.FillActions(allActions, _authUserService.ContragentKindId, _authUserService.Roles); |
| | | 1033 | | var result = ToDtoMapper().Map<Movement, MovementDTO>(movement); |
| | | 1034 | | return await Task.FromResult(result); |
| | | 1035 | | } |
| | | 1036 | | |
| | | 1037 | | /// <summary> |
| | | 1038 | | /// Редактирование отгрузки через обмен с 1С |
| | | 1039 | | /// </summary> |
| | | 1040 | | /// <param name="movementGuid">guid отгрузки</param> |
| | | 1041 | | /// <param name="tnx">токен в системе</param> |
| | | 1042 | | /// <param name="items">массив объектов для редактирования</param> |
| | | 1043 | | /// <returns></returns> |
| | | 1044 | | /// <exception cref="ForbidException"></exception> |
| | | 1045 | | /// <exception cref="ArgumentException"></exception> |
| | | 1046 | | public async Task ExchangeCorretionShipment(Guid movementGuid, Guid tnx, |
| | | 1047 | | ExchangeShipmentCorrectionRequestDto items) |
| | | 1048 | | { |
| | | 1049 | | var token = await _exchangeTokenService.GetToken(tnx) ?? throw new ArgumentException($"Токен не найден"); |
| | | 1050 | | Movement movement = await _movementService.GetMovementForItems(movementGuid) ?? |
| | | 1051 | | throw new KeyNotFoundException($"Документ #{movementGuid} не найден"); |
| | | 1052 | | if (!(token.Department.Contragent.ContragentsKind.Id switch |
| | | 1053 | | { |
| | | 1054 | | (long) ContragentKind.Wholesaler => movement.Supplier.Id == token.DepartmentId, |
| | | 1055 | | _ => false |
| | | 1056 | | })) |
| | | 1057 | | { |
| | | 1058 | | throw new ForbidException(); |
| | | 1059 | | } |
| | | 1060 | | |
| | | 1061 | | if (movement.MovementStatus.Id != (long) MovementsStatus.ShipmentDraft |
| | | 1062 | | && movement.MovementStatus.Id != (long) MovementsStatus.Correction) |
| | | 1063 | | { |
| | | 1064 | | throw new ArgumentException("Документ не может быть измененен"); |
| | | 1065 | | } |
| | | 1066 | | var activeRecord = await GetRecordState((long) RecordState.Active); |
| | | 1067 | | foreach (var item in items.items) |
| | | 1068 | | { |
| | | 1069 | | var good = !string.IsNullOrEmpty(item.VendorCode) |
| | | 1070 | | ? (await _goodService.FindGoodsByVendorsCodes(new List<string> {item.VendorCode.Trim().ToLower()}, t |
| | | 1071 | | : await _goodService.GetGood(item.BarCode); |
| | | 1072 | | if (good == null) |
| | | 1073 | | return; |
| | 2 | 1074 | | if (movement.Items.All(d => d.Good.Id != good.Id) |
| | 2 | 1075 | | || (item.Quantity > movement.Items.FirstOrDefault(d => d.Good.Id == good.Id)?.Quantity) |
| | | 1076 | | && movement.MovementStatus.Id == (long)MovementsStatus.Correction) |
| | | 1077 | | return; |
| | | 1078 | | if (item.Quantity == 0) |
| | | 1079 | | { |
| | 0 | 1080 | | await _movementService.DeleteMovementItem(movement.Items.FirstOrDefault(d => d.GoodId == good.Id)); |
| | | 1081 | | return; |
| | | 1082 | | } |
| | | 1083 | | movement.AddItem(new MovementItem {Good = good, Quantity = item.Quantity}, activeRecord); |
| | | 1084 | | } |
| | | 1085 | | movement.ReCalculate(); |
| | | 1086 | | await _movementService.UpdateMovement(movement); |
| | | 1087 | | } |
| | | 1088 | | |
| | | 1089 | | /// <summary> |
| | | 1090 | | /// Удаляет дочерние документы |
| | | 1091 | | /// </summary> |
| | | 1092 | | /// <param name="movementId">Идентификатор документа родителя</param> |
| | | 1093 | | /// <param name="kind">Тип удаляемых документов</param> |
| | | 1094 | | /// <param name="status">Статус удаляемых документов</param> |
| | | 1095 | | /// <returns></returns> |
| | | 1096 | | public async Task RemoveChildrenDraft(long movementId, MovementKind kind, MovementsStatus status) |
| | 3 | 1097 | | { |
| | 3 | 1098 | | var shipmentType = |
| | 3 | 1099 | | await GetMovementType((long) kind); |
| | 3 | 1100 | | var children = await _movementService.GetMovementsByParent(movementId, |
| | 3 | 1101 | | shipmentType, (long)status); |
| | | 1102 | | // удалим все дочерние черновики |
| | 3 | 1103 | | if (children != null && children.Count > 0) |
| | 2 | 1104 | | { |
| | 10 | 1105 | | foreach (Movement movement in children) |
| | 2 | 1106 | | { |
| | 2 | 1107 | | await _movementService.DeleteMovement(movement); |
| | 2 | 1108 | | } |
| | 2 | 1109 | | } |
| | 3 | 1110 | | } |
| | | 1111 | | |
| | | 1112 | | /// <summary> |
| | | 1113 | | /// Изменение количества товара в документе |
| | | 1114 | | /// </summary> |
| | | 1115 | | /// <param name="id">Идентификатор документа</param> |
| | | 1116 | | /// <param name="itemId">Идентификатор позиции</param> |
| | | 1117 | | /// <param name="quantity">Количество товара</param> |
| | | 1118 | | /// <param name="comment">Причрина изменения количества - для отгрузки</param> |
| | | 1119 | | /// <returns></returns> |
| | | 1120 | | public async Task ChangeQuantityInMovement(long id, long itemId, int quantity, string comment = default) |
| | | 1121 | | { |
| | | 1122 | | try |
| | | 1123 | | { |
| | | 1124 | | await _changeQuantityMovementSlim.WaitAsync(); |
| | | 1125 | | Movement movement = _authUserService.IsAnonym() ? await _anonymousMovementService.GetAnonymMovement(id) |
| | | 1126 | | : await _movementService.GetMovementForItems(id) |
| | | 1127 | | ?? throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 1128 | | |
| | | 1129 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | | 1130 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | | 1131 | | _authUserService.ContragentId, |
| | | 1132 | | _authUserService.IsOwner(), |
| | | 1133 | | userDeps)) |
| | | 1134 | | throw new ForbidException(); |
| | | 1135 | | |
| | | 1136 | | var status = await GetDraft(movement.MovementType); |
| | | 1137 | | if (!movement.CheckEditable(status)) |
| | | 1138 | | { |
| | | 1139 | | throw new ArgumentException($"Документ {movement.Id} невозможно изменить"); |
| | | 1140 | | } |
| | | 1141 | | |
| | 4 | 1142 | | MovementItem item = movement.Items.FirstOrDefault(d => d.Id == itemId) ?? |
| | | 1143 | | throw new KeyNotFoundException($"Позиция {itemId} в документе {id} не найдена"); |
| | | 1144 | | |
| | 0 | 1145 | | var settings = item.Good.DepartmentGoodSettings.FirstOrDefault(d => !d.IsDeleted && d.Department.Id == m |
| | | 1146 | | ?? new DepartmentGoodSetting {MinQuantity = 1, PickingQuantum = 1}; |
| | | 1147 | | CheckSettings(quantity, settings); |
| | | 1148 | | if (movement.MovementStatus.Id == (long) MovementsStatus.Correction) |
| | | 1149 | | { |
| | | 1150 | | var note = new MovementNote |
| | | 1151 | | { |
| | | 1152 | | Body = $"Товар \"{item.Good.Name}\" - изменено количество с {(long)item.Quantity} на {quantity}" |
| | | 1153 | | }; |
| | | 1154 | | movement.Notes.Add(note); |
| | | 1155 | | } |
| | | 1156 | | movement.AddItem(new MovementItem{Good = item.Good, Quantity = quantity}, await GetRecordState((long)Rec |
| | | 1157 | | if (movement.MovementStatus.Id == (long)MovementsStatus.OrderDraft) |
| | | 1158 | | movement.UpdatePriceInDraft(); |
| | | 1159 | | movement.ReCalculate(); |
| | | 1160 | | if (_authUserService.IsAnonym()) |
| | | 1161 | | await _anonymousMovementService.UpdateAnonymMovement((AnonymousMovement) movement); |
| | | 1162 | | else |
| | | 1163 | | await _movementService.UpdateMovement(movement); |
| | | 1164 | | } |
| | | 1165 | | catch (Exception e) |
| | | 1166 | | { |
| | | 1167 | | _logger.LogError($"Ошибка изменения количества позиции {itemId} в заявке {id}"); |
| | | 1168 | | _logger.LogError($"{e.Message} {e.StackTrace}"); |
| | | 1169 | | throw; |
| | | 1170 | | } |
| | | 1171 | | finally |
| | | 1172 | | { |
| | | 1173 | | _changeQuantityMovementSlim.Release(); |
| | | 1174 | | } |
| | | 1175 | | |
| | | 1176 | | } |
| | | 1177 | | |
| | | 1178 | | /// <summary> |
| | | 1179 | | /// Добавляет позицию в документ |
| | | 1180 | | /// </summary> |
| | | 1181 | | /// <param name="id">id документа для обработки</param> |
| | | 1182 | | /// <param name="mItemDto">Позиция для добавления</param> |
| | | 1183 | | /// <returns></returns> |
| | | 1184 | | /// <exception cref="ArgumentException"></exception> |
| | | 1185 | | public async Task<MovementItemResponseDTO> AddItemToMovement(long id, MovementItemRequestDTO mItemDto) |
| | | 1186 | | { |
| | | 1187 | | try |
| | | 1188 | | { |
| | | 1189 | | await _addItemMovementSlim.WaitAsync(); |
| | | 1190 | | Movement movement = _authUserService.IsAnonym() ? await _anonymousMovementService.GetAnonymMovementForIt |
| | | 1191 | | : movement = await _movementService.GetMovementForItems(id) ?? throw new KeyNotFoundException($"Документ |
| | | 1192 | | |
| | | 1193 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | | 1194 | | |
| | | 1195 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | | 1196 | | _authUserService.ContragentId, |
| | | 1197 | | _authUserService.IsOwner(), |
| | | 1198 | | userDeps)) |
| | | 1199 | | throw new ForbidException(); |
| | | 1200 | | |
| | | 1201 | | var status = await GetDraft(movement.MovementType); |
| | | 1202 | | if (!movement.CheckEditable(status)) |
| | | 1203 | | { |
| | | 1204 | | throw new ArgumentException($"Документ {movement.Id} невозможно изменить"); |
| | | 1205 | | } |
| | | 1206 | | var active = await GetRecordState((long) RecordState.Active); |
| | | 1207 | | //Обновим дату создания заявки при первом добавлении товара в нее |
| | | 1208 | | if (movement.MovementType?.Id == (long) MovementKind.Order |
| | | 1209 | | && movement.Items?.Count == 0) |
| | | 1210 | | { |
| | | 1211 | | movement.CreationDateTime = DateTime.UtcNow; |
| | | 1212 | | //Изменим статус документа при первом добавлении позиции в документ |
| | | 1213 | | movement.RecState = movement.RecState?.Id == (long)RecordState.Empty ? active : movement.RecState; |
| | | 1214 | | } |
| | | 1215 | | Good good = await _goodService.GetGood(mItemDto.GoodId) ?? |
| | | 1216 | | throw new ArgumentException($"Товар {mItemDto.GoodId} не найден"); |
| | | 1217 | | |
| | | 1218 | | if (good.RecState?.Id != (long)RecordState.Active) |
| | | 1219 | | throw new ArgumentException($"Товар {good.Id} не может быть добавлен в документ"); |
| | | 1220 | | |
| | 0 | 1221 | | var settings = good.DepartmentGoodSettings.FirstOrDefault(d => !d.IsDeleted && d.Department.Id == moveme |
| | | 1222 | | ?? new DepartmentGoodSetting {MinQuantity = 1, PickingQuantum = 1}; |
| | | 1223 | | CheckSettings(mItemDto.Quantity, settings); |
| | | 1224 | | movement.AddItem(new MovementItem{Good = good, Quantity = mItemDto.Quantity}, active); |
| | | 1225 | | |
| | | 1226 | | if (movement.MovementStatus.Id == (long)MovementsStatus.OrderDraft) |
| | | 1227 | | movement.UpdatePriceInDraft(); |
| | | 1228 | | |
| | | 1229 | | movement.ReCalculate(); |
| | | 1230 | | if (_authUserService.IsAnonym()) |
| | | 1231 | | { |
| | | 1232 | | await _anonymousMovementService.UpdateAnonymMovement((AnonymousMovement)movement); |
| | | 1233 | | } |
| | | 1234 | | else |
| | | 1235 | | { |
| | | 1236 | | await _movementService.UpdateMovement(movement); |
| | | 1237 | | } |
| | 136 | 1238 | | MovementItem movementItem = movement.Items.FirstOrDefault(d => d.Good.Id == mItemDto.GoodId); |
| | | 1239 | | var itemDto = ToItemDTOMapper(movement.Sender.Id, discountColors).Map<MovementItemResponseDTO>(movementI |
| | | 1240 | | return itemDto; |
| | | 1241 | | } |
| | | 1242 | | catch (Exception e) |
| | | 1243 | | { |
| | | 1244 | | _logger.LogError($"Ошибка добавления товара {mItemDto.GoodId} в документ {id}"); |
| | | 1245 | | _logger.LogError($"{e.Message} {e.StackTrace}"); |
| | | 1246 | | throw; |
| | | 1247 | | } |
| | | 1248 | | finally |
| | | 1249 | | { |
| | | 1250 | | _addItemMovementSlim.Release(); |
| | | 1251 | | } |
| | | 1252 | | } |
| | | 1253 | | |
| | | 1254 | | /// <summary> |
| | | 1255 | | /// Удаление позиции из документа |
| | | 1256 | | /// </summary> |
| | | 1257 | | /// <param name="id">Идентификатор документа</param> |
| | | 1258 | | /// <param name="itemId">Идентификатор позиции</param> |
| | | 1259 | | /// <returns></returns> |
| | | 1260 | | public async Task RemoveItemFromMovement(long id, long itemId) |
| | | 1261 | | { |
| | | 1262 | | Movement movement = _authUserService.IsAnonym() |
| | | 1263 | | ? await _anonymousMovementService.GetAnonymMovementForItems(id) |
| | | 1264 | | : await _movementService.GetMovementForItems(id) |
| | | 1265 | | ?? throw new ArgumentException($"Документ {id} не найден"); |
| | | 1266 | | |
| | | 1267 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | | 1268 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | | 1269 | | _authUserService.ContragentId, |
| | | 1270 | | _authUserService.IsOwner(), |
| | | 1271 | | userDeps)) |
| | | 1272 | | throw new ForbidException(); |
| | | 1273 | | |
| | | 1274 | | var status = await GetDraft(movement.MovementType); |
| | | 1275 | | if (!movement.CheckEditable(status)) |
| | | 1276 | | { |
| | | 1277 | | throw new ArgumentException($"Документ {movement.Id} невозможно изменить"); |
| | | 1278 | | } |
| | | 1279 | | |
| | 4 | 1280 | | var item = movement.Items.FirstOrDefault(d => d.Id == itemId) ?? |
| | | 1281 | | throw new ArgumentException($"Позиция #{itemId} не найдена"); |
| | | 1282 | | |
| | | 1283 | | |
| | | 1284 | | await _movementService.DeleteMovementItem(item); |
| | | 1285 | | movement.ReCalculate(); |
| | | 1286 | | |
| | | 1287 | | if (movement.MovementStatus.Id == (long) MovementsStatus.Correction) |
| | | 1288 | | { |
| | | 1289 | | var note = new MovementNote |
| | | 1290 | | { |
| | | 1291 | | Body = $"Товар \"{item.Good.Name}\" удален" |
| | | 1292 | | }; |
| | | 1293 | | movement.Notes.Add(note); |
| | | 1294 | | } |
| | | 1295 | | |
| | | 1296 | | if (_authUserService.IsAnonym()) |
| | | 1297 | | await _anonymousMovementService.UpdateAnonymMovement((AnonymousMovement) movement); |
| | | 1298 | | else |
| | | 1299 | | await _movementService.UpdateMovement(movement); |
| | | 1300 | | } |
| | | 1301 | | |
| | | 1302 | | /// <summary> |
| | | 1303 | | /// Печать универсального передаточного документа (УПД) |
| | | 1304 | | /// </summary> |
| | | 1305 | | /// <param name="id">Идентификатор документа</param> |
| | | 1306 | | /// <returns></returns> |
| | | 1307 | | public async Task<MemoryStream> PrintUpd(long id) |
| | 0 | 1308 | | { |
| | 0 | 1309 | | var type = await GetMovementType((long) MovementKind.Shipment); |
| | 0 | 1310 | | Movement movement = await _movementService.GetMovementReadonly(id, type) ?? |
| | 0 | 1311 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | 0 | 1312 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 1313 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 1314 | | _authUserService.ContragentId, |
| | 0 | 1315 | | _authUserService.IsOwner(), |
| | 0 | 1316 | | userDeps)) |
| | 0 | 1317 | | throw new ForbidException(); |
| | 0 | 1318 | | if (movement.MovementType.Id != (int)MovementKind.Shipment) |
| | 0 | 1319 | | throw new ArgumentException("Этот тип документа не доступен для печати"); |
| | 0 | 1320 | | if (!(movement.MovementStatus.Id switch |
| | 0 | 1321 | | { |
| | 0 | 1322 | | (long) MovementsStatus.ReadyForShipment => true, |
| | 0 | 1323 | | (long) MovementsStatus.Shipped => true, |
| | 0 | 1324 | | (long) MovementsStatus.Received => true, |
| | 0 | 1325 | | (long) MovementsStatus.CustomerReject => true, |
| | 0 | 1326 | | (long) MovementsStatus.ClaimAccepted => true, |
| | 0 | 1327 | | (long) MovementsStatus.ClaimDeclined => true, |
| | 0 | 1328 | | (long) MovementsStatus.ClaimInProgress => true, |
| | 0 | 1329 | | _ => false |
| | 0 | 1330 | | })) |
| | 0 | 1331 | | { |
| | 0 | 1332 | | throw new ArgumentException($"Статус документа не подходит для печати"); |
| | | 1333 | | } |
| | | 1334 | | |
| | 0 | 1335 | | var options = new ConvertOptions |
| | 0 | 1336 | | { |
| | 0 | 1337 | | PageOrientation = Wkhtmltopdf.NetCore.Options.Orientation.Landscape, |
| | 0 | 1338 | | PageMargins = { Left = 10, Bottom = 15, Right = 10, Top = 10 } |
| | 0 | 1339 | | }; |
| | 0 | 1340 | | _generatePdf.SetConvertOptions(options); |
| | | 1341 | | |
| | 0 | 1342 | | var receiver = await _departmentService.GetDepartment(movement.Receiver.Id) ?? |
| | 0 | 1343 | | throw new ArgumentException($"Подразделение #{movement.Receiver.Id} не найдено"); |
| | | 1344 | | |
| | 0 | 1345 | | var contracts = await _suplContractService.GetSupplyContracts(movement.Customer.Id, false); |
| | 0 | 1346 | | if (contracts.Count == 0) |
| | 0 | 1347 | | throw new ArgumentException("Не найдены контракты"); |
| | | 1348 | | |
| | 0 | 1349 | | var supply = contracts |
| | 0 | 1350 | | .Where(d => d.EndDate >= movement.CreationDateTime) |
| | 0 | 1351 | | .Where(d => d.BeginDate <= movement.CreationDateTime) |
| | 0 | 1352 | | .FirstOrDefault(x=>x.Seller.Id == receiver.Cluster.Warehouse.Contragent.Id) ?? |
| | 0 | 1353 | | throw new ArgumentException("Не найден контракт действовавший на момент создания документа"); |
| | | 1354 | | |
| | 0 | 1355 | | var pdf = await _generatePdf.GetByteArray("PrintForms/Upd/upd", new MovementUpd{Movement = movement, SupplyC |
| | 0 | 1356 | | var pdfStream = new MemoryStream(); |
| | 0 | 1357 | | pdfStream.Write(pdf, 0, pdf.Length); |
| | 0 | 1358 | | pdfStream.Position = 0; |
| | 0 | 1359 | | return pdfStream; |
| | 0 | 1360 | | } |
| | | 1361 | | |
| | | 1362 | | /// <summary> |
| | | 1363 | | /// Возвращение вложения из документа |
| | | 1364 | | /// </summary> |
| | | 1365 | | /// <param name="id">Идентификатор документа</param> |
| | | 1366 | | /// <param name="kind">тип документа</param> |
| | | 1367 | | /// <param name="fileType">Тип файла</param> |
| | | 1368 | | /// <returns></returns> |
| | | 1369 | | /// <exception cref="ForbidException">Нет прав доступа к вложению</exception> |
| | | 1370 | | public async Task<MemoryStream> GetFile(long id, MovementKind kind, string fileType) |
| | 0 | 1371 | | { |
| | 0 | 1372 | | MovementType type = await GetMovementType((long) kind); |
| | 0 | 1373 | | Movement order = await _movementService.GetMovementReadonly(id, type) ?? |
| | 0 | 1374 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 1375 | | |
| | 0 | 1376 | | if (_authUserService.IsUserRetailer() && order.Customer.Id != _authUserService.ContragentId) |
| | 0 | 1377 | | throw new ForbidException(); |
| | 0 | 1378 | | string delim = ";"; |
| | 0 | 1379 | | var rows = new List<string[]>(); |
| | 0 | 1380 | | rows.Add(new string[] { "ШК", "Название", "Цена", "Кол-во", "СуммаСНдс", "СуммаБезНдс" }); |
| | | 1381 | | try |
| | 0 | 1382 | | { |
| | 0 | 1383 | | order.Items.ForEach(item => |
| | | 1384 | | { |
| | | 1385 | | var line = new List<string>(); |
| | | 1386 | | line.Add(item.Good.GoodBarcodes.FirstOrDefault(d => d.IsPrimary) != null |
| | | 1387 | | ? item.Good.GoodBarcodes.FirstOrDefault(d => d.IsPrimary).BarCode.Code |
| | | 1388 | | : item.Good.DefaultBarCode.Code); |
| | | 1389 | | line.Add(item.Good.Name); |
| | | 1390 | | line.Add(item.Price.ToString(CultureInfo.CurrentCulture)); |
| | | 1391 | | line.Add(item.Quantity.ToString(CultureInfo.CurrentCulture)); |
| | | 1392 | | line.Add((item.Price * item.Quantity).ToString()); |
| | | 1393 | | line.Add((Math.Ceiling(item.Price / (item.Good.VatsKind.Value == 1 ? 1 : (decimal)item.Good.VatsKind |
| | | 1394 | | rows.Add(line.ToArray()); |
| | | 1395 | | }); |
| | 0 | 1396 | | } |
| | 0 | 1397 | | catch (Exception e) |
| | 0 | 1398 | | { |
| | 0 | 1399 | | _logger.LogError(e.Message, e.StackTrace); |
| | 0 | 1400 | | throw; |
| | | 1401 | | } |
| | | 1402 | | |
| | 0 | 1403 | | var resStream = new MemoryStream(); |
| | 0 | 1404 | | if (fileType == "csv") |
| | 0 | 1405 | | { |
| | 0 | 1406 | | using (var writer = new StreamWriter(resStream, encoding: Encoding.UTF8, leaveOpen: true)) |
| | 0 | 1407 | | { |
| | 0 | 1408 | | rows.ForEach(x => writer.WriteLine(string.Join(delim, x))); |
| | 0 | 1409 | | } |
| | 0 | 1410 | | } |
| | | 1411 | | else |
| | 0 | 1412 | | { |
| | 0 | 1413 | | using (var book = CsvUtil.ToExcel(rows)) |
| | 0 | 1414 | | { |
| | 0 | 1415 | | book.SaveAs(resStream); |
| | 0 | 1416 | | } |
| | 0 | 1417 | | } |
| | 0 | 1418 | | return resStream; |
| | 0 | 1419 | | } |
| | | 1420 | | |
| | | 1421 | | /// <summary> |
| | | 1422 | | /// Печать заказа |
| | | 1423 | | /// </summary> |
| | | 1424 | | /// <param name="id">Идентификатор документа</param> |
| | | 1425 | | /// <param name="kind">Тип документа</param> |
| | | 1426 | | /// <returns></returns> |
| | | 1427 | | public async Task<MemoryStream> PrintOrder(long id, MovementKind kind) |
| | 0 | 1428 | | { |
| | 0 | 1429 | | var movementType = await GetMovementType((long) kind) ?? |
| | 0 | 1430 | | throw new ArgumentException($"Не найден тип документа {kind}"); |
| | 0 | 1431 | | var movement = await _movementService.GetMovementReadonly(id, movementType)?? |
| | 0 | 1432 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 1433 | | |
| | 0 | 1434 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 1435 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 1436 | | _authUserService.ContragentId, |
| | 0 | 1437 | | _authUserService.IsOwner(), |
| | 0 | 1438 | | userDeps)) |
| | 0 | 1439 | | throw new ForbidException(); |
| | | 1440 | | |
| | 0 | 1441 | | var options = new ConvertOptions |
| | 0 | 1442 | | { |
| | 0 | 1443 | | PageOrientation = Wkhtmltopdf.NetCore.Options.Orientation.Landscape, |
| | 0 | 1444 | | PageMargins = { Left = 10, Bottom = 10, Right = 10, Top = 10 } |
| | 0 | 1445 | | }; |
| | 0 | 1446 | | _generatePdf.SetConvertOptions(options); |
| | | 1447 | | |
| | 0 | 1448 | | var pdf = await _generatePdf.GetByteArray("PrintForms/Order/order", movement); |
| | 0 | 1449 | | var pdfStream = new MemoryStream(); |
| | 0 | 1450 | | pdfStream.Write(pdf, 0, pdf.Length); |
| | 0 | 1451 | | pdfStream.Position = 0; |
| | 0 | 1452 | | return pdfStream; |
| | 0 | 1453 | | } |
| | | 1454 | | |
| | | 1455 | | /// <summary> |
| | | 1456 | | /// Создает архив с документами |
| | | 1457 | | /// </summary> |
| | | 1458 | | /// <param name="id">Идентификатор отгрузки</param> |
| | | 1459 | | /// <param name="kind">Тип</param> |
| | | 1460 | | /// <returns></returns> |
| | | 1461 | | public async Task<MemoryStream> CreateZip(long id) |
| | 0 | 1462 | | { |
| | 0 | 1463 | | var kind = MovementKind.Shipment; |
| | 0 | 1464 | | var movementType = await GetMovementType((long) kind) ?? |
| | 0 | 1465 | | throw new ArgumentException($"Не найден тип документа {kind}"); |
| | 0 | 1466 | | var movement = await _movementService.GetMovementReadonly(id, movementType)?? |
| | 0 | 1467 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | 0 | 1468 | | var torgStream = await PrintTorg(id, MovementKind.Shipment); |
| | 0 | 1469 | | var ttnStream = await PrintTtn(id, MovementKind.Shipment); |
| | 0 | 1470 | | var sfStream = await PrintSf(id, MovementKind.Shipment); |
| | 0 | 1471 | | var tnStream = await PrintTn(id, MovementKind.Shipment); |
| | 0 | 1472 | | MemoryStream[] streams = new MemoryStream[] {torgStream, ttnStream, sfStream, tnStream}; |
| | 0 | 1473 | | string[] names = new string[] {"torg.pdf", "ttn.pdf", "sf.pdf", "tn.pdf"}; |
| | | 1474 | | byte[] compressedBytes; |
| | | 1475 | | |
| | 0 | 1476 | | var stream = new MemoryStream(); |
| | 0 | 1477 | | using (var arch = new ZipArchive(stream, ZipArchiveMode.Create, true)) |
| | 0 | 1478 | | { |
| | 0 | 1479 | | for (int i = 0; i < names.Length; i++) |
| | 0 | 1480 | | { |
| | 0 | 1481 | | var file = arch.CreateEntry(names[i]); |
| | 0 | 1482 | | using (var entryStream = file.Open()) |
| | 0 | 1483 | | streams[i].WriteTo(entryStream); |
| | 0 | 1484 | | } |
| | 0 | 1485 | | } |
| | 0 | 1486 | | stream.Position = 0; |
| | 0 | 1487 | | return stream; |
| | 0 | 1488 | | } |
| | | 1489 | | |
| | | 1490 | | /// <summary> |
| | | 1491 | | /// Печать счет фактуры |
| | | 1492 | | /// </summary> |
| | | 1493 | | /// <param name="id">Идентификатор документа</param> |
| | | 1494 | | /// <param name="kind">Тип документа</param> |
| | | 1495 | | /// <returns></returns> |
| | | 1496 | | public async Task<MemoryStream> PrintSf(long id, MovementKind kind, Movement mov = null) |
| | 0 | 1497 | | { |
| | 0 | 1498 | | var movementType = await GetMovementType((long) kind) ?? |
| | 0 | 1499 | | throw new ArgumentException($"Не найден тип документа {kind}"); |
| | 0 | 1500 | | var movement = mov ?? await _movementService.GetMovementReadonly(id, movementType)?? |
| | 0 | 1501 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 1502 | | |
| | 0 | 1503 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 1504 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 1505 | | _authUserService.ContragentId, |
| | 0 | 1506 | | _authUserService.IsOwner(), |
| | 0 | 1507 | | userDeps)) |
| | 0 | 1508 | | throw new ForbidException(); |
| | | 1509 | | |
| | 0 | 1510 | | var options = new ConvertOptions |
| | 0 | 1511 | | { |
| | 0 | 1512 | | PageOrientation = Wkhtmltopdf.NetCore.Options.Orientation.Landscape, |
| | 0 | 1513 | | PageMargins = { Left = 10, Bottom = 10, Right = 10, Top = 10 } |
| | 0 | 1514 | | }; |
| | 0 | 1515 | | _generatePdf.SetConvertOptions(options); |
| | | 1516 | | |
| | 0 | 1517 | | var pdf = await _generatePdf.GetByteArray("PrintForms/SF/sf", movement); |
| | 0 | 1518 | | var pdfStream = new MemoryStream(); |
| | 0 | 1519 | | pdfStream.Write(pdf, 0, pdf.Length); |
| | 0 | 1520 | | pdfStream.Position = 0; |
| | 0 | 1521 | | return pdfStream; |
| | 0 | 1522 | | } |
| | | 1523 | | |
| | | 1524 | | /// <summary> |
| | | 1525 | | /// Печать ТТН |
| | | 1526 | | /// </summary> |
| | | 1527 | | /// <param name="id">Идентификатор документа</param> |
| | | 1528 | | /// <param name="kind">Тип документа</param> |
| | | 1529 | | /// <returns></returns> |
| | | 1530 | | public async Task<MemoryStream> PrintTtn(long id, MovementKind kind, Movement mov = null) |
| | 0 | 1531 | | { |
| | 0 | 1532 | | var movementType = await GetMovementType((long) kind) ?? |
| | 0 | 1533 | | throw new ArgumentException($"Не найден тип документа {kind}"); |
| | 0 | 1534 | | var movement = mov ?? await _movementService.GetMovementReadonly(id, movementType)?? |
| | 0 | 1535 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 1536 | | |
| | 0 | 1537 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 1538 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 1539 | | _authUserService.ContragentId, |
| | 0 | 1540 | | _authUserService.IsOwner(), |
| | 0 | 1541 | | userDeps)) |
| | 0 | 1542 | | throw new ForbidException(); |
| | | 1543 | | |
| | 0 | 1544 | | var options = new ConvertOptions |
| | 0 | 1545 | | { |
| | 0 | 1546 | | PageOrientation = Wkhtmltopdf.NetCore.Options.Orientation.Landscape, |
| | 0 | 1547 | | PageMargins = { Left = 10, Bottom = 10, Right = 10, Top = 10 } |
| | 0 | 1548 | | }; |
| | 0 | 1549 | | _generatePdf.SetConvertOptions(options); |
| | | 1550 | | |
| | 0 | 1551 | | var pdf = await _generatePdf.GetByteArray("PrintForms/TTN/Ttn", movement); |
| | 0 | 1552 | | var pdfStream = new MemoryStream(); |
| | 0 | 1553 | | pdfStream.Write(pdf, 0, pdf.Length); |
| | 0 | 1554 | | pdfStream.Position = 0; |
| | 0 | 1555 | | return pdfStream; |
| | 0 | 1556 | | } |
| | | 1557 | | |
| | | 1558 | | /// <summary> |
| | | 1559 | | /// Печать торг12 |
| | | 1560 | | /// </summary> |
| | | 1561 | | /// <param name="id">Идентификатор документа</param> |
| | | 1562 | | /// <param name="kind">Тип документа</param> |
| | | 1563 | | /// <returns></returns> |
| | | 1564 | | public async Task<MemoryStream> PrintTorg(long id, MovementKind kind, Movement mov = null) |
| | 0 | 1565 | | { |
| | 0 | 1566 | | var movementType = await GetMovementType((long) kind) ?? |
| | 0 | 1567 | | throw new ArgumentException($"Не найден тип документа {kind}"); |
| | 0 | 1568 | | var movement = mov ?? await _movementService.GetMovementReadonly(id, movementType)?? |
| | 0 | 1569 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 1570 | | |
| | 0 | 1571 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 1572 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 1573 | | _authUserService.ContragentId, |
| | 0 | 1574 | | _authUserService.IsOwner(), |
| | 0 | 1575 | | userDeps)) |
| | 0 | 1576 | | throw new ForbidException(); |
| | | 1577 | | |
| | 0 | 1578 | | var options = new ConvertOptions |
| | 0 | 1579 | | { |
| | 0 | 1580 | | PageOrientation = Wkhtmltopdf.NetCore.Options.Orientation.Landscape, |
| | 0 | 1581 | | PageMargins = { Left = 10, Bottom = 10, Right = 10, Top = 10 } |
| | 0 | 1582 | | }; |
| | 0 | 1583 | | _generatePdf.SetConvertOptions(options); |
| | | 1584 | | |
| | 0 | 1585 | | var pdf = await _generatePdf.GetByteArray("PrintForms/TORG/Torg12", movement); |
| | 0 | 1586 | | var pdfStream = new MemoryStream(); |
| | 0 | 1587 | | pdfStream.Write(pdf, 0, pdf.Length); |
| | 0 | 1588 | | pdfStream.Position = 0; |
| | 0 | 1589 | | return pdfStream; |
| | 0 | 1590 | | } |
| | | 1591 | | |
| | | 1592 | | /// <summary> |
| | | 1593 | | /// Печать транспортная накладная |
| | | 1594 | | /// </summary> |
| | | 1595 | | /// <param name="id">Идентификатор документа</param> |
| | | 1596 | | /// <param name="kind">Тип документа</param> |
| | | 1597 | | /// <returns></returns> |
| | | 1598 | | public async Task<MemoryStream> PrintTn(long id, MovementKind kind, Movement mov = null) |
| | 0 | 1599 | | { |
| | 0 | 1600 | | var movementType = await GetMovementType((long) kind) ?? |
| | 0 | 1601 | | throw new ArgumentException($"Не найден тип документа {kind}"); |
| | 0 | 1602 | | var movement = mov ?? await _movementService.GetMovementReadonly(id, movementType)?? |
| | 0 | 1603 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 1604 | | |
| | 0 | 1605 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 1606 | | if(!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 1607 | | _authUserService.ContragentId, |
| | 0 | 1608 | | _authUserService.IsOwner(), |
| | 0 | 1609 | | userDeps)) |
| | 0 | 1610 | | throw new ForbidException(); |
| | | 1611 | | |
| | 0 | 1612 | | var options = new ConvertOptions |
| | 0 | 1613 | | { |
| | 0 | 1614 | | PageOrientation = Wkhtmltopdf.NetCore.Options.Orientation.Portrait, |
| | 0 | 1615 | | PageMargins = { Left = 10, Bottom = 10, Right = 10, Top = 10 } |
| | 0 | 1616 | | }; |
| | 0 | 1617 | | _generatePdf.SetConvertOptions(options); |
| | | 1618 | | |
| | 0 | 1619 | | var pdf = await _generatePdf.GetByteArray("PrintForms/TN/Tn", movement); |
| | 0 | 1620 | | var pdfStream = new MemoryStream(); |
| | 0 | 1621 | | pdfStream.Write(pdf, 0, pdf.Length); |
| | 0 | 1622 | | pdfStream.Position = 0; |
| | 0 | 1623 | | return pdfStream; |
| | 0 | 1624 | | } |
| | | 1625 | | |
| | | 1626 | | /// <summary> |
| | | 1627 | | /// Получает историю мувмента |
| | | 1628 | | /// </summary> |
| | | 1629 | | /// <param name="id">Идентификатор документа</param> |
| | | 1630 | | /// <returns></returns> |
| | | 1631 | | public async Task<List<MovementHistoryResponseDTO>> GetMovementHistory(long id) |
| | 0 | 1632 | | { |
| | 0 | 1633 | | var movement = await _movementService.GetMovement(id) ?? |
| | 0 | 1634 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 1635 | | |
| | 0 | 1636 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 1637 | | if (!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 1638 | | _authUserService.ContragentId, |
| | 0 | 1639 | | _authUserService.IsOwner(), |
| | 0 | 1640 | | userDeps)) |
| | 0 | 1641 | | throw new ForbidException(); |
| | 0 | 1642 | | var events = await _eventService.GetEvents(movement.GUID, nameof(Movement)); |
| | 0 | 1643 | | var history = new List<MovementHistoryResponseDTO>(); |
| | 0 | 1644 | | var jsonSettings = new JsonSerializerSettings |
| | 0 | 1645 | | { |
| | 0 | 1646 | | Culture = new System.Globalization.CultureInfo("ru-RU") //чтобы нормально было преобразование prepaimen |
| | 0 | 1647 | | }; |
| | 0 | 1648 | | foreach (var _event in events) |
| | 0 | 1649 | | { |
| | 0 | 1650 | | var json = JsonConvert.DeserializeObject<MovementHistoryJsonDTO>(_event.ReasonJson, jsonSettings); |
| | 0 | 1651 | | if (json.New == null) |
| | 0 | 1652 | | continue; |
| | 0 | 1653 | | var movementStatus = await _movementTypeStatusService.GetMovementStatus(json.New.MovementStatusId); |
| | 0 | 1654 | | history.Add(new MovementHistoryResponseDTO |
| | 0 | 1655 | | { |
| | 0 | 1656 | | Kind = new IdNameDTO(_event.EventsKind.Id, _event.EventsKind.Name), |
| | 0 | 1657 | | ModifyDateTime = _event.CreationDateTime, |
| | 0 | 1658 | | MovementStatusId = new IdNameDTO(movementStatus.Id, movementStatus.Name), |
| | 0 | 1659 | | PrepaimentSum = json.New.PrepaimentSum, |
| | 0 | 1660 | | User = new IdNameDTO(_event.User.Id, _event.User.FirstName + " " + _event.User.LastName) |
| | 0 | 1661 | | }); |
| | 0 | 1662 | | } |
| | 0 | 1663 | | return history; |
| | 0 | 1664 | | } |
| | | 1665 | | |
| | | 1666 | | /// <summary> |
| | | 1667 | | /// Определяет тип доставки заявки |
| | | 1668 | | /// </summary> |
| | | 1669 | | /// <param name="id">id заявки</param> |
| | | 1670 | | /// <returns></returns> |
| | | 1671 | | public async Task<ClusterDeliveryTypesResponseDTO> GetOrderDeliveryTypes(long id) |
| | 0 | 1672 | | { |
| | 0 | 1673 | | var movement = await _movementService.GetMovement(id) ?? |
| | 0 | 1674 | | throw new KeyNotFoundException($"Документ #{id} не найден"); |
| | | 1675 | | |
| | 0 | 1676 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 1677 | | if (!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 1678 | | _authUserService.ContragentId, |
| | 0 | 1679 | | _authUserService.IsOwner(), |
| | 0 | 1680 | | userDeps)) |
| | 0 | 1681 | | throw new ForbidException(); |
| | 0 | 1682 | | var result = new ClusterDeliveryTypesResponseDTO() |
| | 0 | 1683 | | { |
| | 0 | 1684 | | Cluster = new IdNameDTO(movement.Receiver.Cluster.Id, movement.Receiver.Cluster.Name), |
| | | 1685 | | DeliveryTypes = new List<DeliveryTypeResponseDto>(movement.Receiver.Cluster.ClusterDeliveryTypes.Select |
| | 0 | 1686 | | }; |
| | | 1687 | | |
| | 0 | 1688 | | return result; |
| | 0 | 1689 | | } |
| | | 1690 | | |
| | | 1691 | | /// <summary> |
| | | 1692 | | /// Делает запись в журнал изменения статуса документов |
| | | 1693 | | /// </summary> |
| | | 1694 | | /// <param name="movement">Изменяемый документ</param> |
| | | 1695 | | /// <param name="mStatus">Статус после изменения</param> |
| | | 1696 | | /// <returns></returns> |
| | 102 | 1697 | | private async Task SaveJournal(Movement movement, MovementStatus mStatus) => await _movementStatusJournalService |
| | 102 | 1698 | | { |
| | 102 | 1699 | | Movement = movement, |
| | 102 | 1700 | | StatusCurrent = mStatus |
| | 102 | 1701 | | }); |
| | | 1702 | | |
| | | 1703 | | /// <summary> |
| | | 1704 | | /// Возвращает объект состояния записи |
| | | 1705 | | /// </summary> |
| | | 1706 | | /// <param name="id"></param> |
| | | 1707 | | /// <returns></returns> |
| | | 1708 | | private async Task<RecordsState> GetRecordState(long id) => |
| | 133 | 1709 | | (await _dirService.GetRecordState(id)); |
| | | 1710 | | |
| | | 1711 | | /// <summary> |
| | | 1712 | | /// Возвращает объект типа документа по ид |
| | | 1713 | | /// </summary> |
| | | 1714 | | /// <param name="id"></param> |
| | | 1715 | | /// <returns></returns> |
| | | 1716 | | public async Task<MovementType> GetMovementType(long id) => |
| | 164 | 1717 | | await _movementTypeStatusService.GetMovementType(id); |
| | | 1718 | | |
| | | 1719 | | /// <summary> |
| | | 1720 | | /// Установка параметров запроса |
| | | 1721 | | /// </summary> |
| | | 1722 | | /// <param name="inParam">Объект запроса</param> |
| | | 1723 | | /// <returns></returns> |
| | | 1724 | | /// <exception cref="ForbidException"></exception> |
| | | 1725 | | private async Task<MovementParam> SetUserParam(MovementParam inParam) |
| | | 1726 | | { |
| | | 1727 | | inParam.StatusId = (_authUserService.ContragentKindId, inParam.Kind) switch |
| | | 1728 | | { |
| | | 1729 | | ((long) ContragentKind.Wholesaler, MovementKind.Order) => throw new ForbidException(), |
| | | 1730 | | ((long) ContragentKind.Retailer, MovementKind.Shipment) => inParam.StatusId == (long)MovementsStatus.Shi |
| | | 1731 | | ? throw new ForbidException() |
| | | 1732 | | : inParam.StatusId, |
| | | 1733 | | _ => inParam.StatusId |
| | | 1734 | | }; |
| | | 1735 | | |
| | | 1736 | | inParam.ExcludedStatuses = (inParam.Kind, _authUserService.ContragentKindId) switch |
| | | 1737 | | { |
| | | 1738 | | (MovementKind.Shipment, (long)ContragentKind.Retailer) => new []{(long)MovementsStatus.ShipmentDraft}, |
| | | 1739 | | (MovementKind.Order, (long)ContragentKind.Platform) => new []{(long)MovementsStatus.OrderDraft}, |
| | | 1740 | | _ => null |
| | | 1741 | | }; |
| | | 1742 | | |
| | | 1743 | | switch (_authUserService.ContragentKindId) |
| | | 1744 | | { |
| | | 1745 | | case ((long)ContragentKind.Retailer): |
| | | 1746 | | { |
| | 0 | 1747 | | if (inParam.ReceiverId != 0 && (await _departmentService.GetDepartmentsByUserAndKind(_authUserServic |
| | | 1748 | | throw new ForbidException(); |
| | | 1749 | | inParam.CustomerId = _authUserService.ContragentId; |
| | | 1750 | | inParam.СontragentBuyerFilter = default; |
| | | 1751 | | break; |
| | | 1752 | | } |
| | | 1753 | | case ((long)ContragentKind.Wholesaler): |
| | | 1754 | | { |
| | | 1755 | | inParam.SupplierId = _authUserService.ContragentId; |
| | | 1756 | | break; |
| | | 1757 | | } |
| | | 1758 | | case ((long)ContragentKind.Manufacturer): |
| | | 1759 | | { |
| | | 1760 | | inParam.SupplierId = _authUserService.ContragentId; |
| | | 1761 | | break; |
| | | 1762 | | } |
| | | 1763 | | } |
| | | 1764 | | |
| | | 1765 | | inParam.ShowAnonym = _authUserService.IsAnonym() ? "anonymousonly" : _authUserService.IsUserPlatform() ? in |
| | | 1766 | | inParam.Limit = inParam.Limit <= 0 ? 10 : inParam.Limit; |
| | | 1767 | | return inParam; |
| | | 1768 | | } |
| | | 1769 | | |
| | | 1770 | | /// <summary> |
| | | 1771 | | /// Маппер для анонимной заявки |
| | | 1772 | | /// </summary> |
| | | 1773 | | /// <returns></returns> |
| | | 1774 | | private IMapper FromAnnonymToDtoMapper() |
| | 0 | 1775 | | { |
| | 0 | 1776 | | var config = new MapperConfiguration(cfg => |
| | 0 | 1777 | | { |
| | 0 | 1778 | | cfg.CreateMap<AnonymousMovement, MovementDTO>() |
| | 0 | 1779 | | .ForMember(d => d.ItemsCount, |
| | 0 | 1780 | | e => e.MapFrom(s => s.Items.Count)) |
| | 0 | 1781 | | .ForMember(d => d.Sum, e => |
| | 0 | 1782 | | e.MapFrom(s => s.Items.Sum(g => g.Price * g.Quantity))) |
| | 0 | 1783 | | .ForMember(d => d.SumWithoutVat, e => e.MapFrom(s => Math.Round(s.Items.Sum(x => x.Price / (x.Good.V |
| | 0 | 1784 | | .ForMember(d => d.MovementType, e => e.MapFrom(s => (long)MovementKind.Order)) |
| | 0 | 1785 | | .ForMember(d => d.MovementStatus, e => e.MapFrom(s => new EnumDB_DTO { Id = (long)MovementsStatus.Or |
| | 0 | 1786 | | cfg.CreateMap<Department, DepartmentShortWithAddressDTO>() |
| | 0 | 1787 | | .ForMember(d => d.Address, e => e.MapFrom(s => s.ActualAddress.FullAddress)); |
| | 0 | 1788 | | cfg.CreateMap<Contragent, ContragentShortDTO>() |
| | 0 | 1789 | | .ForMember(d => d.KindId, e => e.MapFrom(s => (int)s.ContragentsKind.Id)); |
| | 0 | 1790 | | cfg.CreateMap<MovementItem, MovementItemResponseDTO>(); |
| | 0 | 1791 | | cfg.CreateMap<Good, GoodShortDTO>() |
| | 0 | 1792 | | .ForMember(d => d.Barcode, e => e.AllowNull()); |
| | 0 | 1793 | | }); |
| | 0 | 1794 | | return config.CreateMapper(); |
| | 0 | 1795 | | } |
| | | 1796 | | |
| | | 1797 | | /// <summary> |
| | | 1798 | | /// Маппер для документа |
| | | 1799 | | /// </summary> |
| | | 1800 | | /// <param name="anonymMapper"></param> |
| | | 1801 | | /// <returns></returns> |
| | | 1802 | | private IMapper ToDtoMapper(bool anonymMapper = false) |
| | 127 | 1803 | | { |
| | 127 | 1804 | | var config = new MapperConfiguration(cfg => |
| | 254 | 1805 | | { |
| | 254 | 1806 | | cfg.CreateMap<Movement, MovementDTO>() |
| | 381 | 1807 | | .ForMember(d => d.ItemsCount, e => e.MapFrom(s => s.Items.Count)) |
| | 381 | 1808 | | .ForMember(d => d.Sum, e => e.MapFrom(s => s.Items.Sum(x => x.Price * x.Quantity))) |
| | 254 | 1809 | | //стобы найти цену без НДС, надо цену разделить на 1.${НДС}. например при НДС 13% и цене 113, цена б |
| | 254 | 1810 | | .ForMember(d => d.SumWithoutVat, |
| | 381 | 1811 | | e => e.MapFrom(s => |
| | 381 | 1812 | | Math.Round( |
| | 381 | 1813 | | s.Items.Sum(x => |
| | 381 | 1814 | | x.Price / (x.Good.VatsKind.Value == 1 |
| | 381 | 1815 | | ? 1 |
| | 381 | 1816 | | : (decimal)x.Good.VatsKind.Value / 100 + 1) * x.Quantity), 2))) |
| | 381 | 1817 | | .ForMember(d => d.MovementType, e => e.MapFrom(s => s.MovementType.Id)) |
| | 254 | 1818 | | .ForMember(d => d.MovementStatus, |
| | 381 | 1819 | | e => e.MapFrom(s => new EnumDB_DTO |
| | 381 | 1820 | | { Id = s.MovementStatus.Id, Code = s.MovementStatus.Code, Name = s.MovementStatus.Name })) |
| | 381 | 1821 | | .ForMember(d => d.DemoId, e => e.MapFrom(s => anonymMapper ? s.GUID : Guid.Empty)) |
| | 381 | 1822 | | .ForMember(d => d.IsDemo, e => e.MapFrom(s => anonymMapper)) |
| | 381 | 1823 | | .ForMember(d => d.InQueueTransferDate, e => e.MapFrom(s => |
| | 381 | 1824 | | s.MovementStatusJournals.Count > 0 && s.MovementStatusJournals |
| | 381 | 1825 | | .Any(d => d.StatusCurrent.Id == (long)MovementsStatus.InQueue) |
| | 381 | 1826 | | ? s.MovementStatusJournals |
| | 381 | 1827 | | .FirstOrDefault(d => d.StatusCurrent.Id == (long)MovementsStatus.InQueue) |
| | 381 | 1828 | | .CreationDateTime |
| | 381 | 1829 | | : DateTime.MinValue)) |
| | 381 | 1830 | | .ForMember(d => d.WasEdited, e => e.MapFrom(s => s.MovementType.Id == (long)MovementKind.Order |
| | 381 | 1831 | | ? s.Children != null |
| | 381 | 1832 | | ? !s.Children.Items.Select(j => new { j.Good, j.Quantity }) |
| | 381 | 1833 | | .Equals(s.Items.Select(j => new { j.Good, j.Quantity })) |
| | 381 | 1834 | | : false |
| | 381 | 1835 | | : false)) |
| | 381 | 1836 | | .ForMember(d => d.MinOrderSum, e => e.MapFrom(s => |
| | 381 | 1837 | | s.MovementStatus.Id == (long)MovementsStatus.OrderDraft |
| | 381 | 1838 | | ? s.Receiver.Cluster.MinOrderSum |
| | 381 | 1839 | | : s.MinOrderSum)) |
| | 381 | 1840 | | .ForMember(d => d.AllowedDeliveryTypes, e => e.MapFrom( |
| | 381 | 1841 | | s => s.Receiver.Cluster.ClusterDeliveryTypes.Select(b => new DeliveryTypeResponseDto |
| | 381 | 1842 | | { |
| | 381 | 1843 | | Id = b.DeliveryType.Id, |
| | 381 | 1844 | | Name = b.DeliveryType.Name |
| | 381 | 1845 | | }))); |
| | 127 | 1846 | | |
| | 254 | 1847 | | cfg.CreateMap<Department, DepartmentShortWithAddressDTO>() |
| | 381 | 1848 | | .ForMember(d => d.Address, e => e.MapFrom(s => s.ActualAddress.FullAddress)); |
| | 254 | 1849 | | cfg.CreateMap<Contragent, ContragentShortDTO>() |
| | 381 | 1850 | | .ForMember(d => d.KindId, e => e.MapFrom(s => (int)s.ContragentsKind.Id)); |
| | 254 | 1851 | | cfg.CreateMap<MovementItem, MovementItemResponseDTO>(); |
| | 254 | 1852 | | cfg.CreateMap<Good, GoodShortDTO>() |
| | 381 | 1853 | | .ForMember(d => d.Barcode, e => e.MapFrom(s => s.GoodBarcodes.FirstOrDefault(d => d.IsPrimary) != nu |
| | 381 | 1854 | | ? s.GoodBarcodes.FirstOrDefault(d => d.IsPrimary).BarCode.Code |
| | 381 | 1855 | | : s.DefaultBarCode.Code)); |
| | 254 | 1856 | | cfg.CreateMap<MovementNote, MovementNoteDTO>() |
| | 381 | 1857 | | .ForMember(d => d.User, e => e.MapFrom(s => new UserNoteDTO { Id = s.CreatedByUser.Id, FullName = $" |
| | 254 | 1858 | | cfg.CreateMap<DeliveryType, IdNameDTO>(); |
| | 254 | 1859 | | }); |
| | 127 | 1860 | | return config.CreateMapper(); |
| | 127 | 1861 | | } |
| | | 1862 | | |
| | | 1863 | | /// <summary> |
| | | 1864 | | /// Маппер для итемов заказа |
| | | 1865 | | /// </summary> |
| | | 1866 | | /// <param name="departmentId"></param> |
| | | 1867 | | /// <param name="discountColors"></param> |
| | | 1868 | | /// <returns></returns> |
| | | 1869 | | private IMapper ToItemDTOMapper(long departmentId, List<DiscountColor> discountColors) |
| | 73 | 1870 | | { |
| | 73 | 1871 | | var config = new MapperConfiguration(cfg => |
| | 146 | 1872 | | { |
| | 146 | 1873 | | cfg.CreateMap<MovementItem, MovementItemResponseDTO>() |
| | 146 | 1874 | | .ForMember(d => d.PriceWithoutVat, |
| | 219 | 1875 | | e => e.MapFrom(s => |
| | 219 | 1876 | | Math.Round(s.Price / (s.Good.VatsKind.Value == 1 |
| | 219 | 1877 | | ? 1 |
| | 219 | 1878 | | : (decimal) s.Good.VatsKind.Value / 100 + 1), 2))) |
| | 146 | 1879 | | .ForMember(d => d.Discount, |
| | 219 | 1880 | | e => e.MapFrom(x => x.Good.Prices.Actual(departmentId).DiscountForDisplay())) |
| | 146 | 1881 | | .ForMember(d => d.LabelColor, |
| | 219 | 1882 | | e => e.MapFrom(x => |
| | 219 | 1883 | | GetDiscountColor(x.Good.Prices.Actual(departmentId).DiscountForDisplay(), discountColors))) |
| | 219 | 1884 | | .ForMember(d => d.OldPrice, e => e.MapFrom(x => |
| | 219 | 1885 | | Math.Ceiling( |
| | 219 | 1886 | | //Текущая цена товара |
| | 219 | 1887 | | x.Good.Prices.Actual(departmentId).PriceOld.GetValueOrDefault(0) |
| | 219 | 1888 | | // умножаем на коофициент контракта |
| | 219 | 1889 | | * _currentMovement.Customer.ContractsAsBuyer.FirstOrDefault(d => !d.IsDeleted |
| | 219 | 1890 | | && d.BeginDate <= |
| | 219 | 1891 | | DateTime.UtcNow |
| | 219 | 1892 | | && d.EndDate > |
| | 219 | 1893 | | DateTime.UtcNow) |
| | 219 | 1894 | | .RatioForCalculations |
| | 219 | 1895 | | // умножаем на коофициент кластера |
| | 219 | 1896 | | * _currentMovement.Receiver.Cluster.RatioForCalculations |
| | 219 | 1897 | | // умножаем на коофициент категории |
| | 219 | 1898 | | * (x.Good.Category.DepartmentCategoryRatios.Any(r => |
| | 219 | 1899 | | !r.IsDeleted && r.DepartmentId == _currentMovement.Receiver.Cluster.WarehouseId) |
| | 219 | 1900 | | ? x.Good.Category.DepartmentCategoryRatios.First(r => |
| | 219 | 1901 | | !r.IsDeleted && r.DepartmentId == _currentMovement.Receiver.Cluster.WarehouseId) |
| | 219 | 1902 | | .RatioForCalculations |
| | 219 | 1903 | | : 1) |
| | 219 | 1904 | | ) |
| | 219 | 1905 | | )) |
| | 219 | 1906 | | .ForMember(d => d.Good, e => e.MapFrom(s => s.Good)) |
| | 146 | 1907 | | .ForPath(d => d.Good.MinQuantity, |
| | 219 | 1908 | | e => e.MapFrom(s => |
| | 219 | 1909 | | (s.Good.DepartmentGoodSettings.FirstOrDefault(g => |
| | 219 | 1910 | | !g.IsDeleted && g.DepartmentId == departmentId) == null |
| | 219 | 1911 | | ? 1 |
| | 219 | 1912 | | : s.Good.DepartmentGoodSettings |
| | 219 | 1913 | | .FirstOrDefault(g => !g.IsDeleted && g.DepartmentId == departmentId) |
| | 219 | 1914 | | .MinQuantity))) |
| | 146 | 1915 | | .ForPath(d => d.Good.PickingQuantum, |
| | 219 | 1916 | | e => e.MapFrom(s => |
| | 219 | 1917 | | s.Good.DepartmentGoodSettings.FirstOrDefault(g => |
| | 219 | 1918 | | !g.IsDeleted && g.DepartmentId == departmentId) == null |
| | 219 | 1919 | | ? 1 |
| | 219 | 1920 | | : s.Good.DepartmentGoodSettings |
| | 219 | 1921 | | .FirstOrDefault(g => !g.IsDeleted && g.DepartmentId == departmentId) |
| | 219 | 1922 | | .PickingQuantum)); |
| | 73 | 1923 | | |
| | 146 | 1924 | | cfg.CreateMap<Good, ItemGoodDTO>() |
| | 219 | 1925 | | .ForMember(d => d.VendorCode, e => e.MapFrom(s => s.GetActualVendorCode(departmentId))) |
| | 219 | 1926 | | .ForMember(d => d.VatKindValue, e => e.MapFrom(s => s.VatsKind.Name)) |
| | 219 | 1927 | | .ForMember(d => d.UnitKindName, e => e.MapFrom(s => s.UnitsKind.Name)) |
| | 146 | 1928 | | .ForMember(d => d.Category, |
| | 219 | 1929 | | e => e.MapFrom(s => new IdNameDTO {Id = s.Category.Id, Name = s.Category.Name})) |
| | 219 | 1930 | | .ForMember(d => d.Brand, e => e.MapFrom(s => new IdNameDTO {Id = s.Brand.Id, Name = s.Brand.Name})) |
| | 219 | 1931 | | .ForMember(d => d.Photo, e => e.MapFrom(g => (g.Photos.FirstOrDefault()))) |
| | 219 | 1932 | | .ForMember(d => d.BarCode, e => e.MapFrom(g => g.GetActualBarCode())); |
| | 146 | 1933 | | }); |
| | 73 | 1934 | | return config.CreateMapper(); |
| | 73 | 1935 | | } |
| | | 1936 | | |
| | | 1937 | | /// <summary> |
| | | 1938 | | /// Добавляет вложение к мувменту |
| | | 1939 | | /// </summary> |
| | | 1940 | | /// <param name="movementId">id мувмента</param> |
| | | 1941 | | /// <param name="file">вложение</param> |
| | | 1942 | | /// <returns></returns> |
| | | 1943 | | public async Task<UploadResultDTO> AddAttachment(long movementId, List<IFormFile> files) |
| | | 1944 | | { |
| | | 1945 | | var movement = await _movementService.GetMovementWithAttachment(movementId) |
| | | 1946 | | ?? throw new KeyNotFoundException($"Документ {movementId} не найден"); |
| | | 1947 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | | 1948 | | if (!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | | 1949 | | _authUserService.ContragentId, _authUserService.IsOwner(), |
| | | 1950 | | userDeps)) |
| | | 1951 | | { |
| | | 1952 | | throw new ForbidException(); |
| | | 1953 | | } |
| | | 1954 | | |
| | | 1955 | | long size = files.Sum(d => d.Length); |
| | | 1956 | | long maxSettingsTotalSize = Int64.Parse(_confSettings.GetConfValue(nameof(MovementAttachmentSettings), nameo |
| | | 1957 | | if (size + movement.TotalAttachmentSize > maxSettingsTotalSize) |
| | | 1958 | | { |
| | | 1959 | | throw new SvetaException($"Занимаемый объем загружаемых файлов {size} и " + |
| | | 1960 | | $"общий объем уже сохраненных файлов {movement.TotalAttachmentSize} превышает " |
| | | 1961 | | $"разрешенный суммарный размер файлов для вложений {maxSettingsTotalSize}", (in |
| | | 1962 | | } |
| | | 1963 | | List<string> errors = new List<string>(); |
| | | 1964 | | long maxSettingsFileSize = Int64.Parse(_confSettings.GetConfValue(nameof(MovementAttachmentSettings), |
| | | 1965 | | nameof(MovementAttachmentSettings.MaxFileSize))); |
| | | 1966 | | foreach (var formFile in files) |
| | | 1967 | | { |
| | | 1968 | | try |
| | | 1969 | | { |
| | | 1970 | | if (formFile.Length > maxSettingsFileSize) |
| | | 1971 | | { |
| | | 1972 | | errors.Add($"Размер файла {formFile.FileName} превышает разрешенный размер файлов {maxSettingsFi |
| | | 1973 | | $"для вложений и не будет загружен"); |
| | | 1974 | | continue ; |
| | | 1975 | | } |
| | | 1976 | | string filename = null; |
| | | 1977 | | filename = await _diskStorageService.SaveUploadToDownloadDir(formFile); |
| | | 1978 | | movement.MovementAttachments.Add(new MovementAttachment |
| | | 1979 | | { |
| | | 1980 | | FileName = filename, |
| | | 1981 | | Size = (int)formFile.Length |
| | | 1982 | | }); |
| | | 1983 | | } |
| | | 1984 | | catch (Exception e) |
| | | 1985 | | { |
| | | 1986 | | _logger.LogError("Ошибка загрузки файла вложения" + " " +e.Message + " " + e.StackTrace); |
| | | 1987 | | errors.Add($"Во время загрузки файла {formFile.FileName} произошла ошибка. Файл не был загружен"); |
| | | 1988 | | } |
| | | 1989 | | } |
| | | 1990 | | |
| | | 1991 | | movement.TotalAttachmentSize = movement.MovementAttachments.Sum(d => d.Size); |
| | | 1992 | | await _movementService.UpdateMovement(movement); |
| | | 1993 | | return new UploadStringResultDto(){ succeed = true , errorCount = errors.Count, Result = errors}; |
| | | 1994 | | } |
| | | 1995 | | |
| | | 1996 | | /// <summary> |
| | | 1997 | | /// Возвращает информацию о вложениях в документ |
| | | 1998 | | /// </summary> |
| | | 1999 | | /// <param name="movementId">Идентификатор документа</param> |
| | | 2000 | | /// <returns></returns> |
| | | 2001 | | public async Task<PaginatedData<List<MovementAttachmentInfoDto>>> GetAttachmentsInfo(long movementId, int page = |
| | | 2002 | | int limit = 10, string filter = default, string sort = default) |
| | 0 | 2003 | | { |
| | 0 | 2004 | | page = page < 0 ? 0 : page; |
| | 0 | 2005 | | limit = limit <= 0 ? 10 : limit; |
| | 0 | 2006 | | var movement = await _movementService.GetMovementWithAttachment(movementId) ?? |
| | 0 | 2007 | | throw new ArgumentException($"Документ {movementId} не найден"); |
| | 0 | 2008 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 2009 | | if (!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 2010 | | _authUserService.ContragentId, _authUserService.IsOwner(), |
| | 0 | 2011 | | userDeps)) |
| | 0 | 2012 | | { |
| | 0 | 2013 | | throw new ForbidException(); |
| | | 2014 | | } |
| | | 2015 | | |
| | 0 | 2016 | | var result = new PaginatedData<List<MovementAttachmentInfoDto>> |
| | 0 | 2017 | | { |
| | 0 | 2018 | | TotalCount = 0, |
| | 0 | 2019 | | TotalFilteredCount = 0, |
| | 0 | 2020 | | Result = new List<MovementAttachmentInfoDto>() |
| | 0 | 2021 | | }; |
| | 0 | 2022 | | var list = new List<MovementAttachmentInfoDto>(); |
| | 0 | 2023 | | movement.MovementAttachments |
| | 0 | 2024 | | .Where(d => string.IsNullOrEmpty(filter) || d.FileName.ToLower().Contains(filter.Trim().ToLower())) |
| | 0 | 2025 | | .ToList() |
| | 0 | 2026 | | .ForEach(attachment => |
| | 0 | 2027 | | { |
| | 0 | 2028 | | list.Add((MovementAttachmentInfoDto) attachment); |
| | 0 | 2029 | | }); |
| | | 2030 | | |
| | | 2031 | | Expression<Func<MovementAttachmentInfoDto, object>> sortingExpression; |
| | | 2032 | | |
| | 0 | 2033 | | if (!string.IsNullOrEmpty(sort)) |
| | 0 | 2034 | | { |
| | 0 | 2035 | | switch ((sort.Split("|").FirstOrDefault() ?? "").ToLower()) |
| | | 2036 | | { |
| | | 2037 | | case "name": |
| | 0 | 2038 | | sortingExpression = e => e.Name; |
| | 0 | 2039 | | break; |
| | | 2040 | | case "created_on": |
| | 0 | 2041 | | sortingExpression = e => e.CreatedDateTime; |
| | 0 | 2042 | | break; |
| | | 2043 | | case "extension": |
| | 0 | 2044 | | sortingExpression = e => e.Extension; |
| | 0 | 2045 | | break; |
| | | 2046 | | case "size": |
| | 0 | 2047 | | sortingExpression = e => e.Size; |
| | 0 | 2048 | | break; |
| | | 2049 | | case "contenttype": |
| | 0 | 2050 | | sortingExpression = e => e.ContentType; |
| | 0 | 2051 | | break; |
| | | 2052 | | default: |
| | 0 | 2053 | | sortingExpression = e => e.Id; |
| | 0 | 2054 | | break; |
| | | 2055 | | } |
| | | 2056 | | |
| | 0 | 2057 | | if((sort.Split("|").LastOrDefault() ?? "").ToLower().Equals("desc")) |
| | 0 | 2058 | | list = list.AsQueryable().OrderByDescending(sortingExpression).ToList(); |
| | | 2059 | | else |
| | 0 | 2060 | | list = list.AsQueryable().OrderBy(sortingExpression).ToList(); |
| | 0 | 2061 | | } |
| | | 2062 | | |
| | 0 | 2063 | | result.TotalCount = movement.MovementAttachments.Count; |
| | 0 | 2064 | | result.TotalFilteredCount = list.Count; |
| | 0 | 2065 | | result.Result = list.Skip((page >= 1 ? page - 1 : page) * limit).Take(limit).ToList(); |
| | 0 | 2066 | | return result; |
| | 0 | 2067 | | } |
| | | 2068 | | |
| | | 2069 | | /// <summary> |
| | | 2070 | | /// Загружает вложение из мувмента |
| | | 2071 | | /// </summary> |
| | | 2072 | | /// <param name="movementId">id мувмента</param> |
| | | 2073 | | /// <returns></returns> |
| | | 2074 | | public async Task<MovementAttachmentReadResult> DownloadAttachment(long movementId, List<long> attachments) |
| | 0 | 2075 | | { |
| | 0 | 2076 | | var movement = await _movementService.GetMovementWithAttachment(movementId) ?? throw new KeyNotFoundExceptio |
| | 0 | 2077 | | var userDeps = await _userService.GetUserDepartments(_authUserService.UserId); |
| | 0 | 2078 | | if (!movement.CheckCanWorkWithMovement(_authUserService.ContragentKindId, |
| | 0 | 2079 | | _authUserService.ContragentId, _authUserService.IsOwner(), |
| | 0 | 2080 | | userDeps)) |
| | 0 | 2081 | | { |
| | 0 | 2082 | | throw new ForbidException(); |
| | | 2083 | | } |
| | | 2084 | | |
| | 0 | 2085 | | if (movement.MovementAttachments.Count == 0) |
| | 0 | 2086 | | { |
| | 0 | 2087 | | throw new SvetaException("Вложения отсутствуют", (int)ErrorCode.Warning); |
| | | 2088 | | } |
| | | 2089 | | |
| | 0 | 2090 | | var result = new MovementAttachmentReadResult(); |
| | 0 | 2091 | | if (attachments.Count == 1) |
| | 0 | 2092 | | { |
| | 0 | 2093 | | var stream = new MemoryStream(); |
| | 0 | 2094 | | var file = movement.MovementAttachments.FirstOrDefault(d => |
| | 0 | 2095 | | d.Id == attachments.First()) ?? throw new ArgumentException( |
| | 0 | 2096 | | $"Файл с идентификатором {attachments.First()} не найден" + |
| | 0 | 2097 | | $"во вложениях"); |
| | 0 | 2098 | | var info = (MovementAttachmentInfoDto) file; |
| | 0 | 2099 | | var path = _diskStorageService.GetDownloadPath(info.Name); |
| | | 2100 | | try |
| | 0 | 2101 | | { |
| | 0 | 2102 | | using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) |
| | 0 | 2103 | | { |
| | 0 | 2104 | | fs.CopyTo(stream); |
| | 0 | 2105 | | } |
| | 0 | 2106 | | } |
| | 0 | 2107 | | catch (FileNotFoundException fe) |
| | 0 | 2108 | | { |
| | 0 | 2109 | | throw new ArgumentException($"Файл {info.Name} не найден на диске"); |
| | | 2110 | | } |
| | 0 | 2111 | | catch (Exception e) |
| | 0 | 2112 | | { |
| | 0 | 2113 | | _logger.LogError("Ошибка чтения файла "+ info.Name + " " + e.Message + " " + e.StackTrace); |
| | 0 | 2114 | | } |
| | | 2115 | | |
| | 0 | 2116 | | result.Stream = stream; |
| | 0 | 2117 | | result.ContentType = info.ContentType; |
| | 0 | 2118 | | result.FileName = info.Name; |
| | 0 | 2119 | | } |
| | | 2120 | | else |
| | 0 | 2121 | | { |
| | 0 | 2122 | | var stream = new MemoryStream(); |
| | 0 | 2123 | | using (var arch = new ZipArchive(stream, ZipArchiveMode.Create, true)) |
| | 0 | 2124 | | { |
| | 0 | 2125 | | movement.MovementAttachments |
| | 0 | 2126 | | .Where(d => attachments.Contains(d.Id)) |
| | 0 | 2127 | | .ToList() |
| | 0 | 2128 | | .ForEach(attachment => |
| | 0 | 2129 | | { |
| | 0 | 2130 | | var info = (MovementAttachmentInfoDto) attachment; |
| | 0 | 2131 | | var path = _diskStorageService.GetDownloadPath(info.Name); |
| | 0 | 2132 | | var file = arch.CreateEntry(info.Name); |
| | 0 | 2133 | | using (var entryStream = file.Open()) |
| | 0 | 2134 | | try |
| | 0 | 2135 | | { |
| | 0 | 2136 | | using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) |
| | 0 | 2137 | | { |
| | 0 | 2138 | | fs.CopyTo(entryStream); |
| | 0 | 2139 | | } |
| | 0 | 2140 | | } |
| | 0 | 2141 | | catch (FileNotFoundException fe) |
| | 0 | 2142 | | { |
| | 0 | 2143 | | throw new ArgumentException($"Файл {info.Name} не найден на диске"); |
| | 0 | 2144 | | } |
| | 0 | 2145 | | catch (Exception e) |
| | 0 | 2146 | | { |
| | 0 | 2147 | | _logger.LogError("Ошибка чтения файла "+ info.Name + " " + e.Message + " " + e.StackTrac |
| | 0 | 2148 | | } |
| | 0 | 2149 | | }); |
| | 0 | 2150 | | } |
| | 0 | 2151 | | result.Stream = stream; |
| | 0 | 2152 | | result.ContentType = ContentTypeChecker.CheckContentType("zip"); |
| | 0 | 2153 | | result.FileName = "archive.zip"; |
| | 0 | 2154 | | } |
| | | 2155 | | |
| | 0 | 2156 | | result.Stream.Position = 0; |
| | 0 | 2157 | | return result; |
| | 0 | 2158 | | } |
| | | 2159 | | |
| | | 2160 | | /// <summary> |
| | | 2161 | | /// Удаляет вложение из мувмента |
| | | 2162 | | /// </summary> |
| | | 2163 | | /// <param name="movementId">id мувмента</param> |
| | | 2164 | | /// <param name="attachmentId">идентификатор вложения</param> |
| | | 2165 | | /// <returns></returns> |
| | | 2166 | | public async Task DeleteAttachment(long movementId, long attachmentId) |
| | | 2167 | | { |
| | | 2168 | | var movement = await _movementService.GetMovementWithAttachment(movementId) |
| | | 2169 | | ?? throw new KeyNotFoundException($"Документ не найден"); |
| | | 2170 | | if (movement.Supplier.Id != _authUserService.ContragentId) |
| | | 2171 | | throw new ForbidException(); |
| | 0 | 2172 | | var attachment = movement.MovementAttachments.FirstOrDefault(d => d.Id == attachmentId) |
| | | 2173 | | ?? throw new SvetaException($"Документ с идентификатором {attachmentId} не найден", (int)Er |
| | | 2174 | | attachment.IsDeleted = true; |
| | 0 | 2175 | | movement.MovementAttachments[movement.MovementAttachments.FindIndex(d => d.Id == attachmentId)] = |
| | | 2176 | | attachment; |
| | | 2177 | | await _movementService.UpdateMovement(movement); |
| | | 2178 | | } |
| | | 2179 | | |
| | | 2180 | | /// <summary> |
| | | 2181 | | /// Назначает заявке тип доставки |
| | | 2182 | | /// </summary> |
| | | 2183 | | /// <param name="orderId">id заявки</param> |
| | | 2184 | | /// <param name="deliveryTypeId">id типа доставки</param> |
| | | 2185 | | /// <returns></returns> |
| | | 2186 | | public async Task SetDeliveryType(long orderId, long deliveryTypeId) |
| | 1 | 2187 | | { |
| | 1 | 2188 | | var movement = await _movementService.GetLightMovement(orderId) ?? throw new KeyNotFoundException($"Документ |
| | 1 | 2189 | | if (movement.Customer.Id != _authUserService.ContragentId) |
| | 0 | 2190 | | throw new ForbidException(); |
| | 1 | 2191 | | var deliveryType = await _dirService.GetDeliveryType(deliveryTypeId) ?? throw new KeyNotFoundException($"Тип |
| | 1 | 2192 | | movement.DeliveryType = deliveryType; |
| | 1 | 2193 | | await _movementService.UpdateMovement(movement); |
| | 1 | 2194 | | } |
| | | 2195 | | |
| | | 2196 | | /// <summary> |
| | | 2197 | | /// Заполняет полный путь к вложению |
| | | 2198 | | /// </summary> |
| | | 2199 | | /// <param name="filename">Имя файла</param> |
| | | 2200 | | /// <returns></returns> |
| | | 2201 | | private List<AttachmentDTO> GetFullPath(List<string> filenames) |
| | 0 | 2202 | | { |
| | 0 | 2203 | | var res = new List<AttachmentDTO>(); |
| | 0 | 2204 | | if (filenames == null || filenames.Count == 0) |
| | 0 | 2205 | | return res; |
| | 0 | 2206 | | filenames.ForEach(filename => |
| | 0 | 2207 | | { |
| | 0 | 2208 | | if (!string.IsNullOrEmpty(filename)) |
| | 0 | 2209 | | { |
| | 0 | 2210 | | res.Add(new AttachmentDTO { FileName = filename, FilePath = _diskStorageService.GetDownloadPath(file |
| | 0 | 2211 | | } |
| | 0 | 2212 | | }); |
| | 0 | 2213 | | return res; |
| | 0 | 2214 | | } |
| | | 2215 | | |
| | | 2216 | | /// <summary> |
| | | 2217 | | /// Возвращает цвет ярлыка для скидки |
| | | 2218 | | /// </summary> |
| | | 2219 | | /// <param name="discount">сумма скидки</param> |
| | | 2220 | | /// <param name="discountColors">массив цветов</param> |
| | | 2221 | | /// <returns></returns> |
| | | 2222 | | private string GetDiscountColor(int? discount, List<DiscountColor> discountColors) |
| | 77 | 2223 | | { |
| | 77 | 2224 | | return discount.HasValue |
| | 0 | 2225 | | ? discountColors.Where(x => x.DiscountLevel <= discount.Value).OrderByDescending(x => x.DiscountLevel).F |
| | 77 | 2226 | | : null; |
| | 77 | 2227 | | } |
| | | 2228 | | |
| | | 2229 | | /// <summary> |
| | | 2230 | | /// Проверка соответствия изменяемого количества настрокам департамента для товара |
| | | 2231 | | /// </summary> |
| | | 2232 | | /// <param name="quantity">количество заказанного товара</param> |
| | | 2233 | | /// <param name="goodSettings">объект настроек товара</param> |
| | | 2234 | | /// <returns></returns> |
| | | 2235 | | /// <exception cref="ArgumentException"></exception> |
| | | 2236 | | private void CheckSettings(decimal quantity, DepartmentGoodSetting goodSettings) |
| | 68 | 2237 | | { |
| | 68 | 2238 | | if (quantity < goodSettings.MinQuantity) |
| | 0 | 2239 | | throw new ArgumentException($"Минимальное количество заказанного товара {goodSettings.Good.Name} должно |
| | 68 | 2240 | | if (quantity % goodSettings.PickingQuantum != 0) |
| | 0 | 2241 | | throw new ArgumentException($"Количество заказанного товара {quantity} не кратно кванту поставки {goodSe |
| | 68 | 2242 | | } |
| | | 2243 | | |
| | | 2244 | | /// <summary> |
| | | 2245 | | /// Получение черновика для типа документа |
| | | 2246 | | /// </summary> |
| | | 2247 | | /// <param name="type">тип документа</param> |
| | | 2248 | | /// <returns></returns> |
| | 174 | 2249 | | private async Task<MovementStatus> GetDraft(MovementType type) => await _movementTypeStatusService |
| | 174 | 2250 | | .GetStatus(type, "draft"); |
| | | 2251 | | |
| | | 2252 | | /// <summary> |
| | | 2253 | | /// Подготовливает список документов |
| | | 2254 | | /// </summary> |
| | | 2255 | | /// <param name="result">Список документов для обработки</param> |
| | | 2256 | | /// <param name="movementType">Тип документов</param> |
| | | 2257 | | /// <returns></returns> |
| | | 2258 | | private async Task<List<Movement>> PrepareStatusList(List<Movement> result, MovementType movementType) |
| | 0 | 2259 | | { |
| | 0 | 2260 | | var allActions = await _movementRouteActionsService.GetActions(); |
| | 0 | 2261 | | Parallel.For(0, result.Count, elem => |
| | 0 | 2262 | | { |
| | 0 | 2263 | | var item = result[elem]; |
| | 0 | 2264 | | item.Statuses = new long[5]; |
| | 0 | 2265 | | item.Actions = new List<MovementAction>(); |
| | 0 | 2266 | | item.FillActions(allActions, _authUserService.ContragentKindId, _authUserService.Roles); |
| | 0 | 2267 | | switch (movementType.Id) |
| | 0 | 2268 | | { |
| | 0 | 2269 | | case (long) MovementKind.Order: |
| | 0 | 2270 | | { |
| | 0 | 2271 | | // если статус родителя отказ то можно не продолжать |
| | 0 | 2272 | | if ((item.Statuses[0] = item.GetPositionStatus(0)) == -1) break; |
| | 0 | 2273 | | var children = item.Children; |
| | 0 | 2274 | | if (children == null) return; |
| | 0 | 2275 | | for (int i = 1; i < item.Statuses.Length; i++) |
| | 0 | 2276 | | { |
| | 0 | 2277 | | item.Statuses[i] = children.GetPositionStatus(i); |
| | 0 | 2278 | | if (item.Statuses[1] == -1) break; |
| | 0 | 2279 | | } |
| | 0 | 2280 | | break; |
| | 0 | 2281 | | } |
| | 0 | 2282 | | case (long) MovementKind.Shipment: |
| | 0 | 2283 | | { |
| | 0 | 2284 | | var parent = item.Parent; |
| | 0 | 2285 | | //Почему то не нашли родителя отгрузки |
| | 0 | 2286 | | if (parent == null) return; |
| | 0 | 2287 | | |
| | 0 | 2288 | | // если статус родителя отказ то можно не продолжать |
| | 0 | 2289 | | if ((item.Statuses[0] = parent.GetPositionStatus(0)) == -1) break; |
| | 0 | 2290 | | for (int i = 1; i < item.Statuses.Length; i++) |
| | 0 | 2291 | | { |
| | 0 | 2292 | | item.Statuses[i] = item.GetPositionStatus(i); |
| | 0 | 2293 | | if (item.Statuses[1] == -1) break; |
| | 0 | 2294 | | } |
| | 0 | 2295 | | break; |
| | 0 | 2296 | | } |
| | 0 | 2297 | | } |
| | 0 | 2298 | | }); |
| | 0 | 2299 | | return result; |
| | 0 | 2300 | | } |
| | | 2301 | | |
| | | 2302 | | /// <summary> |
| | | 2303 | | /// Сортировка документов |
| | | 2304 | | /// </summary> |
| | | 2305 | | /// <param name="movements">Массив документов</param> |
| | | 2306 | | /// <param name="sort">тип сортировки</param> |
| | | 2307 | | /// <returns></returns> |
| | 0 | 2308 | | public IQueryable<MovementDTO> SortMovement(IQueryable<MovementDTO> movements, string sort = default) => (sort ? |
| | 0 | 2309 | | { |
| | 0 | 2310 | | "created_on" => movements.OrderBy(d => d.CreationDateTime), |
| | 0 | 2311 | | "created_on|desc" => movements.OrderByDescending(d => d.CreationDateTime), |
| | 0 | 2312 | | "status" => movements.OrderBy(d => d.MovementStatus.Id), |
| | 0 | 2313 | | "status|desc" => movements.OrderByDescending(d => d.MovementStatus.Id), |
| | 0 | 2314 | | "customer" => movements.OrderBy(d => d.Customer), |
| | 0 | 2315 | | "customer|desc" => movements.OrderByDescending(d => d.Customer), |
| | 0 | 2316 | | "supplier" => movements.OrderBy(d => d.Supplier), |
| | 0 | 2317 | | "supplier|desc" => movements.OrderByDescending(d => d.Supplier), |
| | 0 | 2318 | | "receiver" => movements.OrderBy(d => d.Receiver), |
| | 0 | 2319 | | "receiver|desc" => movements.OrderByDescending(d => d.Receiver), |
| | 0 | 2320 | | "id|desc" => movements.OrderByDescending(d => d.Id), |
| | 0 | 2321 | | _ => movements.OrderBy(d => d.Id), |
| | 0 | 2322 | | }; |
| | | 2323 | | } |
| | | 2324 | | } |