Conflicts:
	src/USPS/Rate.php
pull/9/head
Pereyaslov Konstantin 10 years ago
commit f04c23cb0c

4
.gitignore vendored

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

@ -21,23 +21,19 @@ Add the following lines to your ``composer.json`` file.
Create a shipment object:
```php
$shipment = [
'weight' => 3, // lbs
'dimensions' => [
'width' => 9, // inches
'length' => 9,
'height' => 9,
],
'from' => [
'postal_code' => '90401',
'country_code' => 'US',
],
'to' => [
'postal_code' => '78703',
'country_code' => 'US',
'is_residential' => TRUE,
],
];
$shipment = new Shipment;
$shipment
->setFromStateProvinceCode('IN')
->setFromPostalCode('46205')
->setFromCountryCode('US')
->setToPostalCode('20101')
->setToCountryCode('US')
->setToResidential(true);
$package = new Package;
$package->setLength(12)->setWidth(4)->setHeight(3)->setWeight(3);
$shipment->addPackage($package);
```
## UPS (Stub) Example
@ -78,32 +74,68 @@ $ups_rates = $ups->get_rates();
Output array sorted by cost: (in cents)
```php
array (
0 =>
array (
'code' => '03',
'name' => 'UPS Ground',
'cost' => 1900,
),
1 =>
array (
'code' => '02',
'name' => 'UPS 2nd Day Air',
'cost' => 4900,
),
2 =>
array (
'code' => '13',
'name' => 'UPS Next Day Air Saver',
'cost' => 8900,
),
3 =>
array (
'code' => '01',
'name' => 'UPS Next Day Air',
'cost' => 9300,
),
)
array(4) {
[0] =>
class pdt256\Shipping\Quote#56 (6) {
protected $code =>
string(2) "03"
protected $name =>
string(10) "UPS Ground"
protected $cost =>
int(1900)
protected $transit_time =>
NULL
protected $delivery_ts =>
NULL
protected $carrier =>
string(3) "ups"
}
[1] =>
class pdt256\Shipping\Quote#58 (6) {
protected $code =>
string(2) "02"
protected $name =>
string(15) "UPS 2nd Day Air"
protected $cost =>
int(4900)
protected $transit_time =>
NULL
protected $delivery_ts =>
NULL
protected $carrier =>
string(3) "ups"
}
[2] =>
class pdt256\Shipping\Quote#57 (6) {
protected $code =>
string(2) "13"
protected $name =>
string(22) "UPS Next Day Air Saver"
protected $cost =>
int(8900)
protected $transit_time =>
NULL
protected $delivery_ts =>
NULL
protected $carrier =>
string(3) "ups"
}
[3] =>
class pdt256\Shipping\Quote#55 (6) {
protected $code =>
string(2) "01"
protected $name =>
string(16) "UPS Next Day Air"
protected $cost =>
int(9300)
protected $transit_time =>
NULL
protected $delivery_ts =>
NULL
protected $carrier =>
string(3) "ups"
}
}
```
## USPS (Stub) Example
@ -116,10 +148,7 @@ $usps = new USPS\Rate([
'prod' => FALSE,
'username' => 'XXXX',
'password' => 'XXXX',
'shipment' => array_merge($shipment, [
'size' => 'LARGE',
'container' => 'RECTANGULAR',
]),
'shipment' => $shipment,
'approved_codes' => [
'1', // 1-3 business days
'4', // 2-8 business days
@ -133,20 +162,38 @@ $usps_rates = $usps->get_rates();
Output array sorted by cost: (in cents)
```php
array (
1 =>
array (
'code' => '4',
'name' => 'Parcel Post',
'cost' => 1000,
),
0 =>
array (
'code' => '1',
'name' => 'Priority Mail',
'cost' => 1200,
),
)
array(2) {
[0] =>
class pdt256\Shipping\Quote#30 (6) {
protected $code =>
string(1) "4"
protected $name =>
string(11) "Parcel Post"
protected $cost =>
int(1001)
protected $transit_time =>
NULL
protected $delivery_ts =>
NULL
protected $carrier =>
string(4) "usps"
}
[1] =>
class pdt256\Shipping\Quote#26 (6) {
protected $code =>
string(1) "1"
protected $name =>
string(13) "Priority Mail"
protected $cost =>
int(1220)
protected $transit_time =>
NULL
protected $delivery_ts =>
NULL
protected $carrier =>
string(4) "usps"
}
}
```
## Fedex (Stub) Example
@ -162,9 +209,7 @@ $fedex = new Fedex\Rate([
'account_number' => 'XXXX',
'meter_number' => 'XXXX',
'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
'shipment' => array_merge($shipment, [
'packaging_type' => 'YOUR_PACKAGING',
]),
'shipment' => $shipment,
'approved_codes' => [
'FEDEX_EXPRESS_SAVER', // 1-3 business days
'FEDEX_GROUND', // 1-5 business days
@ -181,40 +226,89 @@ $fedex_rates = $fedex->get_rates();
Output array sorted by cost: (in cents)
```php
array (
3 =>
array (
'code' => 'GROUND_HOME_DELIVERY',
'name' => 'Ground Home Delivery',
'cost' => 1600,
'delivery_ts' => NULL,
'transit_time' => 'THREE_DAYS',
),
2 =>
array (
'code' => 'FEDEX_EXPRESS_SAVER',
'name' => 'Fedex Express Saver',
'cost' => 2900,
'delivery_ts' => '2014-09-30T20:00:00',
'transit_time' => NULL,
),
1 =>
array (
'code' => 'FEDEX_2_DAY',
'name' => 'Fedex 2 Day',
'cost' => 4000,
'delivery_ts' => '2014-09-29T20:00:00',
'transit_time' => NULL,
),
0 =>
array (
'code' => 'STANDARD_OVERNIGHT',
'name' => 'Standard Overnight',
'cost' => 7800,
'delivery_ts' => '2014-09-26T20:00:00',
'transit_time' => NULL,
),
)
array(4) {
[0] =>
class pdt256\Shipping\Quote#65 (6) {
protected $code =>
string(20) "GROUND_HOME_DELIVERY"
protected $name =>
string(20) "Ground Home Delivery"
protected $cost =>
int(1600)
protected $transit_time =>
string(10) "THREE_DAYS"
protected $delivery_ts =>
NULL
protected $carrier =>
string(5) "fedex"
}
[1] =>
class pdt256\Shipping\Quote#63 (6) {
protected $code =>
string(19) "FEDEX_EXPRESS_SAVER"
protected $name =>
string(19) "Fedex Express Saver"
protected $cost =>
int(2900)
protected $transit_time =>
NULL
protected $delivery_ts =>
class Carbon\Carbon#23 (3) {
public $date =>
string(26) "2014-09-30 20:00:00.000000"
public $timezone_type =>
int(3)
public $timezone =>
string(16) "America/New_York"
}
protected $carrier =>
string(5) "fedex"
}
[2] =>
class pdt256\Shipping\Quote#61 (6) {
protected $code =>
string(11) "FEDEX_2_DAY"
protected $name =>
string(11) "Fedex 2 Day"
protected $cost =>
int(4000)
protected $transit_time =>
NULL
protected $delivery_ts =>
class Carbon\Carbon#26 (3) {
public $date =>
string(26) "2014-09-29 20:00:00.000000"
public $timezone_type =>
int(3)
public $timezone =>
string(16) "America/New_York"
}
protected $carrier =>
string(5) "fedex"
}
[3] =>
class pdt256\Shipping\Quote#60 (6) {
protected $code =>
string(18) "STANDARD_OVERNIGHT"
protected $name =>
string(18) "Standard Overnight"
protected $cost =>
int(7800)
protected $transit_time =>
NULL
protected $delivery_ts =>
class Carbon\Carbon#58 (3) {
public $date =>
string(26) "2014-09-26 20:00:00.000000"
public $timezone_type =>
int(3)
public $timezone =>
string(16) "America/New_York"
}
protected $carrier =>
string(5) "fedex"
}
}
```
### License

@ -9,7 +9,6 @@
"email": "pdt256@gmail.com"
}
],
"require": {},
"require-dev": {
"phpunit/phpunit": "4.0.*"
},

@ -1,8 +1,10 @@
<?php
namespace pdt256\Shipping\Fedex;
use DateTime;
use pdt256\Shipping;
use pdt256\Shipping\Arr;
use pdt256\Shipping\Quote;
use pdt256\Shipping\RateAdapter;
use pdt256\Shipping\RateRequest;
use DOMDocument;
@ -88,17 +90,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 +102,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 +149,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>
@ -222,13 +222,18 @@ class Rate extends RateAdapter
->getElementsByTagName('TotalNetCharge')->item(0)
->getElementsByTagName('Amount')->item(0)->nodeValue;
$this->rates[] = array(
'code' => $code,
'name' => $name,
'cost' => (int) ($cost * 100),
'delivery_ts' => $delivery_ts,
'transit_time' => $transit_time,
);
$quote = new Quote;
$quote
->setCarrier('fedex')
->setCode($code)
->setName($name)
->setCost((int) $cost * 100)
->setTransitTime($transit_time);
if ($delivery_ts) {
$quote->setDeliveryEstimate(new DateTime($delivery_ts));
}
$this->rates[] = $quote;
}
return $this;

@ -0,0 +1,81 @@
<?php namespace pdt256\Shipping;
class Package
{
protected $weight;
protected $width;
protected $length;
protected $height;
/**
* @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;
}
}

@ -0,0 +1,123 @@
<?php namespace pdt256\Shipping;
use DateTime;
class Quote
{
protected $code;
protected $name;
protected $cost;
protected $transit_time;
protected $delivery_ts;
protected $carrier;
/**
* @return mixed
*/
public function getCarrier()
{
return $this->carrier;
}
/**
* @param mixed $carrier
* @return $this
*/
public function setCarrier($carrier)
{
$this->carrier = $carrier;
return $this;
}
/**
* @return mixed
*/
public function getCode()
{
return $this->code;
}
/**
* @param string $code
* @return $this
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
* @return $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return mixed
*/
public function getCost()
{
return $this->cost;
}
/**
* Quoted cost of this service, in pennies
*
* @param int $cost
* @return $this
*/
public function setCost($cost)
{
$this->cost = $cost;
return $this;
}
/**
* @return mixed
*/
public function getTransitTime()
{
return $this->transit_time;
}
/**
* @param mixed $transit_time
* @return $this
*/
public function setTransitTime($transit_time)
{
$this->transit_time = $transit_time;
return $this;
}
/**
* @return mixed
*/
public function getDeliveryEstimate()
{
return $this->delivery_ts;
}
/**
* @param DateTime $estimate
* @return $this
*/
public function setDeliveryEstimate(DateTime $estimate)
{
$this->delivery_ts = $estimate;
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)
@ -54,11 +43,11 @@ abstract class RateAdapter
->process()
->sort_by_cost();
return $this->rates;
return array_values($this->rates);
}
protected function sort_by_cost()
{
uasort($this->rates, create_function('$a, $b', 'return ($a["cost"] > $b["cost"]);'));
uasort($this->rates, create_function('$a, $b', 'return ($a->getCost() > $b->getCost());'));
}
}

@ -88,13 +88,13 @@ class Ship
foreach ($rates[$carrier] as $row3) {
if (in_array($row3['code'], $group_codes)) {
$row3['carrier'] = $carrier;
if (in_array($row3->getCode(), $group_codes)) {
$row3->setCarrier($carrier);
if ($cheapest_row === NULL) {
$cheapest_row = $row3;
} else {
if ($row3['cost'] < $cheapest_row['cost']) {
if ($row3->getCost() < $cheapest_row->getCost()) {
$cheapest_row = $row3;
}
}
@ -126,8 +126,8 @@ class Ship
foreach ($rates[$carrier] as $row3) {
if (in_array($row3['code'], $group_codes)) {
$row3['carrier'] = $carrier;
if (in_array($row3->getCode(), $group_codes)) {
$row3->setCarrier($carrier);
$display_rates[$shipping_group][] = $row3;
}
}
@ -142,6 +142,6 @@ class Ship
protected function sort_by_cost( & $rates)
{
uasort($rates, create_function('$a, $b', 'return ($a["cost"] > $b["cost"]);'));
uasort($rates, create_function('$a, $b', 'return ($a->getCost() > $b->getCost());'));
}
}

@ -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;
}
}

@ -3,6 +3,7 @@ namespace pdt256\Shipping\UPS;
use pdt256\Ship;
use pdt256\Shipping\Arr;
use pdt256\Shipping\Quote;
use pdt256\Shipping\RateAdapter;
use pdt256\Shipping\RateRequest;
use DOMDocument;
@ -114,19 +115,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 +155,34 @@ 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>
</Shipment>
</RatingServiceSelectionRequest>';
@ -235,11 +234,13 @@ class Rate extends RateAdapter
continue;
}
$this->rates[] = array(
'code' => $code,
'name' => $name,
'cost' => (int) ($cost * 100),
);
$quote = new Quote;
$quote
->setCarrier('ups')
->setCode($code)
->setName($name)
->setCost((int) $cost * 100);
$this->rates[] = $quote;
}
return $this;

@ -3,6 +3,7 @@ namespace pdt256\Shipping\USPS;
use pdt256\Shipping;
use pdt256\Shipping\Arr;
use pdt256\Shipping\Quote;
use pdt256\Shipping\RateAdapter;
use pdt256\Shipping\RateRequest;
use DOMDocument;
@ -78,6 +79,10 @@ class Rate extends RateAdapter
$this->password = $options['password'];
}
if (isset($options['username'])) {
$this->username = $options['username'];
}
if (isset($options['approved_codes'])) {
$this->approved_codes = $options['approved_codes'];
}
@ -91,50 +96,54 @@ 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');
}
$size = Arr::get($this->shipment, 'size');
// If user has not specified size, determine it automatically
// https://www.usps.com/business/web-tools-apis/rate-calculator-api.htm#_Toc378922331
if ($size === null) {
// Size is considered large if any dimension is larger than 12 inches
foreach ($dimensions as $dimension) {
if ($dimension > 12) {
$size = 'LARGE';
break;
}
}
if (!isset($size)) {
$packages = '';
$sequence_number = 0;
foreach ($this->shipment->getPackages() as $p) {
$sequence_number++;
/**
* RateV4Request / Package / Size
required once
Defined as follows:
REGULAR: Package dimensions are 12 or less;
LARGE: Any package dimension is larger than 12.
For example: <Size>REGULAR</Size>
string
whiteSpace=collapse
enumeration=LARGE
enumeration=REGULAR
*/
if ($p->getWidth() > 12 or $p->getLength() > 12 or $p->getHeight() > 12) {
$size = 'LARGE';
$container = 'RECTANGULAR';
} else {
$size = 'REGULAR';
$container = 'VARIABLE';
}
$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>' . $container . '</Container>
<Size>' . $size . '</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>' . $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;
@ -172,6 +181,9 @@ class Rate extends RateAdapter
throw $e;
}
/** @var Quote[] $rates */
$rates = [];
foreach ($postage_list as $postage) {
$code = @$postage->getAttribute('CLASSID');
$cost = @$postage->getElementsByTagName('Rate')->item(0)->nodeValue;
@ -182,13 +194,24 @@ class Rate extends RateAdapter
continue;
}
$this->rates[] = array(
'code' => $code,
'name' => $name,
'cost' => (int) ($cost * 100),
);
if (array_key_exists($code, $rates)) {
$cost = $rates[$code]->getCost() + ($cost * 100);
} else {
$cost = $cost * 100;
}
$quote = new Quote;
$quote
->setCarrier('usps')
->setCode($code)
->setName($name)
->setCost((int) $cost);
$rates[$quote->getCode()] = $quote;
}
$this->rates = array_values($rates);
return $this;
}
}

