Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/usps issue77 #75

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions DotNetShipping.Tests/Features/FedExShipRates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ public class FedExShipRates : FedExShipRatesTestsBase
[Fact]
public void FedExReturnsRates()
{
var from = new Address("Annapolis", "MD", "21401", "US");
var to = new Address("Fitchburg", "WI", "53711", "US");
var package = new Package(7, 7, 7, 6, 0);
var from = new Address("", "", "60084", "US");
var to = new Address("", "", "80465", "US") { IsResidential = true };
var package = new Package(24, 24, 13, 50, 0);

var r = _rateManager.GetRates(from, to, package);
var fedExRates = r.Rates.ToList();
Expand All @@ -52,6 +52,51 @@ public void FedExReturnsRates()
}
}

[Fact]
public void FedExReturnsDifferentRatesForResidentialAndBusiness()
{
var from = new Address("Annapolis", "MD", "21401", "US");
var residentialTo = new Address("Fitchburg", "WI", "53711", "US") { IsResidential = true };
var businessTo = new Address("Fitchburg", "WI", "53711", "US") { IsResidential = false };
var package = new Package(7, 7, 7, 6, 0);

var residential = _rateManager.GetRates(from, residentialTo, package);
var residentialRates = residential.Rates.ToList();

var business = _rateManager.GetRates(from, businessTo, package);
var businessRates = business.Rates.ToList();

var homeFound = false;
var groundFound = false;

// FedEx Home should come back for Residential
foreach (var rate in residentialRates)
{
if (rate.ProviderCode.Equals("GROUND_HOME_DELIVERY"))
homeFound = true;
if (rate.ProviderCode.Equals("FEDEX_GROUND"))
groundFound = true;
}

Assert.True(homeFound);
Assert.True(!groundFound);

homeFound = false;
groundFound = false;

// FedEx Ground should come back for Business
foreach (var rate in businessRates)
{
if (rate.ProviderCode.Equals("GROUND_HOME_DELIVERY"))
homeFound = true;
if (rate.ProviderCode.Equals("FEDEX_GROUND"))
groundFound = true;
}

Assert.True(!homeFound);
Assert.True(groundFound);
}

[Fact]
public void FedExReturnsDifferentRatesForSignatureOnDelivery()
{
Expand Down
21 changes: 21 additions & 0 deletions DotNetShipping.Tests/Features/USPSDomesticRates.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
Expand Down Expand Up @@ -170,5 +172,24 @@ public void Can_Get_Different_Rates_For_Signature_Required_Lookup()
}
}
}

[Fact]
public void USPS_Domestic_Will_Remove_Rates_That_Are_Not_Uniform_If_RequiredUniformRates_Enabled()
{
var rateManager = new RateManager();
rateManager.AddProvider(new USPSProvider(USPSUserId, "ALL", String.Empty, true));

var package1 = new Package(14, 14, 6, 2, 0);
var package2 = new Package(16, 16, 48, 16, 0);

var origin = new Address("", "", "21401", "US");
var destination = new Address("", "", "54937", "US");

var response = rateManager.GetRates(origin, destination, new List<Package>() { package1, package2 } );

Assert.True(response.RatesExcluded);
Assert.NotNull(response.InfoMessages);
Assert.True(response.InfoMessages.Count > 0);
}
}
}
1 change: 1 addition & 0 deletions DotNetShipping.Tests/Features/USPSInternationalRates.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
Expand Down
3 changes: 3 additions & 0 deletions DotNetShipping/DotNetShipping.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Address.cs" />
<Compile Include="ShippingProvider.cs" />
<Compile Include="FedExRateService.cs" />
<Compile Include="Helpers\Extensions\RequestedShipmentExtensions.cs" />
<Compile Include="Helpers\USPSHelpers.cs" />
<Compile Include="InfoMessage.cs" />
<Compile Include="PoundsAndOunces.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Extensions.cs" />
Expand Down
27 changes: 27 additions & 0 deletions DotNetShipping/Helpers/USPSHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

using static System.String;

namespace DotNetShipping.Helpers
{
public static class USPSHelpers
{
/// <summary>
/// Removes encoded characters from mail service name for human consumption
/// </summary>
/// <param name="mailServiceName"></param>
/// <returns></returns>
public static String SanitizeMailServiceName(this String mailServiceName)
{
if (!IsNullOrEmpty(mailServiceName))
return Regex.Replace(mailServiceName, "&lt.*&gt;", "");

return mailServiceName;
}
}
}
27 changes: 27 additions & 0 deletions DotNetShipping/InfoMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DotNetShipping
{
public class InfoMessage
{
/// <summary>
/// Shipping provider that generated the message
/// </summary>
public ShippingProvider ShippingProvider { get; set; }

/// <summary>
/// Message
/// </summary>
public String Message { get; set; }

public InfoMessage(ShippingProvider shippingProvider, String message)
{
ShippingProvider = shippingProvider;
Message = message;
}
}
}
19 changes: 18 additions & 1 deletion DotNetShipping/Shipment.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
Expand All @@ -13,16 +14,23 @@ public class Shipment
public ICollection<IRateAdjuster> RateAdjusters;
private readonly List<Rate> _rates;
private readonly List<USPSError> _serverErrors;

