Products | Scripts | Services | Tutorials | Books | Links | Contact | Bulletin Board

The CITY Shop

Overview | Concept | Licensing | Demo/Download | Installation | Gallery | Last Updates | FAQ

Templates | Views | Engines | Libraries | Main Setup | Shop Setup | Database | Session | Shipping

Shipping

The shipping is mainly handled by the ShopShippingLib, with some help from ShopOrderLib in preparing data. If you want to set up a shipping method that is not listed here, or is listed but you want to customize it, please contact us for a quote.

ShopSetup

Here you define your shipping options, using the following keys as in the example below:

  -SHOP_CALCULATE_SHIPPING_BY_QUANTITY => 0,
  -SHOP_SHIPPING_FROM_ZIP => '96067',
  -SHOP_SHIPPING_FROM_STATE => 'CA',
  -SHOP_SHIPPING_FROM_COUNTRY => 'US',
  -SHOP_SHIPPING_METHODS => ['usps','ups', 'free', 'custom'],
  -SHOP_SHIPPING_METHOD_DEFAULT => 'custom|byzone-1',
  -SHOP_ALLOWS_FREE_SHIPPING => 1,

We will explain how these relate in the description of the shop shipping module (ShopShippingLib). As a general idea, here are some basic rules:

You need to provide the origin for the shipping, using the 3 keys listed above. Some methods use it, some don't, but we decided to require them by default.

List the main methods in the array of shipping methods. If you have defined more than one custom method, but you want to display only some of them, use the format:

 -SHOP_SHIPPING_METHODS => ['free', 'custom|1', 'custom|2'], 

If you want a certain method to appear as default in your shipping method selector, you can define it using the following key:

 -SHOP_SHIPPING_METHOD_DEFAULT => 'custom|byzone-1',

Some methods require specific settings, please read the text below for explanations.

 

ShopFormatting (for each language)

These modules hold an array of shipping options and the corresponding description. We use an array and not a hash in order to preserve the display order.

 

ShopShippingLib

It takes the following function call (extract from ShopOrderLib):

RequireFile($S->{-SHOP_PATH}, 'ShopShippingLib.pm');
($ShippingCost, 
 $SError, 
 $SWarning) = ShopShippingLib::display($S, [
  $ToZip,
  $ToState,
  $ToCountry,
  $FromZip,
  $FromState,
  $FromCountry,
  $STotals,
  $Weight,
  $SPreferredMethod]);

It is meant to be simple, and not have to handle split-shipping or distributed shipping. You have to process that outside this routine, take example from the ShopOrderLib.

As expected, you have to provide values for all the address fields, and of course, you have to provide values for the totals, weight and shipping method (I guess it could be said: all parameters are required... oh well, you get what I mean).

Methods supported by this module:

Free

This returns 0 for the shipping costs. The required input parameter is "free".

Of course, you will have to enable the following key in the ShopSetup, for the free shipping to be allowed:

 -SHOP_ALLOWS_FREE_SHIPPING => 1,

 

Custom Methods

As in the name, it is not said by the method ID what it will do. We display a few examples in the ShopShippingLib, but if you need something special, feel free to build your own calculations, or call us for a quote (...we love that :))
Here are the methods currently implemented, we will list the method ID needed:

 

custom|1

This is meant as a straight multiplier using the number of items. We even thought of implementing alternatively the calculations by weight, and that would be simple to do, just assign $NumberOfItems = $Weight before the calculation takes place.
The following flag needs to be set in the ShopSetup, in order for the calculations to be successful:

-SHOP_CALCULATE_SHIPPING_BY_QUANTITY => 1,


custom|2

This method uses a straight table of countries and prices by pound or kg. You will have to record your prices per unit in both the states.db and countries.db, using the manager interface, or edit the files and re-upload them. The table field name in question is "PriceByWeight", and should be a numeric, positive value.

As expected, if the source address country is different from the target country, the countries.db will be used, otherwise the states.db. Note that the states.db contains only valid values for US and CA, so if you want to customize this for another country, you need to add the states/regions in that country to the states.db. Since the display of the states data can be sorted alphabetically, you can use a numeric first character for the new states: 0A, 0B, 0C,... if you want them at the top.

One hint here, the module is using named data sources, as defined in the ShopSetup, i.e. $S->{-DATA_SOURCE}{'DisplayStates'}. It might appear odd, but this is the way to keep the data in memory and avoid multiple database queries for the same data. The shop implements caching in this case, given the small table size, and the likelyhood that the data will be needed a few times during a single click.


custom|3

This method is using local defined tables, for shipping by country ONLY.
As in the custom|1, this method uses in older modules, the the following flag to be set in the ShopSetup, in order for the calculations to be successful:

-SHOP_CALCULATE_SHIPPING_BY_QUANTITY => 1,

If you download the last released version of the shop (evt. prerelease), we modified the module to support here either number of items or weight, depending of whether the above flag is set or not. For the impatient, set the following in the module (it was set before on 0):