@ -1,5 +1,8 @@
<?php
use pdt256\Shipping\Package;
use pdt256\Shipping\Quote;
use pdt256\Shipping\Ship;
use pdt256\Shipping\Shipment;
use pdt256\Shipping\USPS;
use pdt256\Shipping\UPS;
use pdt256\Shipping\Fedex;
@ -7,23 +10,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 +48,27 @@ 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);
$s->addPackage($p);
$this->shipment = $s;
}
private function getUSPSOptions()
{
$ship = Ship::factory($this->shipping_options);
@ -69,10 +78,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(),
];
@ -107,9 +113,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(),
];
@ -120,18 +124,23 @@ class ShipTest extends PHPUnit_Framework_TestCase
$usps = new USPS\Rate($this->getUSPSOptions());
$usps_rates = $usps->get_rates();
$this->assertEquals(json_encode([
1 => [
'code' => '4',
'name' => 'Parcel Post',
'cost' => 1001,
],
0 => [
'code' => '1',
'name' => 'Priority Mail',
'cost' => 1220,
],
]), json_encode($usps_rates));
$post = new Quote;
$post
->setCarrier('usps')
->setCode(4)
->setName('Parcel Post')
->setCost(1001);
$priority = new Quote;
$priority
->setCarrier('usps')
->setCode(1)
->setName('Priority Mail')
->setCost(1220);
$expected_return = [$post, $priority];
$this->assertEquals($expected_return, $usps_rates);
}
public function testUPSRate()
@ -139,28 +148,37 @@ class ShipTest extends PHPUnit_Framework_TestCase
$ups = new UPS\Rate($this->getUPSOptions());
$ups_rates = $ups->get_rates();
$this->assertEquals(json_encode([
0 => [
'code' => '03',
'name' => 'UPS Ground',
'cost' => 1910,
],
1 => [
'code' => '02',
'name' => 'UPS 2nd Day Air',
'cost' => 4923,
],
2 => [
'code' => '13',
'name' => 'UPS Next Day Air Saver',
'cost' => 8954,
],
3 => [
'code' => '01',
'name' => 'UPS Next Day Air',
'cost' => 9328,
],
]), json_encode($ups_rates));
$ground = new Quote;
$ground
->setCarrier('ups')
->setCode('03')
->setName('UPS Ground')
->setCost(1900);
$twodayair = new Quote;
$twodayair
->setCarrier('ups')
->setCode('02')
->setName('UPS 2nd Day Air')
->setCost(4900);
$nextdaysaver = new Quote;
$nextdaysaver
->setCarrier('ups')
->setCode('13')
->setName('UPS Next Day Air Saver')
->setCost(8900);
$nextdayair = new Quote;
$nextdayair
->setCarrier('ups')
->setCode('01')
->setName('UPS Next Day Air')
->setCost(9300);
$expected_return = [$ground, $twodayair, $nextdaysaver, $nextdayair];
$this->assertEquals($expected_return, $ups_rates);
}
public function testFedexRate()
@ -168,36 +186,44 @@ class ShipTest extends PHPUnit_Framework_TestCase
$fedex = new Fedex\Rate($this->getFedexOptions());
$fedex_rates = $fedex->get_rates();
$this->assertEquals(json_encode([
3 => [
'code' => 'GROUND_HOME_DELIVERY',
'name' => 'Ground Home Delivery',
'cost' => 1655,
'delivery_ts' => NULL,
'transit_time' => 'THREE_DAYS',
],
2 => [
'code' => 'FEDEX_EXPRESS_SAVER',
'name' => 'Fedex Express Saver',
'cost' => 2989,
'delivery_ts' => '2014-09-30T20:00:00',
'transit_time' => NULL,
],
1 => [
'code' => 'FEDEX_2_DAY',
'name' => 'Fedex 2 Day',
'cost' => 4072,
'delivery_ts' => '2014-09-29T20:00:00',
'transit_time' => NULL,
],
0 => [
'code' => 'STANDARD_OVERNIGHT',
'name' => 'Standard Overnight',
'cost' => 7834,
'delivery_ts' => '2014-09-26T20:00:00',
'transit_time' => NULL,
],
]), json_encode($fedex_rates));
$ground = new Quote;
$ground
->setCarrier('fedex')
->setCode('GROUND_HOME_DELIVERY')
->setName('Ground Home Delivery')
->setCost(1600)
->setTransitTime('THREE_DAYS');
$express = new Quote;
$express
->setCarrier('fedex')
->setCode('FEDEX_EXPRESS_SAVER')
->setName('Fedex Express Saver')
->setCost(2900)
->setDeliveryEstimate(new DateTime('2014-09-30T20:00:00'))
->setTransitTime(null);
$secondday = new Quote;
$secondday
->setCarrier('fedex')
->setCode('FEDEX_2_DAY')
->setName('Fedex 2 Day')
->setCost(4000)
->setDeliveryEstimate(new DateTime('2014-09-29T20:00:00'))
->setTransitTime(null);
$overnight = new Quote;
$overnight
->setCarrier('fedex')
->setCode('STANDARD_OVERNIGHT')
->setName('Standard Overnight')
->setCost(7800)
->setDeliveryEstimate(new DateTime('2014-09-26T20:00:00'))
->setTransitTime(null);
$expected_result = [$ground, $express, $secondday, $overnight];
$this->assertEquals($expected_result, $fedex_rates);
}
public function testDisplayOptions()
@ -216,210 +242,36 @@ class ShipTest extends PHPUnit_Framework_TestCase
$ship = Ship::factory($this->shipping_options);
$display_rates = $ship->get_display_rates($rates);
$this->assertEquals(json_encode([
$post = new Quote;
$post->setCode(4)
->setName('Parcel Post')
->setCost(1001)
->setCarrier('usps');
$fedex_two_day = new Quote;
$fedex_two_day->setCode('FEDEX_2_DAY')
->setName('Fedex 2 Day')
->setCost(4000)
->setDeliveryEstimate(new DateTime('2014-09-29T20:00:00'))
->setCarrier('fedex');
$overnight = new Quote;
$overnight->setCode('STANDARD_OVERNIGHT')
->setName('Standard Overnight')
->setCost(7800)
->setDeliveryEstimate(new DateTime('2014-09-26T20:00:00'))
->setCarrier('fedex');
$this->assertEquals([
'Standard Shipping' => [
0 => [
'code' => '4',
'name' => 'Parcel Post',
'cost' => 1001,
'carrier' => 'usps',
],
$post,
],
'Two-Day Shipping' => [
0 => [
'code' => 'FEDEX_2_DAY',
'name' => 'Fedex 2 Day',
'cost' => 4072,
'delivery_ts' => '2014-09-29T20:00:00',
'transit_time' => NULL,
'carrier' => 'fedex',
],
$fedex_two_day,
],
'One-Day Shipping' => [
0 => [
'code' => 'STANDARD_OVERNIGHT',
'name' => 'Standard Overnight',
'cost' => 7834,
'delivery_ts' => '2014-09-26T20:00:00',
'transit_time' => NULL,
'carrier' => 'fedex',
],
$overnight,
],
]), 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();
], $display_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()
// {
// $usps = new USPS\Rate([
// 'prod' => FALSE,
// 'username' => 'XXXX',
// 'password' => 'XXXX',
// 'shipment' => array_merge($this->shipment, [
// 'size' => 'LARGE',
// 'container' => 'RECTANGULAR',
// ]),
// 'approved_codes' => [
// '1', // 1-3 business days
// '4', // 2-8 business days
// ],
// 'request_adapter' => new RateRequest\StubUSPS(),
// ]);
//
// $usps_rates = $usps->get_rates();
// var_export($usps_rates);
// }
//
// public function testUPSReadmeExample()
// {
// $ups = new UPS\Rate([
// 'prod' => FALSE,
// 'shipment' => $this->shipment,
// 'approved_codes' => [
// '03', // 1-5 business days
// '02', // 2 business days
// '01', // next business day 10:30am
// '13', // next business day by 3pm
// '14', // next business day by 8am
// ],
// 'request_adapter' => new RateRequest\StubUPS(),
// ]);
//
// $ups_rates = $ups->get_rates();
// var_export($ups_rates);
// }
//
// public function testFedexReadmeExample()
// {
// $fedex = new Fedex\Rate([
// 'prod' => FALSE,
// 'key' => 'XXXX',
// 'password' => 'XXXX',
// 'account_number' => 'XXXX',
// 'meter_number' => 'XXXX',
// 'drop_off_type' => 'BUSINESS_SERVICE_CENTER',
// 'shipment' => array_merge($this->shipment, [
// 'packaging_type' => 'YOUR_PACKAGING',
// ]),
// 'approved_codes' => [
// 'FEDEX_EXPRESS_SAVER', // 1-3 business days
// 'FEDEX_GROUND', // 1-5 business days
// 'GROUND_HOME_DELIVERY', // 1-5 business days
// 'FEDEX_2_DAY', // 2 business days
// 'STANDARD_OVERNIGHT', // overnight
// ],
// 'request_adapter' => new RateRequest\StubFedex(),
// ]);
//
// $fedex_rates = $fedex->get_rates();
// var_export($fedex_rates);
// }
}

Loading…
Cancel
Save