[ic] mv_price and CommonAdjust

Jim Balcom interchange-users@icdevgroup.org
Tue Oct 15 17:58:00 2002


On Tue, 15 Oct 2002, John Allman wrote:

JA>>Thanks Karen and Ed. I think i've finally got my head around
JA>>CommonAdjust! I think i have it all working the way i want now. The only
JA>>extra bit of advice i have for anyone else in this situation is to make
JA>>sure that there's nothing Autoload-ed into every page that fiddles with
JA>>your CommonAdjust or your $Config->{PriceField}.

To to either confuse you, or to clear things up, here are 3 pieces that Mike
Heins wrote in April 2001. If you have already seen these, I'm sorry for the
duplication.

------------------------------------

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.

=back

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

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

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

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.

=back

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

=back

----------------------------------

A little known fact about CommonAdjust is that you don't have to have
them all be one, you can maintain multiple ones. Say you have as your
default:

    PriceField   common_adjust
    CommonAdjust  pricing:q12,q24,q48,q96: ;:price

    pricing.txt:
    sku	q12	q24	q48	q96
    AP-S		0.75	0.50

    products.txt slice:
    sku	price	common_adjust
    AP-S	1.00

If item AP-S has nothing in the field common_adjust, it uses the above.
If another item needs different and custom breaks:

    # Same as above, repeated
    PriceField   common_adjust
    CommonAdjust  pricing:q12,q24,q48,q96: ;:price

    price_special.txt:
    sku	q18	q36	q72
    OR-S	0.90	0.75	0.50

    products.txt slice:
    sku	price	common_adjust
    OR-S	1.00	price_special:q18,q36,q72 ;:price

Now OR-S uses the CommonAdjust string from the database and ignores
the default one.

---------------------------------

The normal way to do this would be to have your pricing table with fields
corresponding to the quantities.
The problem with your pricing structure is that the price breaks are not
consistent.

pricing table:
sku	q12	q15	q16	q18	q20	q24	q48	q72	q75	q80	q96
AP-S				0.75				0.50
AP-M						1.25					1
etc...

PriceField	nonexistant_field
CommonAdjust   pricing:q12,q15,q16,q18,q20,q48,q72,q75,q80,q96: ;:price

This commonadjust setting says (starting at the right)
 get the price from the price field in the products table.
 if there is one, replace it with the price from the pricing table field
which corresponds (<=) to the qty specified in the tag, or in the basket.




-= Jim =-

                   .-.
                   /v\
                  // \\
                 /(   )\
                 ^^ - ^^
          > Phear the Penguin <
              L  I  N  U  X

----------------------------------------------------------------
Jim's Linux-Operated Underground Bomb Shelter

Tagline for Tuesday, October 15, 2002 at 17:50 PM:
Interchangeable parts won't.

----------------------------------------------------------------
This Linux System has been up 47 hours

My web page: http://www.idk-enterprises.com
----------------------------------------------------------------