Add UPS shipping driver.
This commit is contained in:
commit
17681a3fb5
6 changed files with 1566 additions and 0 deletions
42
README
Normal file
42
README
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
This repo holds a UPS shipping driver for WebGUI, developed against
|
||||
WebGUI 7.7.8. To install it, you only need to run the install script
|
||||
in the sbin directory, or, manually add "WebGUI::Shop::ShipDriver::UPS"
|
||||
to the shippingDrivers entry in your WebGUI config file.
|
||||
|
||||
Instructions for registering with the UPS are in the Shipping driver
|
||||
edit screen.
|
||||
|
||||
The driver is currently limited to shipping originating in the United
|
||||
States only. Here is what is required to make it handle all available UPS
|
||||
shipping services internationally.
|
||||
|
||||
* The Product needs to be modified to have units for weight. Currently,
|
||||
they are unitless, and likewise, this driver assumes that all weights are in pounds
|
||||
(the current default weight unit in the United States). The chosen unit is added
|
||||
to the PackageWeight XML tag, in buildXML.
|
||||
|
||||
* The driver code itself needs to be extended to add the shipping services.
|
||||
1) Add i18n for the names of the new services, and a shipping type. See
|
||||
the code in the defintion subroutine.
|
||||
2) Add a new key/value pair to the %shippingTypes hash.
|
||||
3) The %shippingServices hash has a unique list of shipping services by code. If you
|
||||
introduce new codes, add the appropriate key/value pairs to the hash. This is
|
||||
required to get the selected code into the generated HTML, regardless of the
|
||||
shippingType chosen.
|
||||
4) There is javascript inside of www/extras/yui-webgui/ShipDriver/UPS.js that will
|
||||
alter the set of shipping services displayed to the user, based on which shipping
|
||||
type they have chosen. It in internationalized.
|
||||
* Inside WebGUI.ShipDriver.UPS.initServiceTables, add a new object to hold codes
|
||||
and i18n labels to describe the services.
|
||||
* Add a new service to the switch (newService) statement. Make sure the new case
|
||||
matches the keys you added to the shippingTypes hash in the definition subroutine.
|
||||
5) Add a new definition property that allows the user to choose the type of currency
|
||||
to use for their shipping rate quote.
|
||||
|
||||
Country names and codes, and currency codes are ISO standards. Country codes are provided
|
||||
by the Locales module, ISO 3166. This module already has translations for 13 languages.
|
||||
For additional languages, they should be added as modules and sent to the Locales module
|
||||
maintainer.
|
||||
|
||||
Currency codes are not currently supported by this driver, nor is there an existing internationalized
|
||||
currency module. This should be a simple extension to Locales.
|
||||
500
lib/WebGUI/Shop/ShipDriver/UPS.pm
Normal file
500
lib/WebGUI/Shop/ShipDriver/UPS.pm
Normal file
|
|
@ -0,0 +1,500 @@
|
|||
package WebGUI::Shop::ShipDriver::UPS;
|
||||
|
||||
use strict;
|
||||
use base qw/WebGUI::Shop::ShipDriver/;
|
||||
use WebGUI::Exception;
|
||||
use XML::Simple;
|
||||
use LWP;
|
||||
use Tie::IxHash;
|
||||
use Locales::Country qw/ en /;
|
||||
use Class::InsideOut qw/ :std /;
|
||||
|
||||
public testMode => my %testMode;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Package WebGUI::Shop::ShipDriver::UPS
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Shipping driver for the United Parcel Service, for US Domestic shipping only.
|
||||
|
||||
The UPS XML interface will only do a lookup for one destination at a time. However,
|
||||
each destination may have multiple packages. This means that if a cart holds packages
|
||||
with multiple destinations, that multiple requests must be sent to the UPS server.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
See the master class, WebGUI::Shop::ShipDriver for information about
|
||||
base methods. These methods are customized in this class:
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 buildXML ( $cart, @packages )
|
||||
|
||||
Returns XML for submitting to the UPS servers
|
||||
|
||||
=head3 $cart
|
||||
|
||||
A WebGUI::Shop::Cart object. This allows us access to the user's
|
||||
address book
|
||||
|
||||
=head3 $packages
|
||||
|
||||
An array reference. Each array element is 1 set of items. The
|
||||
quantity of items will vary in each set. All packages in the set must
|
||||
go to the same zipcode.
|
||||
|
||||
=cut
|
||||
|
||||
sub buildXML {
|
||||
my ($self, $cart, $packages) = @_;
|
||||
#tie my %xmlHash, 'Tie::IxHash';
|
||||
my %xmlHash = (
|
||||
AccessRequest => {},
|
||||
);
|
||||
my $xmlAcc = $xmlHash{AccessRequest};
|
||||
$xmlAcc->{'xml:lang'} = 'en-US';
|
||||
$xmlAcc->{AccessLicenseNumber} = [ $self->get('licenseNo') ];
|
||||
$xmlAcc->{UserId} = [ $self->get('userId') ];
|
||||
$xmlAcc->{Password} = [ $self->get('password') ];
|
||||
my $localizedCountry = Locales::Country->new('en');
|
||||
my $xml = XMLout(\%xmlHash,
|
||||
KeepRoot => 1,
|
||||
NoSort => 1,
|
||||
SuppressEmpty => 0,
|
||||
XMLDecl => 1,
|
||||
);
|
||||
my $destination = $packages->[0]->[0]->getShippingAddress;
|
||||
%xmlHash = (
|
||||
RatingServiceSelectionRequest => {},
|
||||
);
|
||||
my $xmlRate = $xmlHash{RatingServiceSelectionRequest };
|
||||
$xmlRate->{'xml:lang'} = 'en-US';
|
||||
$xmlRate->{Request} = {
|
||||
# Shown in example request, but optional
|
||||
# TransactionReference => {
|
||||
# CustomerContext => [ 'Rating and Service' ],
|
||||
# XpciVersion => [ 1.0001 ],
|
||||
# },
|
||||
RequestAction => [ 'Rate' ],
|
||||
# RequestOption => [ 'shop' ],
|
||||
};
|
||||
$xmlRate->{PickupType} = {
|
||||
Code => [ $self->get('pickupCode') ],
|
||||
};
|
||||
$xmlRate->{Shipment} = {
|
||||
Shipper => {
|
||||
Address => [ {
|
||||
PostalCode => [ $self->get('sourceZip' ) ],
|
||||
CountryCode => [ $localizedCountry->country2code($self->get('sourceCountry'), 'alpha2') ],
|
||||
}, ],
|
||||
},
|
||||
ShipTo => {
|
||||
Address => [ {
|
||||
PostalCode => [ $destination->get('code') ],
|
||||
CountryCode => [ $localizedCountry->country2code($destination->get('country'), 'alpha2') ],
|
||||
} ],
|
||||
},
|
||||
Service => {
|
||||
Code => [ $self->get('shipService') ],
|
||||
},
|
||||
Package => [],
|
||||
};
|
||||
my $packHash = $xmlRate->{Shipment}->{Package};
|
||||
PACKAGE: foreach my $package (@{ $packages }) {
|
||||
my $weight = 0;
|
||||
ITEM: foreach my $item (@{ $package }) {
|
||||
my $sku = $item->getSku();
|
||||
next ITEM unless $sku->isShippingRequired;
|
||||
##If shipsSeparately is set, the item was placed N times in the shippingBundles,
|
||||
##where N is the quantity. This means that the quantity is wrong for
|
||||
##any item where that option is set.
|
||||
my $skuWeight = $sku->getWeight;
|
||||
if (! $sku->shipsSeparately() ) {
|
||||
$skuWeight *= $item->get('quantity');
|
||||
}
|
||||
$weight += $skuWeight;
|
||||
}
|
||||
next PACKAGE unless $weight;
|
||||
my $options = {
|
||||
PackagingType => [ {
|
||||
Code => [ '02' ],
|
||||
} ],
|
||||
PackageWeight => [ {
|
||||
Weight => [ sprintf "%.1f", $weight ], ##Required formatting from spec
|
||||
} ],
|
||||
};
|
||||
push @{ $packHash }, $options;
|
||||
}
|
||||
return '' unless scalar @{ $packHash }; ##Nothing to calculate shipping for.
|
||||
$xml .= XMLout(\%xmlHash,
|
||||
KeepRoot => 1,
|
||||
NoSort => 1,
|
||||
SuppressEmpty => 0,
|
||||
XMLDecl => 1,
|
||||
);
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 calculate ( $cart )
|
||||
|
||||
Returns a shipping price. Since the UPS will only allow a lookup from one source
|
||||
to one destination at a time, this method may make several XML requests from the
|
||||
UPS server.
|
||||
|
||||
=head3 $cart
|
||||
|
||||
A WebGUI::Shop::Cart object. The contents of the cart are analyzed to calculate
|
||||
the shipping costs. If no items in the cart require shipping, then no shipping
|
||||
costs are assessed.
|
||||
|
||||
=cut
|
||||
|
||||
sub calculate {
|
||||
my ($self, $cart) = @_;
|
||||
if (! $self->get('sourceZip')) {
|
||||
WebGUI::Error::InvalidParam->throw(error => q{Driver configured without a source zipcode.});
|
||||
}
|
||||
if (! $self->get('sourceCountry')) {
|
||||
WebGUI::Error::InvalidParam->throw(error => q{Driver configured without a source country.});
|
||||
}
|
||||
if (! $self->get('userId')) {
|
||||
WebGUI::Error::InvalidParam->throw(error => q{Driver configured without a UPS userId.});
|
||||
}
|
||||
if (! $self->get('password')) {
|
||||
WebGUI::Error::InvalidParam->throw(error => q{Driver configured without a UPS password.});
|
||||
}
|
||||
if (! $self->get('licenseNo')) {
|
||||
WebGUI::Error::InvalidParam->throw(error => q{Driver configured without a UPS license number.});
|
||||
}
|
||||
my $cost = 0;
|
||||
##Sort the items into shippable bundles.
|
||||
my @shippableUnits = $self->_getShippableUnits($cart);
|
||||
my $packageCount = scalar @shippableUnits;
|
||||
my $anyShippable = $packageCount > 0 ? 1 : 0;
|
||||
return $cost unless $anyShippable;
|
||||
#$cost = scalar @shippableUnits * $self->get('flatFee');
|
||||
##Build XML ($cart, @shippableUnits)
|
||||
foreach my $unit (@shippableUnits) {
|
||||
if ($packageCount > 200) {
|
||||
WebGUI::Error::InvalidParam->throw(error => q{Cannot do UPS lookups for more than 200 items.});
|
||||
}
|
||||
my $xml = $self->buildXML($cart, $unit);
|
||||
##Do request ($xml)
|
||||
my $response = $self->_doXmlRequest($xml);
|
||||
##Error handling
|
||||
if (! $response->is_success) {
|
||||
WebGUI::Error::RemoteShippingRate->throw(error => 'Problem connecting to UPS Web Tools: '. $response->status_line);
|
||||
}
|
||||
my $returnedXML = $response->content;
|
||||
my $xmlData = XMLin($returnedXML, ForceArray => [qw/RatedPackage/]);
|
||||
if (! $xmlData->{Response}->{ResponseStatusCode}) {
|
||||
WebGUI::Error::RemoteShippingRate->throw(error => 'Problem with UPS Online Tools XML: '. $xmlData->{Response}->{Error}->{ErrorDescription});
|
||||
}
|
||||
##Summarize costs from returned data
|
||||
$cost += $self->_calculateFromXML($xmlData);
|
||||
}
|
||||
return $cost;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 _calculateFromXML ( $xmlData )
|
||||
|
||||
Takes data from the UPS and returns the calculated shipping price.
|
||||
|
||||
=head3 $xmlData
|
||||
|
||||
Processed data from an XML rate request, as a perl data structure. The data is expected to
|
||||
have this structure:
|
||||
|
||||
{
|
||||
RatedShipment => {
|
||||
TotalCharges => {
|
||||
MonetaryValue => xx.yy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=cut
|
||||
|
||||
sub _calculateFromXML {
|
||||
my ($self, $xmlData) = @_;
|
||||
##Additional error checking on the XML data can be done in here. Or, in the future,
|
||||
##individual elements of the cost can be parsed and returned.
|
||||
return $xmlData->{RatedShipment}->{TotalCharges}->{MonetaryValue};
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 definition ( $session )
|
||||
|
||||
This subroutine returns an arrayref of hashrefs, used to validate data put into
|
||||
the object by the user, and to automatically generate the edit form to show
|
||||
the user.
|
||||
|
||||
=cut
|
||||
|
||||
sub definition {
|
||||
my $class = shift;
|
||||
my $session = shift;
|
||||
WebGUI::Error::InvalidParam->throw(error => q{Must provide a session variable})
|
||||
unless ref $session eq 'WebGUI::Session';
|
||||
my $definition = shift || [];
|
||||
my $i18n = WebGUI::International->new($session, 'ShipDriver_UPS');
|
||||
|
||||
tie my %shippingTypes, 'Tie::IxHash';
|
||||
##Other shipping types can be added below, but also need to be handled by the
|
||||
##javascript.
|
||||
$shippingTypes{'us domestic'} = $i18n->get('us domestic');
|
||||
$shippingTypes{'us international'} = $i18n->get('us international');
|
||||
|
||||
tie my %shippingServices, 'Tie::IxHash';
|
||||
##Note, these keys are required XML keywords in the UPS XML API.
|
||||
##It needs a one of every key, regardless of the correct label.
|
||||
##The right set of options is set via JavaScript in the form.
|
||||
$shippingServices{'01'} = $i18n->get('us domestic 01');
|
||||
$shippingServices{'02'} = $i18n->get('us domestic 02');
|
||||
$shippingServices{'03'} = $i18n->get('us domestic 03');
|
||||
$shippingServices{'07'} = $i18n->get('us international 07');
|
||||
$shippingServices{'08'} = $i18n->get('us international 08');
|
||||
$shippingServices{'11'} = $i18n->get('us international 11');
|
||||
$shippingServices{'12'} = $i18n->get('us domestic 12');
|
||||
$shippingServices{'13'} = $i18n->get('us domestic 13');
|
||||
$shippingServices{'14'} = $i18n->get('us domestic 14');
|
||||
$shippingServices{'54'} = $i18n->get('us international 54');
|
||||
$shippingServices{'59'} = $i18n->get('us domestic 59');
|
||||
$shippingServices{'65'} = $i18n->get('us international 65');
|
||||
|
||||
tie my %pickupTypes, 'Tie::IxHash';
|
||||
##Note, these keys are required XML keywords in the UPS XML API.
|
||||
$pickupTypes{'01'} = $i18n->get('pickup code 01');
|
||||
$pickupTypes{'03'} = $i18n->get('pickup code 03');
|
||||
$pickupTypes{'06'} = $i18n->get('pickup code 06');
|
||||
$pickupTypes{'07'} = $i18n->get('pickup code 07');
|
||||
$pickupTypes{'11'} = $i18n->get('pickup code 11');
|
||||
$pickupTypes{'19'} = $i18n->get('pickup code 19');
|
||||
$pickupTypes{'20'} = $i18n->get('pickup code 20');
|
||||
|
||||
my $localizedCountries = Locales::Country->new('en'); ##Note, for future i18n change the locale
|
||||
tie my %localizedCountries, 'Tie::IxHash';
|
||||
%localizedCountries = map { $_ => $_ } grep { !ref $_ } $localizedCountries->all_country_names();
|
||||
|
||||
tie my %fields, 'Tie::IxHash';
|
||||
%fields = (
|
||||
instructions => {
|
||||
fieldType => 'readOnly',
|
||||
label => $i18n->get('instructions'),
|
||||
defaultValue => $i18n->get('ups instructions'),
|
||||
noFormProcess => 1,
|
||||
},
|
||||
userId => {
|
||||
fieldType => 'text',
|
||||
label => $i18n->get('userid'),
|
||||
hoverHelp => $i18n->get('userid help'),
|
||||
defaultValue => '',
|
||||
},
|
||||
password => {
|
||||
fieldType => 'password',
|
||||
label => $i18n->get('password'),
|
||||
hoverHelp => $i18n->get('password help'),
|
||||
defaultValue => '',
|
||||
},
|
||||
licenseNo => {
|
||||
fieldType => 'text',
|
||||
label => $i18n->get('license'),
|
||||
hoverHelp => $i18n->get('license help'),
|
||||
defaultValue => '',
|
||||
},
|
||||
sourceZip => {
|
||||
fieldType => 'zipcode',
|
||||
label => $i18n->get('source zipcode'),
|
||||
hoverHelp => $i18n->get('source zipcode help'),
|
||||
defaultValue => '',
|
||||
},
|
||||
sourceCountry => {
|
||||
fieldType => 'selectBox',
|
||||
label => $i18n->get('source country'),
|
||||
hoverHelp => $i18n->get('source country help'),
|
||||
options => \%localizedCountries,
|
||||
defaultValue => 'US',
|
||||
},
|
||||
shipType => {
|
||||
fieldType => 'selectBox',
|
||||
label => $i18n->get('ship type'),
|
||||
hoverHelp => $i18n->get('ship type help'),
|
||||
options => \%shippingTypes,
|
||||
defaultValue => 'us domestic',
|
||||
extras => q{onchange="WebGUI.ShipDriver.UPS.changeServices(this.options[this.selectedIndex].value,'shipService_formId')"},
|
||||
},
|
||||
shipService => {
|
||||
fieldType => 'selectBox',
|
||||
label => $i18n->get('ship service'),
|
||||
hoverHelp => $i18n->get('ship service help'),
|
||||
options => \%shippingServices,
|
||||
defaultValue => '03',
|
||||
},
|
||||
pickupType => {
|
||||
fieldType => 'selectBox',
|
||||
label => $i18n->get('pickup type'),
|
||||
hoverHelp => $i18n->get('pickup type help'),
|
||||
options => \%pickupTypes,
|
||||
defaultValue => '01',
|
||||
},
|
||||
##Note, if a flat fee is added to this driver, then according to the license
|
||||
##terms the website must display a note to the user (shop customer) that additional
|
||||
##fees have been added.
|
||||
# flatFee => {
|
||||
# fieldType => 'float',
|
||||
# label => $i18n->get('flatFee'),
|
||||
# hoverHelp => $i18n->get('flatFee help'),
|
||||
# defaultValue => 0,
|
||||
# },
|
||||
);
|
||||
my %properties = (
|
||||
name => 'UPS',
|
||||
properties => \%fields,
|
||||
);
|
||||
push @{ $definition }, \%properties;
|
||||
return $class->SUPER::definition($session, $definition);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 _doXmlRequest ( $xml )
|
||||
|
||||
Contact the UPS website and submit the XML for a shipping rate lookup.
|
||||
Returns a LWP::UserAgent response object.
|
||||
|
||||
=head3 $xml
|
||||
|
||||
XML to send. It has some very high standards, including XML components in
|
||||
the right order and sets of allowed tags.
|
||||
|
||||
=cut
|
||||
|
||||
sub _doXmlRequest {
|
||||
my ($self, $xml) = @_;
|
||||
my $userAgent = LWP::UserAgent->new;
|
||||
$userAgent->env_proxy;
|
||||
$userAgent->agent('WebGUI');
|
||||
#
|
||||
my $url;
|
||||
if ($self->testMode) {
|
||||
$url = 'https://wwwcie.ups.com/ups.app/xml/Rate';
|
||||
}
|
||||
else {
|
||||
$url = 'https://wwwcie.ups.com/ups.app/xml/Rate';
|
||||
}
|
||||
my $request = HTTP::Request->new(POST => $url);
|
||||
$request->content_type( 'text/xml' );
|
||||
$request->content( $xml );
|
||||
|
||||
my $response = $userAgent->request($request);
|
||||
return $response;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getEditForm ( )
|
||||
|
||||
Override the master method to stuff in some javascript.
|
||||
|
||||
=cut
|
||||
|
||||
sub getEditForm {
|
||||
my $self = shift;
|
||||
$self->session->style->setScript(
|
||||
$self->session->url->extras('yui/build/utilities/utilities.js'),
|
||||
{ type => 'text/javascript', },
|
||||
);
|
||||
$self->session->style->setScript(
|
||||
$self->session->url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'),
|
||||
{ type => 'text/javascript', },
|
||||
);
|
||||
$self->session->style->setScript(
|
||||
$self->session->url->extras('yui/build/json/json-min.js'),
|
||||
{ type => 'text/javascript', },
|
||||
);
|
||||
$self->session->style->setScript(
|
||||
$self->session->url->extras('yui-webgui/build/i18n/i18n.js'),
|
||||
{ type => 'text/javascript', },
|
||||
);
|
||||
$self->session->style->setScript(
|
||||
$self->session->url->extras('yui-webgui/build/ShipDriver/UPS.js'),
|
||||
{ type => 'text/javascript', },
|
||||
);
|
||||
$self->session->style->setRawHeadTags(<<EOL);
|
||||
<script type="text/javascript">
|
||||
YAHOO.util.Event.onDOMReady( WebGUI.ShipDriver.UPS.initI18n );
|
||||
</script>
|
||||
EOL
|
||||
return $self->SUPER::getEditForm();
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 _getShippableUnits ( $cart )
|
||||
|
||||
This is a private method.
|
||||
|
||||
Sorts items into the cart by how they must be shipped; together, separate,
|
||||
etc, following these rules:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Each item which ships separately is 1 shippable unit.
|
||||
|
||||
=item *
|
||||
|
||||
All loose items are bundled together by zip code.
|
||||
|
||||
=back
|
||||
|
||||
This method returns a
|
||||
|
||||
For an empty cart (which shouldn't ever happen), it would return an empty array.
|
||||
|
||||
=head3 $cart
|
||||
|
||||
A WebGUI::Shop::Cart object. It provides access to the items in the cart
|
||||
that must be sorted.
|
||||
|
||||
=cut
|
||||
|
||||
sub _getShippableUnits {
|
||||
my ($self, $cart) = @_;
|
||||
##All units sorted by zip code. Loose units kept separately so they
|
||||
##can be easily bundled together by zip code.
|
||||
my %shippableUnits = ();
|
||||
my %looseUnits = ();
|
||||
ITEM: foreach my $item (@{$cart->getItems}) {
|
||||
my $sku = $item->getSku;
|
||||
next ITEM unless $sku->isShippingRequired;
|
||||
my $zip = $item->getShippingAddress->get('code');
|
||||
if ($sku->shipsSeparately) {
|
||||
push @{ $shippableUnits{$zip} }, ( [ $item ] ) x $item->get('quantity');
|
||||
}
|
||||
else {
|
||||
push @{ $looseUnits{$zip} }, $item;
|
||||
}
|
||||
}
|
||||
##Merge the two together now
|
||||
while (my ($zip, $units) = each %looseUnits) {
|
||||
push @{ $shippableUnits{$zip} }, $units;
|
||||
}
|
||||
return values %shippableUnits;
|
||||
}
|
||||
|
||||
1;
|
||||
246
lib/WebGUI/i18n/English/ShipDriver_UPS.pm
Normal file
246
lib/WebGUI/i18n/English/ShipDriver_UPS.pm
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
package WebGUI::i18n::English::ShipDriver_UPS;
|
||||
|
||||
use strict;
|
||||
|
||||
our $I18N = {
|
||||
|
||||
'userid' => {
|
||||
message => q|UPS UserId|,
|
||||
lastUpdated => 1203569535,
|
||||
context => q|Label in the ShipDriver edit form.|,
|
||||
},
|
||||
|
||||
'userid help' => {
|
||||
message => q|You can get a UserId by first registering with the UPS.|,
|
||||
lastUpdated => 1203569511,
|
||||
},
|
||||
|
||||
'password' => {
|
||||
message => q|UPS Password|,
|
||||
lastUpdated => 1203569535,
|
||||
context => q|Label in the ShipDriver edit form.|,
|
||||
},
|
||||
|
||||
'password help' => {
|
||||
message => q|You will recieve a password along with your UserId when you register.|,
|
||||
lastUpdated => 1203569511,
|
||||
},
|
||||
|
||||
'license' => {
|
||||
message => q|UPS Access License Number|,
|
||||
lastUpdated => 1203569535,
|
||||
context => q|Label in the ShipDriver edit form.|,
|
||||
},
|
||||
|
||||
'license help' => {
|
||||
message => q|You will recieve a license along with your UserId and password when you register.|,
|
||||
lastUpdated => 1203569511,
|
||||
},
|
||||
|
||||
'instructions' => {
|
||||
message => q|Registration Instructions|,
|
||||
lastUpdated => 1203569535,
|
||||
context => q|Label in the ShipDriver edit form.|,
|
||||
},
|
||||
|
||||
'ups instructions' => {
|
||||
lastUpdated => 1241028258,
|
||||
message => q|<p>In order to use the UPS Shipping Driver, you must first register with the UPS on their <a href="http://www.ups.com/e_comm_access/gettools_index?loc=en_US">website</a>. When you get to the step for an access key, be sure to get an XML access key. Enter your UPS username and password and access key into the form.</p><p>The driver currently supports domestic and international shipping from the United States. The weight property of a Product is considered to be in pounds. All currencies are in United States dollars. The package for shipping is a generic package, and there are no options for tubes, envelopes or fixed size packages available from the UPS.</p>|,
|
||||
},
|
||||
|
||||
'ship service' => {
|
||||
message => q|Shipping service|,
|
||||
lastUpdated => 1203569535,
|
||||
context => q|Label in the ShipDriver edit form.|,
|
||||
},
|
||||
|
||||
'ship service help' => {
|
||||
message => q|Select one from the list of options. If you wish to provide multiple types of shipping, create one additional shipping driver instance for each option.|,
|
||||
lastUpdated => 1203569511,
|
||||
},
|
||||
|
||||
'pickup type' => {
|
||||
message => q|Pickup Type|,
|
||||
lastUpdated => 1243006539,
|
||||
context => q|Label in the ShipDriver edit form.|,
|
||||
},
|
||||
|
||||
'pickup type help' => {
|
||||
message => q|Select how the packages will be delivered to the UPS for shipping.|,
|
||||
lastUpdated => 1203569511,
|
||||
},
|
||||
|
||||
'source zipcode' => {
|
||||
message => q|Shipping Zipcode|,
|
||||
lastUpdated => 1203569535,
|
||||
context => q|Label in the ShipDriver edit form.|,
|
||||
},
|
||||
|
||||
'source zipcode help' => {
|
||||
message => q|The zipcode of the location you will be shipping from.|,
|
||||
lastUpdated => 1203569511,
|
||||
},
|
||||
|
||||
'source country' => {
|
||||
message => q|Shipping Country|,
|
||||
lastUpdated => 1242945847,
|
||||
context => q|Label in the ShipDriver edit form.|,
|
||||
},
|
||||
|
||||
'source country help' => {
|
||||
message => q|The country you will be shipping from.|,
|
||||
lastUpdated => 1242945844,
|
||||
},
|
||||
|
||||
'flatFee' => {
|
||||
message => q|Flat Fee|,
|
||||
lastUpdated => 1241214572,
|
||||
context => q|A fixed amount of money added to a purchase for shipping.|,
|
||||
},
|
||||
|
||||
'flatFee help' => {
|
||||
message => q|A fixed amount of money added to a purchase for shipping, covering shipping materials and handling.|,
|
||||
lastUpdated => 1241214575,
|
||||
},
|
||||
|
||||
'us domestic 01' => {
|
||||
message => q|UPS Next Day Air|,
|
||||
lastUpdated => 1203569511,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us domestic 02' => {
|
||||
message => q|UPS Second Day Air|,
|
||||
lastUpdated => 1203569511,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us domestic 03' => {
|
||||
message => q|UPS Ground|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us domestic 12' => {
|
||||
message => q|UPS Three-Day Select|,
|
||||
lastUpdated => 1203569511,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us domestic 13' => {
|
||||
message => q|UPS Next Day Air Saver|,
|
||||
lastUpdated => 1203569511,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us domestic 14' => {
|
||||
message => q|UPS Next Day Air Early A.M.|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us domestic 59' => {
|
||||
message => q|UPS Second Day Air A.M.|,
|
||||
lastUpdated => 1203569511,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'pickup code 01' => {
|
||||
message => q|Daily Pickup|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'pickup code 03' => {
|
||||
message => q|Customer Counter|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'pickup code 06' => {
|
||||
message => q|One Time Pickup|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'pickup code 07' => {
|
||||
message => q|On Call Air|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'pickup code 11' => {
|
||||
message => q|Suggested Retail Rates|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'pickup code 19' => {
|
||||
message => q|Letter Center|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'pickup code 20' => {
|
||||
message => q|Air Service Center|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us domestic' => {
|
||||
message => q|US Domestic|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us international' => {
|
||||
message => q|US International|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'ship type' => {
|
||||
message => q|Shipping Type|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'ship type help' => {
|
||||
message => q|Pick a type of shipping that will be used. The different types have different services available. Not all services are available in all types, or in all countries.|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us international 07' => {
|
||||
message => q|UPS Worldwide Express|,
|
||||
lastUpdated => 1203569511,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us international 08' => {
|
||||
message => q|UPS Worldwide Expedited|,
|
||||
lastUpdated => 1203569511,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us international 11' => {
|
||||
message => q|UPS Standard|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us international 54' => {
|
||||
message => q|UPS Worldwide Express Plus|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
'us international 65' => {
|
||||
message => q|UPS Saver|,
|
||||
lastUpdated => 1242166045,
|
||||
context => q|Label for a type of shipping from the UPS.|,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
1;
|
||||
181
sbin/installUPSDriver.pl
Normal file
181
sbin/installUPSDriver.pl
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Copyright 2009 Plain Black
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
$|++; # disable output buffering
|
||||
our ($webguiRoot, $configFile, $help, $man);
|
||||
|
||||
BEGIN {
|
||||
$webguiRoot = "..";
|
||||
unshift (@INC, $webguiRoot."/lib");
|
||||
}
|
||||
|
||||
use strict;
|
||||
use Pod::Usage;
|
||||
use Getopt::Long;
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Utility;
|
||||
|
||||
my $quiet = 0;
|
||||
|
||||
# Get parameters here, including $help
|
||||
GetOptions(
|
||||
'configFile=s' => \$configFile,
|
||||
'help' => \$help,
|
||||
'man' => \$man,
|
||||
);
|
||||
|
||||
pod2usage( verbose => 1 ) if $help;
|
||||
pod2usage( verbose => 2 ) if $man;
|
||||
pod2usage( msg => "Must specify a config file!" ) unless $configFile;
|
||||
|
||||
my $session = start( $webguiRoot, $configFile );
|
||||
|
||||
installFilePumpHandler($session);
|
||||
|
||||
# Do your work here
|
||||
finish($session);
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Your sub here
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
sub installFilePumpHandler {
|
||||
my $session = shift;
|
||||
print "\tAdding UPS Shipping Driver... \n" unless $quiet;
|
||||
$session->config->addToArray('shippingDrivers', 'WebGUI::Shop::ShipDriver::UPS');
|
||||
|
||||
print "Done.\n" unless $quiet;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
sub start {
|
||||
my $webguiRoot = shift;
|
||||
my $configFile = shift;
|
||||
my $session = WebGUI::Session->open($webguiRoot,$configFile);
|
||||
$session->user({userId=>3});
|
||||
|
||||
## If your script is adding or changing content you need these lines, otherwise leave them commented
|
||||
#
|
||||
# my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
# $versionTag->set({name => 'Name Your Tag'});
|
||||
#
|
||||
##
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
sub finish {
|
||||
my $session = shift;
|
||||
|
||||
## If your script is adding or changing content you need these lines, otherwise leave them commented
|
||||
#
|
||||
# my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
# $versionTag->commit;
|
||||
##
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->commit;
|
||||
|
||||
$session->var->end;
|
||||
$session->close;
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub updateTemplates {
|
||||
my $session = shift;
|
||||
my $packageDir = "message_center_packages";
|
||||
return undef unless (-d $packageDir);
|
||||
print "\tUpdating packages.\n";
|
||||
opendir(DIR,$packageDir);
|
||||
my @files = readdir(DIR);
|
||||
closedir(DIR);
|
||||
my $newFolder = undef;
|
||||
foreach my $file (@files) {
|
||||
next unless ($file =~ /\.wgpkg$/);
|
||||
# Fix the filename to include a path
|
||||
$file = $packageDir . "/" . $file;
|
||||
addPackage( $session, $file );
|
||||
}
|
||||
}
|
||||
|
||||
sub addPackage {
|
||||
my $session = shift;
|
||||
my $file = shift;
|
||||
|
||||
# Make a storage location for the package
|
||||
my $storage = WebGUI::Storage->createTemp( $session );
|
||||
$storage->addFileFromFilesystem( $file );
|
||||
|
||||
# Import the package into the import node
|
||||
my $package = WebGUI::Asset->getImportNode($session)->importPackage( $storage );
|
||||
|
||||
# Make the package not a package anymore
|
||||
$package->update({ isPackage => 0 });
|
||||
|
||||
# Set the default flag for templates added
|
||||
my $assetIds
|
||||
= $package->getLineage( ['self','descendants'], {
|
||||
includeOnlyClasses => [ 'WebGUI::Asset::Template' ],
|
||||
} );
|
||||
for my $assetId ( @{ $assetIds } ) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
|
||||
if ( !$asset ) {
|
||||
print "Couldn't instantiate asset with ID '$assetId'. Please check package '$file' for corruption.\n";
|
||||
next;
|
||||
}
|
||||
$asset->update( { isDefault => 1 } );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
__END__
|
||||
|
||||
|
||||
=head1 NAME
|
||||
|
||||
utility - A template for WebGUI utility scripts
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
utility --configFile config.conf ...
|
||||
|
||||
utility --help
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This WebGUI utility script helps you...
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item B<--configFile config.conf>
|
||||
|
||||
The WebGUI config file to use. Only the file name needs to be specified,
|
||||
since it will be looked up inside WebGUI's configuration directory.
|
||||
This parameter is required.
|
||||
|
||||
=item B<--help>
|
||||
|
||||
Shows a short summary and usage
|
||||
|
||||
=item B<--man>
|
||||
|
||||
Shows this document
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Copyright 2009 SDH Consulting Group
|
||||
|
||||
=cut
|
||||
|
||||
#vim:ft=perl
|
||||
495
t/Shop/ShipDriver/UPS.t
Normal file
495
t/Shop/ShipDriver/UPS.t
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
# vim:syntax=perl
|
||||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#------------------------------------------------------------------
|
||||
|
||||
# Write a little about what this script tests.
|
||||
#
|
||||
#
|
||||
|
||||
use FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../../lib";
|
||||
use Test::More;
|
||||
use Test::Deep;
|
||||
use XML::Simple;
|
||||
use Data::Dumper;
|
||||
use Locales::Country qw/en/;
|
||||
|
||||
use WebGUI::Test; # Must use this before any other WebGUI modules
|
||||
use WebGUI::Session;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Init
|
||||
my $session = WebGUI::Test->session;
|
||||
my $user = WebGUI::User->create($session);
|
||||
WebGUI::Test->usersToDelete($user);
|
||||
$session->user({user => $user});
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Tests
|
||||
|
||||
my $tests = 37;
|
||||
plan tests => 1 + $tests;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# put your tests here
|
||||
|
||||
my $loaded = use_ok('WebGUI::Shop::ShipDriver::UPS');
|
||||
|
||||
my $storage;
|
||||
my ($driver, $cart);
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
|
||||
my $home = WebGUI::Asset->getDefault($session);
|
||||
|
||||
my $rockHammer = $home->addChild({
|
||||
className => 'WebGUI::Asset::Sku::Product',
|
||||
isShippingRequired => 1, title => 'Rock Hammers',
|
||||
shipsSeparately => 0,
|
||||
});
|
||||
|
||||
my $smallHammer = $rockHammer->setCollateral('variantsJSON', 'variantId', 'new',
|
||||
{
|
||||
shortdesc => 'Small rock hammer', price => 7.50,
|
||||
varSku => 'small-hammer', weight => 1.5,
|
||||
quantity => 9999,
|
||||
}
|
||||
);
|
||||
|
||||
my $bigHammer = $rockHammer->setCollateral('variantsJSON', 'variantId', 'new',
|
||||
{
|
||||
shortdesc => 'Big rock hammer', price => 19.99,
|
||||
varSku => 'big-hammer', weight => 12,
|
||||
quantity => 9999,
|
||||
}
|
||||
);
|
||||
|
||||
my $bible = $home->addChild({
|
||||
className => 'WebGUI::Asset::Sku::Product',
|
||||
isShippingRequired => 1, title => 'Bibles, individuall wrapped and shipped',
|
||||
shipsSeparately => 1,
|
||||
});
|
||||
|
||||
my $kjvBible = $bible->setCollateral('variantsJSON', 'variantId', 'new',
|
||||
{
|
||||
shortdesc => 'King James Bible', price => 17.50,
|
||||
varSku => 'kjv-bible', weight => 2.5,
|
||||
quantity => 99999,
|
||||
}
|
||||
);
|
||||
|
||||
my $nivBible = $bible->setCollateral('variantsJSON', 'variantId', 'new',
|
||||
{
|
||||
shortdesc => 'NIV Bible', price => 22.50,
|
||||
varSku => 'niv-bible', weight => 2.0,
|
||||
quantity => 999999,
|
||||
}
|
||||
);
|
||||
|
||||
$versionTag->commit;
|
||||
WebGUI::Test->tagsToRollback($versionTag);
|
||||
|
||||
SKIP: {
|
||||
|
||||
skip 'Unable to load module WebGUI::Shop::ShipDriver::UPS', $tests unless $loaded;
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# definition
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
my $definition;
|
||||
my $e; ##Exception variable, used throughout the file
|
||||
|
||||
eval { $definition = WebGUI::Shop::ShipDriver::UPS->definition(); };
|
||||
$e = Exception::Class->caught();
|
||||
isa_ok($e, 'WebGUI::Error::InvalidParam', 'definition takes an exception to not giving it a session variable');
|
||||
cmp_deeply(
|
||||
$e,
|
||||
methods(
|
||||
error => 'Must provide a session variable',
|
||||
),
|
||||
'... checking error message',
|
||||
);
|
||||
|
||||
|
||||
isa_ok(
|
||||
$definition = WebGUI::Shop::ShipDriver::UPS->definition($session),
|
||||
'ARRAY'
|
||||
);
|
||||
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# create
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
my $options = {
|
||||
label => 'UPS Driver',
|
||||
enabled => 1,
|
||||
};
|
||||
|
||||
$driver = WebGUI::Shop::ShipDriver::UPS->create($session, $options);
|
||||
|
||||
isa_ok($driver, 'WebGUI::Shop::ShipDriver::UPS');
|
||||
isa_ok($driver, 'WebGUI::Shop::ShipDriver');
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# getName
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
is (WebGUI::Shop::ShipDriver::UPS->getName($session), 'UPS', 'getName returns the human readable name of this driver');
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# delete
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
my $driverId = $driver->getId;
|
||||
$driver->delete;
|
||||
|
||||
my $count = $session->db->quickScalar('select count(*) from shipper where shipperId=?',[$driverId]);
|
||||
is($count, 0, 'delete deleted the object');
|
||||
|
||||
undef $driver;
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# calculate, and private methods.
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
$driver = WebGUI::Shop::ShipDriver::UPS->create($session, {
|
||||
label => 'Shipping from Shawshank',
|
||||
enabled => 1,
|
||||
shipType => 'PARCEL',
|
||||
});
|
||||
|
||||
eval { $driver->calculate() };
|
||||
$e = Exception::Class->caught();
|
||||
isa_ok($e, 'WebGUI::Error::InvalidParam', 'calculate throws an exception when no zipcode has been set');
|
||||
cmp_deeply(
|
||||
$e,
|
||||
methods(
|
||||
error => 'Driver configured without a source zipcode.',
|
||||
),
|
||||
'... checking error message',
|
||||
);
|
||||
|
||||
my $properties = $driver->get();
|
||||
$properties->{sourceZip} = '97123';
|
||||
$driver->update($properties);
|
||||
eval { $driver->calculate() };
|
||||
$e = Exception::Class->caught();
|
||||
isa_ok($e, 'WebGUI::Error::InvalidParam', 'calculate throws an exception when no source country');
|
||||
cmp_deeply(
|
||||
$e,
|
||||
methods(
|
||||
error => 'Driver configured without a source country.',
|
||||
),
|
||||
'... checking error message',
|
||||
);
|
||||
|
||||
$properties = $driver->get();
|
||||
$properties->{sourceCountry} = 'United States';
|
||||
$driver->update($properties);
|
||||
eval { $driver->calculate() };
|
||||
$e = Exception::Class->caught();
|
||||
isa_ok($e, 'WebGUI::Error::InvalidParam', 'calculate throws an exception when no userId');
|
||||
cmp_deeply(
|
||||
$e,
|
||||
methods(
|
||||
error => 'Driver configured without a UPS userId.',
|
||||
),
|
||||
'... checking error message',
|
||||
);
|
||||
|
||||
$properties = $driver->get();
|
||||
$properties->{userId} = 'Me';
|
||||
$driver->update($properties);
|
||||
eval { $driver->calculate() };
|
||||
$e = Exception::Class->caught();
|
||||
isa_ok($e, 'WebGUI::Error::InvalidParam', 'calculate throws an exception when no password');
|
||||
cmp_deeply(
|
||||
$e,
|
||||
methods(
|
||||
error => 'Driver configured without a UPS password.',
|
||||
),
|
||||
'... checking error message',
|
||||
);
|
||||
|
||||
$properties = $driver->get();
|
||||
$properties->{password} = 'knock knock';
|
||||
$driver->update($properties);
|
||||
eval { $driver->calculate() };
|
||||
$e = Exception::Class->caught();
|
||||
isa_ok($e, 'WebGUI::Error::InvalidParam', 'calculate throws an exception when no license number');
|
||||
cmp_deeply(
|
||||
$e,
|
||||
methods(
|
||||
error => 'Driver configured without a UPS license number.',
|
||||
),
|
||||
'... checking error message',
|
||||
);
|
||||
|
||||
$cart = WebGUI::Shop::Cart->newBySession($session);
|
||||
my $addressBook = $cart->getAddressBook;
|
||||
my $workAddress = $addressBook->addAddress({
|
||||
label => 'work',
|
||||
organization => 'Plain Black Corporation',
|
||||
address1 => '1360 Regent St. #145',
|
||||
city => 'Madison', state => 'WI', code => '53715',
|
||||
country => 'USA',
|
||||
});
|
||||
my $wucAddress = $addressBook->addAddress({
|
||||
label => 'wuc',
|
||||
organization => 'Madison Concourse Hotel',
|
||||
address1 => '1 W Dayton St',
|
||||
city => 'Madison', state => 'WI', code => '53703',
|
||||
country => 'USA',
|
||||
});
|
||||
$cart->update({shippingAddressId => $workAddress->getId});
|
||||
|
||||
cmp_deeply(
|
||||
[$driver->_getShippableUnits($cart)],
|
||||
[(), ],
|
||||
'_getShippableUnits: empty cart'
|
||||
);
|
||||
|
||||
$rockHammer->addToCart($rockHammer->getCollateral('variantsJSON', 'variantId', $smallHammer));
|
||||
cmp_deeply(
|
||||
[$driver->_getShippableUnits($cart)],
|
||||
[ [ [ ignore() ], ], ],
|
||||
'_getShippableUnits: one loose item in the cart'
|
||||
);
|
||||
|
||||
$rockHammer->addToCart($rockHammer->getCollateral('variantsJSON', 'variantId', $bigHammer));
|
||||
cmp_bag(
|
||||
[$driver->_getShippableUnits($cart)],
|
||||
[ [ [ ignore(), ignore() ], ], ],
|
||||
'_getShippableUnits: two loose items in the cart'
|
||||
);
|
||||
|
||||
$bible->addToCart($bible->getCollateral('variantsJSON', 'variantId', $kjvBible));
|
||||
cmp_bag(
|
||||
[$driver->_getShippableUnits($cart)],
|
||||
[ bag( [ ignore(), ignore() ], [ ignore() ], ), ],
|
||||
'_getShippableUnits: two loose items, and 1 ships separately item in the cart'
|
||||
);
|
||||
|
||||
my $bibleItem = $bible->addToCart($bible->getCollateral('variantsJSON', 'variantId', $nivBible));
|
||||
$bibleItem->setQuantity(3);
|
||||
cmp_bag(
|
||||
[$driver->_getShippableUnits($cart)],
|
||||
[ bag( [ ignore(), ignore() ], [ ignore() ], [ ignore() ], [ ignore() ], [ ignore() ] ) ],
|
||||
'_getShippableUnits: two loose items, and 4 ships separately item in the cart, due to quantity'
|
||||
);
|
||||
|
||||
my $rockHammer2 = $bible->addToCart($rockHammer->getCollateral('variantsJSON', 'variantId', $smallHammer));
|
||||
$rockHammer2->update({shippingAddressId => $wucAddress->getId});
|
||||
cmp_bag(
|
||||
[$driver->_getShippableUnits($cart)],
|
||||
[
|
||||
bag( [ ignore(), ignore() ], [ ignore() ], [ ignore() ], [ ignore() ], [ ignore() ] ),
|
||||
[ [ ignore() ], ],
|
||||
],
|
||||
'_getShippableUnits: two loose items, and 4 ships separately item in the cart, and another loose item sorted by zipcode'
|
||||
);
|
||||
|
||||
$cart->empty;
|
||||
$bible->addToCart($bible->getCollateral('variantsJSON', 'variantId', $nivBible));
|
||||
cmp_deeply(
|
||||
[$driver->_getShippableUnits($cart)],
|
||||
[ [ ignore() ], ],
|
||||
'_getShippableUnits: only 1 ships separately item in the cart'
|
||||
);
|
||||
$cart->empty;
|
||||
|
||||
my $userId = $session->config->get('testing/UPS_userId');
|
||||
my $hasUPSCredentials = 1;
|
||||
##If there isn't a userId, set a fake one for XML testing.
|
||||
if (! $userId) {
|
||||
$hasUPSCredentials = 0;
|
||||
$userId = "blahBlahBlah";
|
||||
}
|
||||
|
||||
my $password = $session->config->get('testing/UPS_password');
|
||||
##If there isn't a password, set a fake one for XML testing.
|
||||
if (! $password) {
|
||||
$hasUPSCredentials = 0;
|
||||
$password = "nyaahNyaah";
|
||||
}
|
||||
|
||||
my $license = $session->config->get('testing/UPS_licenseNo');
|
||||
##If there isn't a license, set a fake one for XML testing.
|
||||
if (! $license) {
|
||||
$hasUPSCredentials = 0;
|
||||
$license = "bogey";
|
||||
}
|
||||
|
||||
$properties = $driver->get();
|
||||
$properties->{userId} = $userId;
|
||||
$properties->{password} = $password;
|
||||
$properties->{licenseNo} = $license;
|
||||
$properties->{sourceZip} = '97123';
|
||||
$properties->{sourceCountry} = 'United States';
|
||||
$properties->{shipService} = '03';
|
||||
$properties->{pickupCode} = '01';
|
||||
$driver->update($properties);
|
||||
|
||||
$driver->testMode(1);
|
||||
|
||||
$rockHammer->addToCart($rockHammer->getCollateral('variantsJSON', 'variantId', $smallHammer));
|
||||
my @shippableUnits = $driver->_getShippableUnits($cart);
|
||||
|
||||
##Must look them up one zip at a time
|
||||
my $xml = $driver->buildXML($cart, $shippableUnits[0]);
|
||||
like($xml, qr/^<.xml version='1.0'.+?<.xml version=/ms, 'buildXML: has two xml declarations');
|
||||
like($xml, qr/<AccessRequest xml:lang/, '... xml:lang is an attribute of AccessRequest');
|
||||
|
||||
my ($xmlA, $xmlR) = split /\n(?=<\?xml)/, $xml;
|
||||
|
||||
my $xmlAcc = XMLin($xmlA,
|
||||
KeepRoot => 1,
|
||||
);
|
||||
|
||||
cmp_deeply(
|
||||
$xmlAcc,
|
||||
{
|
||||
AccessRequest => {
|
||||
Password => $password,
|
||||
UserId => $userId,
|
||||
'xml:lang' => 'en-US',
|
||||
AccessLicenseNumber => $license,
|
||||
},
|
||||
},
|
||||
'... correct access request data structure for 1 package'
|
||||
);
|
||||
|
||||
my $xmlRate = XMLin($xmlR,
|
||||
KeepRoot => 1,
|
||||
);
|
||||
|
||||
cmp_deeply(
|
||||
$xmlRate, {
|
||||
RatingServiceSelectionRequest => {
|
||||
'xml:lang' => 'en-US',
|
||||
PickupType => { Code => '01', },
|
||||
Request => { RequestAction => 'Rate', },
|
||||
Shipment => {
|
||||
Shipper => {
|
||||
Address => { PostalCode => 97123, CountryCode => 'us', },
|
||||
},
|
||||
ShipTo => {
|
||||
Address => { PostalCode => 53715, CountryCode => 'us', },
|
||||
},
|
||||
Service => { Code => '03', },
|
||||
Package => {
|
||||
PackagingType => { Code => '02', },
|
||||
PackageWeight => { Weight => '1.5', },
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
'... correct access rating request structure for 1 package'
|
||||
);
|
||||
|
||||
SKIP: {
|
||||
|
||||
skip 'No UPS credentials for testing', 3 unless $hasUPSCredentials;
|
||||
|
||||
my $response = $driver->_doXmlRequest($xml);
|
||||
ok($response->is_success, '_doXmlRequest to UPS successful for 1 package');
|
||||
my $xmlData = XMLin($response->content, ForceArray => [qw/RatedPackage/],);
|
||||
ok($xmlData->{Response}->{ResponseStatusCode}, '... responseCode is successful');
|
||||
ok($xmlData->{RatedShipment}->{TotalCharges}->{MonetaryValue}, '... total charges returned');
|
||||
|
||||
}
|
||||
|
||||
TODO: {
|
||||
local $TODO = 'single item shipping cost calculation';
|
||||
ok(0, 'call _calculateFromXML with arranged data');
|
||||
}
|
||||
|
||||
|
||||
$bibleItem = $bible->addToCart($bible->getCollateral('variantsJSON', 'variantId', $nivBible));
|
||||
@shippableUnits = $driver->_getShippableUnits($cart);
|
||||
$xml = $driver->buildXML($cart, @shippableUnits);
|
||||
|
||||
($xmlA, $xmlR) = split /\n(?=<\?xml)/, $xml;
|
||||
|
||||
$xmlRate = XMLin( $xmlR,
|
||||
KeepRoot => 1,
|
||||
ForceArray => ['Package'],
|
||||
);
|
||||
|
||||
cmp_deeply(
|
||||
$xmlRate, {
|
||||
RatingServiceSelectionRequest => {
|
||||
'xml:lang' => 'en-US',
|
||||
PickupType => { Code => '01', },
|
||||
Request => { RequestAction => 'Rate', },
|
||||
Shipment => {
|
||||
Shipper => {
|
||||
Address => { PostalCode => 97123, CountryCode => 'us', },
|
||||
},
|
||||
ShipTo => {
|
||||
Address => { PostalCode => 53715, CountryCode => 'us', },
|
||||
},
|
||||
Service => { Code => '03', },
|
||||
Package => bag(
|
||||
{
|
||||
PackagingType => { Code => '02', },
|
||||
PackageWeight => { Weight => '1.5', },
|
||||
},
|
||||
{
|
||||
PackagingType => { Code => '02', },
|
||||
PackageWeight => { Weight => '2.0', },
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
},
|
||||
'... correct access rating request structure for two packages in cart'
|
||||
);
|
||||
|
||||
SKIP: {
|
||||
|
||||
skip 'No UPS credentials for testing', 3 unless $hasUPSCredentials;
|
||||
|
||||
my $response = $driver->_doXmlRequest($xml);
|
||||
ok($response->is_success, '_doXmlRequest to UPS successful for two package in 1 request');
|
||||
my $xmlData = XMLin($response->content, ForceArray => [qw/RatedPackage/],);
|
||||
ok($xmlData->{Response}->{ResponseStatusCode}, '... responseCode is successful');
|
||||
ok($xmlData->{RatedShipment}->{TotalCharges}->{MonetaryValue}, '... total charges returned');
|
||||
|
||||
}
|
||||
|
||||
ok($driver->getEditForm(), 'getEditForm');
|
||||
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Cleanup
|
||||
END {
|
||||
if (defined $driver && $driver->isa('WebGUI::Shop::ShipDriver')) {
|
||||
$driver->delete;
|
||||
}
|
||||
if (defined $cart && $cart->isa('WebGUI::Shop::Cart')) {
|
||||
my $addressBook = $cart->getAddressBook();
|
||||
$addressBook->delete if $addressBook;
|
||||
$cart->delete;
|
||||
}
|
||||
}
|
||||
102
www/extras/yui-webgui/build/ShipDriver/UPS.js
vendored
Normal file
102
www/extras/yui-webgui/build/ShipDriver/UPS.js
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
|
||||
// Requires YUI Connection and JSON
|
||||
|
||||
if ( typeof WebGUI == "undefined" ) {
|
||||
WebGUI = {};
|
||||
}
|
||||
if ( typeof WebGUI.ShipDriver == "undefined" ) {
|
||||
WebGUI.ShipDriver = {};
|
||||
}
|
||||
if ( typeof WebGUI.ShipDriver.UPS == "undefined" ) {
|
||||
WebGUI.ShipDriver.UPS = {};
|
||||
}
|
||||
|
||||
WebGUI.ShipDriver.UPS.changeServices
|
||||
= function ( newService, elementId ) {
|
||||
|
||||
var el = document.getElementById(elementId);
|
||||
//Delete old options
|
||||
var wasSelected = '';
|
||||
while ( el.options.length >= 1 ) {
|
||||
if ( el.options[0].selected ) {
|
||||
wasSelected = el.options[0].value;
|
||||
}
|
||||
el.remove(0);
|
||||
}
|
||||
var fields = {};
|
||||
switch (newService) {
|
||||
case 'us domestic' : fields = WebGUI.ShipDriver.UPS.US_Domestic;
|
||||
break;
|
||||
case 'us international' : fields = WebGUI.ShipDriver.UPS.US_International;
|
||||
break;
|
||||
default : fields = WebGUI.ShipDriver.UPS.US_Domestic;
|
||||
}
|
||||
//Add new options to the same form element
|
||||
for ( var key in fields ) {
|
||||
var isSelected = key == wasSelected ? true : false;
|
||||
el.options[el.options.length]
|
||||
= new Option( fields[key], key, isSelected, isSelected );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
WebGUI.ShipDriver.UPS.initI18n ( )
|
||||
Initialize the i18n interface
|
||||
*/
|
||||
WebGUI.ShipDriver.UPS.initI18n = function (o) {
|
||||
WebGUI.ShipDriver.UPS.i18n
|
||||
= new WebGUI.i18n( {
|
||||
namespaces : {
|
||||
'ShipDriver_UPS' : [
|
||||
"us domestic 01",
|
||||
"us domestic 02",
|
||||
"us domestic 03",
|
||||
"us domestic 12",
|
||||
"us domestic 13",
|
||||
"us domestic 14",
|
||||
"us domestic 59",
|
||||
"us international 07",
|
||||
"us international 08",
|
||||
"us international 11",
|
||||
"us international 54",
|
||||
"us international 65",
|
||||
]
|
||||
},
|
||||
onpreload : {
|
||||
fn : WebGUI.ShipDriver.UPS.initServiceTables
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
WebGUI.ShipDriver.UPS.initServiceTables = function () {
|
||||
//These objects provide dropdown list labels and values. The values
|
||||
//are API defined UPS Service codes.
|
||||
WebGUI.ShipDriver.UPS.US_Domestic = {
|
||||
'01' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 01'),
|
||||
'02' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 02'),
|
||||
'03' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 03'),
|
||||
'12' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 12'),
|
||||
'13' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 13'),
|
||||
'14' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 14'),
|
||||
'59' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 59')
|
||||
};
|
||||
|
||||
WebGUI.ShipDriver.UPS.US_International = {
|
||||
'01' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 01' ),
|
||||
'02' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 02' ),
|
||||
'03' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 03' ),
|
||||
'07' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us international 07'),
|
||||
'08' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us international 08'),
|
||||
'11' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us international 11'),
|
||||
'12' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 12' ),
|
||||
'14' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 14' ),
|
||||
'54' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us international 54'),
|
||||
'59' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us domestic 59' ),
|
||||
'65' : WebGUI.ShipDriver.UPS.i18n.get('ShipDriver_UPS', 'us international 65')
|
||||
};
|
||||
|
||||
var shipService = document.getElementById('shipType_formId').value;
|
||||
WebGUI.ShipDriver.UPS.changeServices(shipService, 'shipService_formId');
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue