Beispiel #1
0
        private void CanadaPostGetRates(Shipments AllShipments, out string RTShipRequest, out string RTShipResponse, decimal ExtraFee, Decimal MarkupPercent, decimal ShipmentValue, decimal ShippingTaxRate)
        {
            RTShipRequest  = String.Empty;
            RTShipResponse = String.Empty;
            System.Collections.Specialized.NameValueCollection RatesList = new System.Collections.Specialized.NameValueCollection();

            if (!AppLogic.AppConfig("Localization.StoreCurrency").Trim().Equals("cad", StringComparison.InvariantCultureIgnoreCase))
            {
                RTShipResponse += "Localization.StoreCurrency == CAD required to use Canada Post as a carrier.";
                return;
            }

            if (!AppLogic.AppConfig("Localization.WeightUnits").Equals("kg", StringComparison.InvariantCultureIgnoreCase))
            {
                RTShipResponse += "Localization.WeightUnits == kg required to use Canada Post as a carrier.";
                return;
            }

            foreach (Packages Shipment in AllShipments)
            {
                HasFreeItems    = false;
                PackageQuantity = 1;

                if (Shipment.Weight > AppLogic.AppConfigUSDecimal("RTShipping.CanadaPost.MaxWeight"))
                {
                    SM.ErrorMsg = "Canada Post " + AppLogic.AppConfig("RTShipping.CallForShippingPrompt");
                    return;
                }

                // create a rate request
                CanadaPost.ratesAndServicesRequest rateRequest = new CanadaPost.ratesAndServicesRequest();
                rateRequest.merchantCPCID = AppLogic.AppConfig("RTShipping.CanadaPost.MerchantID");  // Canada Post merchant credentials

                // ship-to address
                rateRequest.city        = Shipment.DestinationCity;
                rateRequest.provOrState = Shipment.DestinationStateProvince;
                rateRequest.country     = Shipment.DestinationCountryCode;
                rateRequest.postalCode  = Shipment.DestinationZipPostalCode;

                // create one lineitem request per package in shipment
                rateRequest.lineItems.item = new CanadaPost.item[Shipment.Count];

                int packageIndex = 0;
                foreach (Package p in Shipment)
                {
                    if (p.IsFreeShipping)
                    {
                        HasFreeItems = true;
                    }
                    if (p.IsShipSeparately)
                    {
                        PackageQuantity = p.Quantity;
                    }

                    // shipment details
                    rateRequest.itemsPrice = p.InsuredValue.ToString("#####.00");
                    rateRequest.lineItems.item[packageIndex]        = new CanadaPost.item();
                    rateRequest.lineItems.item[packageIndex].weight = p.Weight.ToString("####.0");

                    // dimensions
                    if (p.Length + p.Width + p.Height == 0)  // if package has no dimensions, we use default
                    {
                        string dimensions = AppLogic.AppConfig("RTShipping.CanadaPost.DefaultPackageSize");
                        p.Width  = Convert.ToDecimal(dimensions.Split('x')[0].Trim());
                        p.Height = Convert.ToDecimal(dimensions.Split('x')[1].Trim());
                        p.Length = Convert.ToDecimal(dimensions.Split('x')[2].Trim());
                    }
                    rateRequest.lineItems.item[packageIndex].length = p.Length.ToString("###.0");
                    rateRequest.lineItems.item[packageIndex].width  = p.Width.ToString("###.0");
                    rateRequest.lineItems.item[packageIndex].height = p.Height.ToString("###.0");

                    packageIndex++;
                }

                // initialize eParcel request
                CanadaPost.eparcel request = new CanadaPost.eparcel();

                // choose language for reply text
                request.language = AppLogic.AppConfig("RTShipping.CanadaPost.Language").Trim();
                if (request.language.Equals("auto", StringComparison.InvariantCultureIgnoreCase))  // set the language based on the customers locale
                {
                    Customer ThisCustomer = ((AspDotNetStorefrontPrincipal)System.Web.HttpContext.Current.User).ThisCustomer;
                    if (ThisCustomer.LocaleSetting.Trim().StartsWith("fr", StringComparison.InvariantCultureIgnoreCase))
                    {
                        request.language = "fr";
                    }
                    else
                    {
                        request.language = "en";
                    }
                }

                request.Items    = new CanadaPost.ratesAndServicesRequest[1];
                request.Items[0] = rateRequest;

                // serialize eParcel request class
                XmlSerializer serRequest = new XmlSerializer(typeof(CanadaPost.eparcel));
                StringWriter  swRequest  = new StringWriter();

                serRequest.Serialize(swRequest, request);
                string req = swRequest.ToString().Replace(@" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema""", "");

                // open a TCP socket with Canada Post server
                Socket     socCanadaPost = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint remoteEndPoint;

                socCanadaPost.ReceiveTimeout = 10000; // milliseconds to wait for a response

                try
                {
                    remoteEndPoint = new IPEndPoint(Dns.GetHostAddresses(AppLogic.AppConfig("RTShipping.CanadaPost.Server"))[0],
                                                    AppLogic.AppConfigNativeInt("RTShipping.CanadaPost.ServerPort"));
                    socCanadaPost.Connect(remoteEndPoint);
                }
                catch (SocketException e)
                {
                    RTShipResponse += "Tried to reach Canada Post Server (" + AppLogic.AppConfig("RTShipping.CanadaPost.Server") +
                                      ":" + AppLogic.AppConfigNativeInt("RTShipping.CanadaPost.ServerPort") + "): " + e.Message;
                    return;
                }


                // send request to Canada Post
                byte[] data = System.Text.Encoding.ASCII.GetBytes(req);
                socCanadaPost.Send(data);

                //receive response from Canada Post
                string resp   = String.Empty;
                byte[] buffer = new byte[8192];
                int    iRx    = 0;

                while (!resp.Contains("<!--END_OF_EPARCEL-->"))
                {
                    try
                    {
                        iRx += socCanadaPost.Receive(buffer, iRx, 8192 - iRx, SocketFlags.None);
                    }
                    catch (SocketException e)
                    {
                        if (e.SocketErrorCode == SocketError.TimedOut)
                        {
                            break;
                        }
                        else
                        {
                            throw e;
                        }
                    }
                    resp = new string((System.Text.Encoding.UTF8.GetChars(buffer, 0, iRx)));  // decode byte array to string
                }

                // close socket
                socCanadaPost.Close();

                // create an eParcel response class
                CanadaPost.eparcel response = new CanadaPost.eparcel();

                // deserialize the xml response into the eParcel response
                XmlSerializer serResponse = new XmlSerializer(typeof(CanadaPost.eparcel));
                StringReader  srResponse  = new StringReader(resp);

                try
                {
                    response = (CanadaPost.eparcel)serResponse.Deserialize(srResponse);
                }
                catch (InvalidOperationException e)  // invalid xml, or no reply received from Canada Post
                {
                    RTShipResponse += "Canada Post error: Could not parse response from Canada Post server: " + e.Message
                                      + " Response received: " + resp;
                    return;
                }

                srResponse.Close();

                // Check the response object for Faults
                if (response.Items[0] is CanadaPost.error)
                {
                    CanadaPost.error respError = (CanadaPost.error)response.Items[0];
                    RTShipResponse += respError.statusMessage[0];
                    return;
                }

                // Check the response object for Ratings
                if (!(response.Items[0] is CanadaPost.ratesAndServicesResponse))
                {
                    RTShipResponse += "Canada Post Error: No rating responses returned from Canada Post";
                    return;
                }

                // no faults, so extract rate information
                CanadaPost.ratesAndServicesResponse ratesResp = (CanadaPost.ratesAndServicesResponse)response.Items[0];
                foreach (CanadaPost.product product in ratesResp.product)
                {
                    decimal total = Localization.ParseUSDecimal(product.rate);

                    // ignore zero-cost methods, and methods not allowed
                    if (total != 0 && ShippingMethodIsAllowed(product.name, "Canada Post"))
                    {
                        total = total * PackageQuantity * (1.00M + (MarkupPercent / 100.0M));

                        decimal vat = Decimal.Round(total * ShippingTaxRate);

                        if (!SM.MethodExists(product.name))
                        {
                            ShipMethod s_method = new ShipMethod();
                            s_method.Carrier     = "Canada Post";
                            s_method.ServiceName = product.name;
                            s_method.ServiceRate = total;
                            s_method.VatRate     = vat;

                            if (HasFreeItems)
                            {
                                s_method.FreeItemsRate = total;
                            }

                            SM.AddMethod(s_method);
                        }
                        else
                        {
                            int        IndexOf  = SM.GetIndex(product.name);
                            ShipMethod s_method = SM[IndexOf];
                            s_method.ServiceRate += total;
                            s_method.VatRate     += vat;

                            if (HasFreeItems)
                            {
                                s_method.FreeItemsRate += total;
                            }

                            SM[IndexOf] = s_method;
                        }
                    }
                }
                RTShipRequest  += req; // stash request & response for this shipment
                RTShipResponse += resp;
            }

            // Handling fee should only be added per shipping address not per package
            // let's just compute it here after we've gone through all the packages.
            // Also, since we can't be sure about the ordering of the method call here
            // and that the collection SM includes shipping methods from all possible carriers
            // we'll need to filter out the methods per this carrier to avoid side effects on the main collection
            foreach (ShipMethod shipMethod in SM.PerCarrier("Canada Post"))
            {
                shipMethod.ServiceRate += ExtraFee;
            }
        }
