private void SetDestination(FedexRate.RateRequest request, GetShippingOptionRequest getShippingOptionRequest) { request.RequestedShipment.Recipient = new FedexRate.Party { Address = new FedexRate.Address() }; if (_fedexSettings.UseResidentialRates) { request.RequestedShipment.Recipient.Address.Residential = true; request.RequestedShipment.Recipient.Address.ResidentialSpecified = true; } request.RequestedShipment.Recipient.Address.StreetLines = new[] { getShippingOptionRequest.ShippingAddress.Address1 }; request.RequestedShipment.Recipient.Address.City = getShippingOptionRequest.ShippingAddress.City; if (getShippingOptionRequest.ShippingAddress.StateProvince != null && IncludeStateProvinceCode(getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode)) { request.RequestedShipment.Recipient.Address.StateOrProvinceCode = getShippingOptionRequest.ShippingAddress.StateProvince.Abbreviation; } else { request.RequestedShipment.Recipient.Address.StateOrProvinceCode = string.Empty; } request.RequestedShipment.Recipient.Address.PostalCode = getShippingOptionRequest.ShippingAddress.ZipPostalCode; request.RequestedShipment.Recipient.Address.CountryCode = getShippingOptionRequest.ShippingAddress.Country.TwoLetterIsoCode; }
private void SetPayment(FedexRate.RateRequest request) { request.RequestedShipment.ShippingChargesPayment = new FedexRate.Payment { PaymentType = FedexRate.PaymentType.SENDER, // Payment options are RECIPIENT, SENDER, THIRD_PARTY PaymentTypeSpecified = true, Payor = new FedexRate.Payor { ResponsibleParty = new FedexRate.Party { AccountNumber = _fedexSettings.AccountNumber } } }; // Payment Information }
/// <summary> /// Create packages (total dimensions 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 SetIndividualPackageLineItems(FedexRate.RateRequest request, GetShippingOptionRequest getShippingOptionRequest, decimal orderSubTotal, string currencyCode) { var(length, height, width) = GetDimensions(getShippingOptionRequest.Items); var weight = GetWeight(getShippingOptionRequest); if (!IsPackageTooHeavy(weight) && !IsPackageTooLarge(length, height, width)) { request.RequestedShipment.PackageCount = "1"; var package = CreatePackage(width, length, height, weight, orderSubTotal, "1", currencyCode); package.GroupPackageCount = "1"; request.RequestedShipment.RequestedPackageLineItems = new[] { package }; } else { var totalPackagesDims = 1; var totalPackagesWeights = 1; if (IsPackageTooHeavy(weight)) { totalPackagesWeights = Convert.ToInt32(Math.Ceiling(weight / FedexShippingDefaults.MAX_PACKAGE_WEIGHT)); } if (IsPackageTooLarge(length, height, width)) { totalPackagesDims = Convert.ToInt32(Math.Ceiling(TotalPackageSize(length, height, width) / 108M)); } var totalPackages = totalPackagesDims > totalPackagesWeights ? totalPackagesDims : totalPackagesWeights; if (totalPackages == 0) { totalPackages = 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 orderSubTotal2 = orderSubTotal / totalPackages; request.RequestedShipment.PackageCount = totalPackages.ToString(); request.RequestedShipment.RequestedPackageLineItems = Enumerable.Range(1, totalPackages - 1) .Select(i => CreatePackage(width, length, height, weight, orderSubTotal2, i.ToString(), currencyCode)).ToArray(); } }
private void SetOrigin(FedexRate.RateRequest request, GetShippingOptionRequest getShippingOptionRequest) { request.RequestedShipment.Shipper = new FedexRate.Party { Address = new FedexRate.Address() }; if (getShippingOptionRequest.CountryFrom is null) { throw new Exception("FROM country is not specified"); } request.RequestedShipment.Shipper.Address.StreetLines = new[] { getShippingOptionRequest.AddressFrom }; request.RequestedShipment.Shipper.Address.City = getShippingOptionRequest.CityFrom; if (IncludeStateProvinceCode(getShippingOptionRequest.CountryFrom.TwoLetterIsoCode)) { var stateProvinceAbbreviation = getShippingOptionRequest.StateProvinceFrom is null ? "" : getShippingOptionRequest.StateProvinceFrom.Abbreviation; request.RequestedShipment.Shipper.Address.StateOrProvinceCode = stateProvinceAbbreviation; } request.RequestedShipment.Shipper.Address.PostalCode = getShippingOptionRequest.ZipPostalCodeFrom; request.RequestedShipment.Shipper.Address.CountryCode = getShippingOptionRequest.CountryFrom.TwoLetterIsoCode; }
/// <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(); }
private void SetShipmentDetails(FedexRate.RateRequest request, decimal orderSubTotal, string currencyCode) { //set drop off type switch (_fedexSettings.DropoffType) { case DropoffType.BusinessServiceCenter: request.RequestedShipment.DropoffType = FedexRate.DropoffType.BUSINESS_SERVICE_CENTER; break; case DropoffType.DropBox: request.RequestedShipment.DropoffType = FedexRate.DropoffType.DROP_BOX; break; case DropoffType.RegularPickup: request.RequestedShipment.DropoffType = FedexRate.DropoffType.REGULAR_PICKUP; break; case DropoffType.RequestCourier: request.RequestedShipment.DropoffType = FedexRate.DropoffType.REQUEST_COURIER; break; case DropoffType.Station: request.RequestedShipment.DropoffType = FedexRate.DropoffType.STATION; break; default: request.RequestedShipment.DropoffType = FedexRate.DropoffType.BUSINESS_SERVICE_CENTER; break; } request.RequestedShipment.TotalInsuredValue = new FedexRate.Money { Amount = orderSubTotal, Currency = currencyCode }; //Saturday pickup is available for certain FedEx Express U.S. service types: //http://www.fedex.com/us/developer/product/WebServices/MyWebHelp/Services/Options/c_SaturdayShipAndDeliveryServiceDetails.html //If the customer orders on a Saturday, the rate calculation will use Saturday as the shipping date, and the rates will include a Saturday pickup surcharge //More info: https://www.nopcommerce.com/boards/t/27348/fedex-rate-can-be-excessive-for-express-methods-if-calculated-on-a-saturday.aspx var shipTimestamp = DateTime.Now; if (shipTimestamp.DayOfWeek == DayOfWeek.Saturday) { shipTimestamp = shipTimestamp.AddDays(2); } request.RequestedShipment.ShipTimestamp = shipTimestamp; // Shipping date and time request.RequestedShipment.ShipTimestampSpecified = true; request.RequestedShipment.RateRequestTypes = new[] { FedexRate.RateRequestType.PREFERRED, FedexRate.RateRequestType.LIST }; //request.RequestedShipment.PackageDetail = RequestedPackageDetailType.INDIVIDUAL_PACKAGES; //request.RequestedShipment.PackageDetailSpecified = true; //for India domestic shipping add additional details if (request.RequestedShipment.Shipper.Address.CountryCode.Equals("IN", StringComparison.InvariantCultureIgnoreCase) && request.RequestedShipment.Recipient.Address.CountryCode.Equals("IN", StringComparison.InvariantCultureIgnoreCase)) { var commodity = new FedexRate.Commodity { Name = "1", NumberOfPieces = "1", CustomsValue = new FedexRate.Money { Amount = orderSubTotal, AmountSpecified = true, Currency = currencyCode } }; request.RequestedShipment.CustomsClearanceDetail = new FedexRate.CustomsClearanceDetail { CommercialInvoice = new FedexRate.CommercialInvoice { Purpose = FedexRate.PurposeOfShipmentType.SOLD, PurposeSpecified = true }, Commodities = new[] { commodity } }; } }
/// <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(); }
/// <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); }