diff --git a/README b/README new file mode 100644 index 000000000..db500c978 --- /dev/null +++ b/README @@ -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. diff --git a/lib/WebGUI/Shop/ShipDriver/UPS.pm b/lib/WebGUI/Shop/ShipDriver/UPS.pm new file mode 100644 index 000000000..c58e2ef4f --- /dev/null +++ b/lib/WebGUI/Shop/ShipDriver/UPS.pm @@ -0,0 +1,533 @@ +package WebGUI::Shop::ShipDriver::UPS; + +use strict; +use base qw/WebGUI::Shop::ShipDriver/; +use WebGUI::Exception; +use WebGUI::Exception::Shop; +use XML::Simple; +use LWP; +use Tie::IxHash; +use Locales::Country qw/ en /; +use Class::InsideOut qw/ :std /; +use Data::Dumper; + +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('pickupType') ], + }; + $xmlRate->{CustomerClassification} = { + Code => [ $self->get('customerClassification') ], + }; + $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 => [], + }; + if ($self->get('residentialIndicator') eq 'residential') { + $xmlRate->{Shipment}->{ShipTo}->{Address}->[0]->{ResidentialAddressIndicator} = ['']; + } + 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; + $weight = sprintf "%.1f", $weight; + $weight = '0.1' if $weight == 0; + my $options = { + PackagingType => [ { + Code => [ '02' ], + } ], + PackageWeight => [ { + Weight => [ $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 => '', + 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::Shop::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::Shop::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'); + + tie my %customerClassification, 'Tie::IxHash'; + ##Note, these keys are required XML keywords in the UPS XML API. + $customerClassification{'01'} = $i18n->get('customer classification 01'); + $customerClassification{'03'} = $i18n->get('customer classification 03'); + $customerClassification{'04'} = $i18n->get('customer classification 04'); + + 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', + }, + customerClassification => { + fieldType => 'selectBox', + label => $i18n->get('customer classification'), + hoverHelp => $i18n->get('customer classification help'), + options => \%customerClassification, + defaultValue => '01', + }, + residentialIndicator => { + fieldType => 'radioList', + label => $i18n->get('residential'), + hoverHelp => $i18n->get('residential help'), + options => { + residential => $i18n->get('residential'), + commercial => $i18n->get('commercial'), + }, + defaultValue => 'commercial', + }, +##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(< + YAHOO.util.Event.onDOMReady( WebGUI.ShipDriver.UPS.initI18n ); + +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; diff --git a/lib/WebGUI/i18n/English/ShipDriver_UPS.pm b/lib/WebGUI/i18n/English/ShipDriver_UPS.pm new file mode 100644 index 000000000..42715e2fa --- /dev/null +++ b/lib/WebGUI/i18n/English/ShipDriver_UPS.pm @@ -0,0 +1,295 @@ +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|

In order to use the UPS Shipping Driver, you must first register with the UPS on their website. 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.

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.

|, + }, + + '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, + }, + + 'customer classification' => { + message => q|Customer Classification|, + lastUpdated => 1241214572, + context => q|What kind or type of customer are you?|, + }, + + 'customer classification help' => { + message => q|The kind or type of customer you are.|, + lastUpdated => 1247110533, + }, + + 'residentialIndicator' => { + message => q|Residential or Commercial?|, + lastUpdated => 1248113596, + context => q|Residential (a person's home) versus Commercial, a business address.|, + }, + + 'residential help' => { + message => q|The UPS rates for delivering to a residential address, or a commercial address differ. WebGUI will not ask the user which is which, so you will need to configure drivers for both kinds of destinations.|, + lastUpdated => 1248113598, + }, + + 'residential' => { + message => q|Residential|, + lastUpdated => 1248113596, + context => q|Residential (a person's home)|, + }, + + 'commercial' => { + message => q|Commercial|, + lastUpdated => 1248113596, + context => q|A business address|, + }, + + 'customer classification 01' => { + message => q|Wholesale|, + lastUpdated => 1247110533, + }, + + 'customer classification 03' => { + message => q|Occasional|, + lastUpdated => 1247110533, + }, + + 'customer classification 04' => { + message => q|Retail|, + lastUpdated => 1247110533, + }, + + '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. Changing the service will change the Ship Service options below.|, + lastUpdated => 1247111015, + 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; diff --git a/sbin/installUPSDriver.pl b/sbin/installUPSDriver.pl new file mode 100644 index 000000000..49c0ef9b6 --- /dev/null +++ b/sbin/installUPSDriver.pl @@ -0,0 +1,182 @@ +#!/usr/bin/env perl + +#------------------------------------------------------------------- +# Copyright 2009 Plain Black +#------------------------------------------------------------------- + +$|++; # disable output buffering +our ($webguiRoot, $configFile, $help, $man); + +BEGIN { + $webguiRoot = "/data/WebGUI"; + unshift (@INC, $webguiRoot."/lib"); +} + +use lib '/data/ups-driver'; +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 ); + +installUPSDriver($session); + +# Do your work here +finish($session); + +#---------------------------------------------------------------------------- +# Your sub here + +#---------------------------------------------------------------------------- +sub installUPSDriver + 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 diff --git a/t/Shop/ShipDriver/UPS.t b/t/Shop/ShipDriver/UPS.t new file mode 100644 index 000000000..40c2a4d9b --- /dev/null +++ b/t/Shop/ShipDriver/UPS.t @@ -0,0 +1,556 @@ +# 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 = 41; +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, + } +); + +my $feather = $home->addChild({ + className => 'WebGUI::Asset::Sku::Product', + isShippingRequired => 1, title => 'Feathers', + shipsSeparately => 0, +}); + +my $blueFeather = $feather->setCollateral('variantsJSON', 'variantId', 'new', + { + shortdesc => 'blue feather', price => 1.00, + varSku => 'blue', weight => 0.001, + 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 = WebGUI::Error->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->{pickupType} = '01'; +$properties->{customerClassification} = '04'; +$properties->{residentialIndicator} = 'residential'; +$driver->update($properties); + +$driver->testMode(1); + +my $rockItem = $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/ 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', }, + CustomerClassification => { Code => '04', }, + Request => { RequestAction => 'Rate', }, + Shipment => { + Shipper => { + Address => { PostalCode => 97123, CountryCode => 'us', }, + }, + ShipTo => { + Address => { PostalCode => 53715, CountryCode => 'us', ResidentialAddressIndicator => {}, }, + }, + 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'); + #diag $response->content; + my $xmlData = XMLin($response->content, ForceArray => [qw/RatedPackage/],); + ok($xmlData->{Response}->{ResponseStatusCode}, '... responseCode is successful'); + ok($xmlData->{RatedShipment}->{TotalCharges}->{MonetaryValue}, '... total charges returned'); + diag($xmlData->{RatedShipment}->{TotalCharges}->{MonetaryValue}); + +} + +$rockItem->setQuantity(2); +@shippableUnits = $driver->_getShippableUnits($cart); +$xml = $driver->buildXML($cart, $shippableUnits[0]); +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 item, quantity=2'); + #diag $response->content; + my $xmlData = XMLin($response->content, ForceArray => [qw/RatedPackage/],); + ok($xmlData->{Response}->{ResponseStatusCode}, '... responseCode is successful'); + ok($xmlData->{RatedShipment}->{TotalCharges}->{MonetaryValue}, '... total charges returned'); + diag($xmlData->{RatedShipment}->{TotalCharges}->{MonetaryValue}); + +} + + +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; + +#diag $xmlR; + +$xmlRate = XMLin( $xmlR, + KeepRoot => 1, + ForceArray => ['Package'], +); + +diag Dumper $xmlRate; + +cmp_deeply( + $xmlRate, { + RatingServiceSelectionRequest => { + 'xml:lang' => 'en-US', + PickupType => { Code => '01', }, + CustomerClassification => { Code => '04', }, + Request => { RequestAction => 'Rate', }, + Shipment => { + Shipper => { + Address => { PostalCode => 97123, CountryCode => 'us', }, + }, + ShipTo => { + Address => { PostalCode => 53715, CountryCode => 'us', ResidentialAddressIndicator => {}, }, + }, + Service => { Code => '03', }, + Package => bag( + { + PackagingType => { Code => '02', }, + PackageWeight => { Weight => '3.0', }, + }, + { + 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'); + diag $xmlData->{RatedShipment}->{TotalCharges}->{MonetaryValue}; + +} + +ok($driver->getEditForm(), 'getEditForm'); + +$cart->empty; +$feather->addToCart($feather->getCollateral('variantsJSON', 'variantId', $blueFeather)); +$xml = $driver->buildXML($cart, $driver->_getShippableUnits($cart)); +($xmlA, $xmlR) = split /\n(?=<\?xml)/, $xml; + +$xmlRate = XMLin( $xmlR, + KeepRoot => 1, + ForceArray => ['Package'], +); + +is ( + $xmlRate->{RatingServiceSelectionRequest}->{Shipment}->{Package}->[0]->{PackageWeight}->{Weight}, + '0.1', + 'Weight is clipped at 0.1 pounds.' +); + +$cart->empty; +} + +#---------------------------------------------------------------------------- +# 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; + } +} diff --git a/www/extras/yui-webgui/build/ShipDriver/UPS.js b/www/extras/yui-webgui/build/ShipDriver/UPS.js new file mode 100644 index 000000000..8807b3714 --- /dev/null +++ b/www/extras/yui-webgui/build/ShipDriver/UPS.js @@ -0,0 +1,100 @@ + +// 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 = el.options[el.selectedIndex].value; + while ( el.options.length >= 1 ) { + 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 shipType = document.getElementById('shipType_formId'); + var selectedType = shipType.options[shipType.selectedIndex].value; + WebGUI.ShipDriver.UPS.changeServices(selectedType, 'shipService_formId'); + +}