Interchange Ecommerce Functions
===============================
ic_ecommerce.1.25 (Draft)
1. THE ORDER PROCESS
====================
Interchange has a completely flexible order basket and checkout
scheme. The foundation demo presents a common use of this process, in
the directory pages/ord -- the files are:
basket.html The order basket displayed by default
checkout.html The form where the customer enters their billing
and shipping info
and in the directory etc:
receipt.html The receipt displayed to the customer
report The order report mailed to you
mail_receipt The customer's email copy (if requested)
It is not strictly necessary to display an order basket when an item
is ordered. If you specify a different page to be displayed that is
fine, but most customers will be confused if you don't give them an
indication that the order operation has succeeded.
Any order basket is an HTML FORM. It will have a number of variables
on it. At the minimum it must have an [item-list] to loop through the
items, and the quantity of each item must be set in some place on that
form. Any valid Interchange tags may be used on the page, and you may
use multiple item lists if necessary.
1.1. How to order an item
-------------------------
Interchange can either use a form-based order or a link-based order to
place an item in the shopping cart. The link-based order uses the
special [order item-code] tag:
[order code]
named attributes:
[order code="sku" quantity="n"* href="page"* cart="cartname"* base="table"*]
* = optional parameters
Expands into a hypertext link which will include the specified
code in the list of products to order and display the order page.
code should be a product SKU listed in one of the "products"
tables, and is the only required parameter. quantity may be
specified if more than one (the default) of the item should be
placed in the cart. href allows some page other than the default
order page to be displayed once the item has been added to the
cart. cart selects the shopping cart the item will be placed in.
The optional argument base constrains the order to a particular
products file -- if not specified, all tables defined as products
files will be searched in sequence for the item.
Example:
Order a [order TK112]Toaster today.
Note that this is the same as:
Order a [page order TK112]Toaster today.
You can change frames for the order with:
Order a Toaster today.
[/order]
Expands into . May be used to give the order tag the
appearance of being a container tag, but neither necessary nor
recommended.
To order with a form, you set the form variable mv_order_item to the
item-code/SKU and use the refresh action:
You may batch select whole groups of items:
Items that have a quantity of zero (or blank) will be skipped, and
only items with a positive quantity will be placed in the basket.
You may also specify attributes like size or color at time of order
(see How to set up an order button).
1.2. How to set up an order link
--------------------------------
On a product display page, use:
[order 00-0011]Order the Mona Lisa
If coming from a search results or on-the-fly page, you may use the
generated [item-code] thusly:
[order [item-code]]Order [item-field name]
Bear in mind that if you have not reached the page via a search or
on-the-fly operation, [item-code] means nothing and will cause an
error.
1.3. How to set up an order button
----------------------------------
Interchange can order via form submission as well. This allows you to
order a product (or group of products) via a form button. In its
simplest form, it is:
The default quantity is one. An initial quantity may be set by the
user by adding an mv_order_quantity variable:
Number to order:
You can order multiple items by stacking the variables:
Initial size or color may be set as well, provided UseModifier is set
up properly:
If the order is coming from a generated flypage, loop list, or search
results page, you can get a canned select box from the
[item-accessories size] or [item-accessories size] tag. See Item
Attributes.
1.4. How to set up an on-the-fly item
-------------------------------------
If you enable the catalog directive OnFly, setting it to the name of a
subroutine (or possibly a UserTag) that can handle its calls, then
Interchange will add items to the basket that are not in the product
database. Interchange supplies an internal onfly subroutine, which
will work according to the examples given below.
In catalog.cfg:
OnFly onfly
If your item code is not to be named mv_order_item then you must
perform a rename in the Autoload routine.
A basic link can be generated like:
Order item 000101
The form parameter value mv_order_fly can contain any number of fields
which will set corresponding parameters in the item attributes. The
fields are separated by the pipe (|) character and contain
value-parameter pairs separated by an = sign. (These are URL-encoded
by the [area ...] or [page ...] tag, of course.) You can set a size,
color, or any other parameter.
The special attribute mv_price can be used in conjunction with the
CommonAdjust atom $ to set the price for checkout and display.
The [item-list] sub-tag [item-description], when used with an
item-list, will use the item attribute description to display in the
basket. Note that [item-field description] or [item-data products
description] will NOT work, as both of these tags reference an actual
field value for a record in the products table - not applicable for
on-the-fly items. Similarly, an attempt to generate a flypage for an
on-the-fly item ([page 000101], for example) will fail, resulting in
the display of the SpecialPage missing.
If you wish to set up a UserTag to process on-the-fly items, it should
accept a call of
usertag(mv_item_code, mv_item_quantity, mv_order_fly)
The mv_item_code and mv_order_fly parameters are required to trigger
Interchange's add_item routine (along with mv_todo=refresh to set the
action).
The item will always act as if SeparateItems or mv_separate_items is
set.
Multiple items can be ordered at once by stacking the variables. If
there is only one mv_order_item instance, however, you can stack the
mv_order_fly variable so that all are concatenated together as with
the | symbol. So the above example could be done as:
[area form="
mv_todo=refresh
mv_order_item=000101
mv_order_fly=description=An on-the-fly item
mv_order_fly=price=100.00
"]
Multiple items would need multiple instances of mv_order_item with a
corresponding mv_order_fly for each mv_order_item. You can order both
000101 and 000101 as follows:
[area form="
mv_todo=refresh
mv_order_item=000101
mv_order_fly=description=An on-the-fly item|price=100.00
mv_order_item=000102
mv_order_fly=description=Another on-the-fly item|price=200.00
"]
The following two forms correspond to the above two examples, in
order, with the slight refinement of adding a quantity:
1.5. Order Groups
-----------------
Interchange allows you to group items together, making a master item
and sub-items. This can be used to delete accessories or options when
the master item is deleted. In its simplest form, you order just one
master item and all subsequent items are sub-items.
If you wish to stack more than one master item, then you must define
mv_order_group for all items, with either a 1 value (master) or 0
value (sub-item). A master owns all subsequent sub-items until the
next master is defined.
When the master item 00-0011 is deleted from the basket, 00-0011a will
be deleted as well. And when 19-202 is deleted, then 99-102 will be
deleted from the basket.
NOTE: Use of checkboxes for this type of thing can be hazardous, as
they do not pass a value when unchecked. It is preferable to use
radio groups or select/drop-down widgets. If you must use checkboxes,
be sure to explicitly clear mv_order_group and mv_order_item somewhere
on the page which contains the form:
[value name=mv_order_group set='']
[value name=mv_order_item set='']
The attributes mv_mi and mv_si are set to the group and sub-item
status of each item. The group, contained in the attribute mv_mi, is
a meaningless yet unique integer. All items in a group will have the
same value of mv_mi. The attribute mv_si is set to 0 if the item is a
master item, and 1 if it is a sub-item.
1.6. Basket display
-------------------
The basket page(s) are where the items are tracked and adjusted by the
customer. It is possible to have an unlimited number of basket pages.
It is also possible to have multiple shopping carts, as in buy or
sell. This allows a basket/checkout type of ordering scheme, with
custom order pages for items which have many accessories.
The name of the page to display can be configured in several ways:
1. Set the SpecialPage order to the page to display when an item is
ordered.
2. Use the [order code=item page=page_name] Order it! form of
order tag to specify an arbitrary order page for an item.
3. If already on an order page, set the mv_orderpage, mv_nextpage,
mv_successpage, or mv_failpage variables.
The following variables can be used to control cart selection and page
display:
mv_cartname
The shopping cart (default is main) to be used for this order
operation.
mv_failpage
Page to be displayed on a failed order check (see Advanced
Multi-level Order Pages)
mv_nextpage
Page to display on a return operation.
mv_orderpage
Page to be displayed on a refresh.
mv_successpage
Page to be displayed on a successful order check (see Advanced
Multi-level Order Pages).
mv_order_profile
Order profile to be used if the form action is submit (see
Advanced Multi-level Order Pages).
1.7. Multiple Shopping Carts
----------------------------
Interchange allows you to define and maintain multiple shopping carts.
One shopping cart -- main, by name -- is defined when the user session
starts. If the user orders item M1212 with the following tag:
[order code=M1212 cart=layaway] Order this item!
the order will be placed in the cart named layaway. However, by
default you won't see the just-ordered item on the basket page. That
is because the default shopping basket displays the contents of the
'main' cart only. So copy the default basket page
(pages/ord/basket.html in the demo) to a new file, insert a [cart
layaway] tag, and specify it as the target page in your [order] tag:
[order code=M1212 cart=layaway page=ord/lay_basket] Order this item!
Now the contents of the layaway cart will be displayed. Most of the
ITL tags that are fundamental to cart display accept a 'cartname'
option, allowing you to specify which cart to be used:
[cart cartname]
A 'sticky' setting of the default cart name to use for all
subsequent cart-related tags. Convenient, but you must remember
to use [cart main] to get back to the primary cart! As an
alternative, you can specify the desired cart as a parameter of
the other tags. These are not sticky, referencing the specified
cart only for the instance in which they are called:
[item-list cartname]...[/item-list]
Iterates over the items in the specified cart - tags like
[item-quantity] and [item-price] will be evaluated accordingly;
[nitems cartname]
Returns the total number of items in the specified cart;
[subtotal cartname]
Returns the monetary subtotal for the contents of specified cart;
[shipping cartname], [handling cartname], [salestax cartname],
[total-cost cartname]
You get the idea. It is worth noting that tags which summarize
cart contents do not need to be in used concert, or in
conjunction with an [item-list]. For instance, you can display
just the grand total for a cart on the sidebar or bottom of each
page, using [total-cost] by itself, if you wish.
You can also order items from a form, using the mv_order_item,
mv_cartname, and optional mv_order_quantity variables.
If you need to utilize an alternative item price in conjunction with
the use of a custom cart, see the section on PRODUCT PRICING for
pricing methods and strategies.
2. PRODUCT PRICING
==================
Interchange maintains a price in its database for every product. The
price field is the one required field in the product database -- it is
necessary to build the price routines.
For speed, Interchange builds the code that is used to determine a
product's price at catalog configuration time. If you choose to change
a directive that affects product pricing you must reconfigure the
catalog.
2.1. Simple pricing
-------------------
The simplest method is flat pricing based on a fixed value in the
products database. If you put that price in a field named price, you
don't need to do more. If you want to change pricing based on
quantity, size, color or other factors read on.
2.2. Price Maintenance with CommonAdjust
----------------------------------------
A flexible chained pricing scheme is available when the CommonAdjust
directive is set.
We talk below about a CommonAdjust string; it will be defined in due
time.
A few rules about CommonAdjust, all assuming the PriceField directive
is set to price:
1
If CommonAdjust is set to any value, a valid CommonAdjust string or
not, extended price adjustments are enabled. It may also hold the
default pricing scheme.
2
The price field may also hold a CommonAdjust string. It takes
precedence over the default.
3
If the value of the CommonAdjust directive is set to a CommonAdjust
string, and the price field is empty or specifically 0, then it will
be used to set the price of the items.
4
If no CommonAdjust strings are found, then the price will be 0,
subject to any later application of discounts.
5
If another CommonAdjust string is found as the result of an operation,
it will be re-parsed and the result applied. Chaining is retained; a
fallback may be passed and will take effect.
Prices may be adjusted in several ways, and the individual actions are
referred to below as atoms. Price atoms may be final, chained, or
fallback. A final price atom is always applied if it does not evaluate
to zero. A chained price atom is subject to further adjustment. A
fallback price atom is skipped if a previous chained price was not
zero.
Atoms are separated by whitespace, and may be quoted (although there
should not normally be whitespace in a setting). A chained item ends
with a comma. A fallback item has a leading semi-colon. Final atoms
have no comma appended or semi-colon prepended.
A settor is the means by which the price is set. There are eight
different types of price settors. All settors can then yield another
CommonAdjust string.
It is quite possible to create endless loops, so the maximum number of
initial CommonAdjust strings is set to 16, and there may be only 32
iterations by default before the price will return zero on an error.
(The maximum iterations is specified with the Limit directive.)
NOTE: Common needs are easily shown but not so easily explained; skip
to the examples in the reference below if your vision starts to blur
when reading the next section. 8-)
USAGE: Optional items below have asterisks appended. The asterisk
should not be used in the actual string. Optional base or table always
defaults to the active products database table. The optional key
defaults to the item code except in a special case for the
attribute-based lookup. The field name is not optional except in the
case of an attribute lookup.
N.NN or -N.NN
where N is a digit. A number which is applied directly; for
instance 10 will yield a price of 10. May be a positive or
negative number.
N.NN%
where N is a digit. A number which is applied as a percentage of
the current price value. May be a positive or negative number.
For example, if the price is 10 and -8% is applied, the next
price value will be 9.20.
table*:column:key*
Causes a straight lookup in a database table. The optional table
defaults to the main products database table for the item
(subject of course to multiple product files). The column must
always be present. The optional key defaults to the item code
except in a special case for the attribute-based lookup. The
return value is then re-parsed as another price settor.
table*:col1..col5,col10:key*
Causes a quantity lookup in database table table (which defaults
to the products database), with a set of comma-separated fields,
looked up by the optional key. (Key defaults to the item code, of
course). If ranges are specified with .., each column in the
sequence will be used; Therefore
pricing:p1,p2,p3,p4,p5,p10:
is the same as
pricing:p1..p5,p10:
Leading non-digits are stripped, and the item quantity is
compared with the numerical portion of the column name. The price
is set to the value of the database column (numeric portion) that
is at least equal to it but doesn't yet reach the next break.
WARNING: If the field at the appropriate quantity level is blank,
a zero cost will be returned from the atom. It is important to
have all columns populated.
==attribute:table*:column*:key*
Does an attribute-based adjustment. The attribute is looked up in
the database table, with the optional column defaulting to the
same name as the value of the attribute. If the column is not
left blank, the key is set to the value of the attribute if
blank.
& CODE
The leading & sign is stripped and the code is passed to the
equivalent of a [calc] tag. No Interchange tags can be used, but
the &tag_data routine is available, the current value of the
price and quantity are available as $s, and the current item
(code, quantity, price, and any attributes) are available as
$item, all forced to the package Vend::Interpolate. That means
that in a UserTag:
$Vend::Interpolate::item is the current item
$Vend::Interpolate::item->{code} gives key for current item
$Vend::Interpolate::item->{size} gives size for current item (if there)
$Vend::Interpolate::item->{mv_ib} gives database ordered from
[valid Interchange tags]
If the settor begins with a square bracket ([) or underscore, it
is parsed for Interchange tags with variable substitution (but no
Locale substitution). You may define a price in a Variable in
this fashion. The string is re-submitted as an atom, so it may
yield yet another settor.
$
Tells Interchange to look in the mv_price attribute of the
shopping cart, and apply that price as the final price, if it
exists. The attribute must be a numerical value.
>>word
Tells the routine to return word directly as the result. This is
not useful in pricing, as it will evaluate to zero. But when
CommonAdjust is used for shipping, it is a way of re-directing
shipping modes.
word
The value of word, which must not match any of the other settors,
is available as a key for the next lookup (only). If the next
settor is a database lookup, and it contains a dollar sign ($)
the word will be substituted; i.e. table:column:$ becomes
table:column:word.
( settor )
The value returned by settor will be used as a key for the next
lookup, as above.
2.3. CommonAdjust Examples
--------------------------
Most examples below use an outboard database table named pricing, but
any valid table including the products table can be used. We will
refer to this pricing table:
code common q1 q5 q10 XL S red
99-102 10 9 8 1 -0.50 0.75
00-343 2
red 0.75
The simplest case is a straight lookup on an attribute; size in this
case.
10.00, ==size:pricing
With this value in the price field, a base price of 10.00 will be
adjusted with the value of the size attribute. If size for the item
99-102 is set to XL then 1.00 will be added for a total price of
11.00; if it is S then .50 will be subtracted for a total price of
9.50; for any other value of size no further adjustment would be made.
00-343 would be adjusted up 2.00 only for XL.
10.00, ==size:pricing, ==color:pricing
This is the same as above, except both size and color are adjusted
for. A color value of red for item code 99-102 would add 0.75 to the
price. For 00-343 it would have no effect.
10.00, ==size:pricing, ==color:pricing:common
Here price is set based on a common column, keyed by the value of the
color attribute. Any item with a color value of red would have 0.75
added to the base price.
pricing:q1,q5,q10:, ;10.00, ==size:pricing, ==color:pricing:common
Here is a quantity price lookup, with a fallback price setting. If
there is a valid price found at the quantity of 1, 5, or 10, depending
on item quantity, then it will be used. The fallback of 10.00 only
applies if no non-zero/non-blank price was found at the quantity
lookup. In either case, size/color adjustment is applied.
pricing:q1,q5,q10:, ;10.00 ==size:pricing, ==color:pricing:common
Removing the comma from the end of the fallback string stops
color/size lookup if it reaches that point. If a quantity price was
found, then size and color are chained.
pricing:q1,q5,q10:, ;products:list_price, ==size:pricing, ==color:pricing
The value of the database column list_price is used as a fallback
instead of the fixed 10.00 value. The above value might be a nice one
to use as the default for a typical retail catalog that has items with
colors and sizes.
2.4. PriceBreaks, discounts, and PriceAdjustment
------------------------------------------------
There are several ways that Interchange can modify the price of a
product during normal catalog operation. Several of them require that
the pricing.asc file be present, and that you define a pricing
database. You do that by placing the following directive in
catalog.cfg:
Database pricing pricing.asc 1
NOTE: PriceAdjustment is slightly deprecated by CommonAdjust, but will
remain in use at least through the end of Version 3 of Interchange.
Configurable directives and tags with regard to pricing:
o Quantity price breaks are configured by means of the PriceBreaks
and MixMatch directives. They require a field named specifically
price in the pricing database. The price field contains a
space-separated list of prices that correspond to the quantity
levels defined in the PriceBreaks directive. If quantity is to be
applied to all items in the shopping cart (as opposed to quantity
of just that item) then the MixMatch directive should be set to
Yes.
o Individual line-item prices can be adjusted according to the
value of their attributes. See PriceAdjustment and CommonAdjust.
The pricing database must be defined unless you define the
CommonAdjust behavior.
o Product discounts for individual products, specific product
codes, all products, or the entire order can be configured with
the [discount ...] tag. Discounts are applied on a per-user basis
-- you can gate the discount based on membership in a club or
other arbitrary means. See Product Discounts.
For example, if you decided to adjust the price of T-shirt part number
99-102 up 1.00 when the size is extra large and down 1.00 when the
size is small, you would have the following directives defined in
:
Database pricing pricing.asc 1
UseModifier size
PriceAdjustment size
To enable automatic modifier handling, you define a size field in
products.txt:
code description price size
99-102 T-Shirt 10.00 S=Small, M=Medium, L=Large*, XL=Extra Large
You would place the proper tag within your [item-list] on the
shopping-basket or order page:
[item-accessories size]
In the pricing.asc database source, you would need:
code S XL
99-102 -1.00 1.00
If you want to assign a price based on the option, precede the number
with an equals sign:
code S M L XL
99-102 =9.00 =10 =10 =11
IMPORTANT NOTE: Price adjustments occur AFTER quantity price breaks,
so the above would negate anything set with the PriceBreaks
directive/option.
Numbers that begin with an equals sign (=) are used as absolute prices
and are interpolated for Interchange tags first, so you can use
subroutines to set the price. To facilitate coordination with the
subroutine, the session variables item_code and item_quantity are set
to the code and quantity of the item being evaluated. They would be
accessed in a global subroutine with $Vend::Session->{item_code} and
$Vend::Session->{item_quantity}.
The pricing information must always come from a database because of
security.
2.5. Item Attributes
--------------------
Interchange allows item attributes to be set for each ordered item.
This allows a size, color, or other modifier to be attached to a
common part number. If multiple attributes are set, then they should
be separated by commas. Previous attribute values can be saved by
means of a hidden field on a form, and multiple attributes for each
item can be stacked on top of each other.
The configuration file directive UseModifier is used to set the name
of the modifier or modifiers. For example
UseModifier size,color
will attach both a size and color attribute to each item code that is
ordered.
IMPORTANT NOTE: You may not use the following names for attributes:
item group quantity code mv_ib mv_mi mv_si
You can also set it in scratch with the mv_UseModifier scratch
variable -- [set mv_UseModifier]size color[/set] has the same effect
as above. This allows multiple options to be set for products.
Whichever one is in effect at order time will be used. Be careful, you
cannot set it more than once on the same page. Setting the
mv_separate_items or global directive SeparateItems places each
ordered item on a separate line, simplifying attribute handling. The
scratch setting for mv_separate_items has the same effect.
The modifier value is accessed in the [item-list] loop with the
[item-modifier attribute] tag, and form input fields are placed with
the [modifier-name attribute] tag. This is similar to the way that
quantity is handled, except that attributes can be "stacked" by
setting multiple values in an input form.
You cannot define a modifier name of code or quantity, as they are
already used. You must be sure that no fields in your forms have
digits appended to their names if the variable is the same name as the
attribute name you select, as the [modifier-name size] variables will
be placed in the user session as the form variables size0, size1,
size2, etc.
You can use the [loop arg="item item item"] list to reference multiple
display or selection fields for modifiers, or you can use the built-in
[PREFIX-accessories ...] tags available in most Interchange list
operations. The modifier value can then be used to select data from an
arbitrary database for attribute selection and display.
Below is a fragment from a shopping basket display form which shows a
selectable size with "sticky" setting. Note that this would always be
contained within the [item_list] [/item-list] pair.
It could just as easily be done with a radio button group combined
with the [checked ...] tag.
Interchange will automatically generate the above select box when the
[accessories or [item-accessories size] tags are called.
They have the syntax:
[item_accessories attribute*, type*, field*, database*, name*, outboard*]
[accessories code attribute*, type*, field*, database*, name*, outboard*]
code
Not needed for item-accessories, this is the product code of the
item to reference.
attribute
The item attribute as specified in the UseModifier configuration
directive. Typical are size or color.
type
The action to be taken. One of:
select Builds a dropdown