Beispiel #2
0
        private void FedExGetRates(Shipments AllShipments, out string RTShipRequest, out string RTShipResponse, decimal ExtraFee, Decimal MarkupPercent, decimal ShipmentValue, decimal ShippingTaxRate)        // Retrieves FedEx rates
        {
            RTShipRequest  = string.Empty;
            RTShipResponse = string.Empty;

            Decimal maxWeight = AppLogic.AppConfigUSDecimal("RTShipping.Fedex.MaxWeight");

            if (maxWeight == 0)
            {
                maxWeight = 150;
            }

            if (ShipmentWeight > maxWeight)
            {
                SM.ErrorMsg = "FedEx " + AppLogic.AppConfig("RTShipping.CallForShippingPrompt");
                return;
            }

            foreach (Packages Shipment in AllShipments)
            {
                foreach (Package p in Shipment)
                {
                    if (p.IsFreeShipping)
                    {
                        HasFreeItems = true;
                        break;
                    }
                }

                RateRequest request = CreateRateRequest(Shipment);
                RateService service = new RateService(); // Initialize the service
                service.Url = this.FedexServer;
                try
                {
                    RateReply reply = service.getRates(request);

                    string rateRequest = SerializeObject(request);
                    string rateReply   = SerializeObject(reply);
                    System.Diagnostics.Debug.WriteLine(rateRequest);
                    System.Diagnostics.Debug.WriteLine(rateReply);

                    if (reply.RateReplyDetails != null && (reply.HighestSeverity == NotificationSeverityType.SUCCESS || reply.HighestSeverity == NotificationSeverityType.NOTE || reply.HighestSeverity == NotificationSeverityType.WARNING))// check if the call was successful
                    {
                        //create list of available services

                        for (int i = 0; i < reply.RateReplyDetails.Length; i++)
                        {
                            RateReplyDetail rateReplyDetail = reply.RateReplyDetails[i];

                            // listRatedShipmentDetail is currently unused - could be used in the future to support list or pro-rated rates
                            RatedShipmentDetail listRatedShipmentDetail = rateReplyDetail.RatedShipmentDetails
                                                                          .FirstOrDefault(rsd => rsd.ShipmentRateDetail.RateType == ReturnedRateType.PAYOR_LIST_PACKAGE ||
                                                                                          rsd.ShipmentRateDetail.RateType == ReturnedRateType.RATED_LIST_PACKAGE ||
                                                                                          rsd.ShipmentRateDetail.RateType == ReturnedRateType.PAYOR_LIST_SHIPMENT ||
                                                                                          rsd.ShipmentRateDetail.RateType == ReturnedRateType.RATED_LIST_SHIPMENT);

                            RatedShipmentDetail ratedShipmentDetail = rateReplyDetail.RatedShipmentDetails
                                                                      .FirstOrDefault(rsd => rsd.ShipmentRateDetail.RateType == ReturnedRateType.PAYOR_ACCOUNT_PACKAGE ||
                                                                                      rsd.ShipmentRateDetail.RateType == ReturnedRateType.PAYOR_ACCOUNT_SHIPMENT ||
                                                                                      rsd.ShipmentRateDetail.RateType == ReturnedRateType.RATED_ACCOUNT_PACKAGE ||
                                                                                      rsd.ShipmentRateDetail.RateType == ReturnedRateType.RATED_ACCOUNT_SHIPMENT);

                            string rateName = "FedEx " + FedExGetCodeDescription(rateReplyDetail.ServiceType.ToString());

                            if (ShippingMethodIsAllowed(rateName, "FEDEX"))
                            {
                                decimal totalCharges = ratedShipmentDetail.ShipmentRateDetail.TotalNetCharge.Amount;

                                //multiply the returned rate by the quantity in the package to avoid calling
                                //more than necessary if there were multiple IsShipSeparately items
                                //ordered.  If there weren't, Shipment.PackageCount is 1 and the rate is normal
                                totalCharges = totalCharges * Shipment.PackageCount;

                                if (MarkupPercent != System.Decimal.Zero)
                                {
                                    totalCharges = Decimal.Round(totalCharges * (1.00M + (MarkupPercent / 100.0M)), 2, MidpointRounding.AwayFromZero);
                                }

                                decimal vat = Decimal.Round(totalCharges * ShippingTaxRate, 2, MidpointRounding.AwayFromZero);

                                if (!SM.MethodExists(rateName))
                                {
                                    ShipMethod s_method = new ShipMethod();

                                    s_method.Carrier     = "FEDEX";
                                    s_method.ServiceName = rateName;
                                    s_method.ServiceRate = totalCharges;
                                    s_method.VatRate     = vat;

                                    if (HasFreeItems)
                                    {
                                        s_method.FreeItemsRate = totalCharges;
                                    }

                                    SM.AddMethod(s_method);
                                }
                                else
                                {
                                    int        IndexOf  = SM.GetIndex(rateName);
                                    ShipMethod s_method = SM[IndexOf];

                                    s_method.ServiceRate += totalCharges;
                                    s_method.VatRate     += vat;

                                    if (HasFreeItems)
                                    {
                                        s_method.FreeItemsRate += totalCharges;
                                    }

                                    SM[IndexOf] = s_method;
                                }
                            }
                        }

                        // Handling fee should only be added per shipping address not per package
                        // let's just compute it here after we've gone through all the packages.
                        // Also, since we can't be sure about the ordering of the method call here
                        // and that the collection SM includes shipping methods from all possible carriers
                        // we'll need to filter out the methods per this carrier to avoid side effects on the main collection
                        foreach (ShipMethod shipMethod in SM.PerCarrier("FEDEX"))
                        {
                            shipMethod.ServiceRate += ExtraFee;
                        }
                    }
                    else
                    {
                        RTShipResponse = "Error: Call Not Successful " + reply.Notifications[0].Message;
                    }

                    RTShipRequest  = Serialize(request);
                    RTShipResponse = Serialize(reply);
                }
                catch (SoapException e)
                {
                    RTShipResponse = "FedEx Error: " + e.Detail.InnerXml;
                }
                catch (Exception e)
                {
                    RTShipResponse = "FedEx Error: " + e.InnerException.Message;
                }
            }
        }
