private decimal?GetRate(decimal weight, int shippingMethodId,
                                int storeId, int warehouseId, int countryId, int stateProvinceId, string zip, GetShippingOptionRequest getShippingOptionRequest)
        {
            decimal queryWeight = weight;
            decimal realWeight  = _shippingService.GetTotalWeight(getShippingOptionRequest);

            if (realWeight > weight)
            {
                queryWeight = realWeight;
            }

            var dhlRecord = _dhlService.FindRecord(shippingMethodId,
                                                   storeId, warehouseId, countryId, stateProvinceId, zip, queryWeight);

            if (dhlRecord == null)
            {
                if (_dhlSettings.LimitMethodsToCreated)
                {
                    return(null);
                }

                return(decimal.Zero);
            }

            #region X Üzeri Ücretsiz Kargo

            decimal shippingTotal = decimal.Zero;
            decimal?orderTotal    = decimal.Zero;


            foreach (var packageItem in getShippingOptionRequest.Items)
            {
                //TODO we should use getShippingOptionRequest.Items.GetQuantity() method to get subtotal
                orderTotal += _priceCalculationService.GetSubTotal(packageItem.ShoppingCartItem);
            }

            if (orderTotal.HasValue)
            {
                var trueOrderTotal = _currencyService.ConvertToPrimaryStoreCurrency(orderTotal.Value, _workContext.WorkingCurrency);
                if (dhlRecord.FreeShippingOverXEnabled && trueOrderTotal >= dhlRecord.FreeShippingOverXValue)
                {
                    return(decimal.Zero);
                }
            }

            #endregion X Üzeri Ücretsiz Kargo

            if (dhlRecord.IsFixedRate)
            {
                return(dhlRecord.FixedRate);
            }

            shippingTotal += dhlRecord.Factor;

            if (shippingTotal < decimal.Zero)
            {
                shippingTotal = decimal.Zero;
            }
            return(shippingTotal);
        }
Example #2
0
        /// <summary>
        ///  Gets available shipping options
        /// </summary>
        /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
        /// <returns>Represents a response of getting shipping rate options</returns>
        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
            {
                throw new ArgumentNullException("getShippingOptionRequest");
            }

            var response = new GetShippingOptionResponse();

            if (getShippingOptionRequest.Items == null || getShippingOptionRequest.Items.Count == 0)
            {
                response.AddError("No shipment items");
                return(response);
            }
            if (getShippingOptionRequest.ShippingAddress == null)
            {
                var originAddress = _shippingSettings.ShippingOriginAddressId > 0
                                  ? _addressService.GetAddressById(_shippingSettings.ShippingOriginAddressId)
                                  : null;
                if (originAddress == null)
                {
                    response.AddError("Origin Address or Shipping address is not set");
                    return(response);
                }
                getShippingOptionRequest.ShippingAddress = originAddress;
            }

            int countryId = getShippingOptionRequest.ShippingAddress.CountryId.HasValue ? getShippingOptionRequest.ShippingAddress.CountryId.Value : 0;

            decimal subTotal = decimal.Zero;

            foreach (var shoppingCartItem in getShippingOptionRequest.Items)
            {
                if (shoppingCartItem.IsFreeShipping || !shoppingCartItem.IsShipEnabled)
                {
                    continue;
                }
                subTotal += _priceCalculationService.GetSubTotal(shoppingCartItem, true);
            }

            decimal deci = GetShoppingCartTotalDeci(getShippingOptionRequest.Items);

            var     shippingMethods = _shippingService.GetAllShippingMethods(countryId);
            decimal additionalRate  = _settingService.GetSettingByKey <decimal>("ShippingRateComputationMethod.ByWeight.DHLRate");

            additionalRate = Math.Max(1, additionalRate);
            foreach (var shippingMethod in shippingMethods)
            {
                decimal?rate = GetRate(subTotal, deci, shippingMethod.Id, countryId);
                if (rate.HasValue)
                {
                    var shippingOption = new ShippingOption();
                    shippingOption.Name        = shippingMethod.GetLocalized(x => x.Name);
                    shippingOption.Description = shippingMethod.GetLocalized(x => x.Description);
                    shippingOption.Rate        = rate.Value * additionalRate;
                    response.ShippingOptions.Add(shippingOption);
                }
            }


            return(response);
        }
Example #3
0
        public GetShippingOptionResponse GetShippingOptions(
            GetShippingOptionRequest getShippingOptionRequest
            )
        {
            if (getShippingOptionRequest == null)
            {
                throw new ArgumentNullException(
                          nameof(getShippingOptionRequest)
                          );
            }

            // Validate that 'lb' measure weight exists in system
            var lbsMeasureWeight =
                _measureService.GetMeasureWeightBySystemKeyword("lb");

            if (lbsMeasureWeight == null)
            {
                return(new GetShippingOptionResponse {
                    Errors = new[] { "Cannot perform weight conversion, unable " +
                                     "to find 'lb' measure weight. Make sure a measure weight " +
                                     "with 'lb' system keyword exists." }
                });
            }

            if (!getShippingOptionRequest.Items?.Any() ?? true)
            {
                return(new GetShippingOptionResponse {
                    Errors = new[] { "No shipment items" }
                });
            }

            // Check that destination zip/postal was provided
            var destinationZip =
                getShippingOptionRequest.ShippingAddress?.ZipPostalCode;

            if (destinationZip == null)
            {
                return(new GetShippingOptionResponse {
                    Errors = new[]
                    {
                        "Destination shipping zip/postal code is not set"
                    }
                });
            }

            var weight = _shippingService.GetTotalWeight(
                getShippingOptionRequest
                );
            var weightInLbs = _measureService.ConvertFromPrimaryMeasureWeight(
                weight, lbsMeasureWeight);

            // If under minimum, don't provide as option
            if (weightInLbs < _settings.MinimumWeightLimitInLbs)
            {
                return(new GetShippingOptionResponse());
            }

            decimal rateQuote;

            try {
                rateQuote = _rateQuoteService.GetRateQuote(
                    destinationZip,
                    decimal.ToInt32(decimal.Round(weightInLbs))
                    );
            }
            catch (NopException ex) {
                return(new GetShippingOptionResponse {
                    Errors = new[] { $"{ex.Message}" }
                });
            }

            var response = new GetShippingOptionResponse();

            response.ShippingOptions.Add(new ShippingOption()
            {
                Name = _settings.ShippingOptionName,
                Rate = rateQuote
            });
            return(response);
        }
        /// <summary>
        /// Create packages (total volume of shopping cart items determines number of packages)
        /// </summary>
        /// <param name="request">Shipping request</param>
        /// <param name="getShippingOptionRequest">Shipping option request</param>
        /// <param name="orderSubTotal"></param>
        /// <param name="currencyCode">Currency code</param>
        private void SetIndividualPackageLineItemsCubicRootDimensions(FedexRate.RateRequest request, GetShippingOptionRequest getShippingOptionRequest, decimal orderSubTotal, string currencyCode)
        {
            //From FedEx Guide (Ground):
            //Dimensional weight is based on volume (the amount of space a package
            //occupies in relation to its actual weight). If the cubic size of your FedEx
            //Ground package measures three cubic feet (5,184 cubic inches or 84,951
            //cubic centimetres) or greater, you will be charged the greater of the
            //dimensional weight or the actual weight.
            //A package weighing 150 lbs. (68 kg) or less and measuring greater than
            //130 inches (330 cm) in combined length and girth will be classified by
            //FedEx Ground as an “Oversize” package. All packages must have a
            //combined length and girth of no more than 165 inches (419 cm). An
            //oversize charge of $30 per package will also apply to any package
            //measuring greater than 130 inches (330 cm) in combined length and
            //girth.
            //Shipping charges for packages smaller than three cubic feet are based
            //on actual weight

            // Dimensional Weight applies to packages with volume 5,184 cubic inches or more
            // cube root(5184) = 17.3

            // Packages that exceed 130 inches in length and girth (2xHeight + 2xWidth)
            // are considered “oversize” packages.
            // Assume a cube (H=W=L) of that size: 130 = D + (2xD + 2xD) = 5xD :  D = 130/5 = 26
            // 26x26x26 = 17,576
            // Avoid oversize by using 25"
            // 25x25x25 = 15,625

            // Which is less $  - multiple small packages, or one large package using dimensional weight
            //  15,625 / 5184 = 3.014 =  3 packages
            // Ground for total weight:             60lbs     15lbs
            //  3 packages 17x17x17 (20 lbs each) = $66.21    39.39
            //  1 package  25x25x25 (60 lbs)      = $71.70    71.70

            var totalPackagesDims = 1;
            var length            = 0M;
            var height            = 0M;
            var width             = 0M;

            if (getShippingOptionRequest.Items.Count == 1 && getShippingOptionRequest.Items[0].GetQuantity() == 1)
            {
                var sci = getShippingOptionRequest.Items[0].ShoppingCartItem;

                //get dimensions and weight of the single cubic size of package
                var item = getShippingOptionRequest.Items.FirstOrDefault().ShoppingCartItem;
                (width, length, height) = GetDimensionsForSingleItem(item);
            }
            else
            {
                //or try to get them
                var dimension = 0;

                //get total volume of the package
                var totalVolume = getShippingOptionRequest.Items.Sum(item =>
                {
                    //get dimensions and weight of the single item
                    var(itemWidth, itemLength, itemHeight) = GetDimensionsForSingleItem(item.ShoppingCartItem);
                    return(item.GetQuantity() * itemWidth * itemLength * itemHeight);
                });
                if (totalVolume > decimal.Zero)
                {
                    //use default value (in cubic inches) if not specified
                    var packageVolume = _fedexSettings.PackingPackageVolume;
                    if (packageVolume <= 0)
                    {
                        packageVolume = 5184;
                    }

                    //calculate cube root (floor)
                    dimension = Convert.ToInt32(Math.Floor(Math.Pow(Convert.ToDouble(packageVolume), 1.0 / 3.0)));
                    if (IsPackageTooLarge(dimension, dimension, dimension))
                    {
                        throw new NopException("fedexSettings.PackingPackageVolume exceeds max package size");
                    }

                    //adjust package volume for dimensions calculated
                    packageVolume = dimension * dimension * dimension;

                    totalPackagesDims = Convert.ToInt32(Math.Ceiling(totalVolume / packageVolume));
                }

                width = length = height = dimension;
            }

            width  = Math.Max(width, 1);
            length = Math.Max(length, 1);
            height = Math.Max(height, 1);

            var weight = GetWeight(getShippingOptionRequest);

            var totalPackagesWeights = 1;

            if (IsPackageTooHeavy(weight))
            {
                totalPackagesWeights = Convert.ToInt32(Math.Ceiling(weight / FedexShippingDefaults.MAX_PACKAGE_WEIGHT));
            }

            var totalPackages = totalPackagesDims > totalPackagesWeights ? totalPackagesDims : totalPackagesWeights;

            var orderSubTotalPerPackage = orderSubTotal / totalPackages;
            var weightPerPackage        = weight / totalPackages;

            request.RequestedShipment.PackageCount = totalPackages.ToString();

            request.RequestedShipment.RequestedPackageLineItems = Enumerable.Range(1, totalPackages)
                                                                  .Select(i => CreatePackage(width, length, height, weightPerPackage, orderSubTotalPerPackage, i.ToString(), currencyCode))
                                                                  .ToArray();
        }
