Expanded to support multiple packages per request, and now more object oriented.

USPS still provides the cost of each package back individually, so we sum those up per service requested
pull/3/head
Troy Davisson 10 years ago
parent 9d7c8335ff
commit a89504e722

4
.gitignore vendored

@ -0,0 +1,4 @@
vendor/
.idea/
composer.lock

@ -88,17 +88,6 @@ class Rate extends RateAdapter
protected function prepare()
{
$to = Arr::get($this->shipment, 'to');
$shipper = Arr::get($this->shipment, 'from');
$dimensions = Arr::get($this->shipment, 'dimensions');
$pounds = (int) Arr::get($this->shipment, 'weight');
$ounces = 0;
if ($pounds < 1) {
throw new Exception('Weight missing');
}
$date = time();
$day_name = date('l', $date);
@ -111,6 +100,28 @@ class Rate extends RateAdapter
// http://www.fedex.com/templates/components/apps/wpor/secure/downloads/pdf/Aug13/PropDevGuide.pdf
// http://www.fedex.com/us/developer/product/WebServices/MyWebHelp_August2010/Content/Proprietary_Developer_Guide/Rate_Services_conditionalized.htm
$packages = '';
$sequence_number = 0;
foreach ($this->shipment->getPackages() as $p) {
$sequence_number++;
$packages .= '<RequestedPackageLineItems>
<SequenceNumber>' . $sequence_number . '</SequenceNumber>
<GroupPackageCount>1</GroupPackageCount>
<Weight>
<Units>LB</Units>
<Value>' . $p->getWeight() . '</Value>
</Weight>
<Dimensions>
<Length>' . $p->getLength() . '</Length>
<Width>' . $p->getWidth() . '</Width>
<Height>' . $p->getHeight() . '</Height>
<Units>IN</Units>
</Dimensions>
</RequestedPackageLineItems>';
}
$this->data =
'<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://fedex.com/ws/rate/v13">
@ -136,37 +147,24 @@ class Rate extends RateAdapter
<RequestedShipment>
<ShipTimestamp>' . date('c') . '</ShipTimestamp>
<DropoffType>' . $this->drop_off_type . '</DropoffType>
<PackagingType>' . Arr::get($this->shipment, 'packaging_type') . '</PackagingType>
<PackagingType>YOUR_PACKAGING</PackagingType>
<Shipper>
<Address>
<PostalCode>' . Arr::get($shipper, 'postal_code') . '</PostalCode>
<CountryCode>' . Arr::get($shipper, 'country_code') . '</CountryCode>
' . ((Arr::get($shipper, 'is_residential')) ? '<Residential>1</Residential>' : '') . '
<PostalCode>' . $this->shipment->getFromPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getFromCountryCode() . '</CountryCode>
' . (($this->shipment->isFromResidential()) ? '<Residential>1</Residential>' : '') . '
</Address>
</Shipper>
<Recipient>
<Address>
<PostalCode>' . Arr::get($to, 'postal_code') . '</PostalCode>
<CountryCode>' . Arr::get($to, 'country_code') . '</CountryCode>
' . ((Arr::get($to, 'is_residential')) ? '<Residential>1</Residential>' : '') . '
<PostalCode>' . $this->shipment->getToPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getToCountryCode() . '</CountryCode>
' . (($this->shipment->isToResidential()) ? '<Residential>1</Residential>' : '') . '
</Address>
</Recipient>
<RateRequestTypes>LIST</RateRequestTypes>
<PackageCount>1</PackageCount>
<RequestedPackageLineItems>
<SequenceNumber>1</SequenceNumber>
<GroupPackageCount>1</GroupPackageCount>
<Weight>
<Units>LB</Units>
<Value>' . $pounds . '</Value>
</Weight>
<Dimensions>
<Length>' . Arr::get($dimensions, 'length') . '</Length>
<Width>' . Arr::get($dimensions, 'width') . '</Width>
<Height>' . Arr::get($dimensions, 'height') . '</Height>
<Units>IN</Units>
</Dimensions>
</RequestedPackageLineItems>
<PackageCount>' . $this->shipment->packageCount() . '</PackageCount>
' . $packages . '
</RequestedShipment>
</RateRequest>
</SOAP-ENV:Body>

@ -0,0 +1,123 @@
<?php namespace pdt256\Shipping;
class Package
{
const FEDEX_YOUR_PACKAGING = 'YOUR_PACKAGING';
const USPS_CONTAINER_RECTANGULAR = 'RECTANGULAR';
const USPS_SIZE_LARGE = 'LARGE';
protected $weight;
protected $width;
protected $length;
protected $height;
protected $packaging;
protected $size_classification;
/**
* @return mixed
*/
public function getSizeClassification()
{
return $this->size_classification;
}
/**
* @param mixed $size_classification
* @return $this
*/
public function setSizeClassification($size_classification)
{
$this->size_classification = $size_classification;
return $this;
}
/**
* @return mixed
*/
public function getPackaging()
{
return $this->packaging;
}
/**
* @param mixed $packaging
* @return $this
*/
public function setPackaging($packaging)
{
$this->packaging = $packaging;
return $this;
}
/**
* @return mixed
*/
public function getWeight()
{
return $this->weight;
}
/**
* @param mixed $weight
* @return $this
*/
public function setWeight($weight)
{
$this->weight = $weight;
return $this;
}
/**
* @return mixed
*/
public function getWidth()
{
return $this->width;
}
/**
* @param mixed $width
* @return $this
*/
public function setWidth($width)
{
$this->width = $width;
return $this;
}
/**
* @return mixed
*/
public function getLength()
{
return $this->length;
}
/**
* @param mixed $length
* @return $this
*/
public function setLength($length)
{
$this->length = $length;
return $this;
}
/**
* @return mixed
*/
public function getHeight()
{
return $this->height;
}
/**
* @param mixed $height
* @return $this
*/
public function setHeight($height)
{
$this->height = $height;
return $this;
}
}

@ -7,6 +7,7 @@ abstract class RateAdapter
{
protected $is_prod = FALSE;
/** @var Shipment */
protected $shipment;
protected $data;
protected $response;
@ -27,18 +28,6 @@ abstract class RateAdapter
if (isset($options['shipment'])) {
$this->shipment = $options['shipment'];
}
if (empty($this->shipment['to'])) {
throw new Exception('Shipment "to" missing');
}
if (empty($this->shipment['from'])) {
throw new Exception('Shipment "from" missing');
}
if (empty($this->shipment['dimensions'])) {
throw new Exception('Shipment "dimensions" missing');
}
}
public function set_request_adapter(RateRequest\Adapter $rate_request)

@ -0,0 +1,169 @@
<?php namespace pdt256\Shipping;
class Shipment
{
/** @var Package[] */
protected $packages = [];
protected $from_postal_code;
protected $from_country_code;
protected $to_postal_code;
protected $to_country_code;
/** @var bool */
protected $to_is_residential;
/** @var bool */
protected $from_is_residential;
protected $from_state_province;
/**
* @param Package $package
* @return $this
*/
public function addPackage(Package $package)
{
$this->packages[] = $package;
return $this;
}
/**
* @return Package[]
*/
public function getPackages()
{
return $this->packages;
}
/**
* @return int
*/
public function packageCount()
{
return count($this->getPackages());
}
/**
* @return mixed
*/
public function getFromPostalCode()
{
return $this->from_postal_code;
}
/**
* @param mixed $from_postal_code
* @return $this
*/
public function setFromPostalCode($from_postal_code)
{
$this->from_postal_code = $from_postal_code;
return $this;
}
/**
* @return mixed
*/
public function getFromCountryCode()
{
return $this->from_country_code;
}
/**
* @param mixed $from_country_code
* @return $this
*/
public function setFromCountryCode($from_country_code)
{
$this->from_country_code = $from_country_code;
return $this;
}
/**
* @return mixed
*/
public function getToPostalCode()
{
return $this->to_postal_code;
}
/**
* @param mixed $to_postal_code
* @return $this
*/
public function setToPostalCode($to_postal_code)
{
$this->to_postal_code = $to_postal_code;
return $this;
}
/**
* @return mixed
*/
public function getToCountryCode()
{
return $this->to_country_code;
}
/**
* @param mixed $to_country_code
* @return $this
*/
public function setToCountryCode($to_country_code)
{
$this->to_country_code = $to_country_code;
return $this;
}
/**
* @return boolean
*/
public function isToResidential()
{
return $this->to_is_residential;
}
/**
* @param boolean $to_is_residential
* @return $this
*/
public function setToResidential($to_is_residential)
{
$this->to_is_residential = $to_is_residential;
return $this;
}
/**
* @return bool
*/
public function isFromResidential()
{
return $this->from_is_residential;
}
/**
* @param $from_is_residential
* @return $this
*/
public function setFromResidential($from_is_residential)
{
$this->from_is_residential = $from_is_residential;
return $this;
}
/**
* @return mixed
*/
public function getFromStateProvinceCode()
{
return $this->from_state_province;
}
/**
* @param $from_state_province
* @return $this
*/
public function setFromStateProvinceCode($from_state_province)
{
$this->from_state_province = $from_state_province;
return $this;
}
}

@ -114,19 +114,31 @@ class Rate extends RateAdapter
protected function prepare()
{
$to = Arr::get($this->shipment, 'to');
$shipper = Arr::get($this->shipment, 'from');
$dimensions = Arr::get($this->shipment, 'dimensions');
$pounds = (int) Arr::get($this->shipment, 'weight');
$ounces = 0;
$service_code = '03';
if ($pounds < 1) {
throw new Exception('Weight missing');
$packages = '';
foreach ($this->shipment->getPackages() as $p) {
$packages .= '<Package>
<PackagingType>
<Code>02</Code>
</PackagingType>
<Dimensions>
<UnitOfMeasurement>
<Code>IN</Code>
</UnitOfMeasurement>
<Length>' . $p->getLength() . '</Length>
<Width>' . $p->getWidth() . '</Width>
<Height>' . $p->getHeight() . '</Height>
</Dimensions>
<PackageWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>' . $p->getWeight() . '</Weight>
</PackageWeight>
</Package>';
}
$service_code = '03';
$this->data =
'<?xml version="1.0"?>
<AccessRequest xml:lang="en-US">
@ -142,48 +154,31 @@ class Rate extends RateAdapter
<Shipment>
<Shipper>
<Address>
<PostalCode>' . Arr::get($shipper, 'postal_code') . '</PostalCode>
<CountryCode>' . Arr::get($shipper, 'country_code') . '</CountryCode>
' . ((Arr::get($shipper, 'is_residential')) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
<PostalCode>' . $this->shipment->getFromPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getFromCountryCode() . '</CountryCode>
' . (($this->shipment->isFromResidential()) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
</Address>
<ShipperNumber>' . $this->shipper_number . '</ShipperNumber>
</Shipper>
<ShipTo>
<Address>
<PostalCode>' . Arr::get($to, 'postal_code') . '</PostalCode>
<CountryCode>' . Arr::get($to, 'country_code') . '</CountryCode>
' . ((Arr::get($to, 'is_residential')) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
<PostalCode>' . $this->shipment->getToPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getToCountryCode() . '</CountryCode>
' . (($this->shipment->isToResidential()) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
</Address>
</ShipTo>
<ShipFrom>
<Address>
<PostalCode>' . Arr::get($shipper, 'postal_code') . '</PostalCode>
<CountryCode>' . Arr::get($shipper, 'country_code') . '</CountryCode>
' . ((Arr::get($shipper, 'is_residential')) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
<StateProvinceCode>' . $this->shipment->getFromStateProvinceCode() . '</StateProvinceCode>
<PostalCode>' . $this->shipment->getFromPostalCode() . '</PostalCode>
<CountryCode>' . $this->shipment->getFromCountryCode() . '</CountryCode>
' . (($this->shipment->isFromResidential()) ? '<ResidentialAddressIndicator>1</ResidentialAddressIndicator>' : '') . '
</Address>
</ShipFrom>
<Service>
<Code>' . $service_code . '</Code>
</Service>
<Package>
<PackagingType>
<Code>02</Code>
</PackagingType>
<Dimensions>
<UnitOfMeasurement>
<Code>IN</Code>
</UnitOfMeasurement>
<Length>' . Arr::get($dimensions, 'length') . '</Length>
<Width>' . Arr::get($dimensions, 'width') . '</Width>
<Height>' . Arr::get($dimensions, 'height') . '</Height>
</Dimensions>
<PackageWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>' . $pounds . '</Weight>
</PackageWeight>
</Package>
' . $packages . '
<RateInformation>
<NegotiatedRatesIndicator/>
</RateInformation>

@ -95,34 +95,31 @@ class Rate extends RateAdapter
protected function prepare()
{
$to = Arr::get($this->shipment, 'to');
$shipper = Arr::get($this->shipment, 'from');
$dimensions = Arr::get($this->shipment, 'dimensions');
// https://www.usps.com/business/web-tools-apis/rate-calculators-v1-7a.htm
$pounds = (int) Arr::get($this->shipment, 'weight');
$ounces = 0;
if ($pounds < 1) {
throw new Exception('Weight missing');
$packages = '';
$sequence_number = 0;
foreach ($this->shipment->getPackages() as $p) {
$sequence_number++;
$packages .= '<Package ID="' . $sequence_number .'">
<Service>ALL</Service>
<ZipOrigination>' . $this->shipment->getFromPostalCode() . '</ZipOrigination>
<ZipDestination>' . $this->shipment->getToPostalCode() . '</ZipDestination>
<Pounds>' . $p->getWeight() . '</Pounds>
<Ounces>0</Ounces>
<Container>' . $p->getPackaging() . '</Container>
<Size>' . $p->getSizeClassification() . '</Size>
<Width>' . $p->getWidth() . '</Width>
<Length>' . $p->getLength() . '</Length>
<Height>' . $p->getHeight() . '</Height>
<Machinable>' . 'False' . '</Machinable>
</Package>';
}
$this->data =
'<RateV4Request USERID="' . $this->username . '">
<Revision/>
<Package ID="1">
<Service>ALL</Service>
<ZipOrigination>' . Arr::get($shipper, 'postal_code') . '</ZipOrigination>
<ZipDestination>' . Arr::get($to, 'postal_code') . '</ZipDestination>
<Pounds>' . $pounds . '</Pounds>
<Ounces>' . $ounces . '</Ounces>
<Container>' . Arr::get($this->shipment, 'container') . '</Container>
<Size>' . Arr::get($this->shipment, 'size') . '</Size>
<Width>' . Arr::get($dimensions, 'width') . '</Width>
<Length>' . Arr::get($dimensions, 'length') . '</Length>
<Height>' . Arr::get($dimensions, 'height') . '</Height>
<Machinable>' . 'False' . '</Machinable>
</Package>
' . $packages . '
</RateV4Request>';
return $this;
@ -160,6 +157,8 @@ class Rate extends RateAdapter
throw $e;
}
$rates = [];
foreach ($postage_list as $postage) {
$code = @$postage->getAttribute('CLASSID');
$cost = @$postage->getElementsByTagName('Rate')->item(0)->nodeValue;
@ -170,13 +169,21 @@ class Rate extends RateAdapter
continue;
}
$this->rates[] = array(
if (array_key_exists($code, $rates)) {
$cost = $rates[$code]['cost'] + ($cost * 100);
} else {
$cost = $cost * 100;
}
$rates[$code] = [
'code' => $code,
'name' => $name,
'cost' => (int) $cost * 100,
);
'cost' => (int) $cost,
];
}
$this->rates = array_values($rates);
return $this;
}
}

@ -1,5 +1,7 @@
<?php
use pdt256\Shipping\Package;
use pdt256\Shipping\Ship;
use pdt256\Shipping\Shipment;
use pdt256\Shipping\USPS;
use pdt256\Shipping\UPS;
use pdt256\Shipping\Fedex;
@ -7,23 +9,8 @@ use pdt256\Shipping\RateRequest;
class ShipTest extends PHPUnit_Framework_TestCase
{
public $shipment = [
'weight' => 3, // lbs
'dimensions' => [
'width' => 9,
'length' => 9,
'height' => 9,
],
'from' => [
'postal_code' => '90401',
'country_code' => 'US',
],
'to' => [
'postal_code' => '78703',
'country_code' => 'US',
'is_residential' => TRUE,
],
];
/** @var Shipment */
public $shipment;
public $shipping_options = [
'Standard Shipping' => [
@ -60,6 +47,29 @@ class ShipTest extends PHPUnit_Framework_TestCase
],
];
public function setUp()
{
$s = new Shipment;
$s->setFromStateProvinceCode('CA')
->setFromPostalCode('90401')
->setFromCountryCode('US')
->setToPostalCode('78703')
->setToCountryCode('US')
->setToResidential(true);
$p = new Package;
$p->setWeight(3)
->setWidth(9)
->setLength(9)
->setHeight(9)
->setPackaging(Package::USPS_CONTAINER_RECTANGULAR)
->setSizeClassification(Package::USPS_SIZE_LARGE);
$s->addPackage($p);
$this->shipment = $s;
}
private function getUSPSOptions()
{
$ship = Ship::factory($this->shipping_options);
@ -69,10 +79,7 @@ class ShipTest extends PHPUnit_Framework_TestCase
'prod' => FALSE,
'username' => 'XXXX',
'password' => 'XXXX',
'shipment' => array_merge($this->shipment, [
'size' => 'LARGE',
'container' => 'RECTANGULAR',
]),
'shipment' => $this->shipment,
'approved_codes' => $approved_codes,
'request_adapter' => new RateRequest\StubUSPS(),
];
@ -100,6 +107,11 @@ class ShipTest extends PHPUnit_Framework_TestCase
$ship = Ship::factory($this->shipping_options);
$approved_codes = $ship->get_approved_codes('fedex');
$ps = $this->shipment->getPackages();
foreach ($ps as $p) {
$p->setPackaging(Package::FEDEX_YOUR_PACKAGING);
}
return [
'prod' => FALSE,
'key' => 'XXXX',
@ -107,9 +119,7 @@ class ShipTest extends PHPUnit_Framework_TestCase
'account_number' => 'XXXX',
'meter_number' => 'XXXX',
'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
'shipment' => array_merge($this->shipment, [
'packaging_type' => 'YOUR_PACKAGING',
]),
'shipment' => $this->shipment,
'approved_codes' => $approved_codes,
'request_adapter' => new RateRequest\StubFedex(),
];
@ -124,12 +134,12 @@ class ShipTest extends PHPUnit_Framework_TestCase
1 => [
'code' => '4',
'name' => 'Parcel Post',
'cost' => 1000,
'cost' => 1001,
],
0 => [
'code' => '1',
'name' => 'Priority Mail',
'cost' => 1200,
'cost' => 1220,
],
]), json_encode($usps_rates));
}
@ -221,7 +231,7 @@ class ShipTest extends PHPUnit_Framework_TestCase
0 => [
'code' => '4',
'name' => 'Parcel Post',
'cost' => 1000,
'cost' => 1001,
'carrier' => 'usps',
],
],
@ -248,114 +258,6 @@ class ShipTest extends PHPUnit_Framework_TestCase
]), json_encode($display_rates));
}
/**
* @expectedException Exception
*/
public function testUSPSRateMissingTo()
{
$usps_options = $this->getUSPSOptions();
unset($usps_options['shipment']['to']);
$usps = new USPS\Rate($usps_options);
$usps_rates = $usps->get_rates();
}
/**
* @expectedException Exception
*/
public function testUSPSRateMissingFrom()
{
$usps_options = $this->getUSPSOptions();
unset($usps_options['shipment']['from']);
$usps = new USPS\Rate($usps_options);
$usps_rates = $usps->get_rates();
}
/**
* @expectedException Exception
*/
public function testUSPSRateMissingDimensions()
{
$usps_options = $this->getUSPSOptions();
unset($usps_options['shipment']['dimensions']);
$usps = new USPS\Rate($usps_options);
$usps_rates = $usps->get_rates();
}
/**
* @expectedException Exception
*/
public function testUPSRateMissingTo()
{
$ups_options = $this->getUPSOptions();
unset($ups_options['shipment']['to']);
$ups = new UPS\Rate($ups_options);
$ups_rates = $ups->get_rates();
}
/**
* @expectedException Exception
*/
public function testUPSRateMissingFrom()
{
$ups_options = $this->getUPSOptions();
unset($ups_options['shipment']['from']);
$ups = new UPS\Rate($ups_options);
$ups_rates = $ups->get_rates();
}
/**
* @expectedException Exception
*/
public function testUPSRateMissingDimensions()
{
$ups_options = $this->getUPSOptions();
unset($ups_options['shipment']['dimensions']);
$ups = new UPS\Rate($ups_options);
$ups_rates = $ups->get_rates();
}
/**
* @expectedException Exception
*/
public function testFedexRateMissingTo()
{
$fedex_options = $this->getFedexOptions();
unset($fedex_options['shipment']['to']);
$fedex = new Fedex\Rate($fedex_options);
$fedex_rates = $fedex->get_rates();
}
/**
* @expectedException Exception
*/
public function testFedexRateMissingFrom()
{
$fedex_options = $this->getFedexOptions();
unset($fedex_options['shipment']['from']);
$fedex = new Fedex\Rate($fedex_options);
$fedex_rates = $fedex->get_rates();
}
/**
* @expectedException Exception
*/
public function testFedexRateMissingDimensions()
{
$fedex_options = $this->getFedexOptions();
unset($fedex_options['shipment']['dimensions']);
$fedex = new Fedex\Rate($fedex_options);
$fedex_rates = $fedex->get_rates();
}
// // Readme Examples:
// public function testUSPSReadmeExample()
// {

Loading…
Cancel
Save