public ApplyOrderResult Apply(ILogService log,
                                      IUnitOfWork db,
                                      IOrderHistoryService orderHistoryService,
                                      IQuantityManager quantityManager,
                                      DateTime when,
                                      long?by)
        {
            var dbOrder                 = db.Orders.GetById(EntityId);
            var addressChanged          = false;
            var shipmentProviderChanged = false;
            var shouldRecalcRates       = dbOrder.IsInsured != IsInsured ||
                                          dbOrder.IsSignConfirmation != IsSignConfirmation;

            var manuallyAddress = ComposeAddressDto();

            //NOTE: empty when fields was readonly
            if (!AddressHelper.IsEmptyManually(manuallyAddress))
            {
                var originalAddress = dbOrder.GetAddressDto();

                addressChanged    = AddressHelper.CompareWithManuallyAllFields(originalAddress, manuallyAddress);
                shouldRecalcRates = shouldRecalcRates || AddressHelper.CompareWithManuallyBigChanges(originalAddress, manuallyAddress);

                if (addressChanged)
                {
                    orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ManuallyPersonNameKey, dbOrder.IsManuallyUpdated ? dbOrder.ManuallyPersonName : dbOrder.PersonName, ManuallyPersonName, by);
                    dbOrder.ManuallyPersonName = ManuallyPersonName;
                    orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ManuallyShippingAddress1Key, dbOrder.IsManuallyUpdated ? dbOrder.ManuallyShippingAddress1 : dbOrder.ShippingAddress1, ManuallyShippingAddress1, by);
                    dbOrder.ManuallyShippingAddress1 = ManuallyShippingAddress1;
                    orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ManuallyShippingAddress2Key, dbOrder.IsManuallyUpdated ? dbOrder.ManuallyShippingAddress2 : dbOrder.ShippingAddress2, ManuallyShippingAddress2, by);
                    dbOrder.ManuallyShippingAddress2 = ManuallyShippingAddress2;
                    orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ManuallyShippingCityKey, dbOrder.IsManuallyUpdated ? dbOrder.ManuallyShippingCity : dbOrder.ShippingCity, ManuallyShippingCity, by);
                    dbOrder.ManuallyShippingCity = ManuallyShippingCity;
                    orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ManuallyShippingCountryKey, dbOrder.IsManuallyUpdated ? dbOrder.ManuallyShippingCountry : dbOrder.ShippingCountry, ManuallyShippingCountry, by);
                    dbOrder.ManuallyShippingCountry = ManuallyShippingCountry;

                    dbOrder.ManuallyShippingState = !ShippingUtils.IsInternational(dbOrder.ManuallyShippingCountry)
                                                    // == "US"
                        ? ManuallyShippingUSState
                        : ManuallyShippingState;

                    orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ManuallyShippingZipKey, dbOrder.IsManuallyUpdated ? dbOrder.ManuallyShippingZip : dbOrder.ShippingZip, ManuallyShippingZip, by);
                    dbOrder.ManuallyShippingZip = StringHelper.TrimWhitespace(ManuallyShippingZip);
                    orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ManuallyShippingZipAddonKey, dbOrder.IsManuallyUpdated ? dbOrder.ManuallyShippingZipAddon : dbOrder.ShippingZipAddon, ManuallyShippingZipAddon, by);
                    dbOrder.ManuallyShippingZipAddon = StringHelper.TrimWhitespace(ManuallyShippingZipAddon);
                    orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ManuallyShippingPhoneKey, dbOrder.IsManuallyUpdated ? dbOrder.ManuallyShippingPhone : dbOrder.ShippingPhone, ManuallyShippingPhone, by);
                    dbOrder.ManuallyShippingPhone = ManuallyShippingPhone;

                    dbOrder.IsManuallyUpdated = true;
                }
                else
                {
                    dbOrder.ManuallyPersonName       = String.Empty;
                    dbOrder.ManuallyShippingAddress1 = String.Empty;
                    dbOrder.ManuallyShippingAddress2 = String.Empty;
                    dbOrder.ManuallyShippingCity     = String.Empty;
                    dbOrder.ManuallyShippingCountry  = String.Empty;

                    dbOrder.ManuallyShippingState    = String.Empty;
                    dbOrder.ManuallyShippingZip      = String.Empty;
                    dbOrder.ManuallyShippingZipAddon = String.Empty;
                    dbOrder.ManuallyShippingPhone    = String.Empty;

                    dbOrder.IsManuallyUpdated = false;
                }
            }

            dbOrder.InsuredValue = InsuredValue;
            orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.IsInsuredKey, dbOrder.IsInsured, IsInsured, by);
            dbOrder.IsInsured = IsInsured;
            orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.IsSignConfirmationKey, dbOrder.IsSignConfirmation, IsSignConfirmation, by);
            dbOrder.IsSignConfirmation = IsSignConfirmation;

            if (ManuallyShipmentProviderType.HasValue)
            {
                if (dbOrder.ShipmentProviderType != ManuallyShipmentProviderType.Value)
                {
                    shipmentProviderChanged = true;
                    shouldRecalcRates       = true;
                }

                orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ShipmentProviderTypeKey, dbOrder.ShipmentProviderType, ManuallyShipmentProviderType, by);
                dbOrder.ShipmentProviderType = ManuallyShipmentProviderType.Value;
            }

            //dbOrder.OnHold = OnHold;

            dbOrder.UpdateDate = when;
            dbOrder.UpdatedBy  = by;

            if (Items.All(i => i.NewListingId > 0)) //NOTE: only when enabled items edit
            {
                var changeNotes        = "";
                var itemSizeWasChanged = false;
                var joinItems          = JoinItems(Items);
                var dbOrderItems       = db.OrderItems.GetAll().Where(i => i.OrderId == dbOrder.Id).ToList();
                var orderItemSources   = db.OrderItemSources.GetAllAsDto().Where(i => i.OrderId == dbOrder.Id).ToList();

                foreach (var item in joinItems)
                {
                    var dbOrderItem = dbOrderItems.FirstOrDefault(im => im.ItemOrderIdentifier == item.ItemOrderId);

                    //NOTE: Get source info for set proprotionally ItemPrice etc.
                    var sourceItemOrderId = item.SourceItemOrderId;
                    var sourceItemMapping = orderItemSources.FirstOrDefault(i => i.ItemOrderIdentifier == sourceItemOrderId);

                    if (dbOrderItem != null)
                    {
                        log.Info("Updated orderItemId=" + item.ItemOrderId + ", qty=" + item.Quantity);
                    }
                    else
                    {
                        log.Info("Added orderItemId=" + item.ItemOrderId + ", qty=" + item.Quantity);
                        dbOrderItem            = db.OrderItemSources.CreateItemFromSourceDto(sourceItemMapping);
                        dbOrderItem.CreateDate = when;

                        db.OrderItems.Add(dbOrderItem);
                    }

                    dbOrderItem.ItemOrderIdentifier = item.ItemOrderId;
                    dbOrderItem.QuantityOrdered     = item.Quantity;
                    dbOrderItem.ListingId           = item.NewListingId;

                    dbOrderItem.SourceListingId           = sourceItemMapping.ListingId;
                    dbOrderItem.SourceItemOrderIdentifier = sourceItemMapping.ItemOrderIdentifier;

                    var newListing = db.Listings.GetViewListingsAsDto(withUnmaskedStyles: false)
                                     .FirstOrDefault(l => l.Id == item.NewListingId);

                    var keepListingUpdateOnlyStyle = newListing.StyleItemId != dbOrderItem.StyleItemId;
                    if (dbOrderItem.Id == 0 ||
                        item.NewListingId != item.ListingId ||
                        keepListingUpdateOnlyStyle)
                    {
                        var oldListing = db.Listings.GetViewListingsAsDto(withUnmaskedStyles: false)
                                         .FirstOrDefault(l => l.Id == sourceItemMapping.ListingId);

                        if (newListing != null && oldListing != null)
                        {
                            itemSizeWasChanged = newListing.StyleItemId != oldListing.StyleItemId;
                            if (itemSizeWasChanged)
                            {
                                var isStyleChanged = newListing.StyleString != oldListing.StyleString;
                                changeNotes += (isStyleChanged ? "Order item" : "Size") + " was changed from: " +
                                               (isStyleChanged ? oldListing.StyleString + " - " : " ") +
                                               SizeHelper.ToVariation(oldListing.StyleSize, oldListing.StyleColor)
                                               + " to: " + (isStyleChanged ? newListing.StyleString + " - " : "") +
                                               SizeHelper.ToVariation(newListing.StyleSize, newListing.StyleColor);

                                dbOrderItem.ReplaceType = (int)ItemReplaceTypes.Change;
                                dbOrderItem.ReplaceDate = when;
                                orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ReplaceItemKey, dbOrderItem.StyleItemId, dbOrderItem.Id.ToString(), newListing.StyleItemId, null, by);

                                var quantityOperation = new QuantityOperationDTO()
                                {
                                    Type            = (int)QuantityOperationType.Lost,
                                    QuantityChanges = new List <QuantityChangeDTO>()
                                    {
                                        new QuantityChangeDTO()
                                        {
                                            StyleId     = oldListing.StyleId.Value,
                                            StyleItemId = oldListing.StyleItemId.Value,
                                            Quantity    = dbOrderItem.QuantityOrdered,
                                            //NOTE: W/o sign means that the qty will be substracted
                                        }
                                    },
                                    Comment = "Order edit, change style/size",
                                };

                                quantityManager.AddQuantityOperation(db,
                                                                     quantityOperation,
                                                                     when,
                                                                     by);
                            }

                            //NOTE: Actualize current style info
                            dbOrderItem.StyleId     = newListing.StyleId;
                            dbOrderItem.StyleItemId = newListing.StyleItemId;
                            dbOrderItem.StyleString = newListing.StyleString;

                            dbOrderItem.SourceStyleString = oldListing.StyleString;
                            dbOrderItem.SourceStyleItemId = oldListing.StyleItemId;
                            dbOrderItem.SourceStyleSize   = oldListing.StyleSize;
                            dbOrderItem.SourceStyleColor  = oldListing.StyleColor;
                        }
                    }

                    if (dbOrderItem.ItemOrderIdentifier != dbOrderItem.SourceItemOrderIdentifier)
                    {
                        var portionCoef = dbOrderItem.QuantityOrdered / (decimal)sourceItemMapping.QuantityOrdered;

                        dbOrderItem.ItemPrice             = sourceItemMapping.ItemPrice * portionCoef;
                        dbOrderItem.ItemPriceInUSD        = sourceItemMapping.ItemPriceInUSD * portionCoef;
                        dbOrderItem.ShippingPrice         = sourceItemMapping.ShippingPrice * portionCoef;
                        dbOrderItem.ShippingPriceInUSD    = sourceItemMapping.ShippingPriceInUSD * portionCoef;
                        dbOrderItem.ShippingDiscount      = sourceItemMapping.ShippingDiscount * portionCoef;
                        dbOrderItem.ShippingDiscountInUSD = sourceItemMapping.ShippingDiscountInUSD * portionCoef;
                    }
                    else //NOTE: m.b. no needed, for now no cases, but can be found in future
                    {
                        dbOrderItem.ItemPrice             = sourceItemMapping.ItemPrice;
                        dbOrderItem.ItemPriceInUSD        = sourceItemMapping.ItemPriceInUSD;
                        dbOrderItem.ShippingPrice         = sourceItemMapping.ShippingPrice;
                        dbOrderItem.ShippingPriceInUSD    = sourceItemMapping.ShippingPriceInUSD;
                        dbOrderItem.ShippingDiscount      = sourceItemMapping.ShippingDiscount;
                        dbOrderItem.ShippingDiscountInUSD = sourceItemMapping.ShippingDiscountInUSD;
                    }
                }
                db.Commit();

                var toRemoveOrderItems = dbOrderItems.Where(oi => joinItems.All(i => i.ItemOrderId != oi.ItemOrderIdentifier) &&
                                                            oi.QuantityOrdered > 0).ToList(); //Keeping cancelled items with qty = 0
                foreach (var toRemove in toRemoveOrderItems)
                {
                    log.Info("Remove orderItem, ordrItemId=" + toRemove.ItemOrderIdentifier
                             + ", qty=" + toRemove.QuantityOrdered);
                    db.OrderItems.Remove(toRemove);

                    itemSizeWasChanged = true;
                }
                db.Commit();

                if (itemSizeWasChanged)
                {
                    shouldRecalcRates = true;

                    Comments.Add(new CommentViewModel()
                    {
                        Comment = changeNotes,
                        Type    = (int)CommentType.ReturnExchange
                    });
                }
            }


            if (!string.IsNullOrEmpty(ManuallyShippingGroupId))
            {
                var groupId   = int.Parse(ManuallyShippingGroupId);
                var shippings = db.OrderShippingInfos.GetByOrderId(EntityId).ToList();
                var previousIsActiveMethodIds = String.Join(";", shippings.Where(sh => sh.IsActive).Select(sh => sh.ShippingMethodId).ToList());
                var hasDropdown = shippings.Where(sh => sh.IsVisible).GroupBy(sh => sh.ShippingMethodId).Count() > 1;
                if (shippings.Any(sh => sh.ShippingGroupId == groupId))
                {
                    foreach (var shipping in shippings)
                    {
                        shipping.IsActive = shipping.ShippingGroupId == groupId;
                        if (hasDropdown) //Keep is visible
                        {
                            shipping.IsVisible = shipping.IsVisible || shipping.ShippingGroupId == groupId;
                        }
                        else
                        {
                            shipping.IsVisible = shipping.ShippingGroupId == groupId;
                        }
                    }
                    var newIsActiveMethodIds = String.Join(";", shippings.Where(sh => sh.IsActive).Select(sh => sh.ShippingMethodId).ToList());
                    orderHistoryService.AddRecord(dbOrder.Id, OrderHistoryHelper.ShippingMethodKey, previousIsActiveMethodIds, newIsActiveMethodIds, by);
                }
                else
                {
                    //Can't change active shipping to not exists
                }
            }
            db.Commit();

            //Update Package Sizes
            var activeShippings = db.OrderShippingInfos.GetByOrderId(EntityId)
                                  .Where(sh => sh.IsActive)
                                  .OrderBy(sh => sh.Id)
                                  .ToList();

            for (var i = 0; i < activeShippings.Count; i++)
            {
                if (Packages != null && i < Packages.Count)
                {
                    if (activeShippings[i].PackageLength != Packages[i].PackageLength)
                    {
                        log.Info("Changed length: " + activeShippings[i].PackageLength + "=>" + Packages[i].PackageLength);
                        activeShippings[i].PackageLength = Packages[i].PackageLength;

                        shouldRecalcRates = true;
                    }
                    if (activeShippings[i].PackageWidth != Packages[i].PackageWidth)
                    {
                        log.Info("Changed width: " + activeShippings[i].PackageWidth + "=>" + Packages[i].PackageWidth);
                        activeShippings[i].PackageWidth = Packages[i].PackageWidth;

                        shouldRecalcRates = true;
                    }
                    if (activeShippings[i].PackageHeight != Packages[i].PackageHeight)
                    {
                        log.Info("Changed height: " + activeShippings[i].PackageHeight + "=>" + Packages[i].PackageHeight);
                        activeShippings[i].PackageHeight = Packages[i].PackageHeight;

                        shouldRecalcRates = true;
                    }
                }
            }
            db.Commit();



            foreach (var comment in Comments)
            {
                if (comment.Id == 0)
                {
                    log.Info("New comment: " + comment.Comment);
                }
            }

            db.OrderComments.AddComments(
                Comments.Select(c => new CommentDTO()
            {
                Id      = c.Id,
                Message = c.Comment,
                Type    = c.Type,
            }).ToList(),
                EntityId,
                when,
                by);

            return(new ApplyOrderResult()
            {
                RateRecalcRequested = shouldRecalcRates,
                AddressValidationRequested = addressChanged ||
                                             dbOrder.AddressValidationStatus == (int)Core.Models.AddressValidationStatus.ExceptionCommunication ||
                                             dbOrder.AddressValidationStatus == (int)Core.Models.AddressValidationStatus.Exception,
                ShipmentProviderChanged = shipmentProviderChanged
            });
        }