Example #5
0
        /// <summary>
        ///  Gets available shipping options
        /// </summary>
        /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
        /// <returns>Represents a response of getting shipping rate options</returns>
        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
            {
                throw new ArgumentNullException(nameof(getShippingOptionRequest));
            }

            var response = new GetShippingOptionResponse();

            if (getShippingOptionRequest.Items == null || !getShippingOptionRequest.Items.Any())
            {
                response.AddError("No shipment items");
                return(response);
            }

            //choose the shipping rate calculation method
            if (_fixedOrByWeightSettings.ShippingByWeightEnabled)
            {
                //shipping rate calculation by products weight

                if (getShippingOptionRequest.ShippingAddress == null)
                {
                    response.AddError("Shipping address is not set");
                    return(response);
                }

                var storeId         = getShippingOptionRequest.StoreId != 0 ? getShippingOptionRequest.StoreId : _storeContext.CurrentStore.Id;
                var countryId       = getShippingOptionRequest.ShippingAddress.CountryId ?? 0;
                var stateProvinceId = getShippingOptionRequest.ShippingAddress.StateProvinceId ?? 0;
                var warehouseId     = getShippingOptionRequest.WarehouseFrom?.Id ?? 0;
                var zip             = getShippingOptionRequest.ShippingAddress.ZipPostalCode;

                //get subtotal of shipped items
                var subTotal = decimal.Zero;
                foreach (var packageItem in getShippingOptionRequest.Items)
                {
                    if (packageItem.ShoppingCartItem.IsFreeShipping(_productService, _productAttributeParser))
                    {
                        continue;
                    }

                    //TODO we should use getShippingOptionRequest.Items.GetQuantity() method to get subtotal
                    subTotal += _priceCalculationService.GetSubTotal(packageItem.ShoppingCartItem);
                }

                //get weight of shipped items (excluding items with free shipping)
                var weight = _shippingService.GetTotalWeight(getShippingOptionRequest, ignoreFreeShippedItems: true);

                foreach (var shippingMethod in _shippingService.GetAllShippingMethods(countryId))
                {
                    var rate = GetRate(subTotal, weight, shippingMethod.Id, storeId, warehouseId, countryId, stateProvinceId, zip);
                    if (!rate.HasValue)
                    {
                        continue;
                    }

                    response.ShippingOptions.Add(new ShippingOption
                    {
                        Name        = shippingMethod.GetLocalized(x => x.Name),
                        Description = shippingMethod.GetLocalized(x => x.Description),
                        Rate        = rate.Value
                    });
                }
            }
            else
            {
                //shipping rate calculation by fixed rate
                var restrictByCountryId = getShippingOptionRequest.ShippingAddress?.Country?.Id;
                response.ShippingOptions = _shippingService.GetAllShippingMethods(restrictByCountryId).Select(shippingMethod => new ShippingOption
                {
                    Name        = shippingMethod.GetLocalized(x => x.Name),
                    Description = shippingMethod.GetLocalized(x => x.Description),
                    Rate        = GetRate(shippingMethod.Id)
                }).ToList();
            }

            return(response);
        }
        private string CreateRequest(string accessKey, string username, string password,
                                     GetShippingOptionRequest getShippingOptionRequest, UPSCustomerClassification customerClassification,
                                     UPSPickupType pickupType, UPSPackagingType packagingType)
        {
            string zipPostalCodeFrom = getShippingOptionRequest.ZipPostalCodeFrom;
            string zipPostalCodeTo   = getShippingOptionRequest.ShippingAddress.ZipPostalCode;
            string countryCodeFrom   = getShippingOptionRequest.CountryFrom.TwoLetterIsoCode;
            var    country           = _countryService.GetCountryById(getShippingOptionRequest.ShippingAddress.CountryId);
            string countryCodeTo     = country.TwoLetterIsoCode;

            var sb = new StringBuilder();

            sb.Append("<?xml version='1.0'?>");
            sb.Append("<AccessRequest xml:lang='en-US'>");
            sb.AppendFormat("<AccessLicenseNumber>{0}</AccessLicenseNumber>", accessKey);
            sb.AppendFormat("<UserId>{0}</UserId>", username);
            sb.AppendFormat("<Password>{0}</Password>", password);
            sb.Append("</AccessRequest>");
            sb.Append("<?xml version='1.0'?>");
            sb.Append("<RatingServiceSelectionRequest xml:lang='en-US'>");
            sb.Append("<Request>");
            sb.Append("<TransactionReference>");
            sb.Append("<CustomerContext>Bare Bones Rate Request</CustomerContext>");
            sb.Append("<XpciVersion>1.0001</XpciVersion>");
            sb.Append("</TransactionReference>");
            sb.Append("<RequestAction>Rate</RequestAction>");
            sb.Append("<RequestOption>Shop</RequestOption>");
            sb.Append("</Request>");
            if (String.Equals(countryCodeFrom, "US", StringComparison.InvariantCultureIgnoreCase))
            {
                sb.Append("<PickupType>");
                sb.AppendFormat("<Code>{0}</Code>", GetPickupTypeCode(pickupType));
                sb.Append("</PickupType>");
                sb.Append("<CustomerClassification>");
                sb.AppendFormat("<Code>{0}</Code>", GetCustomerClassificationCode(customerClassification));
                sb.Append("</CustomerClassification>");
            }
            sb.Append("<Shipment>");
            sb.Append("<Shipper>");
            sb.Append("<Address>");
            sb.AppendFormat("<PostalCode>{0}</PostalCode>", zipPostalCodeFrom);
            sb.AppendFormat("<CountryCode>{0}</CountryCode>", countryCodeFrom);
            sb.Append("</Address>");
            sb.Append("</Shipper>");
            sb.Append("<ShipTo>");
            sb.Append("<Address>");
            sb.Append("<ResidentialAddressIndicator/>");
            sb.AppendFormat("<PostalCode>{0}</PostalCode>", zipPostalCodeTo);
            sb.AppendFormat("<CountryCode>{0}</CountryCode>", countryCodeTo);
            sb.Append("</Address>");
            sb.Append("</ShipTo>");
            sb.Append("<ShipFrom>");
            sb.Append("<Address>");
            sb.AppendFormat("<PostalCode>{0}</PostalCode>", zipPostalCodeFrom);
            sb.AppendFormat("<CountryCode>{0}</CountryCode>", countryCodeFrom);
            sb.Append("</Address>");
            sb.Append("</ShipFrom>");
            sb.Append("<Service>");
            sb.Append("<Code>03</Code>");
            sb.Append("</Service>");

            string currencyCode = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId).CurrencyCode;

            //get subTotalWithoutDiscountBase, for use as insured value (when Settings.InsurePackage)
            //(note: prior versions used "with discount", but "without discount" better reflects true value to insure.)
            decimal         orderSubTotalDiscountAmount;
            List <Discount> orderSubTotalAppliedDiscounts;
            decimal         subTotalWithoutDiscountBase;
            decimal         subTotalWithDiscountBase;

            //TODO we should use getShippingOptionRequest.Items.GetQuantity() method to get subtotal
            _orderTotalCalculationService.GetShoppingCartSubTotal(getShippingOptionRequest.Items.Select(x => x.ShoppingCartItem).ToList(),
                                                                  false, out orderSubTotalDiscountAmount, out orderSubTotalAppliedDiscounts,
                                                                  out subTotalWithoutDiscountBase, out subTotalWithDiscountBase);

            if (_upsSettings.Tracing)
            {
                _traceMessages.AppendLine(" Packing Type: " + _upsSettings.PackingType.ToString());
            }

            switch (_upsSettings.PackingType)
            {
            case PackingType.PackByOneItemPerPackage:
                SetIndividualPackageLineItemsOneItemPerPackage(sb, getShippingOptionRequest, packagingType, currencyCode);
                break;

            case PackingType.PackByVolume:
                SetIndividualPackageLineItemsCubicRootDimensions(sb, getShippingOptionRequest, packagingType, subTotalWithoutDiscountBase, currencyCode);
                break;

            case PackingType.PackByDimensions:
            default:
                SetIndividualPackageLineItems(sb, getShippingOptionRequest, packagingType, subTotalWithoutDiscountBase, currencyCode);
                break;
            }

            sb.Append("</Shipment>");
            sb.Append("</RatingServiceSelectionRequest>");

            return(sb.ToString());
        }
        /// <summary>
        ///  Gets available shipping options
        /// </summary>
        /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
        /// <returns>Represents a response of getting shipping rate options</returns>
        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
            {
                throw new ArgumentNullException("getShippingOptionRequest");
            }

            var response = new GetShippingOptionResponse();

            if (getShippingOptionRequest.Items == null)
            {
                response.AddError("No shipment items");
                return(response);
            }
            if (getShippingOptionRequest.ShippingAddress == null)
            {
                response.AddError("Shipping address is not set");
                return(response);
            }
            if (getShippingOptionRequest.ShippingAddress.Country == null)
            {
                response.AddError("Shipping country is not set");
                return(response);
            }
            if (getShippingOptionRequest.ShippingAddress.StateProvince == null)
            {
                response.AddError("Shipping state is not set");
                return(response);
            }

            try
            {
                var profile = new Profile();
                profile.MerchantId = _canadaPostSettings.CustomerId;

                var destination = new Destination();
                destination.City            = getShippingOptionRequest.ShippingAddress.City;
                destination.StateOrProvince = getShippingOptionRequest.ShippingAddress.StateProvince.Abbreviation;
                destination.Country         = getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode;
                destination.PostalCode      = getShippingOptionRequest.ShippingAddress.ZipPostalCode;

                var items = CreateItems(getShippingOptionRequest);

                var lang = CanadaPostLanguageEnum.English;
                if (_workContext.WorkingLanguage.LanguageCulture.StartsWith("fr", StringComparison.InvariantCultureIgnoreCase))
                {
                    lang = CanadaPostLanguageEnum.French;
                }

                var requestResult = GetShippingOptionsInternal(profile, destination, items, lang);
                if (requestResult.IsError)
                {
                    response.AddError(requestResult.StatusMessage);
                }
                else
                {
                    foreach (var dr in requestResult.AvailableRates)
                    {
                        var so = new ShippingOption();
                        so.Name = dr.Name;
                        if (!string.IsNullOrEmpty(dr.DeliveryDate))
                        {
                            so.Name += string.Format(" - {0}", dr.DeliveryDate);
                        }
                        so.Rate = dr.Amount;
                        response.ShippingOptions.Add(so);
                    }
                }

                foreach (var shippingOption in response.ShippingOptions)
                {
                    if (!shippingOption.Name.StartsWith("canada post", StringComparison.InvariantCultureIgnoreCase))
                    {
                        shippingOption.Name = string.Format("Canada Post {0}", shippingOption.Name);
                    }
                }
            }
            catch (Exception e)
            {
                response.AddError(e.Message);
            }

            return(response);
        }
 /// <summary>
 /// Gets fixed shipping rate (if shipping rate computation method allows it and the rate can be calculated before checkout).
 /// </summary>
 /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
 /// <returns>
 /// A task that represents the asynchronous operation
 /// The task result contains the fixed shipping rate; or null in case there's no fixed shipping rate
 /// </returns>
 public Task <decimal?> GetFixedRateAsync(GetShippingOptionRequest getShippingOptionRequest)
 {
     return(Task.FromResult <decimal?>(null));
 }
        private void SetIndividualPackageLineItems(RateRequest request, GetShippingOptionRequest getShippingOptionRequest, decimal orderSubTotal, string currencyCode)
        {
            // Rate request setup - Total Dimensions of Shopping Cart Items determines number of packages

            var usedMeasureWeight    = GetUsedMeasureWeight();
            var usedMeasureDimension = GetUsedMeasureDimension();
            int length = ConvertFromPrimaryMeasureDimension(getShippingOptionRequest.GetTotalLength(), usedMeasureDimension);
            int height = ConvertFromPrimaryMeasureDimension(getShippingOptionRequest.GetTotalHeight(), usedMeasureDimension);
            int width  = ConvertFromPrimaryMeasureDimension(getShippingOptionRequest.GetTotalWidth(), usedMeasureDimension);
            int weight = ConvertFromPrimaryMeasureWeight(_shippingService.GetShoppingCartTotalWeight(getShippingOptionRequest.Items), usedMeasureWeight);

            if (length < 1)
            {
                length = 1;
            }
            if (height < 1)
            {
                height = 1;
            }
            if (width < 1)
            {
                width = 1;
            }
            if (weight < 1)
            {
                weight = 1;
            }

            if ((!IsPackageTooHeavy(weight)) && (!IsPackageTooLarge(length, height, width)))
            {
                request.RequestedShipment.PackageCount = "1";

                request.RequestedShipment.RequestedPackageLineItems    = new RequestedPackageLineItem[1];
                request.RequestedShipment.RequestedPackageLineItems[0] = new RequestedPackageLineItem();
                request.RequestedShipment.RequestedPackageLineItems[0].SequenceNumber = "1";                                  // package sequence number
                request.RequestedShipment.RequestedPackageLineItems[0].Weight         = new RateServiceWebReference.Weight(); // package weight
                request.RequestedShipment.RequestedPackageLineItems[0].Weight.Units   = RateServiceWebReference.WeightUnits.LB;
                request.RequestedShipment.RequestedPackageLineItems[0].Weight.Value   = weight;
                request.RequestedShipment.RequestedPackageLineItems[0].Dimensions     = new RateServiceWebReference.Dimensions(); // package dimensions

                request.RequestedShipment.RequestedPackageLineItems[0].Dimensions.Length     = _fedexSettings.PassDimensions ? length.ToString() : "0";
                request.RequestedShipment.RequestedPackageLineItems[0].Dimensions.Width      = _fedexSettings.PassDimensions ? width.ToString() : "0";
                request.RequestedShipment.RequestedPackageLineItems[0].Dimensions.Height     = _fedexSettings.PassDimensions ? height.ToString() : "0";
                request.RequestedShipment.RequestedPackageLineItems[0].Dimensions.Units      = RateServiceWebReference.LinearUnits.IN;
                request.RequestedShipment.RequestedPackageLineItems[0].InsuredValue          = new RateServiceWebReference.Money(); // insured value
                request.RequestedShipment.RequestedPackageLineItems[0].InsuredValue.Amount   = orderSubTotal;
                request.RequestedShipment.RequestedPackageLineItems[0].InsuredValue.Currency = currencyCode;
            }
            else
            {
                int totalPackages        = 1;
                int totalPackagesDims    = 1;
                int totalPackagesWeights = 1;
                if (IsPackageTooHeavy(weight))
                {
                    totalPackagesWeights = Convert.ToInt32(Math.Ceiling((decimal)weight / (decimal)MAXPACKAGEWEIGHT));
                }
                if (IsPackageTooLarge(length, height, width))
                {
                    totalPackagesDims = Convert.ToInt32(Math.Ceiling((decimal)TotalPackageSize(length, height, width) / (decimal)108));
                }
                totalPackages = totalPackagesDims > totalPackagesWeights ? totalPackagesDims : totalPackagesWeights;
                if (totalPackages == 0)
                {
                    totalPackages = 1;
                }

                int weight2 = weight / totalPackages;
                int height2 = height / totalPackages;
                int width2  = width / totalPackages;
                int length2 = length / totalPackages;
                if (weight2 < 1)
                {
                    weight2 = 1;
                }
                if (height2 < 1)
                {
                    height2 = 1;
                }
                if (width2 < 1)
                {
                    width2 = 1;
                }
                if (length2 < 1)
                {
                    length2 = 1;
                }

                decimal orderSubTotal2 = orderSubTotal / totalPackages;

                request.RequestedShipment.PackageCount = totalPackages.ToString();
                request.RequestedShipment.RequestedPackageLineItems = new RequestedPackageLineItem[totalPackages];

                for (int i = 0; i < totalPackages; i++)
                {
                    request.RequestedShipment.RequestedPackageLineItems[i] = new RequestedPackageLineItem();
                    request.RequestedShipment.RequestedPackageLineItems[i].SequenceNumber = (i + 1).ToString();                   // package sequence number
                    request.RequestedShipment.RequestedPackageLineItems[i].Weight         = new RateServiceWebReference.Weight(); // package weight
                    request.RequestedShipment.RequestedPackageLineItems[i].Weight.Units   = RateServiceWebReference.WeightUnits.LB;
                    request.RequestedShipment.RequestedPackageLineItems[i].Weight.Value   = (decimal)weight2;
                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions     = new RateServiceWebReference.Dimensions(); // package dimensions

                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Length     = _fedexSettings.PassDimensions ? length2.ToString() : "0";
                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Width      = _fedexSettings.PassDimensions ? width2.ToString() : "0";
                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Height     = _fedexSettings.PassDimensions ? height2.ToString() : "0";
                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Units      = RateServiceWebReference.LinearUnits.IN;
                    request.RequestedShipment.RequestedPackageLineItems[i].InsuredValue          = new RateServiceWebReference.Money(); // insured value
                    request.RequestedShipment.RequestedPackageLineItems[i].InsuredValue.Amount   = orderSubTotal2;
                    request.RequestedShipment.RequestedPackageLineItems[i].InsuredValue.Currency = currencyCode;
                }
            }
        }