/// <summary>
/// Will contain any informative messages
/// </summary>
private readonly List<InfoMessage> _infoMessages;

public readonly Address DestinationAddress;
public readonly Address OriginAddress;

public Shipment(Address originAddress, Address destinationAddress, List<Package> packages)
{
OriginAddress = originAddress;
DestinationAddress = destinationAddress;
Packages = packages.AsReadOnly();
_rates = new List<Rate>();
_serverErrors = new List<USPSError>();
_infoMessages = new List<InfoMessage>();
}

public int PackageCount
Expand All @@ -41,5 +49,14 @@ public List<USPSError> ServerErrors
{
get { return _serverErrors; }
}
public List<InfoMessage> InfoMessages
{
get { return _infoMessages; }
}

/// <summary>
/// If true, some rates were excluded. See InfoMessages for more information.
/// </summary>
public bool RatesExcluded { get; set; }
}
}
17 changes: 17 additions & 0 deletions DotNetShipping/ShippingProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DotNetShipping
{
public enum ShippingProvider
{
UPS,
USPS,
USPSInternational,
FedEx,
FedExSmartPost
}
}
53 changes: 52 additions & 1 deletion DotNetShipping/ShippingProviders/USPSInternationalProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
using System.Xml;
using System.Xml.Linq;

using DotNetShipping.Helpers;

namespace DotNetShipping.ShippingProviders
{
/// <summary>
Expand All @@ -19,6 +21,16 @@ public class USPSInternationalProvider : AbstractShippingProvider
private const string PRODUCTION_URL = "http://production.shippingapis.com/ShippingAPI.dll";
private readonly string _service;
private readonly string _userId;

/// <summary>
/// If set to true, the rates that are returned and combined across packages will only be done if even package has the same mail service available across returned packages.
/// <example>
/// If 2 packages are calculated and the first package has rates for Priority Mail 2 Day and Standard Post, but the second package only supports Standard Post, then only Standard Post rates would be returned.
/// In instances where this happens, ignored shipping rates will be populated in the InfoMessages property of the Shipment.
/// </example>
/// </summary>
private readonly bool _requireUniformMailServices;

private readonly Dictionary<string, string> _serviceCodes = new Dictionary<string, string>
{
{"Priority Mail Express International","Priority Mail Express International"},
Expand Down Expand Up @@ -76,6 +88,17 @@ public USPSInternationalProvider(string userId, string service)
_service = service;
}

/// <summary>
/// </summary>
/// <param name="userId"></param>
public USPSInternationalProvider(string userId, string service, bool requireUniformMailServices)
{
Name = "USPS";
_userId = userId;
_service = service;
_requireUniformMailServices = requireUniformMailServices;
}

public bool Commercial { get; set; }

/// <summary>
Expand Down Expand Up @@ -183,8 +206,36 @@ public bool IsDomesticUSPSAvailable()
private void ParseResult(string response)
{
var document = XDocument.Load(new StringReader(response));
var excludedMailServices = new List<String>();

var rates = document.Descendants("Service").GroupBy(item => (string) item.Element("SvcDescription")).Select(g => new {Name = g.Key, TotalCharges = g.Sum(x => Decimal.Parse((string) x.Element("Postage")))}).ToList();

var rates = document.Descendants("Service").GroupBy(item => (string) item.Element("SvcDescription")).Select(g => new {Name = g.Key, TotalCharges = g.Sum(x => Decimal.Parse((string) x.Element("Postage")))});
if (_requireUniformMailServices)
{
// Put together a list of excluded mail services by getting a count of packages and a count of each mail service returned
var totalPackages = document.Descendants("Package").Count();
var mailServices = from item in document.Descendants("Postage")
group item by (string)item.Element("MailService")
into g
select new
{
Name = g.Key,
Count = g.Count()
};

excludedMailServices.AddRange(from mailService in mailServices where mailService.Count < totalPackages select mailService.Name);
}

// Remove excluded rates
if (excludedMailServices.Count > 0)
{
rates.RemoveAll(x => excludedMailServices.Contains(x.Name));

var message = $"Removed {String.Join(", ", excludedMailServices.Select(x => x.SanitizeMailServiceName()))} from returned rates. Rate not available on all packages in Shipment.";

Shipment.InfoMessages.Add(new InfoMessage(ShippingProvider.USPSInternational, message));
Shipment.RatesExcluded = true;
}

if (_service == "ALL")
{
Expand Down
Loading