Note: This describes the cart only; not any interface to edit items being sold.
I. behavoir of shopping cart
A. cart persistence:
each user should have one cart, accessible from any computer (i.e. not stored in cookies). expiration of the items after a given time period (to save server space, presumably) is optional.
B. modes (views and actions):
1. overview of functions to start with
mode name description params
--------------------------------------------
view view cart
add add to cart item identifier
remove remove from cart item identifier
clear remove all from cart
update update items ids, quantities, ship prefs
checkout go to billing,etc
2 view mode: item data
This is the default mode. A table shows contents of cart with the following for each item in the cart:
a. item description, with optional link, etc.
b. quantity, enclosed in a text field so it can be sent to the Update mode.
c. unit price. including any special discounts.
d. subtotal (price * quantity)
3. view mode: other elements
a. current shipping cost (if applicable)
b. shipping preferences input(s) (if applicable)
c. Total price
d. submit buttons for Update and Checkout
3. Add mode:
accessed from outside the cart somewhere.
adds an item and goes to view mode.
4. Update mode:
accessed by from view mode.
updates the cart display and returns to view mode.
5. Remove mode:
accessed by from view mode.
removes an item from the cart and returns to view mode.
6. Clear mode:
accessed by from view mode.
removes all items and returns to view mode.
7. Checkout mode:
accessed by from view mode.
Includes such inputs as shipping address (if applicable) and billing info.
This form will submit back to checkout mode, to get the input data checked out and redisplay the form(s) if necessary. Depending on the params, other messages and/or inputs can be shown. Eventually the order will be finalized and the user may receive a reference number and a link to the nearest fun page.
C. integration with other functions
1. wishlist (some prospects for a future feature):
items in this list do not have associated quantities, and the date of addition is somewhat a useful datum.
a user can share his list to be viewed by others.
a transfer function would move items into the shopcart.
II. DB schemas
A.
cart_items
----------
cart_item_id [PK]
uid NN
class NN
field NN
value NN
quantity NN
entry_time NN
unique index on (uid,class,field,value).
class+field+value uniquely identifies the item,
for example book/isbn/999999000 would refer to a unique record in the "book" table, where isbn="9999999000"
B. a different option
Alternatively, there could be an additional table "store_item" tying a itemid to a class+field+value. That way the price can be stored here too, and the lookup would use be a faster key.
so cart_items would look like
cart_items
----------
cart_item_id [PK]
uid NN
itemid NN
quantity NN
entry_time NN
and we'd have another table
store_item
----------
itemid PK
class
field
value
price NN
category
label
url
unique index on (class,field,value)
This option requires an additional select for each item (to the store_item table), but the better index may have better scaleability. Notice the added fields label,url -- in case class,field,and value are Null, a simple store item can be fully represented in this table (without matching a record in another table to complete the data).
what do you think?
in any case, i'll continue writing assuming class-field-value approach.
C.
The following table allows varying prices, eg:
1.
During the month of october all premium users will receive a 90% discount on book A when purchased with book B.
2.
Get a 20% discount on the purchase of 3 or more books in the "trashy" category.
price_specials
-------------
price_special_id [PK]
class NN
field NN
value NN
cond_name NN
cond_value
price_multipler NN
only_group
start_date
end_date
priority
unique index on (class,field,value,cond_name,only_group).
Some values for cond_name include:
item_quantity
category_quantity
cart_quantity
item_combo
cart_total
III. CODE
A. location:
the business logic should be kept in boxes, but some of the more general control and utility routines can go in Scoop/Cart.pm
B. caching:
not much room for a cart cache if price_specials is used, because a price can vary by date so it may become outdated.
Another cause for outdated prices even without using price_specials would be if the cart cache were not reset when item prices change. since item prices would be kept in whatever tables the items data are in, cache clearing would have to be added to the functions that update those tables.
C. a new OP, say "cart", would be needed.
D. functions
1. cart function (associated with OP)
a switch box... checks the "action" param and calls a corresponding function.
param->('action') function
--------------------------------
checkout checkout
add add
remove remove
clear remove
update update
[default] view
2. action functions listed above
Any of these could be boxes, but more likely they'll be part of the Scoop package with "cart_" prefixed to the names above. I guess a "cart" class might be more elegant but worse in performance... but subclassing for alternative checkout methods might be quite useful. what do you think?
The checkout function has high flexibility needs, so if it should probably include little more than calls to boxes that handle shipping inputs, billing inputs, form checking, and processing.
3. $S->{CART} would hold the current users cart.
We could have a function
sub cart {$->[0]->{CART} ||= $->[0]->cart_construct}
4. the function that gathers data about user's cart and assembles it into a hash. it's important so i'll put an example here...
sub cart_construct {
my $rect = {};
my ($rv, $sth) = $S->db_select({
WHAT=>'cart_item_id,class,field,value,quantity',
FROM > 'cart_item',
WHERE => "uid = $S->{UID}",
ORDER_BY => 'cart_item_id' });
while (my $item = $sth->fetchrow_hashref) {
## we must get a default price for this item,
## as well as an optional category (used for the
## price_special condition known as category_quantity)
($item->{price},$item->{category}) = $S->sale_item($item->{class},$item->{field},$item->{value});
push @{$cart->{ITEMS}}, $item;
$cart->{TOTAL} +
$item->{price};
}
## adjust item prices according to current conditions
$S->cart_apply_specials($cart);
return $cart;
}
Well that's it for now, until i see whether anyone cares to sift thru this abstrusity...