Example #10
0
        /// <summary>
        /// Create packages (total volume of shopping cart items determines number of packages)
        /// </summary>
        /// <param name="shippingOptionRequest">shipping option request</param>
        /// <returns>
        /// A task that represents the asynchronous operation
        /// The task result contains the packages
        /// </returns>
        private async Task <IEnumerable <UPSRate.PackageType> > GetPackagesByCubicRootAsync(GetShippingOptionRequest shippingOptionRequest)
        {
            //Dimensional weight is based on volume (the amount of space a package occupies in relation to its actual weight).
            //If the cubic size of package measures three cubic feet (5,184 cubic inches or 84,951 cubic centimetres) or greater, you will be charged the greater of the dimensional weight or the actual weight.
            //This algorithm devides total package volume by the UPS settings PackingPackageVolume so that no package requires dimensional weight; this could result in an under-charge.

            var totalPackagesBySizeLimit = 1;
            var width  = 0M;
            var length = 0M;
            var height = 0M;

            //if there is only one item, no need to calculate dimensions
            if (shippingOptionRequest.Items.Count == 1 && shippingOptionRequest.Items.FirstOrDefault().GetQuantity() == 1)
            {
                //get dimensions and weight of the single cubic size of package
                var item = shippingOptionRequest.Items.FirstOrDefault();
                (width, length, height) = await GetDimensionsForSingleItemAsync(item.ShoppingCartItem, item.Product);
            }
            else
            {
                //or try to get them
                var dimension = 0;

                //get total volume of the package
                var totalVolume = await shippingOptionRequest.Items.SumAwaitAsync(async item =>
                {
                    //get dimensions and weight of the single item
                    var(itemWidth, itemLength, itemHeight) = await GetDimensionsForSingleItemAsync(item.ShoppingCartItem, item.Product);
                    return(item.GetQuantity() * itemWidth *itemLength *itemHeight);
                });

                if (totalVolume > decimal.Zero)
                {
                    //use default value (in cubic inches) if not specified
                    var packageVolume = _upsSettings.PackingPackageVolume;
                    if (packageVolume <= 0)
                    {
                        packageVolume = 5184;
                    }

                    //calculate cube root (floor)
                    dimension = Convert.ToInt32(Math.Floor(Math.Pow(Convert.ToDouble(packageVolume), 1.0 / 3.0)));
                    if (GetPackageSize(dimension, dimension, dimension) > await GetSizeLimitAsync())
                    {
                        throw new NopException("PackingPackageVolume exceeds max package size");
                    }

                    //adjust package volume for dimensions calculated
                    packageVolume = dimension * dimension * dimension;

                    totalPackagesBySizeLimit = Convert.ToInt32(Math.Ceiling(totalVolume / packageVolume));
                }

                width = length = height = dimension;
            }

            //get total packages number according to package limits
            var weight = await GetWeightAsync(shippingOptionRequest);

            var weightLimit = await GetWeightLimitAsync();

            var totalPackagesByWeightLimit = weight > weightLimit
                ? Convert.ToInt32(Math.Ceiling(weight / weightLimit))
                : 1;

            var totalPackages = Math.Max(Math.Max(totalPackagesBySizeLimit, totalPackagesByWeightLimit), 1);

            var insuranceAmountPerPackage = 0;

            if (_upsSettings.InsurePackage)
            {
                //The maximum declared amount per package: 50000 USD.
                //use subTotalWithoutDiscount as insured value
                var cart = shippingOptionRequest.Items.Select(item =>
                {
                    var shoppingCartItem      = item.ShoppingCartItem;
                    shoppingCartItem.Quantity = item.GetQuantity();
                    return(shoppingCartItem);
                }).ToList();
                var(_, _, subTotalWithoutDiscount, _, _) = await _orderTotalCalculationService.GetShoppingCartSubTotalAsync(cart, false);

                insuranceAmountPerPackage = Convert.ToInt32(subTotalWithoutDiscount / totalPackages);
            }

            //create packages according to calculated value
            var package = await CreatePackageAsync(width, length, height, weight / totalPackages, insuranceAmountPerPackage);

            return(Enumerable.Repeat(package, totalPackages));
        }
Example #11
0
        /// <summary>
        /// Gets shipping rates
        /// </summary>
        /// <param name="shippingOptionRequest">Shipping option request details</param>
        /// <param name="saturdayDelivery">Whether to get rates for Saturday Delivery</param>
        /// <returns>
        /// A task that represents the asynchronous operation
        /// The task result contains the shipping options; errors if exist
        /// </returns>
        private async Task <(IList <ShippingOption> shippingOptions, string error)> GetShippingOptionsAsync(GetShippingOptionRequest shippingOptionRequest,
                                                                                                            bool saturdayDelivery = false)
        {
            try
            {
                //create request details
                var request = await CreateRateRequestAsync(shippingOptionRequest, saturdayDelivery);

                //get rate response
                var rateResponse = await GetRatesAsync(request);

                //prepare shipping options
                return((await PrepareShippingOptionsAsync(rateResponse)).Select(shippingOption =>
                {
                    //correct option name
                    if (!shippingOption.Name.ToLowerInvariant().StartsWith("ups"))
                    {
                        shippingOption.Name = $"UPS {shippingOption.Name}";
                    }
                    if (saturdayDelivery)
                    {
                        shippingOption.Name = $"{shippingOption.Name} - Saturday Delivery";
                    }

                    //add additional handling charge
                    shippingOption.Rate += _upsSettings.AdditionalHandlingCharge;

                    return shippingOption;
                }).ToList(), null);
            }
            catch (Exception exception)
            {
                //log errors
                var message = $"Error while getting UPS rates{Environment.NewLine}{exception.Message}";
                await _logger.ErrorAsync(message, exception, shippingOptionRequest.Customer);

                return(new List <ShippingOption>(), message);
            }
        }
Example #12
0
        /// <summary>
        /// Create packages (total dimensions of shopping cart items determines number of packages)
        /// </summary>
        /// <param name="shippingOptionRequest">shipping option request</param>
        /// <returns>
        /// A task that represents the asynchronous operation
        /// The task result contains the packages
        /// </returns>
        private async Task <IEnumerable <UPSRate.PackageType> > GetPackagesByDimensionsAsync(GetShippingOptionRequest shippingOptionRequest)
        {
            //get dimensions and weight of the whole package
            var(width, length, height) = await GetDimensionsAsync(shippingOptionRequest.Items);

            var weight = await GetWeightAsync(shippingOptionRequest);

            //whether the package doesn't exceed the weight and size limits
            var weightLimit = await GetWeightLimitAsync();

            var sizeLimit = await GetSizeLimitAsync();

            if (weight <= weightLimit && GetPackageSize(width, length, height) <= sizeLimit)
            {
                var insuranceAmount = 0;
                if (_upsSettings.InsurePackage)
                {
                    //The maximum declared amount per package: 50000 USD.
                    //use subTotalWithoutDiscount as insured value
                    var cart = shippingOptionRequest.Items.Select(item =>
                    {
                        var shoppingCartItem      = item.ShoppingCartItem;
                        shoppingCartItem.Quantity = item.GetQuantity();
                        return(shoppingCartItem);
                    }).ToList();
                    var(_, _, subTotalWithoutDiscount, _, _) = await _orderTotalCalculationService.GetShoppingCartSubTotalAsync(cart, false);

                    insuranceAmount = Convert.ToInt32(subTotalWithoutDiscount);
                }

                return(new[] { await CreatePackageAsync(width, length, height, weight, insuranceAmount) });
            }

            //get total packages number according to package limits
            var totalPackagesByWeightLimit = weight > weightLimit
                ? Convert.ToInt32(Math.Ceiling(weight / weightLimit))
                : 1;

            var totalPackagesBySizeLimit = GetPackageSize(width, length, height) > sizeLimit
                ? Convert.ToInt32(Math.Ceiling(GetPackageSize(width, length, height) / await GetLengthLimitAsync()))
                : 1;

            var totalPackages = Math.Max(Math.Max(totalPackagesBySizeLimit, totalPackagesByWeightLimit), 1);

            width  = Math.Max(width / totalPackages, 1);
            length = Math.Max(length / totalPackages, 1);
            height = Math.Max(height / totalPackages, 1);
            weight = Math.Max(weight / totalPackages, 1);

            var insuranceAmountPerPackage = 0;

            if (_upsSettings.InsurePackage)
            {
                //The maximum declared amount per package: 50000 USD.
                //use subTotalWithoutDiscount as insured value
                var cart = shippingOptionRequest.Items.Select(item =>
                {
                    var shoppingCartItem      = item.ShoppingCartItem;
                    shoppingCartItem.Quantity = item.GetQuantity();
                    return(shoppingCartItem);
                }).ToList();
                var(_, _, subTotalWithoutDiscount, _, _) = await _orderTotalCalculationService.GetShoppingCartSubTotalAsync(cart, false);

                insuranceAmountPerPackage = Convert.ToInt32(subTotalWithoutDiscount / totalPackages);
            }

            //create packages according to calculated value
            var package = await CreatePackageAsync(width, length, height, weight, insuranceAmountPerPackage);

            return(Enumerable.Repeat(package, totalPackages));
        }
Example #13
0
        /// <summary>
        /// Create packages (each shopping cart item is a separate package)
        /// </summary>
        /// <param name="shippingOptionRequest">shipping option request</param>
        /// <returns>
        /// A task that represents the asynchronous operation
        /// The task result contains the packages
        /// </returns>
        private async Task <IEnumerable <UPSRate.PackageType> > GetPackagesForOneItemPerPackageAsync(GetShippingOptionRequest shippingOptionRequest)
        {
            return(await shippingOptionRequest.Items.SelectManyAwait(async packageItem =>
            {
                //get dimensions and weight of the single item
                var(width, length, height) = await GetDimensionsForSingleItemAsync(packageItem.ShoppingCartItem, packageItem.Product);
                var weight = await GetWeightForSingleItemAsync(packageItem.ShoppingCartItem, shippingOptionRequest.Customer, packageItem.Product);

                var insuranceAmount = 0;
                if (_upsSettings.InsurePackage)
                {
                    //The maximum declared amount per package: 50000 USD.
                    insuranceAmount = Convert.ToInt32(packageItem.Product.Price);
                }

                //create packages according to item quantity
                var package = await CreatePackageAsync(width, length, height, weight, insuranceAmount);

                return Enumerable.Repeat(package, packageItem.GetQuantity());
            }).ToListAsync());
        }