Beispiel #3
0
        private void AusPostGetRates(Shipments AllShipments, out string RTShipRequest, out string RTShipResponse, decimal ExtraFee, Decimal MarkupPercent, decimal ShipmentValue, decimal ShippingTaxRate)
        {
            RTShipRequest  = String.Empty;
            RTShipResponse = String.Empty;

            if (!AppLogic.AppConfig("Localization.StoreCurrency").Trim().Equals("aud", StringComparison.InvariantCultureIgnoreCase))
            {
                throw new Exception("Localization.StoreCurrency == AUD required to use Australia Post as a carrier.");
            }

            if (!AppLogic.AppConfig("Localization.WeightUnits").Equals("kg", StringComparison.InvariantCultureIgnoreCase))
            {
                throw new Exception("Localization.WeightUnits == kg required to use Australia Post as a carrier.");
            }

            // is weight within shippable limit?
            if (ShipmentWeight > AppLogic.AppConfigUSDecimal("RTShipping.AusPost.MaxWeight"))
            {
                SM.ErrorMsg = "Australia Post " + AppLogic.AppConfig("RTShipping.CallForShippingPrompt");
                return;
            }

            foreach (Packages Shipment in AllShipments)
            {
                HasFreeItems    = false;
                PackageQuantity = 1;

                foreach (Package p in Shipment)
                {
                    if (p.IsFreeShipping)
                    {
                        HasFreeItems = true;
                    }

                    if (p.IsShipSeparately)
                    {
                        PackageQuantity = p.Quantity;
                    }

                    // initialize rate requests
                    ausPost.RateRequest rateRequests = new ausPost.RateRequest();

                    // get list of service classes
                    string AusPostServices;
                    if (Shipment.DestinationCountryCode == "AU")
                    {
                        AusPostServices = AppLogic.AppConfig("RTShipping.AusPost.DomesticServices");
                    }
                    else
                    {
                        AusPostServices = AppLogic.AppConfig("RTShipping.AusPost.IntlServices");
                    }

                    // create individual requests, one for each service
                    foreach (string service in AusPostServices.Split(','))
                    {
                        ausPost.Request request = new ausPost.Request();

                        // dimensions (all specified in mm)
                        if (p.Length + p.Width + p.Height == 0)  // if package has no dimensions, we use default
                        {
                            string dimensions = AppLogic.AppConfig("RTShipping.AusPost.DefaultPackageSize");
                            try
                            {
                                p.Width  = Convert.ToDecimal(dimensions.Split('x')[0].Trim());
                                p.Height = Convert.ToDecimal(dimensions.Split('x')[1].Trim());
                                p.Length = Convert.ToDecimal(dimensions.Split('x')[2].Trim());
                            }
                            catch (FormatException e)
                            {
                                throw new Exception("Check the RTShipping.AusPost.DefaultPackageSize AppConfig. " +
                                                    "Must be three dimensions (in cm) separated by 'x'. Example: 15x15x15. " + e.Message);
                            }
                        }
                        request.Length = Convert.ToInt32(Math.Ceiling(p.Length * 10)); // convert all from cm to mm
                        request.Width  = Convert.ToInt32(Math.Ceiling(p.Width * 10));
                        request.Height = Convert.ToInt32(Math.Ceiling(p.Height * 10));

                        request.Weight   = Convert.ToInt32(Math.Ceiling(p.Weight * 1000)); // convert from kg to g
                        request.Quantity = PackageQuantity;

                        // shipping addresses
                        request.Pickup_Postcode      = OriginZipPostalCode.PadRight(4).Substring(0, 4);
                        request.Country              = Shipment.DestinationCountryCode;
                        request.Destination_Postcode = Shipment.DestinationZipPostalCode.PadRight(4).Substring(0, 4);

                        // Service Type
                        try
                        {
                            request.Service_Type =
                                (ausPost.Request.ServiceType)Enum.Parse(typeof(ausPost.Request.ServiceType), service.Split(';')[0], true);
                        }
                        catch (ArgumentException e)
                        {
                            if (Shipment.DestinationCountryCode == "AU")
                            {
                                throw new Exception("Check the RTShipping.AusPost.DomesticServices AppConfig. " +
                                                    "Legal values are STANDARD or EXPRESS, followed by a semi-colon and Name. " + e.Message);
                            }
                            else
                            {
                                throw new Exception("Check the RTShipping.AusPost.IntlServices AppConfig. " +
                                                    "Legal values are AIR, SEA, ECI_D, ECI_M, or EPI, followed by a semi-colon and Name. " + e.Message);
                            }
                        }

                        // convert rateRequests into a URL
                        string req = Convert.ToString(request);

                        // Send rate request to AusPost server
                        HttpWebRequest webRequest = (HttpWebRequest)System.Net.WebRequest.Create(req);

                        // get the response from AusPost
                        WebResponse webResponse;
                        string      resp;
                        try
                        {
                            webResponse = webRequest.GetResponse();
                        }
                        catch (WebException e)  // could not receive a response from AusPost endpoint
                        {
                            RTShipResponse += "No response from Australia Post Server: " + e.Message;
                            return;
                        }

                        using (StreamReader sr = new StreamReader(webResponse.GetResponseStream()))
                        {
                            resp = sr.ReadToEnd();
                            sr.Close();
                        }
                        webResponse.Close();

                        // Convert the response into a Response object
                        ausPost.Response response;

                        try
                        {
                            response = (ausPost.Response)TypeDescriptor.GetConverter(typeof(ausPost.Response)).ConvertFromString(resp);
                        }
                        catch (InvalidOperationException e)  // invalid or no reply received from AusPost
                        {
                            RTShipResponse += "Could not parse response from Australia Post server: " + e.Message
                                              + " Response received: " + resp;
                            return;
                        }

                        // Check the response object for an error
                        if (response.Err_msg != "OK")
                        {
                            RTShipResponse += "Austalia Post Error: " + response.Err_msg + Environment.NewLine;
                            continue;
                        }

                        // we have a good estimate
                        decimal total = response.Charge;

                        // ignore zero-cost methods, and methods not allowed
                        if ((total == 0 && AppLogic.AppConfigBool("FilterOutShippingMethodsThatHave0Cost")) || !ShippingMethodIsAllowed(service.Split(';')[1], string.Empty))
                        {
                            continue;
                        }

                        total = total * (1.00M + (MarkupPercent / 100.0M));
                        decimal vat = Decimal.Round(total * ShippingTaxRate);

                        // add shipping method
                        if (!SM.MethodExists(service.Split(';')[1]))
                        {
                            ShipMethod s_method = new ShipMethod();
                            s_method.Carrier     = "Australia Post";
                            s_method.ServiceName = service.Split(';')[1];
                            s_method.ServiceRate = total;
                            s_method.VatRate     = vat;

                            if (HasFreeItems)
                            {
                                s_method.FreeItemsRate = total;
                            }

                            SM.AddMethod(s_method);
                        }
                        else
                        {
                            int        IndexOf  = SM.GetIndex(service.Split(';')[1]);
                            ShipMethod s_method = SM[IndexOf];
                            s_method.ServiceRate += total;
                            s_method.VatRate     += vat;

                            if (HasFreeItems)
                            {
                                s_method.FreeItemsRate += total;
                            }

                            SM[IndexOf] = s_method;
                        }

                        RTShipRequest  += req + Environment.NewLine;
                        RTShipResponse += resp.Replace('\n', ' ').Replace('\r', ' ') + Environment.NewLine;
                    }
                }
            }

            // Handling fee should only be added per shipping address not per package
            // let's just compute it here after we've gone through all the packages.
            // Also, since we can't be sure about the ordering of the method call here
            // and that the collection SM includes shipping methods from all possible carriers
            // we'll need to filter out the methods per this carrier to avoid side effects on the main collection
            foreach (ShipMethod shipMethod in SM.PerCarrier("Australia Post"))
            {
                shipMethod.ServiceRate += ExtraFee;
            }
        }
