public async Task <Order> CreateOrderAsync(Guid userId, CreateOrderDTO orderRequest) { // проверяем, не пришел ли второй раз тот же самый запрос var existOrder = _orderContext.Orders.FirstOrDefault(g => g.IdempotencyKey == orderRequest.IdempotencyKey); if (existOrder != null) { return(existOrder); } Order order = null; bool wasReserveProducts = false; try { var bucket = await _bucketRepository.GetBucketForUserAsync(userId); if (bucket == null || !bucket.Items.Any()) { return new Order { Status = OrderStatus.Error } } ; // 1. расчитываем стоимость (decimal totalPrice, decimal discount)price = await CalculateTotalPriceAsync(bucket, userId); // 2. сохраняем заказ в БД var orderItems = bucket.Items.Select(g => new OrderItem { ProductId = g.ProductId, Quantity = g.Quantity }).ToList(); using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { order = new Order { CreatedOnUtc = DateTime.UtcNow, DeliveryAddress = orderRequest.DeliveryAddress, Items = orderItems, Status = OrderStatus.Pending, UserId = userId, TotalPrice = price.totalPrice }; foreach (var item in orderItems) { item.Order = order; } _orderContext.Add(order); await _orderContext.SaveChangesAsync(); await _bucketRepository.ClearBucketAsync(userId); scope.Complete(); } // 3. резервируем на складе _warehouseServiceClient.AddHeader(Constants.USERID_HEADER, userId.ToString()); var reserve = await _warehouseServiceClient.ReserveAsync(new ReserveProductRequestDTO { OrderNumber = order.OrderNumber.ToString(), Products = order.Items.Select(g => new ReserveProductDTO { Id = g.ProductId, Count = g.Quantity }).ToList() }); wasReserveProducts = true; if (reserve.IsSuccess == false) { throw new Exception("Не удалось полностью зарезервировать товар"); } } catch (Exception ex) { if (wasReserveProducts) { try { await _warehouseServiceClient.CancelAsync(order.OrderNumber.ToString()); } catch (Exception inEx) { // логируем, что товар не сняли с резервирования } } try { if (order != null) { existOrder = _orderContext.Orders.FirstOrDefault(g => g.OrderNumber == order.OrderNumber); existOrder.Status = OrderStatus.Error; existOrder.ErrorDescription = ex.Message; await _orderContext.SaveChangesAsync(); } } catch (Exception inEx) { // логируем, что не удалось изменить статус } _metricReporter.RegisterFailedOrder(); } return(order); }