Example #14
0
        /// <summary>
        /// Create request details to get shipping rates
        /// </summary>
        /// <param name="shippingOptionRequest">Shipping option request</param>
        /// <param name="saturdayDelivery">Whether to get rates for Saturday Delivery</param>
        /// <returns>
        /// A task that represents the asynchronous operation
        /// The task result contains the rate request details
        /// </returns>
        private async Task <UPSRate.RateRequest> CreateRateRequestAsync(GetShippingOptionRequest shippingOptionRequest, bool saturdayDelivery = false)
        {
            //set request details
            var request = new UPSRate.RateRequest
            {
                Request = new UPSRate.RequestType
                {
                    //used to define the request type
                    //Shop - the server validates the shipment, and returns rates for all UPS products from the ShipFrom to the ShipTo addresses
                    RequestOption = new[] { "Shop" }
                }
            };

            //prepare addresses details
            var stateCodeTo     = (await _stateProvinceService.GetStateProvinceByAddressAsync(shippingOptionRequest.ShippingAddress))?.Abbreviation;
            var stateCodeFrom   = shippingOptionRequest.StateProvinceFrom?.Abbreviation;
            var countryCodeFrom = (shippingOptionRequest.CountryFrom ?? (await _countryService.GetAllCountriesAsync()).FirstOrDefault())?.TwoLetterIsoCode ?? string.Empty;

            var addressFromDetails = new UPSRate.ShipAddressType
            {
                AddressLine       = new[] { shippingOptionRequest.AddressFrom },
                City              = shippingOptionRequest.CityFrom,
                StateProvinceCode = stateCodeFrom,
                CountryCode       = countryCodeFrom,
                PostalCode        = shippingOptionRequest.ZipPostalCodeFrom
            };
            var addressToDetails = new UPSRate.ShipToAddressType
            {
                AddressLine                 = new[] { shippingOptionRequest.ShippingAddress.Address1, shippingOptionRequest.ShippingAddress.Address2 },
                City                        = shippingOptionRequest.ShippingAddress.City,
                StateProvinceCode           = stateCodeTo,
                CountryCode                 = (await _countryService.GetCountryByAddressAsync(shippingOptionRequest.ShippingAddress))?.TwoLetterIsoCode,
                PostalCode                  = shippingOptionRequest.ShippingAddress.ZipPostalCode,
                ResidentialAddressIndicator = string.Empty
            };

            //set shipment details
            request.Shipment = new UPSRate.ShipmentType
            {
                Shipper = new UPSRate.ShipperType
                {
                    ShipperNumber = _upsSettings.AccountNumber,
                    Address       = addressFromDetails
                },
                ShipFrom = new UPSRate.ShipFromType
                {
                    Address = addressFromDetails
                },
                ShipTo = new UPSRate.ShipToType
                {
                    Address = addressToDetails
                }
            };

            //set pickup options and customer classification for US shipments
            if (countryCodeFrom.Equals("US", StringComparison.InvariantCultureIgnoreCase))
            {
                request.PickupType = new UPSRate.CodeDescriptionType
                {
                    Code = GetUpsCode(_upsSettings.PickupType)
                };
                request.CustomerClassification = new UPSRate.CodeDescriptionType
                {
                    Code = GetUpsCode(_upsSettings.CustomerClassification)
                };
            }

            //set negotiated rates details
            if (!string.IsNullOrEmpty(_upsSettings.AccountNumber) && !string.IsNullOrEmpty(stateCodeFrom) && !string.IsNullOrEmpty(stateCodeTo))
            {
                request.Shipment.ShipmentRatingOptions = new UPSRate.ShipmentRatingOptionsType
                {
                    NegotiatedRatesIndicator   = string.Empty,
                    UserLevelDiscountIndicator = string.Empty
                };
            }

            //set Saturday delivery details
            if (saturdayDelivery)
            {
                request.Shipment.ShipmentServiceOptions = new UPSRate.ShipmentServiceOptionsType
                {
                    SaturdayDeliveryIndicator = string.Empty
                };
            }

            //set packages details
            request.Shipment.Package = _upsSettings.PackingType switch
            {
                PackingType.PackByOneItemPerPackage => (await GetPackagesForOneItemPerPackageAsync(shippingOptionRequest)).ToArray(),
                PackingType.PackByVolume => (await GetPackagesByCubicRootAsync(shippingOptionRequest)).ToArray(),
                _ => (await GetPackagesByDimensionsAsync(shippingOptionRequest)).ToArray()
            };
            return(request);
        }
        private void SetIndividualPackageLineItemsCubicRootDimensions(StringBuilder sb, GetShippingOptionRequest getShippingOptionRequest, UPSPackagingType packagingType, decimal orderSubTotal, string currencyCode)
        {
            // Rate request setup - Total Volume of Shopping Cart Items determines number of packages

            //Dimensional weight is based on volume (the amount of space a package
            //occupies in relation to its actual weight). If the cubic size of your
            //package measures three cubic feet (5,184 cubic inches or 84,951
            //cubic centimetres) or greater, you will be charged the greater of the
            //dimensional weight or the actual weight.
            //This algorithm devides total package volume by the UPS settings PackingPackageVolume
            //so that no package requires dimensional weight; this could result in an under-charge.

            var usedMeasureWeight    = GetUsedMeasureWeight();
            var usedMeasureDimension = GetUsedMeasureDimension();

            int totalPackagesDims;
            int length;
            int height;
            int width;

            if (getShippingOptionRequest.Items.Count == 1 && getShippingOptionRequest.Items[0].GetQuantity() == 1)
            {
                var sci = getShippingOptionRequest.Items[0].ShoppingCartItem;

                //get dimensions for qty 1
                decimal lengthTmp, widthTmp, heightTmp;
                _shippingService.GetDimensions(new List <GetShippingOptionRequest.PackageItem>
                {
                    new GetShippingOptionRequest.PackageItem(sci, 1)
                }, out widthTmp, out lengthTmp, out heightTmp);

                totalPackagesDims = 1;
                length            = ConvertFromPrimaryMeasureDimension(lengthTmp, usedMeasureDimension);
                height            = ConvertFromPrimaryMeasureDimension(heightTmp, usedMeasureDimension);
                width             = ConvertFromPrimaryMeasureDimension(widthTmp, usedMeasureDimension);
            }
            else
            {
                decimal totalVolume = 0;
                foreach (var item in getShippingOptionRequest.Items)
                {
                    var sci = item.ShoppingCartItem;

                    //get dimensions for qty 1
                    decimal lengthTmp, widthTmp, heightTmp;
                    _shippingService.GetDimensions(new List <GetShippingOptionRequest.PackageItem>
                    {
                        new GetShippingOptionRequest.PackageItem(sci, 1)
                    }, out widthTmp, out lengthTmp, out heightTmp);

                    int productLength = ConvertFromPrimaryMeasureDimension(lengthTmp, usedMeasureDimension);
                    int productHeight = ConvertFromPrimaryMeasureDimension(lengthTmp, usedMeasureDimension);
                    int productWidth  = ConvertFromPrimaryMeasureDimension(widthTmp, usedMeasureDimension);
                    totalVolume += item.GetQuantity() * (productHeight * productWidth * productLength);
                }

                int dimension;
                if (totalVolume == 0)
                {
                    dimension         = 0;
                    totalPackagesDims = 1;
                }
                else
                {
                    // cubic inches
                    int packageVolume = _upsSettings.PackingPackageVolume;
                    if (packageVolume <= 0)
                    {
                        packageVolume = 5184;
                    }

                    // cube root (floor)
                    dimension = Convert.ToInt32(Math.Floor(Math.Pow(Convert.ToDouble(packageVolume), (double)(1.0 / 3.0))));
                    if (IsPackageTooLarge(dimension, dimension, dimension))
                    {
                        throw new NopException("upsSettings.PackingPackageVolume exceeds max package size");
                    }

                    // adjust packageVolume for dimensions calculated
                    packageVolume = dimension * dimension * dimension;

                    totalPackagesDims = Convert.ToInt32(Math.Ceiling(totalVolume / packageVolume));
                }

                length = width = height = dimension;
            }
            if (length < 1)
            {
                length = 1;
            }
            if (height < 1)
            {
                height = 1;
            }
            if (width < 1)
            {
                width = 1;
            }

            int weight = ConvertFromPrimaryMeasureWeight(_shippingService.GetTotalWeight(getShippingOptionRequest), usedMeasureWeight);

            if (weight < 1)
            {
                weight = 1;
            }

            int totalPackagesWeights = 1;

            if (IsPackageTooHeavy(weight))
            {
                totalPackagesWeights = Convert.ToInt32(Math.Ceiling((decimal)weight / (decimal)MAXPACKAGEWEIGHT));
            }

            int totalPackages = totalPackagesDims > totalPackagesWeights ? totalPackagesDims : totalPackagesWeights;

            int weightPerPackage = weight / totalPackages;

            //The maximum declared amount per package: 50000 USD.
            int insuranceAmountPerPackage = _upsSettings.InsurePackage ? Convert.ToInt32(orderSubTotal / totalPackages) : 0;

            for (int i = 0; i < totalPackages; i++)
            {
                AppendPackageRequest(sb, packagingType, length, height, width, weightPerPackage, insuranceAmountPerPackage, currencyCode);
            }
        }
        private void SetIndividualPackageLineItemsOneItemPerPackage(RateRequest request, GetShippingOptionRequest getShippingOptionRequest, decimal orderSubTotal, string currencyCode)
        {
            // Rate request setup - each Shopping Cart Item is a separate package

            var usedMeasureWeight    = GetUsedMeasureWeight();
            var usedMeasureDimension = GetUsedMeasureDimension();

            var items      = getShippingOptionRequest.Items;
            var totalItems = items.GetTotalProducts();

            request.RequestedShipment.PackageCount = totalItems.ToString();
            request.RequestedShipment.RequestedPackageLineItems = new RequestedPackageLineItem[totalItems];

            int i = 0;

            foreach (var sci in items)
            {
                int length = ConvertFromPrimaryMeasureDimension(sci.Item.Product.Length, usedMeasureDimension);
                int height = ConvertFromPrimaryMeasureDimension(sci.Item.Product.Height, usedMeasureDimension);
                int width  = ConvertFromPrimaryMeasureDimension(sci.Item.Product.Width, usedMeasureDimension);
                int weight = ConvertFromPrimaryMeasureWeight(sci.Item.Product.Weight, usedMeasureWeight);
                if (length < 1)
                {
                    length = 1;
                }
                if (height < 1)
                {
                    height = 1;
                }
                if (width < 1)
                {
                    width = 1;
                }
                if (weight < 1)
                {
                    weight = 1;
                }

                for (int j = 0; j < sci.Item.Quantity; j++)
                {
                    request.RequestedShipment.RequestedPackageLineItems[i] = new RequestedPackageLineItem();
                    request.RequestedShipment.RequestedPackageLineItems[i].SequenceNumber = (i + 1).ToString();                   // package sequence number
                    request.RequestedShipment.RequestedPackageLineItems[i].Weight         = new RateServiceWebReference.Weight(); // package weight
                    request.RequestedShipment.RequestedPackageLineItems[i].Weight.Units   = RateServiceWebReference.WeightUnits.LB;
                    request.RequestedShipment.RequestedPackageLineItems[i].Weight.Value   = (decimal)weight;

                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions        = new RateServiceWebReference.Dimensions(); // package dimensions
                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Length = length.ToString();
                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Height = height.ToString();
                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Width  = width.ToString();
                    request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Units  = RateServiceWebReference.LinearUnits.IN;

                    request.RequestedShipment.RequestedPackageLineItems[i].InsuredValue          = new RateServiceWebReference.Money(); // insured value
                    request.RequestedShipment.RequestedPackageLineItems[i].InsuredValue.Amount   = sci.Item.Product.Price;
                    request.RequestedShipment.RequestedPackageLineItems[i].InsuredValue.Currency = currencyCode;

                    i++;
                }
            }
        }
        /// <summary>
        ///  Gets available shipping options
        /// </summary>
        /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
        /// <returns>Represents a response of getting shipping rate options</returns>
        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
            {
                throw new ArgumentNullException("getShippingOptionRequest");
            }

            var response = new GetShippingOptionResponse();

            if (getShippingOptionRequest.Items == null)
            {
                response.AddError("No shipment items");
                return(response);
            }

            if (getShippingOptionRequest.ShippingAddress == null)
            {
                response.AddError("Shipping address is not set");
                return(response);
            }

            if (String.IsNullOrEmpty(getShippingOptionRequest.ShippingAddress.CountryId))
            {
                response.AddError("Shipping country is not set");
                return(response);
            }

            if (getShippingOptionRequest.CountryFrom == null)
            {
                getShippingOptionRequest.CountryFrom = _countryService.GetAllCountries().FirstOrDefault();
            }

            try
            {
                string requestString = CreateRequest(_upsSettings.AccessKey, _upsSettings.Username, _upsSettings.Password, getShippingOptionRequest,
                                                     _upsSettings.CustomerClassification, _upsSettings.PickupType, _upsSettings.PackagingType);
                if (_upsSettings.Tracing)
                {
                    _traceMessages.AppendLine("Request:").AppendLine(requestString);
                }

                string responseXml = DoRequest(_upsSettings.Url, requestString);
                if (_upsSettings.Tracing)
                {
                    _traceMessages.AppendLine("Response:").AppendLine(responseXml);
                }

                string error           = "";
                var    shippingOptions = ParseResponse(responseXml, ref error);
                if (String.IsNullOrEmpty(error))
                {
                    foreach (var shippingOption in shippingOptions)
                    {
                        if (!shippingOption.Name.ToLower().StartsWith("ups"))
                        {
                            shippingOption.Name = string.Format("UPS {0}", shippingOption.Name);
                        }
                        shippingOption.Rate += _upsSettings.AdditionalHandlingCharge;
                        response.ShippingOptions.Add(shippingOption);
                    }
                }
                else
                {
                    response.AddError(error);
                }
            }
            finally
            {
                if (_upsSettings.Tracing && _traceMessages.Length > 0)
                {
                    string shortMessage = String.Format("UPS Get Shipping Options for customer {0}.  {1} item(s) in cart",
                                                        getShippingOptionRequest.Customer.Email, getShippingOptionRequest.Items.Count);
                    _logger.Information(shortMessage, new Exception(_traceMessages.ToString()), getShippingOptionRequest.Customer);
                }
            }

            return(response);
        }
        private void SetIndividualPackageLineItemsCubicRootDimensions(RateRequest request, GetShippingOptionRequest getShippingOptionRequest, decimal orderSubTotal, string currencyCode)
        {
            // Rate request setup - Total Volume of Shopping Cart Items determines number of packages

            //From FedEx Guide (Ground):
            //Dimensional weight is based on volume (the amount of space a package
            //occupies in relation to its actual weight). If the cubic size of your FedEx
            //Ground package measures three cubic feet (5,184 cubic inches or 84,951
            //cubic centimetres) or greater, you will be charged the greater of the
            //dimensional weight or the actual weight.
            //A package weighing 150 lbs. (68 kg) or less and measuring greater than
            //130 inches (330 cm) in combined length and girth will be classified by
            //FedEx Ground as an “Oversize” package. All packages must have a
            //combined length and girth of no more than 165 inches (419 cm). An
            //oversize charge of $30 per package will also apply to any package
            //measuring greater than 130 inches (330 cm) in combined length and
            //girth.
            //Shipping charges for packages smaller than three cubic feet are based
            //on actual weight

            // Dimensional Weight applies to packages with volume 5,184 cubic inches or more
            // cube root(5184) = 17.3

            // Packages that exceed 130 inches in length and girth (2xHeight + 2xWidth)
            // are considered “oversize” packages.
            // Assume a cube (H=W=L) of that size: 130 = D + (2xD + 2xD) = 5xD :  D = 130/5 = 26
            // 26x26x26 = 17,576
            // Avoid oversize by using 25"
            // 25x25x25 = 15,625

            // Which is less $  - multiple small pakages, or one large package using dimensional weight
            //  15,625 / 5184 = 3.014 =  3 packages
            // Ground for total weight:             60lbs     15lbs
            //  3 packages 17x17x17 (20 lbs each) = $66.21    39.39
            //  1 package  25x25x25 (60 lbs)      = $71.70    71.70


            var usedMeasureWeight    = GetUsedMeasureWeight();
            var usedMeasureDimension = GetUsedMeasureDimension();

            int totalPackagesDims;
            int length;
            int height;
            int width;

            if (getShippingOptionRequest.Items.Count == 1 && getShippingOptionRequest.Items[0].Item.Quantity == 1)
            {
                totalPackagesDims = 1;
                var product = getShippingOptionRequest.Items[0].Item.Product;
                length = ConvertFromPrimaryMeasureDimension(product.Length, usedMeasureDimension);
                height = ConvertFromPrimaryMeasureDimension(product.Height, usedMeasureDimension);
                width  = ConvertFromPrimaryMeasureDimension(product.Width, usedMeasureDimension);
            }
            else
            {
                decimal totalVolume = 0;
                foreach (var item in getShippingOptionRequest.Items)
                {
                    var product       = item.Item.Product;
                    int productLength = ConvertFromPrimaryMeasureDimension(product.Length, usedMeasureDimension);
                    int productHeight = ConvertFromPrimaryMeasureDimension(product.Height, usedMeasureDimension);
                    int productWidth  = ConvertFromPrimaryMeasureDimension(product.Width, usedMeasureDimension);
                    totalVolume += item.Item.Quantity * (productHeight * productWidth * productLength);
                }

                int dimension;
                if (totalVolume == 0)
                {
                    dimension         = 0;
                    totalPackagesDims = 1;
                }
                else
                {
                    // cubic inches
                    int packageVolume = _fedexSettings.PackingPackageVolume;
                    if (packageVolume <= 0)
                    {
                        packageVolume = 5184;
                    }

                    // cube root (floor)
                    dimension = Convert.ToInt32(Math.Floor(Math.Pow(Convert.ToDouble(packageVolume), (double)(1.0 / 3.0))));
                    if (IsPackageTooLarge(dimension, dimension, dimension))
                    {
                        throw new SmartException("fedexSettings.PackingPackageVolume exceeds max package size");
                    }

                    // adjust packageVolume for dimensions calculated
                    packageVolume = dimension * dimension * dimension;

                    totalPackagesDims = Convert.ToInt32(Math.Ceiling(totalVolume / packageVolume));
                }

                length = width = height = dimension;
            }
            if (length < 1)
            {
                length = 1;
            }
            if (height < 1)
            {
                height = 1;
            }
            if (width < 1)
            {
                width = 1;
            }

            int weight = ConvertFromPrimaryMeasureWeight(_shippingService.GetShoppingCartTotalWeight(getShippingOptionRequest.Items), usedMeasureWeight);

            if (weight < 1)
            {
                weight = 1;
            }

            int totalPackagesWeights = 1;

            if (IsPackageTooHeavy(weight))
            {
                totalPackagesWeights = Convert.ToInt32(Math.Ceiling((decimal)weight / (decimal)MAXPACKAGEWEIGHT));
            }

            int totalPackages = totalPackagesDims > totalPackagesWeights ? totalPackagesDims : totalPackagesWeights;

            decimal orderSubTotalPerPackage = orderSubTotal / totalPackages;
            int     weightPerPackage        = weight / totalPackages;

            request.RequestedShipment.PackageCount = totalPackages.ToString();
            request.RequestedShipment.RequestedPackageLineItems = new RequestedPackageLineItem[totalPackages];

            for (int i = 0; i < totalPackages; i++)
            {
                request.RequestedShipment.RequestedPackageLineItems[i] = new RequestedPackageLineItem();
                request.RequestedShipment.RequestedPackageLineItems[i].SequenceNumber = (i + 1).ToString();                   // package sequence number
                request.RequestedShipment.RequestedPackageLineItems[i].Weight         = new RateServiceWebReference.Weight(); // package weight
                request.RequestedShipment.RequestedPackageLineItems[i].Weight.Units   = RateServiceWebReference.WeightUnits.LB;
                request.RequestedShipment.RequestedPackageLineItems[i].Weight.Value   = (decimal)weightPerPackage;

                request.RequestedShipment.RequestedPackageLineItems[i].Dimensions            = new RateServiceWebReference.Dimensions(); // package dimensions
                request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Length     = length.ToString();
                request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Height     = height.ToString();
                request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Width      = width.ToString();
                request.RequestedShipment.RequestedPackageLineItems[i].Dimensions.Units      = RateServiceWebReference.LinearUnits.IN;
                request.RequestedShipment.RequestedPackageLineItems[i].InsuredValue          = new RateServiceWebReference.Money(); // insured value
                request.RequestedShipment.RequestedPackageLineItems[i].InsuredValue.Amount   = orderSubTotalPerPackage;
                request.RequestedShipment.RequestedPackageLineItems[i].InsuredValue.Currency = currencyCode;
            }
        }
        private string CreateRequest(string username, string password, GetShippingOptionRequest getShippingOptionRequest)
        {
            var usedMeasureWeight = _measureService.GetMeasureWeightBySystemKeyword(MEASUREWEIGHTSYSTEMKEYWORD);

            if (usedMeasureWeight == null)
            {
                throw new SmartException(string.Format("USPS shipping service. Could not load \"{0}\" measure weight", MEASUREWEIGHTSYSTEMKEYWORD));
            }

            var baseusedMeasureWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId);

            if (baseusedMeasureWeight == null)
            {
                throw new SmartException("Primary weight can't be loaded");
            }

            var usedMeasureDimension = _measureService.GetMeasureDimensionBySystemKeyword(MEASUREDIMENSIONSYSTEMKEYWORD);

            if (usedMeasureDimension == null)
            {
                throw new SmartException(string.Format("USPS shipping service. Could not load \"{0}\" measure dimension", MEASUREDIMENSIONSYSTEMKEYWORD));
            }

            var baseusedMeasureDimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);

            if (usedMeasureDimension == null)
            {
                throw new SmartException("Primary dimension can't be loaded");
            }


            int length = Convert.ToInt32(Math.Ceiling(getShippingOptionRequest.GetTotalLength() / baseusedMeasureDimension.Ratio * usedMeasureDimension.Ratio));
            int height = Convert.ToInt32(Math.Ceiling(getShippingOptionRequest.GetTotalHeight() / baseusedMeasureDimension.Ratio * usedMeasureDimension.Ratio));
            int width  = Convert.ToInt32(Math.Ceiling(getShippingOptionRequest.GetTotalWidth() / baseusedMeasureDimension.Ratio * usedMeasureDimension.Ratio));
            int weight = Convert.ToInt32(Math.Ceiling(_shippingService.GetShoppingCartTotalWeight(getShippingOptionRequest.Items) / baseusedMeasureWeight.Ratio * usedMeasureWeight.Ratio));

            if (length < 1)
            {
                length = 1;
            }
            if (height < 1)
            {
                height = 1;
            }
            if (width < 1)
            {
                width = 1;
            }
            if (weight < 1)
            {
                weight = 1;
            }

            string zipPostalCodeFrom = getShippingOptionRequest.ZipPostalCodeFrom;
            string zipPostalCodeTo   = getShippingOptionRequest.ShippingAddress.ZipPostalCode;

            //valid values for testing. http://testing.shippingapis.com/ShippingAPITest.dll
            //Zip to = "20008"; Zip from ="10022"; weight = 2;

            int pounds  = Convert.ToInt32(weight / 16);
            int ounces  = Convert.ToInt32(weight - (pounds * 16.0M));
            int girth   = height + height + width + width;
            var taxRate = decimal.Zero;
            //Get shopping cart sub-total.  V2 International rates require the package value to be declared.
            decimal subTotal = decimal.Zero;

            foreach (var shoppingCartItem in getShippingOptionRequest.Items)
            {
                if (shoppingCartItem.Item.IsFreeShipping || !shoppingCartItem.Item.IsShipEnabled)
                {
                    continue;
                }

                var itemSubTotal        = _priceCalculationService.GetSubTotal(shoppingCartItem, true);
                var itemSubTotalInclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, itemSubTotal, true, getShippingOptionRequest.Customer, out taxRate);
                subTotal += itemSubTotalInclTax;
            }

            string requestString = string.Empty;

            bool isDomestic = IsDomesticRequest(getShippingOptionRequest);

            if (isDomestic)
            {
                #region domestic request
                zipPostalCodeFrom = zipPostalCodeFrom.Truncate(5).EnsureNumericOnly();
                zipPostalCodeTo   = zipPostalCodeTo.Truncate(5).EnsureNumericOnly();

                var sb = new StringBuilder();
                sb.AppendFormat("<RateV4Request USERID=\"{0}\" PASSWORD=\"{1}\">", username, password);
                sb.Append("<Revision>2</Revision>");

                var xmlStrings = new USPSStrings(); // Create new instance with string array

                if ((!IsPackageTooHeavy(pounds)) && (!IsPackageTooLarge(length, height, width)))
                {
                    var packageSize = GetPackageSize(length, height, width);
                    // RJH get all XML strings not commented out for USPSStrings.
                    // RJH V3 USPS Service must be Express, Express SH, Express Commercial, Express SH Commercial, First Class, Priority, Priority Commercial, Parcel, Library, BPM, Media, ALL or ONLINE;
                    // AC - Updated to V4 API and made minor improvements to allow First Class Packages (package only - not envelopes).


                    foreach (string element in xmlStrings.Elements) // Loop over elements with property
                    {
                        if ((element == "First Class") && (weight >= 14))
                        {
                            // AC - At the time of coding there aren't any First Class shipping options for packages over 13 ounces.
                        }
                        else
                        {
                            sb.Append("<Package ID=\"0\">");

                            sb.AppendFormat("<Service>{0}</Service>", element);
                            if (element == "First Class")
                            {
                                sb.Append("<FirstClassMailType>PARCEL</FirstClassMailType>");
                            }

                            sb.AppendFormat("<ZipOrigination>{0}</ZipOrigination>", zipPostalCodeFrom);
                            sb.AppendFormat("<ZipDestination>{0}</ZipDestination>", zipPostalCodeTo);
                            sb.AppendFormat("<Pounds>{0}</Pounds>", pounds);
                            sb.AppendFormat("<Ounces>{0}</Ounces>", ounces);
                            sb.Append("<Container/>");
                            sb.AppendFormat("<Size>{0}</Size>", packageSize);
                            sb.AppendFormat("<Width>{0}</Width>", width);
                            sb.AppendFormat("<Length>{0}</Length>", length);
                            sb.AppendFormat("<Height>{0}</Height>", height);
                            sb.AppendFormat("<Girth>{0}</Girth>", girth);

                            sb.Append("<Machinable>FALSE</Machinable>");

                            sb.Append("</Package>");
                        }
                    }
                }
                else
                {
                    int totalPackages        = 1;
                    int totalPackagesDims    = 1;
                    int totalPackagesWeights = 1;
                    if (IsPackageTooHeavy(pounds))
                    {
                        totalPackagesWeights = Convert.ToInt32(Math.Ceiling((decimal)pounds / (decimal)MAXPACKAGEWEIGHT));
                    }
                    if (IsPackageTooLarge(length, height, width))
                    {
                        totalPackagesDims = Convert.ToInt32(Math.Ceiling((decimal)TotalPackageSize(length, height, width) / (decimal)108));
                    }
                    totalPackages = totalPackagesDims > totalPackagesWeights ? totalPackagesDims : totalPackagesWeights;
                    if (totalPackages == 0)
                    {
                        totalPackages = 1;
                    }

                    int pounds2 = pounds / totalPackages;
                    //we don't use ounces
                    int ounces2 = ounces / totalPackages;
                    int height2 = height / totalPackages;
                    int width2  = width / totalPackages;
                    int length2 = length / totalPackages;

                    if (pounds2 < 1)
                    {
                        pounds2 = 1;
                    }
                    if (height2 < 1)
                    {
                        height2 = 1;
                    }
                    if (width2 < 1)
                    {
                        width2 = 1;
                    }
                    if (length2 < 1)
                    {
                        length2 = 1;
                    }

                    var packageSize = GetPackageSize(length2, height2, width2);

                    int girth2 = height2 + height2 + width2 + width2;

                    for (int i = 0; i < totalPackages; i++)
                    {
                        foreach (string element in xmlStrings.Elements)
                        {
                            if ((element == "First Class") && (weight >= 14))
                            {
                                // AC - At the time of coding there aren't any First Class shipping options for packages over 13 ounces.
                            }
                            else
                            {
                                sb.AppendFormat("<Package ID=\"{0}\">", i.ToString());
                                sb.AppendFormat("<Service>{0}</Service>", element);
                                if (element == "First Class")
                                {
                                    sb.Append("<FirstClassMailType>PARCEL</FirstClassMailType>");
                                }
                                sb.AppendFormat("<ZipOrigination>{0}</ZipOrigination>", zipPostalCodeFrom);
                                sb.AppendFormat("<ZipDestination>{0}</ZipDestination>", zipPostalCodeTo);
                                sb.AppendFormat("<Pounds>{0}</Pounds>", pounds2);
                                sb.AppendFormat("<Ounces>{0}</Ounces>", ounces2);
                                sb.Append("<Container/>");
                                sb.AppendFormat("<Size>{0}</Size>", packageSize);
                                sb.AppendFormat("<Width>{0}</Width>", width2);
                                sb.AppendFormat("<Length>{0}</Length>", length2);
                                sb.AppendFormat("<Height>{0}</Height>", height2);
                                sb.AppendFormat("<Girth>{0}</Girth>", girth2);
                                sb.Append("<Machinable>FALSE</Machinable>");
                                sb.Append("</Package>");
                            }
                        }
                    }
                }

                sb.Append("</RateV4Request>");

                requestString = "API=RateV4&XML=" + sb.ToString();
                #endregion
            }
            else
            {
                #region international request
                var sb = new StringBuilder();
                // sb.AppendFormat("<IntlRateRequest USERID=\"{0}\" PASSWORD=\"{1}\">", username, password);
                sb.AppendFormat("<IntlRateV2Request USERID=\"{0}\" PASSWORD=\"{1}\">", username, password);

                //V2 International rates require the package value to be declared.  Max content value for most shipping options is $400 so it is limited here.
                decimal intlSubTotal = decimal.Zero;
                if (subTotal > 400)
                {
                    intlSubTotal = 400;
                }
                else
                {
                    intlSubTotal = subTotal;
                }

                //little hack here for international requests
                length = 12;
                width  = 12;
                height = 12;
                girth  = height + height + width + width;

                string mailType    = "Package"; //Package, Envelope
                var    packageSize = GetPackageSize(length, height, width);

                if ((!IsPackageTooHeavy(pounds)) && (!IsPackageTooLarge(length, height, width)))
                {
                    sb.Append("<Package ID=\"0\">");
                    sb.AppendFormat("<Pounds>{0}</Pounds>", pounds);
                    sb.AppendFormat("<Ounces>{0}</Ounces>", ounces);
                    sb.Append("<Machinable>FALSE</Machinable>");
                    sb.AppendFormat("<MailType>{0}</MailType>", mailType);
                    sb.Append("<GXG>");
                    sb.Append("<POBoxFlag>N</POBoxFlag>");
                    sb.Append("<GiftFlag>N</GiftFlag>");
                    sb.Append("</GXG>");
                    sb.AppendFormat("<ValueOfContents>{0}</ValueOfContents>", intlSubTotal);
                    sb.AppendFormat("<Country>{0}</Country>", getShippingOptionRequest.ShippingAddress.Country.Name);
                    sb.Append("<Container>RECTANGULAR</Container>");
                    sb.AppendFormat("<Size>{0}</Size>", packageSize);
                    sb.AppendFormat("<Width>{0}</Width>", width);
                    sb.AppendFormat("<Length>{0}</Length>", length);
                    sb.AppendFormat("<Height>{0}</Height>", height);
                    sb.AppendFormat("<Girth>{0}</Girth>", girth);
                    sb.Append("<CommercialFlag>N</CommercialFlag>");
                    sb.Append("</Package>");
                }
                else
                {
                    int totalPackages        = 1;
                    int totalPackagesDims    = 1;
                    int totalPackagesWeights = 1;
                    if (IsPackageTooHeavy(pounds))
                    {
                        totalPackagesWeights = Convert.ToInt32(Math.Ceiling((decimal)pounds / (decimal)MAXPACKAGEWEIGHT));
                    }
                    if (IsPackageTooLarge(length, height, width))
                    {
                        totalPackagesDims = Convert.ToInt32(Math.Ceiling((decimal)TotalPackageSize(length, height, width) / (decimal)108));
                    }
                    totalPackages = totalPackagesDims > totalPackagesWeights ? totalPackagesDims : totalPackagesWeights;
                    if (totalPackages == 0)
                    {
                        totalPackages = 1;
                    }

                    int pounds2 = pounds / totalPackages;
                    //we don't use ounces
                    int ounces2 = ounces / totalPackages;
                    int height2 = height / totalPackages;
                    int width2  = width / totalPackages;
                    int length2 = length / totalPackages;
                    if (pounds2 < 1)
                    {
                        pounds2 = 1;
                    }
                    if (height2 < 1)
                    {
                        height2 = 1;
                    }
                    if (width2 < 1)
                    {
                        width2 = 1;
                    }
                    if (length2 < 1)
                    {
                        length2 = 1;
                    }

                    //little hack here for international requests
                    length2 = 12;
                    width2  = 12;
                    height2 = 12;
                    var packageSize2 = GetPackageSize(length2, height2, width2);
                    int girth2       = height2 + height2 + width2 + width2;

                    for (int i = 0; i < totalPackages; i++)
                    {
                        sb.AppendFormat("<Package ID=\"{0}\">", i.ToString());
                        sb.AppendFormat("<Pounds>{0}</Pounds>", pounds2);
                        sb.AppendFormat("<Ounces>{0}</Ounces>", ounces2);
                        sb.Append("<Machinable>FALSE</Machinable>");
                        sb.AppendFormat("<MailType>{0}</MailType>", mailType);
                        sb.Append("<GXG>");
                        sb.Append("<POBoxFlag>N</POBoxFlag>");
                        sb.Append("<GiftFlag>N</GiftFlag>");
                        sb.Append("</GXG>");
                        sb.AppendFormat("<ValueOfContents>{0}</ValueOfContents>", intlSubTotal);
                        sb.AppendFormat("<Country>{0}</Country>", getShippingOptionRequest.ShippingAddress.Country.Name);
                        sb.Append("<Container>RECTANGULAR</Container>");
                        sb.AppendFormat("<Size>{0}</Size>", packageSize2);
                        sb.AppendFormat("<Width>{0}</Width>", width2);
                        sb.AppendFormat("<Length>{0}</Length>", length2);
                        sb.AppendFormat("<Height>{0}</Height>", height2);
                        sb.AppendFormat("<Girth>{0}</Girth>", girth2);
                        sb.Append("<CommercialFlag>N</CommercialFlag>");
                        sb.Append("</Package>");
                    }
                }

                sb.Append("</IntlRateV2Request>");

                requestString = "API=IntlRateV2&XML=" + sb.ToString();
                #endregion
            }

            return(requestString);
        }
        private RateRequest CreateRateRequest(GetShippingOptionRequest getShippingOptionRequest, out Currency requestedShipmentCurrency)
        {
            // Build the RateRequest
            var request = new RateRequest();

            request.WebAuthenticationDetail = new RateServiceWebReference.WebAuthenticationDetail();
            request.WebAuthenticationDetail.UserCredential          = new RateServiceWebReference.WebAuthenticationCredential();
            request.WebAuthenticationDetail.UserCredential.Key      = _fedexSettings.Key;
            request.WebAuthenticationDetail.UserCredential.Password = _fedexSettings.Password;

            request.ClientDetail = new RateServiceWebReference.ClientDetail();
            request.ClientDetail.AccountNumber = _fedexSettings.AccountNumber;
            request.ClientDetail.MeterNumber   = _fedexSettings.MeterNumber;

            request.TransactionDetail = new RateServiceWebReference.TransactionDetail();
            request.TransactionDetail.CustomerTransactionId = "***Rate Available Services v7 Request***"; // This is a reference field for the customer.  Any value can be used and will be provided in the response.

            request.Version = new RateServiceWebReference.VersionId();                                    // WSDL version information, value is automatically set from wsdl

            request.ReturnTransitAndCommit          = true;
            request.ReturnTransitAndCommitSpecified = true;
            request.CarrierCodes = new RateServiceWebReference.CarrierCodeType[2];
            // Insert the Carriers you would like to see the rates for
            request.CarrierCodes[0] = RateServiceWebReference.CarrierCodeType.FDXE;
            request.CarrierCodes[1] = RateServiceWebReference.CarrierCodeType.FDXG;

            decimal  subTotalBase = decimal.Zero;
            decimal  orderSubTotalDiscountAmount  = decimal.Zero;
            Discount orderSubTotalAppliedDiscount = null;
            decimal  subTotalWithoutDiscountBase  = decimal.Zero;
            decimal  subTotalWithDiscountBase     = decimal.Zero;

            _orderTotalCalculationService.GetShoppingCartSubTotal(getShippingOptionRequest.Items,
                                                                  out orderSubTotalDiscountAmount, out orderSubTotalAppliedDiscount, out subTotalWithoutDiscountBase, out subTotalWithDiscountBase);

            subTotalBase = subTotalWithDiscountBase;

            request.RequestedShipment = new RequestedShipment();

            SetOrigin(request, getShippingOptionRequest);
            SetDestination(request, getShippingOptionRequest);

            requestedShipmentCurrency = GetRequestedShipmentCurrency(
                request.RequestedShipment.Shipper.Address.CountryCode,    // origin
                request.RequestedShipment.Recipient.Address.CountryCode); // destination

            decimal subTotalShipmentCurrency;
            var     primaryStoreCurrency = _services.StoreContext.CurrentStore.PrimaryStoreCurrency;

            if (requestedShipmentCurrency.CurrencyCode == primaryStoreCurrency.CurrencyCode)
            {
                subTotalShipmentCurrency = subTotalBase;
            }
            else
            {
                subTotalShipmentCurrency = _currencyService.ConvertFromPrimaryStoreCurrency(subTotalBase, requestedShipmentCurrency);
            }

            Debug.WriteLine(String.Format("SubTotal (Primary Currency) : {0} ({1})", subTotalBase, primaryStoreCurrency.CurrencyCode));
            Debug.WriteLine(String.Format("SubTotal (Shipment Currency): {0} ({1})", subTotalShipmentCurrency, requestedShipmentCurrency.CurrencyCode));

            SetShipmentDetails(request, getShippingOptionRequest, subTotalShipmentCurrency, requestedShipmentCurrency.CurrencyCode);
            SetPayment(request, getShippingOptionRequest);

            switch (_fedexSettings.PackingType)
            {
            case PackingType.PackByOneItemPerPackage:
                SetIndividualPackageLineItemsOneItemPerPackage(request, getShippingOptionRequest, subTotalShipmentCurrency, requestedShipmentCurrency.CurrencyCode);
                break;

            case PackingType.PackByVolume:
                SetIndividualPackageLineItemsCubicRootDimensions(request, getShippingOptionRequest, subTotalShipmentCurrency, requestedShipmentCurrency.CurrencyCode);
                break;

            case PackingType.PackByDimensions:
            default:
                SetIndividualPackageLineItems(request, getShippingOptionRequest, subTotalShipmentCurrency, requestedShipmentCurrency.CurrencyCode);
                break;
            }
            return(request);
        }
        /// <summary>
        /// Create request details to get shipping rates
        /// </summary>
        /// <param name="shippingOptionRequest">Shipping option request</param>
        /// <param name="saturdayDelivery">Whether to get rates for Saturday Delivery</param>
        /// <returns>Rate request details</returns>
        private FedexRate.RateRequest CreateRateRequest(GetShippingOptionRequest shippingOptionRequest, out Currency requestedShipmentCurrency)
        {
            // Build the RateRequest
            var request = new FedexRate.RateRequest
            {
                WebAuthenticationDetail = new FedexRate.WebAuthenticationDetail
                {
                    UserCredential = new FedexRate.WebAuthenticationCredential
                    {
                        Key      = _fedexSettings.Key,
                        Password = _fedexSettings.Password
                    }
                },

                ClientDetail = new FedexRate.ClientDetail
                {
                    AccountNumber = _fedexSettings.AccountNumber,
                    MeterNumber   = _fedexSettings.MeterNumber
                },

                TransactionDetail = new FedexRate.TransactionDetail
                {
                    CustomerTransactionId = "***Rate Available Services v16 Request - nopCommerce***" // This is a reference field for the customer.  Any value can be used and will be provided in the response.
                },

                Version = new FedexRate.VersionId(), // WSDL version information, value is automatically set from wsdl

                ReturnTransitAndCommit          = true,
                ReturnTransitAndCommitSpecified = true,
                // Insert the Carriers you would like to see the rates for
                CarrierCodes = new[] {
                    FedexRate.CarrierCodeType.FDXE,
                    FedexRate.CarrierCodeType.FDXG
                }
            };

            //TODO we should use getShippingOptionRequest.Items.GetQuantity() method to get subtotal
            _orderTotalCalculationService.GetShoppingCartSubTotal(
                shippingOptionRequest.Items.Select(x => x.ShoppingCartItem).ToList(),
                false, out var _, out var _, out var _, out var subTotalWithDiscountBase);

            request.RequestedShipment = new FedexRate.RequestedShipment();

            SetOrigin(request, shippingOptionRequest);
            SetDestination(request, shippingOptionRequest);

            requestedShipmentCurrency = GetRequestedShipmentCurrency(
                request.RequestedShipment.Shipper.Address.CountryCode,    // origin
                request.RequestedShipment.Recipient.Address.CountryCode); // destination

            decimal subTotalShipmentCurrency;
            var     primaryStoreCurrency = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId);

            if (requestedShipmentCurrency.CurrencyCode == primaryStoreCurrency.CurrencyCode)
            {
                subTotalShipmentCurrency = subTotalWithDiscountBase;
            }
            else
            {
                subTotalShipmentCurrency = _currencyService.ConvertFromPrimaryStoreCurrency(subTotalWithDiscountBase, requestedShipmentCurrency);
            }

            Debug.WriteLine($"SubTotal (Primary Currency) : {subTotalWithDiscountBase} ({primaryStoreCurrency.CurrencyCode})");
            Debug.WriteLine($"SubTotal (Shipment Currency): {subTotalShipmentCurrency} ({requestedShipmentCurrency.CurrencyCode})");

            SetShipmentDetails(request, subTotalShipmentCurrency, requestedShipmentCurrency.CurrencyCode);
            SetPayment(request);

            //set packages details
            switch (_fedexSettings.PackingType)
            {
            case PackingType.PackByOneItemPerPackage:
                SetIndividualPackageLineItemsOneItemPerPackage(request, shippingOptionRequest, requestedShipmentCurrency.CurrencyCode);
                break;

            case PackingType.PackByVolume:
                SetIndividualPackageLineItemsCubicRootDimensions(request, shippingOptionRequest, subTotalShipmentCurrency, requestedShipmentCurrency.CurrencyCode);
                break;

            case PackingType.PackByDimensions:
            default:
                SetIndividualPackageLineItems(request, shippingOptionRequest, subTotalShipmentCurrency, requestedShipmentCurrency.CurrencyCode);
                break;
            }
            return(request);
        }
        /// <summary>
        ///  Gets available shipping options
        /// </summary>
        /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
        /// <returns>Represents a response of getting shipping rate options</returns>
        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
            {
                throw new ArgumentNullException("getShippingOptionRequest");
            }

            var response = new GetShippingOptionResponse();

            if (getShippingOptionRequest.Items == null)
            {
                response.AddError(T("Admin.System.Warnings.NoShipmentItems"));
                return(response);
            }

            if (getShippingOptionRequest.ShippingAddress == null || getShippingOptionRequest.ShippingAddress.Country == null)
            {
                return(response);
            }

            Currency requestedShipmentCurrency;
            var      request = CreateRateRequest(getShippingOptionRequest, out requestedShipmentCurrency);
            var      service = new RateService(); // Initialize the service

            service.Url = _fedexSettings.Url;

            try
            {
                // This is the call to the web service passing in a RateRequest and returning a RateReply
                var reply = service.getRates(request); // Service call

                if (reply.HighestSeverity == RateServiceWebReference.NotificationSeverityType.SUCCESS ||
                    reply.HighestSeverity == RateServiceWebReference.NotificationSeverityType.NOTE ||
                    reply.HighestSeverity == RateServiceWebReference.NotificationSeverityType.WARNING) // check if the call was successful
                {
                    if (reply != null && reply.RateReplyDetails != null)
                    {
                        var shippingOptions = ParseResponse(reply, requestedShipmentCurrency);
                        foreach (var shippingOption in shippingOptions)
                        {
                            response.ShippingOptions.Add(shippingOption);
                        }
                    }
                    else
                    {
                        if (reply != null &&
                            reply.Notifications != null &&
                            reply.Notifications.Length > 0 &&
                            !String.IsNullOrEmpty(reply.Notifications[0].Message))
                        {
                            response.AddError(string.Format("{0} (code: {1})", reply.Notifications[0].Message, reply.Notifications[0].Code));
                            return(response);
                        }
                        else
                        {
                            response.AddError("Could not get reply from shipping server");
                            return(response);
                        }
                    }
                }
                else
                {
                    Debug.WriteLine(reply.Notifications[0].Message);
                    response.AddError(reply.Notifications[0].Message);
                    return(response);
                }
            }
            catch (SoapException e)
            {
                Debug.WriteLine(e.Detail.InnerText);
                response.AddError(e.Detail.InnerText);
                return(response);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
                response.AddError(e.Message);
                return(response);
            }

            return(response);
        }
        /// <summary>
        /// Create packages (each shopping cart item is a separate package)
        /// </summary>
        /// <param name="request">Shipping request</param>
        /// <param name="getShippingOptionRequest">Shipping option request</param>
        /// <param name="currencyCode">Currency code</param>
        private void SetIndividualPackageLineItemsOneItemPerPackage(FedexRate.RateRequest request, GetShippingOptionRequest getShippingOptionRequest, string currencyCode)
        {
            // Rate request setup - each Shopping Cart Item is a separate package
            var i          = 1;
            var items      = getShippingOptionRequest.Items;
            var totalItems = items.Sum(x => x.GetQuantity());

            request.RequestedShipment.PackageCount = totalItems.ToString();
            request.RequestedShipment.RequestedPackageLineItems = getShippingOptionRequest.Items.SelectMany(packageItem =>
            {
                //get dimensions and weight of the single item
                var(width, length, height) = GetDimensionsForSingleItem(packageItem.ShoppingCartItem);
                var weight = GetWeightForSingleItem(packageItem.ShoppingCartItem);

                var package = CreatePackage(width, length, height, weight, packageItem.ShoppingCartItem.Product.Price, (i + 1).ToString(), currencyCode);
                package.GroupPackageCount = "1";

                var packs = Enumerable.Range(i, packageItem.GetQuantity())
                            .Select(j => CreatePackage(width, length, height, weight, packageItem.ShoppingCartItem.Product.Price, j.ToString(), currencyCode)).ToArray();
                i += packageItem.GetQuantity();

                return(packs);
            }).ToArray();
        }
        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
            {
                throw new ArgumentNullException("getShippingOptionRequest");
            }

            var response = new GetShippingOptionResponse();

            if (getShippingOptionRequest.Items == null || getShippingOptionRequest.Items.Count == 0)
            {
                response.AddError("No shipment items");
                return(response);
            }
            if (getShippingOptionRequest.ShippingAddress == null)
            {
                response.AddError("Shipping address is not set");
                return(response);
            }
            if (getShippingOptionRequest.ShippingAddress.Country == null)
            {
                response.AddError("Shipping country is not set");
                return(response);
            }

            var orderRef = "NOP" + getShippingOptionRequest.Items.FirstOrDefault().ShoppingCartItem.Id;

            var checkSum = CheckSumCalculator.CalculateChecksum(
                new Dictionary <string, string>
            {
                { "accountId", _settings.AccountId },
                { "action", "START" },
                { "customerCountry", getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode },
                { "orderReference", orderRef }
            }, _settings.PassPhrase);

            var storeUrl = _storeContext.CurrentStore.Url;

            if (!storeUrl.EndsWith("/"))
            {
                storeUrl += "/";
            }

            var confirmUrl = storeUrl + "Plugins/BpostShippingManager/ConfirmHandler";
            var cancelUrl  = storeUrl + "Plugins/BpostShippingManager/CancelHandler";
            var errorUrl   = storeUrl + "Plugins/BpostShippingManager/ErrorHandler";

            var street       = string.Empty;
            var streetNumber = string.Empty;

            if (!string.IsNullOrEmpty(getShippingOptionRequest.ShippingAddress.Address1))
            {
                var streetNumberStart = getShippingOptionRequest.ShippingAddress.Address1.IndexOfAny("0123456789".ToCharArray());
                if (streetNumberStart >= 0)
                {
                    street       = getShippingOptionRequest.ShippingAddress.Address1.Substring(0, streetNumberStart - 1);
                    streetNumber = getShippingOptionRequest.ShippingAddress.Address1.Substring(streetNumberStart);
                }
            }

            var rate = _workContext.CurrentCustomer.GetAttribute <decimal>(CustomCustomerAttributeNames.DeliveryMethodRate, _storeContext.CurrentStore.Id);
            var deliveryMethodAddress = _workContext.CurrentCustomer.GetAttribute <string>(CustomCustomerAttributeNames.DeliveryMethodAddress, _storeContext.CurrentStore.Id);
            var deliveryMethod        = _workContext.CurrentCustomer.GetAttribute <string>(CustomCustomerAttributeNames.DeliveryMethod, _storeContext.CurrentStore.Id);

            LocaleStringResource deliveryMethodDescription = null;
            var buttonDiv = string.Empty;

            var loadShm = string.Format(
                "loadShm('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', '{10}', '{11}', '{12}', '{13}', '{14}');",
                _settings.AccountId,
                orderRef,
                getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode,
                checkSum,
                getShippingOptionRequest.ShippingAddress.FirstName,
                getShippingOptionRequest.ShippingAddress.LastName,
                getShippingOptionRequest.Customer.Email,
                street,
                getShippingOptionRequest.ShippingAddress.ZipPostalCode,
                getShippingOptionRequest.ShippingAddress.City,
                confirmUrl,
                cancelUrl,
                errorUrl,
                _workContext.WorkingLanguage.UniqueSeoCode,
                streetNumber);

            var startupShmScript = @"<script>$(document).ready(function() { " + loadShm + " expandShm(); }); </script>";

            if (!string.IsNullOrEmpty(deliveryMethod))
            {
                deliveryMethodDescription = _localizationService.GetLocaleStringResourceByName(
                    $"MakeIT.Nop.Shipping.Bpost.ShippingManager.DeliveryMethod.{deliveryMethod.Replace(" ", "")}");

                buttonDiv =
                    @"<div><input class='{13}' type='button' onclick=""loadShm('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', '{10}', '{11}', '{12}', '{15}', '{16}');"" value='{14}'></div>";

                startupShmScript = @"
                    <script>
                        $(document).ready(function() {
                            collapseShm();
                        });
                    </script>
                    ";
            }

            var collectPoint = String.Empty;

            if (!string.IsNullOrEmpty(deliveryMethodAddress) && deliveryMethodDescription != null)
            {
                collectPoint = $"<div style='margin-bottom: 10px;'>{deliveryMethodDescription.ResourceValue}<br/>{deliveryMethodAddress}</div>";
            }

            var description = string.Format(
                collectPoint + buttonDiv +
                @"<div id='shm-inline-container' style='width: 100%; margin-top: 10px;'></div>",
                _settings.AccountId,
                orderRef,
                getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode,
                checkSum,
                getShippingOptionRequest.ShippingAddress.FirstName,
                getShippingOptionRequest.ShippingAddress.LastName,
                getShippingOptionRequest.Customer.Email,
                street,
                getShippingOptionRequest.ShippingAddress.ZipPostalCode,
                getShippingOptionRequest.ShippingAddress.City,
                confirmUrl,
                cancelUrl,
                errorUrl,
                _settings.ButtonCssClass,
                _localizationService.GetResource("MakeIT.Nop.Shipping.Bpost.ShippingManager.ButtonCaption"),
                _workContext.WorkingLanguage.UniqueSeoCode,
                streetNumber);

            var shmShippingOption = new ShippingOption
            {
                Name = _localizationService.GetResource("MakeIT.Nop.Shipping.Bpost.ShippingManager.ShippingOptionTitle"),
                Rate = (rate > 0) ? rate : _settings.Standardprice,
                ShippingRateComputationMethodSystemName = "Shipping.Bpost.ShippingManager",
                Description = description + startupShmScript
            };

            response.ShippingOptions.Add(shmShippingOption);

            return(response);
        }
        /// <summary>
        ///  Gets available shipping options
        /// </summary>
        /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
        /// <returns>Represents a response of getting shipping rate options</returns>
        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
            {
                throw new ArgumentNullException("getShippingOptionRequest");
            }

            if (getShippingOptionRequest.Items == null)
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               "No shipment items"
                           }
                }
            }
            ;

            if (getShippingOptionRequest.ShippingAddress == null)
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               "Shipping address is not set"
                           }
                }
            }
            ;

            if (getShippingOptionRequest.ShippingAddress.Country == null)
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               "Shipping country is not set"
                           }
                }
            }
            ;

            if (string.IsNullOrEmpty(getShippingOptionRequest.ZipPostalCodeFrom))
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               "Origin postal code is not set"
                           }
                }
            }
            ;

            //get available services
            string errors;
            var    availableServices = CanadaPostHelper.GetServices(getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode,
                                                                    _canadaPostSettings.ApiKey, _canadaPostSettings.UseSandbox, out errors);

            if (availableServices == null)
            {
                return new GetShippingOptionResponse {
                           Errors = new List <string> {
                               errors
                           }
                }
            }
            ;

            //create object for the get rates requests
            var    result = new GetShippingOptionResponse();
            object destinationCountry;

            switch (getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode.ToLowerInvariant())
            {
            case "us":
                destinationCountry = new mailingscenarioDestinationUnitedstates
                {
                    zipcode = getShippingOptionRequest.ShippingAddress.ZipPostalCode
                };

                break;

            case "ca":
                destinationCountry = new mailingscenarioDestinationDomestic
                {
                    postalcode = getShippingOptionRequest.ShippingAddress.ZipPostalCode.Replace(" ", string.Empty).ToUpperInvariant()
                };
                break;

            default:
                destinationCountry = new mailingscenarioDestinationInternational
                {
                    countrycode = getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode
                };
                break;
            }

            var mailingScenario = new mailingscenario
            {
                quotetype          = mailingscenarioQuotetype.counter,
                quotetypeSpecified = true,
                originpostalcode   = getShippingOptionRequest.ZipPostalCodeFrom.Replace(" ", string.Empty).ToUpperInvariant(),
                destination        = new mailingscenarioDestination
                {
                    Item = destinationCountry
                }
            };

            //set contract customer properties
            if (!string.IsNullOrEmpty(_canadaPostSettings.CustomerNumber))
            {
                mailingScenario.quotetype      = mailingscenarioQuotetype.commercial;
                mailingScenario.customernumber = _canadaPostSettings.CustomerNumber;
                mailingScenario.contractid     = !string.IsNullOrEmpty(_canadaPostSettings.ContractId) ? _canadaPostSettings.ContractId : null;
            }

            //get original parcel characteristics
            decimal originalLength;
            decimal originalWidth;
            decimal originalHeight;
            decimal originalWeight;

            GetWeight(getShippingOptionRequest, out originalWeight);
            GetDimensions(getShippingOptionRequest, out originalLength, out originalWidth, out originalHeight);

            //get rate for all available services
            var errorSummary = new StringBuilder();

            foreach (var service in availableServices.service)
            {
                var currentService = CanadaPostHelper.GetServiceDetails(_canadaPostSettings.ApiKey, service.link.href, service.link.mediatype, out errors);
                if (currentService != null)
                {
                    #region parcels count calculation

                    var totalParcels = 1;

                    //parcels count by weight
                    var maxWeight = currentService.restrictions != null &&
                                    currentService.restrictions.weightrestriction != null &&
                                    currentService.restrictions.weightrestriction.maxSpecified
                        ? currentService.restrictions.weightrestriction.max : int.MaxValue;
                    if (originalWeight * 1000 > maxWeight)
                    {
                        var parcelsOnWeight = Convert.ToInt32(Math.Ceiling(originalWeight * 1000 / maxWeight));
                        if (parcelsOnWeight > totalParcels)
                        {
                            totalParcels = parcelsOnWeight;
                        }
                    }

                    //parcels count by length
                    var maxLength = currentService.restrictions != null &&
                                    currentService.restrictions.dimensionalrestrictions != null &&
                                    currentService.restrictions.dimensionalrestrictions.length != null &&
                                    currentService.restrictions.dimensionalrestrictions.length.maxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.length.max : int.MaxValue;
                    if (originalLength > maxLength)
                    {
                        var parcelsOnLength = Convert.ToInt32(Math.Ceiling(originalLength / maxLength));
                        if (parcelsOnLength > totalParcels)
                        {
                            totalParcels = parcelsOnLength;
                        }
                    }

                    //parcels count by width
                    var maxWidth = currentService.restrictions != null &&
                                   currentService.restrictions.dimensionalrestrictions != null &&
                                   currentService.restrictions.dimensionalrestrictions.width != null &&
                                   currentService.restrictions.dimensionalrestrictions.width.maxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.width.max : int.MaxValue;
                    if (originalWidth > maxWidth)
                    {
                        var parcelsOnWidth = Convert.ToInt32(Math.Ceiling(originalWidth / maxWidth));
                        if (parcelsOnWidth > totalParcels)
                        {
                            totalParcels = parcelsOnWidth;
                        }
                    }

                    //parcels count by height
                    var maxHeight = currentService.restrictions != null &&
                                    currentService.restrictions.dimensionalrestrictions != null &&
                                    currentService.restrictions.dimensionalrestrictions.height != null &&
                                    currentService.restrictions.dimensionalrestrictions.height.maxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.height.max : int.MaxValue;
                    if (originalHeight > maxHeight)
                    {
                        var parcelsOnHeight = Convert.ToInt32(Math.Ceiling(originalHeight / maxHeight));
                        if (parcelsOnHeight > totalParcels)
                        {
                            totalParcels = parcelsOnHeight;
                        }
                    }

                    //parcel count by girth
                    var lengthPlusGirthMax = currentService.restrictions != null &&
                                             currentService.restrictions.dimensionalrestrictions != null &&
                                             currentService.restrictions.dimensionalrestrictions.lengthplusgirthmaxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.lengthplusgirthmax : int.MaxValue;
                    var lengthPlusGirth = 2 * (originalWidth + originalHeight) + originalLength;
                    if (lengthPlusGirth > lengthPlusGirthMax)
                    {
                        var parcelsOnHeight = Convert.ToInt32(Math.Ceiling(lengthPlusGirth / lengthPlusGirthMax));
                        if (parcelsOnHeight > totalParcels)
                        {
                            totalParcels = parcelsOnHeight;
                        }
                    }

                    //parcel count by sum of length, width and height
                    var lengthWidthHeightMax = currentService.restrictions != null &&
                                               currentService.restrictions.dimensionalrestrictions != null &&
                                               currentService.restrictions.dimensionalrestrictions.lengthheightwidthsummaxSpecified
                        ? currentService.restrictions.dimensionalrestrictions.lengthheightwidthsummax : int.MaxValue;
                    var lengthWidthHeight = originalLength + originalWidth + originalHeight;
                    if (lengthWidthHeight > lengthWidthHeightMax)
                    {
                        var parcelsOnHeight = Convert.ToInt32(Math.Ceiling(lengthWidthHeight / lengthWidthHeightMax));
                        if (parcelsOnHeight > totalParcels)
                        {
                            totalParcels = parcelsOnHeight;
                        }
                    }

                    #endregion

                    //set parcel characteristics
                    mailingScenario.services = new[] { currentService.servicecode };
                    mailingScenario.parcelcharacteristics = new mailingscenarioParcelcharacteristics
                    {
                        weight     = Math.Round(originalWeight / totalParcels, 3),
                        dimensions = new mailingscenarioParcelcharacteristicsDimensions
                        {
                            length = Math.Round(originalLength / totalParcels, 1),
                            width  = Math.Round(originalWidth / totalParcels, 1),
                            height = Math.Round(originalHeight / totalParcels, 1)
                        }
                    };

                    //get rate
                    var priceQuotes = CanadaPostHelper.GetShippingRates(mailingScenario, _canadaPostSettings.ApiKey, _canadaPostSettings.UseSandbox, out errors);
                    if (priceQuotes != null)
                    {
                        foreach (var option in priceQuotes.pricequote)
                        {
                            result.ShippingOptions.Add(new ShippingOption
                            {
                                Name        = option.servicename,
                                Rate        = PriceToPrimaryStoreCurrency(option.pricedetails.due * totalParcels),
                                Description = string.Format("Delivery {0}into {1} parcels",
                                                            option.servicestandard != null && !string.IsNullOrEmpty(option.servicestandard.expectedtransittime)
                                    ? string.Format("in {0} days ", option.servicestandard.expectedtransittime) : string.Empty, totalParcels),
                            });
                        }
                    }
                    else
                    {
                        errorSummary.AppendLine(errors);
                    }
                }
                else
                {
                    errorSummary.AppendLine(errors);
                }
            }

            //write errors
            var errorString = errorSummary.ToString();
            if (!string.IsNullOrEmpty(errorString))
            {
                _logger.Error(errorString);
            }
            if (!result.ShippingOptions.Any())
            {
                result.AddError(errorString);
            }

            return(result);
        }
        private void SetIndividualPackageLineItems(StringBuilder sb, GetShippingOptionRequest getShippingOptionRequest, UPSPackagingType packagingType, decimal orderSubTotal, string currencyCode)
        {
            // Rate request setup - Total Dimensions of Shopping Cart Items determines number of packages

            var usedMeasureWeight    = GetUsedMeasureWeight();
            var usedMeasureDimension = GetUsedMeasureDimension();

            decimal lengthTmp, widthTmp, heightTmp;

            _shippingService.GetDimensions(getShippingOptionRequest.Items, out widthTmp, out lengthTmp, out heightTmp);

            int length = ConvertFromPrimaryMeasureDimension(lengthTmp, usedMeasureDimension);
            int height = ConvertFromPrimaryMeasureDimension(heightTmp, usedMeasureDimension);
            int width  = ConvertFromPrimaryMeasureDimension(widthTmp, usedMeasureDimension);
            int weight = ConvertFromPrimaryMeasureWeight(_shippingService.GetTotalWeight(getShippingOptionRequest), usedMeasureWeight);

            if (length < 1)
            {
                length = 1;
            }
            if (height < 1)
            {
                height = 1;
            }
            if (width < 1)
            {
                width = 1;
            }
            if (weight < 1)
            {
                weight = 1;
            }

            if ((!IsPackageTooHeavy(weight)) && (!IsPackageTooLarge(length, height, width)))
            {
                if (!_upsSettings.PassDimensions)
                {
                    length = width = height = 0;
                }

                int insuranceAmount = _upsSettings.InsurePackage ? Convert.ToInt32(orderSubTotal) : 0;
                AppendPackageRequest(sb, packagingType, length, height, width, weight, insuranceAmount, currencyCode);
            }
            else
            {
                int totalPackagesDims    = 1;
                int totalPackagesWeights = 1;
                if (IsPackageTooHeavy(weight))
                {
                    totalPackagesWeights = Convert.ToInt32(Math.Ceiling((decimal)weight / (decimal)MAXPACKAGEWEIGHT));
                }
                if (IsPackageTooLarge(length, height, width))
                {
                    totalPackagesDims = Convert.ToInt32(Math.Ceiling((decimal)TotalPackageSize(length, height, width) / (decimal)108));
                }
                var totalPackages = totalPackagesDims > totalPackagesWeights ? totalPackagesDims : totalPackagesWeights;
                if (totalPackages == 0)
                {
                    totalPackages = 1;
                }

                int weight2 = weight / totalPackages;
                int height2 = height / totalPackages;
                int width2  = width / totalPackages;
                int length2 = length / totalPackages;
                if (weight2 < 1)
                {
                    weight2 = 1;
                }
                if (height2 < 1)
                {
                    height2 = 1;
                }
                if (width2 < 1)
                {
                    width2 = 1;
                }
                if (length2 < 1)
                {
                    length2 = 1;
                }

                if (!_upsSettings.PassDimensions)
                {
                    length2 = width2 = height2 = 0;
                }

                //The maximum declared amount per package: 50000 USD.
                int insuranceAmountPerPackage = _upsSettings.InsurePackage ? Convert.ToInt32(orderSubTotal / totalPackages) : 0;

                for (int i = 0; i < totalPackages; i++)
                {
                    AppendPackageRequest(sb, packagingType, length2, height2, width2, weight2, insuranceAmountPerPackage, currencyCode);
                }
            }
        }
