[ic] CommonAdjust

Mike Heins mikeh@minivend.com
Sun, 15 Apr 2001 04:51:43 -0400

Quoting Jonathan Clark (jonc@webmaint.com):
> Note added to the docs. Incidentally, I found the CommonAdjust
> documentation; its on the [price] ictags page. I have added a note to the
> CommonAdjust directive page to point people in the right direction.

Actually, there is more detailed documentation. I have edited this to
reflect current reality, and offer:

=head2 Chained Pricing

A flexible chained pricing scheme is available when the I<CommonAdjust>
directive is set.

We talk below about a I<CommonAdjust string>; it will be defined
in due time.

NOTE: while we call the field C<common_adjust> below, usually PriceField
is set to C<no_price> in the Interchange demos.
A few rules about CommonAdjust, all assuming the I<PriceField> directive
is set to C<common_adjust>:

=over 4

=item 1

If C<CommonAdjust> is set to any value, a valid I<CommonAdjust string> or
not, extended price adjustments are enabled. It may and often does hold the
default pricing scheme.

=item 2

The C<common_adjust> field may also hold a I<CommonAdjust string>. It takes
precedence over the default.

=item 3

If the value of the C<CommonAdjust> directive is set to a CommonAdjust
string, and the C<common_adjust> field is empty or specifically I<0>, then it
will be used to set the price of the items.

=item 4

If no CommonAdjust strings are found, then the price will be 0, subject
to any later application of discounts.

=item 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 I<atoms>.  Price atoms they may be I<final>,
I<chained>, or I<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 unless it is ITL or Perl
code). 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 I<settor> is the means by which the price is set. There 
are several different types of price settors. All non-literal 
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 a sufficiently high number to
prevent this; there are also limited iterations before the price will
return zero on an error. NOTE: Interchange 4.7.x and higher allow setting
of this with the Limit directive.

B<NOTE>:  Common needs are easily shown but not so easily explained;
skip to the examples if 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 B<base> or B<table> always
defaults to the active C<products> database table.  The optional B<key>
defaults to the item code except in a special case for the attribute-based
lookup. The B<field> name is not optional except in the case of an
attribute lookup.

=over 4

=item 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.

=item N.NN%

where N is a digit.  A number which is applied as a percentage of
the I<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.

=item table*:column:key*

Causes a straight lookup in a database table. The optional B<table> 
defaults to the main products database table for the item (subject
of course to multiple product files). The B<column> must always
be present. The optional B<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.

=item table*:col1..col5,col10:key*

Causes a quantity lookup in database table B<table> (which defaults to
the products database), with a set of comma-separated fields, looked
up by the optional B<key>. (Key defaults to the item code, of course).
If ranges are specified with .., each column in the sequence will be used;


is the same as


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.

=item table*:price_group,col1..col5,col10:key*

Causes a mix-and-match quantity lookup in database table B<table>
(which defaults to the products database), with a set of comma-separated
fields, looked up by the optional B<key>. The lookup is grouped
by the attribute C<group>. The group field must not end in a digit,
as that is how it is distinguished from a quantity.

The quantity handling is as in the straight quantity lookup.

The item attribute must be preloaded in the item to effect the
lookup -- this is usually done with an AutoModifier setting
in L<catalog.cfg>:

    AutoModifier   pricing:price_group

See L<AutoModifier> for more information on that.

Price group values must contain at least one non-digit character, i.e.
not all digits. "Autos" is a valid price group -- "2" is not.

The effect of the grouping is for the quantity of all items with the
same price group to be added together for the purpose of quantity

If you have the SKUS:

    sku    price_group   q5      q10
    S102   shirts        11.95   9.95
    S103   shirts        11.95   9.95
    P102   pants         22.95   19.95

Ordering two S102 and three S103 will cause the item price to be 11.95
per item. Ordering 5 of each will drop the price of each to 9.95.
Ordering 20 of P102, which is in group C<pants>, will have no effect on
the quantity price of either item in group C<shirts>.

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.

=item ==attribute:table*:column*:key*

Does an attribute-based adjustment. The attribute is looked up in the
database B<table>, with the optional B<column> defaulting to the same
name as the I<value> of the B<attribute>. If the column is not left blank,
the I<key> is set to the I<value> of the B<attribute> if blank.

4.7.x and higher: if there is only ==attribute, with no C<colon>
calling a database table and field, then the special C<options>
table will be used for price lookups. See (when it exists)
L<Automated Option Handling>.

=item & CODE

The leading C<&> sign is stripped and the code is passed to the
equivalent of a C<[calc]> tag. No Interchange tags can be used (i.e. no
tag interpolation), but the full range of $Tag and other embedded Perl
objects is available.  The current (or interim) value of the price
chaining is available as C<$s>, and C<$q>; the current item hash is are
available as C<$item>.  This means:

  $item->{code}  gives key for current item
  $item->{size}  gives size for current item (if there)
  $item->{mv_ib} gives database ordered from
  $s is the prospective item price (not settable).

=item [valid minivend tags]

If the settor begins with a square bracket (C<[>) or underscore, it
is parsed for Interchange tags with variable substitution (but no Locale
substitution). You may define a price in a I<Variable> in this fashion.
The string is re-submitted as an atom, so it may yield yet another 

IMPORTANT NOTE: You must use quotes to surround the tags if they have
any whitespace, and any embedded quotes must be backslashed.

=item $

Tells Interchange to look in the C<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.

=item >>word

Tells the routine to return C<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.

=item word

The value of C<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 (C<$>) the C<word>
will be substituted; i.e. C<table:column:$> becomes C<table:column:word>.

=item ( settor )

The value returned by C<settor> will be used as a key for the next
lookup, as above.


=head2 CommonAdjust Examples

There are several examples of CommonAdjust setups in the L<Foundation>
demo, and they fit many requirements without change.

Most examples below use an outboard database table named B<pricing>, but
any valid table including the B<products> table can be used. We will refer
to this B<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

=over 4 

=item *

  pricing:q1,q5,q10:, ;10.00

This 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.

=item *

You can do lookups on a product attribute; I<size> in this case. 

  10.00, ==size:pricing

With this value in the C<common_adjust> field, a base price of 10.00 will be
adjusted with the value of the I<size> attribute. If size for the item
99-102 is set to C<XL> then 1.00 will be added for a total price of 11.00;
if it is C<S> then .50 will be subtracted for a total price of 9.50;
for any other value of I<size> no further adjustment would be made. 00-343
would be adjusted up 2.00 only for I<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

=item *

  pricing:q1,q5,q10:, ;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.

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.

=item *

  pricing:q1,q5,q10:, ;products:list_price, ==size:pricing, ==color:pricing

The value of the database column C<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.


Red Hat, Inc., 131 Willow Lane, Floor 2, Oxford, OH  45056
phone +1.513.523.7621 fax 7501 <mheins@redhat.com>

Nature, to be commanded, must be obeyed. -- Francis Bacon