my $NumberOfItems = $Weight;

Look at the top of the module and you will see the following global variables:

%CountryZones = (
  'US' => 0,
  'CA' => 1,
  'DE' => 2,
);
%ZonesDetails = (
  0 => [qw(US CA)],
  1 => [qw(RU GR)],
  2 => [qw(DE FR IT AT)],
);
%ShippingData = (
  0 => 3,
  1 => 4.5,
  2 => 6,
);
$DefaultShipping = 10;

It looks complex, it is not. The way it functions:

  • You have a country defined in the %CountryZones, with a number assigned to it. This number is an index to the hash %ShippingData, which contains the prices per unit of measurement (weight or number of items, see above). The shipping cost will be calculated by multiplying the unit of measurement with the price, no preservatives added :).
  • The country is defined in one of the lists contained in the values of %ZonesDetails. The corresponding key of that hash is the actual index that you will use to calculate the shipping price, as in the above example.
  • If nothing is defined for the country of destination, the value in the $DefaultShipping will be used.

That is all there is to it. Yes, I can hear the questions, "what about the states, what about the handling fees,..." - this is a custom method meant as an example. If you want to customize it further (sic.,*giggle*), you can either write your own code around a method you like most, or call us for a quote (you already heard it, we love that :)).


custom|byzone-*

Look at the top of the module and you will see the following global variables:

@CustomShippingThresholds = qw(2 5 10); # these are weight limits
%ZonePricesByType = (
  1 => { # urgent
    0 => [qw(5.15 5.8 7.3)],
    1 => [qw(5.9 7.3 9.8)],
    2 => [qw(6.3 7.95 11.25)],
  },
  2 => { # express
    0 => [qw(6.5 7.6 8.4)],
    1 => [qw(7.9 9.3 11.8)],
    2 => [qw(9 10.75 16)],
  },
  3 => { # preferred
    0 => [qw(8.45 8.9 15.15)],
    1 => [qw(11.25 13.5 23.35)],
    2 => [qw(12.25 15.2 26.5)],
  },
  4 => { # overnight
    0 => [qw(8.45 8.9 15.15)],
    1 => [qw(11.25 13.5 23.35)],
  },
);

As expected, the "*" is a variable parameter that you should define in each of the ShopFormatting modules:

'custom|byzone-1' => 'Urgent',
'custom|byzone-2' => 'Expres',
'custom|byzone-3' => 'Preferred',
'custom|byzone-4' => 'Overnight',

The parameter value is numeric, but you can define it otherwise, if you understand the principle. Here it is, explained:
every parameter associates with a valid key in the hash %ZonePricesByType. Let's say you chose "Urgent", this will give you the following hash to work with:

{ # urgent
  0 => [qw(5.15 5.8 7.3)],
  1 => [qw(5.9 7.3 9.8)],
  2 => [qw(6.3 7.95 11.25)],
}

The module checks for the weight, if it fits in one of the thresholds defined in the @CustomShippingThresholds. That means, if the order is between 0 and 1.9x lbs, it will return 0, if it is between 2 and 4.9x lbs it will return 1, between 5 and 9.9x lbs it will return 2, and will return 3 if above 10 lbs.

This number will be used as a key to extract the mapping array from the hash above. Given a package weighing 3 lbs, where the index returned is 1, the following array is what we have to work with:

[qw(5.9 7.3 9.8)]

Now the magic is in the states.db or countries.db.
As expected, if the source address country is different from the target country, the countries.db will be used, otherwise the states.db. Note that the states.db contains only valid values for US and CA, so if you want to customize this for another country, you need to add the states/regions in that country to the states.db. Since the display of the states data can be sorted alphabetically, you can use a numeric first character for the new states: 0A, 0B, 0C,... if you want them at the top.

The change that you will need to make in the module then, will be for example:

 if ($FromCountry eq 'US' && $ToCountry eq 'US' ||
     $FromCountry eq 'CA' && $ToCountry eq 'CA' ||
     $FromCountry eq 'DE' && $ToCountry eq 'DE') {
   ... 

The table field that will be the index to that array, is "ShippingZone". The shipping cost will be the element in the array above, indexed by the value in the "ShippingZone", which of course, has to be numeric and positive.

 

UPS

This method is using the ShippingUPS module. It makes use of the US based UPS online gateway, and of course, it works ONLY for shippments originating US or CA.

As soon I get the time and the documentation to other UPS gateways worldwide, I will expand the ShippingUPS, with no changes to the ShopShippingLib.

In order to use it, you have to define the submethods in the ShopFormatting for every language, and add it in the array of supported shipping methods, in the ShopSetup (see module related explanation above)
The gateway will calculate the shipping costs between the source address and the destination address.
The submethod to be used, needs to be given as a part of the shipping method, in the format:

'ups|1DM' => 'UPS Next Day Early',
'ups|1DA' => 'UPS Next Day Air', 
'ups|GND' => 'UPS Ground',
...

See the complete list in the Views/eng/ShopFormatting.pm.