Example #27
0
 public static string GetFromPostalCode(IAddressService _addressService, ShippingSettings _shippingSettings, GetShippingOptionRequest getShippingOptionRequest)
 {
     if (String.IsNullOrEmpty(getShippingOptionRequest.ZipPostalCodeFrom))
     {
         return(_addressService.GetAddressById(_shippingSettings.ShippingOriginAddressId).ZipPostalCode);
     }
     else
     {
         return(getShippingOptionRequest.ZipPostalCodeFrom);
     }
 }
        private void SetIndividualPackageLineItemsOneItemPerPackage(StringBuilder sb, GetShippingOptionRequest getShippingOptionRequest, UPSPackagingType packagingType, string currencyCode)
        {
            // Rate request setup - each Shopping Cart Item is a separate package

            var usedMeasureWeight    = GetUsedMeasureWeight();
            var usedMeasureDimension = GetUsedMeasureDimension();

            foreach (var packageItem in getShippingOptionRequest.Items)
            {
                var sci = packageItem.ShoppingCartItem;
                var qty = packageItem.GetQuantity();

                //get dimensions for qty 1
                decimal lengthTmp, widthTmp, heightTmp;
                _shippingService.GetDimensions(new List <GetShippingOptionRequest.PackageItem>
                {
                    new GetShippingOptionRequest.PackageItem(sci, 1)
                }, out widthTmp, out lengthTmp, out heightTmp);

                int length  = ConvertFromPrimaryMeasureDimension(lengthTmp, usedMeasureDimension);
                int height  = ConvertFromPrimaryMeasureDimension(heightTmp, usedMeasureDimension);
                int width   = ConvertFromPrimaryMeasureDimension(widthTmp, usedMeasureDimension);
                var product = EngineContext.Current.Resolve <IProductService>().GetProductById(sci.ProductId);
                int weight  = ConvertFromPrimaryMeasureWeight(product.Weight, usedMeasureWeight);
                if (length < 1)
                {
                    length = 1;
                }
                if (height < 1)
                {
                    height = 1;
                }
                if (width < 1)
                {
                    width = 1;
                }
                if (weight < 1)
                {
                    weight = 1;
                }

                //The maximum declared amount per package: 50000 USD.
                //TODO: Currently using Product.Price - should we use GetUnitPrice() instead?
                // Convert.ToInt32(_priceCalculationService.GetUnitPrice(sci, includeDiscounts:false))
                //One could argue that the insured value should be based on Cost rather than Price.
                //GetUnitPrice handles Attribute Adjustments and also Customer Entered Price.
                //But, even with includeDiscounts:false, it could apply a "discount" from Tier pricing.
                int insuranceAmountPerPackage = _upsSettings.InsurePackage ? Convert.ToInt32(product.Price) : 0;

                for (int j = 0; j < qty; j++)
                {
                    AppendPackageRequest(sb, packagingType, length, height, width, weight, insuranceAmountPerPackage, currencyCode);
                }
            }
        }
Example #29
0
 public decimal?GetFixedRate(
     GetShippingOptionRequest getShippingOptionRequest
     )
 {
     return(0.0M);
 }
        private int GetWidth(GetShippingOptionRequest getShippingOptionRequest)
        {
            int value = Convert.ToInt32(Math.Ceiling(this._measureService.ConvertFromPrimaryMeasureDimension(_shippingService.GetTotalWidth(getShippingOptionRequest.Items), this.GatewayMeasureDimension)));

            return(value < MIN_LENGTH ? MIN_LENGTH : value);
        }