package ShoppingCart;
# modules/ShoppingCart.pm
# This subclass of Session.pm handles the business logic side of the
# shopping cart application.
use strict;
use Session;
use Carp;
use DBI;
use CGI qw(p li ol strong cite);
use vars '@ISA';
@ISA = 'Session';
# called at initialization time to process any command-line arguments
sub initialize {
my $self = shift;
my $db = shift || croak "Need a database handle";
$self->{'db'} = $db;
}
# This gets called to create a new (blank) shopping cart session.
# It inserts a new record into the Session table, and returns the
# newly-created session_id
sub new_id {
my $self = shift;
return unless my $db = $self->{'db'};
return unless $db->do('INSERT INTO Session VALUES (NULL,NULL)');
return $db->func('_InsertID');
}
# verify_id() does a select on the Session table to verify that the
# given ID is in the table.
sub verify_id {
my ($self,$id) = @_;
return unless $id;
return unless my $db = $self->{'db'};
return $db->do("SELECT 1 FROM Session WHERE session_id=$id") > 0;
}
# verify_catalog() does a select on the ProductList table to confirm
# that a catalog number is valid
sub verify_catalog {
my ($self,$catalog) = @_;
return unless my $db = $self->{'db'};
return $db->do("SELECT 1 FROM ProductList WHERE catalog='$catalog'") > 0;
}
# erase_id() removes the id from the Session table and the ShoppingCart
# returns the number of rows affected by deletion -- only for interest's sake
sub erase_id {
my $self = shift;
return unless my $db = $self->{'db'};
return unless my $id = $self->id;
my $rows = $db->do("DELETE FROM Session WHERE session_id=$id");
$rows += $db->do("DELETE FROM ShoppingCart WHERE session_id=$id");
return $rows;
}
# return the contents of the shopping cart as a hash
sub fetch_data {
my $self = shift;
return unless my $db = $self->{'db'};
return unless my $id = $self->id;
my $sth = $db->prepare(<<END) || croak $db->errstr;
SELECT catalog,count FROM ShoppingCart
WHERE session_id=$id
END
$sth->execute || croak $db->errstr;
my %cart;
while (my ($catalog,$count) = $sth->fetchrow_array) {
$cart{$catalog} = $count;
}
return \%cart;
}
# store/update the contents of the shopping cart from a hash
sub store_data {
my $self = shift;
my $data = shift;
return unless my $db = $self->{'db'};
return unless my $id = $self->id;
$db->do('LOCK TABLES ShoppingCart WRITE, ProductList READ');
# The select is needed to determine whether to do an
# update or an insert
my $select = $db->prepare(<<END) || croak $db->errstr;
SELECT 1 FROM ShoppingCart WHERE catalog=? AND session_id=?
END
# the update statement assumes that the catalog item
# is already in the shopping cart, and the user is changing
# the quantity.
my $update = $db->prepare(<<END) || croak $db->errstr;
UPDATE ShoppingCart SET count=?
WHERE catalog=? AND session_id=?
END
# the insert statement assumes that a new item is being
# added to the shopping cart
my $insert = $db->prepare(<<END) || croak $db->errstr;
INSERT INTO ShoppingCart (count,catalog,session_id) VALUES (?,?,?)
END
for my $catalog (keys %$data) {
next unless $self->verify_catalog($catalog);
my $count = $data->{$catalog};
if ($select->execute($catalog,$id) > 0) {
$update->execute($count,$catalog,$id) || croak $db->errstr;
} else {
$insert->execute($count,$catalog,$id) || croak $db->errstr;
}
}
$update->finish;
$insert->finish;
$select->finish;
$db->do("DELETE FROM ShoppingCart WHERE session_id=$id AND count <=0");
$db->do('UNLOCK TABLES');
return 1;
}
# return the number of unobligated items in stock for the given catalog numer.
# sum of the stocklist + all active shopping carts.
sub items_left {
my $self = shift;
my $catalog = shift;
return unless my $db = $self->{'db'};
my $sth = $db->prepare(<<END) || $db->errstr;
SELECT quantity-sum(count)
FROM StockList,ShoppingCart
WHERE StockList.catalog='$catalog'
AND StockList.catalog=ShoppingCart.catalog
END
$sth->execute || return;
my ($count) = $sth->fetchrow_array;
$sth->finish;
return $count;
}
# Enter an order into the order entry system. Also updates the
# stocklist and removes the session. Argument ($data) must be a
# hash ref containining customer shipping and billing information
# (see code for details).
#
# If successful, returns the confirmation number. Otherwise
# returns an HTMLized description of the error.
sub place_order {
my $self = shift;
my $data = shift;
# default error message
$@ = p(strong({-class=>'alert'},'Software error'));
return unless my $db = $self->{'db'};
return unless my $id = $self->id;
return unless ref($data) eq 'HASH';
my @missing;
foreach (qw(name address1 city state country zip telephone ccno expires)) {
push(@missing,cite($_)) unless $data->{$_};
}
if (@missing) {
$@ = p(strong({-class=>'alert'},
'The following field(s) must be filled out',
'to place your order:')) .
ol(li(\@missing));
return;
}
# enter shipping/billing information into the database.
my $custinfo = $db->prepare(<<END) || die 'process_order: ',$db->errstr;
INSERT INTO CustInfo (order_id,name,address1,address2,city,state,
country,zip,telephone,ccno,expires)
VALUES(?,?,?,?,?,?,?,?,?,?,?)
END
my @vars = (undef,@{$data}{qw(name address1 address2 city state
country zip telephone ccno expires)});
unless ($custinfo->execute(@vars)) {
$@ = p(strong({-class=>'alert'},
'Unable to enter customer information into database.',
'Please contact customer service.'));
return;
}
my $orderid = $db->func("_InsertID");
$custinfo->finish;
#enter the order itself into the database
my $result = $db->do(<<END);
INSERT INTO OrderInfo (order_id,catalog,quantity)
SELECT $orderid,catalog,count
FROM ShoppingCart
WHERE session_id=$id
END
unless ($result > 0) {
$@ = p(strong({-class=>'alert'},
'Unable to enter your order into the database.',
'Please contact customer service.'));
return;
}
# Decrement stocklist to reflect the order.
# In the real world, this shouldn't be done until the item is
# shipped, but who cares about reality?
my $update_stock = $db->prepare(<<END) || return;
UPDATE StockList SET quantity=quantity-? WHERE catalog=?
END
my $items = $self->fetch_data;
for my $catalog (keys %$items) {
$update_stock->execute($items->{$catalog},$catalog) || return;
}
$update_stock->finish;
# delete the shopping cart -- it's now empty
$db->do("DELETE FROM ShoppingCart WHERE session_id=$id");
return $orderid;
}
1;
|