Beispiel #4
0
        /// <summary>
        /// Get shipping rates from DHL for International shipments
        /// </summary>
        private void DHLIntlGetRates(Shipments allShipments, out string rtShipRequest, out string rtShipResponse, decimal extraFee, decimal markupPercent, decimal shipmentValue, decimal shippingTaxRate)
        {
            rtShipRequest  = String.Empty;
            rtShipResponse = String.Empty;
            System.Collections.Specialized.NameValueCollection ratesList = new System.Collections.Specialized.NameValueCollection();

            // is weight within shippable limit?
            if (ShipmentWeight > AppLogic.AppConfigUSDecimal("RTShipping.DHLIntl.MaxWeight"))
            {
                SM.ErrorMsg = "DHL " + AppLogic.AppConfig("RTShipping.CallForShippingPrompt");
                return;
            }

            // retrieve correct DHL Server URL
            string dhlServer;

            if (AppLogic.AppConfigBool("RTShipping.UseTestRates"))
            {
                dhlServer = AppLogic.AppConfig("RTShipping.DHL.TestServer");
            }
            else
            {
                dhlServer = AppLogic.AppConfig("RTShipping.DHL.Server");
            }

            // calculate legal ship date
            DateTime shipDate = DateTime.Now.AddDays(AppLogic.AppConfigUSDouble("RTShipping.DHL.ShipInDays"));

            if (shipDate.DayOfWeek == DayOfWeek.Saturday)
            {
                shipDate = shipDate.AddDays(2);
            }
            if (shipDate.DayOfWeek == DayOfWeek.Sunday)
            {
                shipDate = shipDate.AddDays(1);
            }

            // error 4112 is tripped by asking for a rate quote on a Sunday or a Holiday
            bool error4112 = false;

            do
            {
                if (error4112)
                {
                    error4112 = false;
                    shipDate  = shipDate.AddDays(1);
                }

                foreach (Packages shipment in allShipments)
                {
                    HasFreeItems    = false;
                    PackageQuantity = 1;

                    foreach (Package p in shipment)
                    {
                        if (p.IsFreeShipping)
                        {
                            HasFreeItems = true;
                        }
                        if (p.IsShipSeparately)
                        {
                            PackageQuantity = p.Quantity;
                        }

                        // initialize rate requests
                        dhl.InternationalRateRequest rateRequests = new dhl.InternationalRateRequest();
                        rateRequests.Requestor.ID       = AppLogic.AppConfig("RTShipping.DHL.APISystemID");
                        rateRequests.Requestor.Password = AppLogic.AppConfig("RTShipping.DHL.APISystemPassword");

                        string dhlServices = AppLogic.AppConfig("RTShipping.DHLIntl.Services");

                        // create an array of individual requests, one for each service
                        rateRequests.Shipment = new dhl.InternationalRequest[dhlServices.Split(',').Length];

                        // populate the array
                        int serviceIndex = 0;
                        foreach (string service in dhlServices.Split(','))
                        {
                            rateRequests.Shipment[serviceIndex] = new dhl.InternationalRequest();
                            dhl.InternationalRequest request = rateRequests.Shipment[serviceIndex];

                            // DHL rating API credentials
                            request.ShippingCredentials.ShippingKey = AppLogic.AppConfig("RTShipping.DHLIntl.ShippingKey");
                            request.ShippingCredentials.AccountNbr  = AppLogic.AppConfig("RTShipping.DHL.AccountNumber");

                            // shipment details
                            request.ShipmentDetail.ShipDate          = shipDate.ToString("yyyy-MM-dd");
                            request.ShipmentDetail.ShipmentType.Code = AppLogic.AppConfig("RTShipping.DHLIntl.Packaging").ToUpperInvariant();

                            // used to allow 'O' (Other) packaging types, which are now 'P' types
                            if (request.ShipmentDetail.ShipmentType.Code == "O")
                            {
                                request.ShipmentDetail.ShipmentType.Code = "P";
                            }

                            // package weight
                            if (request.ShipmentDetail.ShipmentType.Code == "L")
                            {
                                request.ShipmentDetail.Weight = p.Weight.ToString("#0.0");
                            }
                            else
                            {
                                request.ShipmentDetail.Weight = Math.Ceiling(p.Weight).ToString("##0");
                            }

                            request.ShipmentDetail.ContentDesc = "ContentDesc";

                            // billing details
                            request.Billing.Party.Code = AppLogic.AppConfig("RTShipping.DHLIntl.BillingParty").ToUpperInvariant();
                            if (request.Billing.Party.Code != "S")
                            {
                                request.Billing.AccountNbr = AppLogic.AppConfig("RTShipping.DHLIntl.BillingAccountNbr").ToUpperInvariant();
                            }

                            request.Billing.DutyPaymentType = AppLogic.AppConfig("RTShipping.DHLIntl.DutyPayment").ToUpperInvariant();
                            if (request.Billing.DutyPaymentType == "3")
                            {
                                request.Billing.DutyPaymentAccountNbr = AppLogic.AppConfig("RTShipping.DHLIntl.DutyPaymentAccountNbr").ToUpperInvariant();
                            }

                            // import duty declaration
                            request.Dutiable.DutiableFlag = AppLogic.AppConfig("RTShipping.DHLIntl.Dutiable").ToUpperInvariant();
                            request.Dutiable.CustomsValue = p.InsuredValue.ToString("######");

                            // overrides
                            string overrideCodes = AppLogic.AppConfig("RTShipping.DHLIntl.Overrides");
                            if (overrideCodes.Length > 0)
                            {
                                request.ShipmentProcessingInstructions.Overrides = new dhl.Override[overrideCodes.Split(',').Length];

                                int overrideIndex = 0;
                                foreach (string overrideCode in overrideCodes.Split(','))
                                {
                                    request.ShipmentProcessingInstructions.Overrides[overrideIndex]      = new dhl.Override();
                                    request.ShipmentProcessingInstructions.Overrides[overrideIndex].Code = overrideCode;

                                    overrideIndex++;
                                }
                            }

                            // ship-to address
                            request.Receiver.Address.Street = "Street";
                            request.Receiver.Address.City   = shipment.DestinationCity;
                            if (shipment.DestinationCountryCode.Equals("ca", StringComparison.OrdinalIgnoreCase) ||
                                shipment.DestinationCountryCode.Equals("us", StringComparison.OrdinalIgnoreCase))
                            {
                                request.Receiver.Address.State = shipment.DestinationStateProvince;
                            }

                            request.Receiver.Address.Country    = shipment.DestinationCountryCode.ToUpperInvariant();
                            request.Receiver.Address.PostalCode = shipment.DestinationZipPostalCode.ToUpperInvariant();

                            // dimensions
                            if (p.Length + p.Width + p.Height != 0)
                            {
                                request.ShipmentDetail.Dimensions        = new dhl.Dimensions();
                                request.ShipmentDetail.Dimensions.Length = p.Length.ToString("###");
                                request.ShipmentDetail.Dimensions.Width  = p.Width.ToString("###");
                                request.ShipmentDetail.Dimensions.Height = p.Height.ToString("###");
                            }

                            // insurance
                            if (p.Insured && p.InsuredValue != 0)
                            {
                                request.ShipmentDetail.AdditionalProtection.Code  = "AP"; // additional protection
                                request.ShipmentDetail.AdditionalProtection.Value = p.InsuredValue.ToString("######");
                            }
                            else
                            {
                                request.ShipmentDetail.AdditionalProtection.Code  = "NR"; // not required
                                request.ShipmentDetail.AdditionalProtection.Value = "0";
                            }

                            // add the service code, and service name to the request
                            request.ShipmentDetail.Service.Code = service.Split(';')[0];
                            request.TransactionTrace            = service.Split(';')[1];

                            // add this individual service request to the rateRequests
                            rateRequests.Shipment[serviceIndex] = request;

                            serviceIndex++;
                        }

                        // serialize rateRequests into an xml string
                        XmlWriterSettings xwSettings = new XmlWriterSettings();
                        xwSettings.OmitXmlDeclaration = true;

                        XmlSerializer serRequest = new XmlSerializer(rateRequests.GetType());
                        StringWriter  swRequest  = new StringWriter();
                        XmlWriter     xwRequest  = XmlWriter.Create(swRequest, xwSettings);
                        serRequest.Serialize(xwRequest, rateRequests);

                        string req = swRequest.ToString();

                        // Send xml rate request to DHL server
                        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(dhlServer);
                        webRequest.Method = "POST";

                        webRequest.ProtocolVersion = HttpVersion.Version11;
                        webRequest.ContentType     = "application/xml; charset=UTF-8";
                        webRequest.Accept          = "application/xml; charset=UTF-8";

                        // Transmit the request to DHL
                        byte[] data = System.Text.Encoding.ASCII.GetBytes(req);
                        webRequest.ContentLength = data.Length;
                        Stream requestStream;

                        try
                        {
                            requestStream = webRequest.GetRequestStream();
                        }
                        catch (WebException e)
                        {
                            // could not connect to DHL endpoint
                            rtShipResponse += "Tried to reach DHL Server (" + dhlServer + "): " + e.Message;
                            return;
                        }

                        requestStream.Write(data, 0, data.Length);
                        requestStream.Close();

                        // get the response from DHL
                        WebResponse webResponse = null;
                        string      resp;
                        try
                        {
                            webResponse = webRequest.GetResponse();
                        }
                        catch (WebException e)
                        {
                            // could not receive a response from DHL endpoint
                            rtShipResponse += "No response from DHL Server (" + dhlServer + "): " + e.Message;
                            return;
                        }

                        using (StreamReader sr = new StreamReader(webResponse.GetResponseStream()))
                        {
                            resp = sr.ReadToEnd();
                            sr.Close();
                        }

                        webResponse.Close();

                        // deserialize the xml response into an InternationalRateResponse object
                        dhl.InternationalRateResponse response = new dhl.InternationalRateResponse();
                        XmlSerializer serResponse = new XmlSerializer(typeof(dhl.InternationalRateResponse));
                        StringReader  srResponse  = new StringReader(resp);

                        try
                        {
                            response = (dhl.InternationalRateResponse)serResponse.Deserialize(srResponse);
                        }
                        catch (InvalidOperationException e)
                        {
                            // invalid xml, or no reply received from DHL
                            rtShipResponse += "Could not parse response from DHL server: " + e.Message + " Response received: " + resp;
                            return;
                        }

                        srResponse.Close();

                        // Check the response object for Ratings
                        if (response.Shipment == null)
                        {
                            rtShipResponse += "DHL Error: No rating responses returned from DHL.";
                            return;
                        }

                        // Check the response object for Faults
                        if (response.Shipment[0].Faults != null)
                        {
                            if (response.Shipment[0].Faults[0].Code == "4112")
                            {
                                error4112 = true;
                            }
                            else
                            {
                                rtShipResponse += "DHL Error: " + response.Shipment[0].Faults[0].Desc;
                                return;
                            }
                        }

                        // one response for each rate request
                        foreach (dhl.Response rateResponse in response.Shipment)
                        {
                            // check for a rate estimate
                            if (rateResponse.EstimateDetail == null)
                            {
                                break;
                            }

                            // we have a good estimate
                            decimal total = Localization.ParseUSDecimal(rateResponse.EstimateDetail.RateEstimate.TotalChargeEstimate);

                            // ignore zero-cost methods, and methods not allowed
                            if ((total == 0 && AppLogic.AppConfigBool("FilterOutShippingMethodsThatHave0Cost")) ||
                                !ShippingMethodIsAllowed(rateResponse.TransactionTrace, string.Empty))
                            {
                                break;
                            }

                            total = total * PackageQuantity * (1.00M + (markupPercent / 100.0M));
                            decimal vat = Decimal.Round(total * shippingTaxRate);

                            if (!SM.MethodExists(rateResponse.TransactionTrace))
                            {
                                ShipMethod s_method = new ShipMethod();
                                s_method.Carrier     = "DHL";
                                s_method.ServiceName = rateResponse.TransactionTrace;
                                s_method.ServiceRate = total;
                                s_method.VatRate     = vat;

                                if (HasFreeItems)
                                {
                                    s_method.FreeItemsRate = total;
                                }

                                SM.AddMethod(s_method);
                            }
                            else
                            {
                                int        indexOf  = SM.GetIndex(rateResponse.TransactionTrace);
                                ShipMethod s_method = SM[indexOf];

                                s_method.ServiceRate += total;
                                s_method.VatRate     += vat;

                                if (HasFreeItems)
                                {
                                    s_method.FreeItemsRate += total;
                                }

                                SM[indexOf] = s_method;
                            }
                        }

                        rtShipRequest  += req;
                        rtShipResponse += resp;
                    }
                }
            }while (error4112 == true);

            // Handling fee should only be added per shipping address not per package
            // let's just compute it here after we've gone through all the packages.
            // Also, since we can't be sure about the ordering of the method call here
            // and that the collection SM includes shipping methods from all possible carriers
            // we'll need to filter out the methods per this carrier to avoid side effects on the main collection
            foreach (ShipMethod shipMethod in SM.PerCarrier("DHL"))
            {
                shipMethod.ServiceRate += extraFee;
            }
        }