diff --git a/docs/upgrades/upgrade_7.5.2-7.5.3.pl b/docs/upgrades/upgrade_7.5.2-7.5.3.pl index ecab3d86a..4d62c2a9a 100644 --- a/docs/upgrades/upgrade_7.5.2-7.5.3.pl +++ b/docs/upgrades/upgrade_7.5.2-7.5.3.pl @@ -31,10 +31,40 @@ createSkuAsset($session); createDonationAsset($session); addShippingDrivers($session); addShoppingHandler($session); +addAddressBook($session); finish($session); # this line required +#------------------------------------------------- +sub addAddressBook { + my $session = shift; + print "\tInstalling address book.\n" unless ($quiet); + $session->db->write("create table addressBook ( + addressBookId varchar(22) binary not null primary key, + sessionId varchar(22) binary, + userId varchar(22) binary, + lastPayId varchar(22) binary, + lastShipId varchar(22) binary, + index userId (sessionId), + index sessionId (sessionId) + )"); + $session->db->write("create table address ( + addressId varchar(22) binary not null primary key, + addressBookId varchar(22) binary not null, + label varchar(35), + name varchar(35), + address1 varchar(35), + address2 varchar(35), + address3 varchar(35), + state varchar(35), + country varchar(35), + code varchar(35), + phoneNumber varchar(35), + index addressBookId_addressId (addressBookId,addressId) + )"); +} + #------------------------------------------------- sub addShoppingHandler { my $session = shift; diff --git a/lib/WebGUI/Content/Shop.pm b/lib/WebGUI/Content/Shop.pm index f4d3a5154..890a475f3 100644 --- a/lib/WebGUI/Content/Shop.pm +++ b/lib/WebGUI/Content/Shop.pm @@ -16,6 +16,7 @@ package WebGUI::Content::Shop; use strict; use WebGUI::AdminConsole; +use WebGUI::Shop::AddressBook; use WebGUI::Shop::Cart; #use WebGUI::Shop::Pay; use WebGUI::Shop::Ship; @@ -79,6 +80,25 @@ sub www_cart { #------------------------------------------------------------------- +=head2 www_address () + +Hand off to the address book. + +=cut + +sub www_address { + my $session = shift; + my $output = undef; + my $method = "www_". ( $session->form->get("method") || "view"); + my $cart = WebGUI::Shop::AddressBook->create($session); + if ($cart->can($method)) { + $output = $cart->$method(); + } + return $output; +} + +#------------------------------------------------------------------- + =head2 www_manageSettings () Display the commerce settings page. diff --git a/lib/WebGUI/Shop/Address.pm b/lib/WebGUI/Shop/Address.pm new file mode 100644 index 000000000..cf3897d5b --- /dev/null +++ b/lib/WebGUI/Shop/Address.pm @@ -0,0 +1,231 @@ +package WebGUI::Shop::Address; + +use strict; +use Class::InsideOut qw{ :std }; +use WebGUI::Exception::Shop; + +=head1 NAME + +Package WebGUI::Shop::Address + +=head1 DESCRIPTION + +An address is used to track shipping or payment addresses in the commerce system. + +=head1 SYNOPSIS + + use WebGUI::Shop::Address; + + my $address = WebGUI::Shop::Address->new($addressBook, $addressId); + +=head1 METHODS + +These subroutines are available from this package: + +=cut + +readonly addressBook => my %addressBook; +private properties => my %properties; + +#------------------------------------------------------------------- + +=head2 addressBook ( ) + +Returns a reference to the Address Book. + +=cut + +#------------------------------------------------------------------- + +=head2 create ( addressBook, address) + +Constructor. Adds an address to an address book. Returns a reference to the address. + +=head3 addressBook + +A reference to a WebGUI::Shop::AddressBook object. + +=head3 address + +A hash reference containing the properties to set in the address. + +=cut + +sub create { + my ($class, $book, $addressData) = @_; + unless (defined $book && $book->isa("WebGUI::Shop::AddressBook")) { + WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Shop::AddressBook", got=>(ref $book), error=>"Need an address book.", param=>$book); + } + unless (defined $addressData && ref $addressData eq "HASH") { + WebGUI::Error::InvalidParam->throw(param=>$addressData, error=>"Need a hash reference."); + } + my $id = $book->session->db->setRow("addressBook","addressBookId", {addressId=>"new"}); + my $address = $class->new($book, $id); + $address->update($addressData); + return $address; +} + +#------------------------------------------------------------------- + +=head2 delete ( ) + +Removes this address from the book. + +=cut + +sub delete { + my $self = shift; + $self->addressBook->session->db->deleteRow("address","addressId",$self->getId); + undef $self; + return undef; +} + +#------------------------------------------------------------------- + +=head2 get ( [ property ] ) + +Returns a duplicated hash reference of this object’s data. + +=head3 property + +Any field − returns the value of a field rather than the hash reference. + +=cut + +sub get { + my ($self, $name) = @_; + if (defined $name) { + return $properties{id $self}{$name}; + } + my %copyOfHashRef = %{$properties{id $self}}; + return \%copyOfHashRef; +} + +#------------------------------------------------------------------- + +=head2 getId () + +Returns the unique id of this item. + +=cut + +sub getId { + my $self = shift; + return $self->get("addressId"); +} + + +#------------------------------------------------------------------- + +=head2 new ( addressBook, addressId ) + +Constructor. Instanciates an address based upon addressId. + +=head3 addressBook + +A reference to a WebGUI::Shop::AdressBook object. + +=head3 addressId + +The unique id of the address to instanciate. + +=cut + +sub new { + my ($class, $book, $addressId) = @_; + unless (defined $book && $book->isa("WebGUI::Shop::AddressBook")) { + WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Shop::AddressBook", got=>(ref $book), error=>"Need an address book."); + } + unless (defined $addressId) { + WebGUI::Error::InvalidParam->throw(error=>"Need an addressId.", param=>$addressId); + } + my $address = $book->session->db->quickHashRef('select * from address where addressId=?', [$addressId]); + if ($address->{addressId} eq "") { + WebGUI::Error::ObjectNotFound->throw(error=>"Address not found.", id=>$addressId); + } + if ($address->{addressBookId} ne $book->getId) { + WebGUI::Error::ObjectNotFound->throw(error=>"Address not in this address book.", id=>$addressId); + } + my $self = register $class; + my $id = id $self; + $addressBook{ $id } = $book; + $properties{ $id } = $address; + return $self; +} + + +#------------------------------------------------------------------- + +=head2 update ( properties ) + +Sets properties of the address. + +=head3 properties + +A hash reference that contains one or more of the following: + +=head4 label + +A human readable label like "home" or "work". + +=head4 name + +The name of the company or person to address this to. + +=head4 address1 + +The street name and number. + +=head4 address2 + +Suite number or other addressing information. + +=head4 address3 + +Care of info or other addressing information. + +=head4 city + +The city that this address is in. + +=head4 state + +The state or province that this address is in. + +=head4 code + +The postal code or zip code that this address is in. + +=head4 country + +The country that this address is in. + +=head4 phoneNumber + +A telephone number for this address. It is required by some shippers. + +=head4 addressBookId + +The address book that this address belongs to. + +=cut + +sub update { + my ($self, $newProperties) = @_; + my $id = id $self; + $properties{$id}{address2} = (exists $newProperties->{address2}) ? $newProperties->{address2} : $properties{$id}{address2}; + $properties{$id}{address3} = (exists $newProperties->{address3}) ? $newProperties->{address3} : $properties{$id}{address3}; + $properties{$id}{state} = (exists $newProperties->{state}) ? $newProperties->{state} : $properties{$id}{state}; + $properties{$id}{code} = $newProperties->{code} || $properties{$id}{code}; + $properties{$id}{city} = $newProperties->{city} || $properties{$id}{city}; + $properties{$id}{label} = $newProperties->{label} || $properties{$id}{label}; + $properties{$id}{name} = $newProperties->{name} || $properties{$id}{name}; + $properties{$id}{country} = $newProperties->{country} || $properties{$id}{country}; + $properties{$id}{address1} = $newProperties->{address1} || $properties{$id}{address1}; + $properties{$id}{phoneNumber} = $newProperties->{phoneNumber} || $properties{$id}{phoneNumber}; + $properties{$id}{addressBookId} = $self->addressBook->getId; + $self->addressBook->session->db->setRow("address","addressId",$properties{$id}); +} + + +1; diff --git a/lib/WebGUI/Shop/AddressBook.pm b/lib/WebGUI/Shop/AddressBook.pm new file mode 100644 index 000000000..84a9245db --- /dev/null +++ b/lib/WebGUI/Shop/AddressBook.pm @@ -0,0 +1,276 @@ +package WebGUI::Shop::AddressBook; + +use strict; + +use Class::InsideOut qw{ :std }; +use WebGUI::Asset::Template; +use WebGUI::Exception::Shop; +use WebGUI::International; +use WebGUI::Shop::Address; + +=head1 NAME + +Package WebGUI::Shop::AddressBook; + +=head1 DESCRIPTION + +Managing addresses for commerce. + +=head1 SYNOPSIS + + use WebGUI::Shop::AddressBook; + + my $book = WebGUI::Shop::AddressBook->new($session); + +=head1 METHODS + +These subroutines are available from this package: + +=cut + +readonly session => my %session; +private properties => my %properties; +private error => my %error; + +#------------------------------------------------------------------- + +=head2 addAddress ( address ) + +Adds an address to the address book. + +=head2 address + +A hash reference containing address information. + +=cut + +sub addAddress { + my ($self, $address) = @_; + my $addressObj = WebGUI::Shop::Address->create( $self, $address); + return $addressObj; +} + +#------------------------------------------------------------------- + +=head2 convertToUser ( userId ) + +Converts a session based address book to be owned by a user. If the user already has an address book then the address book will be merged with this one. + +=head3 userId + +The userId to own this address book. + +=cut + +sub convertToUser { + my ($self, $userId) = @_; + $self->update({userId=>$userId}); + my $other = $self->session->db->read("select addressBookId from addressBook where addressBookId<>? and userId=?", [$self->getId, $userId]); + while (my ($id) = $other->array) { + my $book = __PACKAGE__->new($self->session, $id); + foreach my $address (@{$book->getAddresses}) { + $address->update({addressBookId=>$self->getId}); + } + $book->delete; + } +} + + +#------------------------------------------------------------------- + +=head2 create ( session ) + +Constructor. Creates a new address book for this user if they don't have one. If the user is not logged in creates an address book attached to the session if there isn't one for the session. In any case returns a reference to the address book. + +=head3 session + +A reference to the current session. + +=cut + +sub create { + my ($class, $session) = @_; + unless (defined $session && $session->isa("WebGUI::Session")) { + WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Session", got=>(ref $session), error=>"Need a session."); + } + # check to see if we're dealing with a registered user or just a visitor + if ($session->user->userId ne "1") { + # check to see if this user or his session already has an address book + my $addressBookId = ""; + my @ids = $session->db->buildArray("select addressBookId from addressBook where userId=? or sessionId=?",[$session->user->userId, $session->getId]); + if (scalar(@ids) > 0) { + # how are we looking + my $book = $class->new($session, $ids[0]); + if ($book->get("userId") eq "" || scalar(@ids) > 1) { + # it's attached to the session or we have too many + $book->convertToUser($session->user->userId); + } + # it's ours + return $book; + } + else { + # nope create one for the user + my $id = $session->db->setRow("addressBook", "addressBookId", {addressBookId=>"new", userId=>$session->user->userId}); + return $class->new($session, $id); + } + } + else { + # check to see if this session already has an address book + my $addressBookId = $session->db->quickScalar("select addressBookId from addressBook where sessionId=?",[$session->getId]); + if ($addressBookId eq "") { + # nope, create one for the session + $addressBookId = $session->db->setRow("addressBook", "addressBookId", {addressBookId=>"new", sessionId=>$session->getId}); + } + return $class->new($session, $addressBookId); + } +} + +#------------------------------------------------------------------- + +=head2 delete () + +Deletes this address book and all addresses contained in it. + +=cut + +sub delete { + my ($self) = @_; + foreach my $address (@{$self->addresses}) { + $address->delete; + } + $self->session->db->write("delete from addressBook where addressBookId=?",[$self->getId]); + undef $self; + return undef; +} + +#------------------------------------------------------------------- + +=head2 get ( [ property ] ) + +Returns a duplicated hash reference of this object’s data. + +=head3 property + +Any field − returns the value of a field rather than the hash reference. + +=cut + +sub get { + my ($self, $name) = @_; + if (defined $name) { + return $properties{id $self}{$name}; + } + my %copyOfHashRef = %{$properties{id $self}}; + return \%copyOfHashRef; +} + +#------------------------------------------------------------------- + +=head2 getId () + +Returns the unique id for this cart. + +=cut + +sub getId { + my ($self) = @_; + return $self->get("addressBookId"); +} + +#------------------------------------------------------------------- + +=head2 getAddresses ( ) + +Returns an array reference of address objects that are in this book. + +=cut + +sub getAddresses { + my ($self) = @_; + my @addressObjects = (); + my $addresses = $self->session->db->read("select addressId from addresses where addressBookId=?",[$self->getId]); + while (my ($addressId) = $addresses->array) { + push(@addressObjects, WebGUI::Shop::Address->new($self, $addressId)); + } + return \@addressObjects; +} + +#------------------------------------------------------------------- + +=head2 new ( session, addressBookId ) + +Constructor. Instanciates a cart based upon a addressBookId. + +=head3 session + +A reference to the current session. + +=head3 addressBookId + +The unique id of an address book to instanciate. + +=cut + +sub new { + my ($class, $session, $addressBookId) = @_; + unless (defined $session && $session->isa("WebGUI::Session")) { + WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Session", got=>(ref $session), error=>"Need a session."); + } + unless (defined $addressBookId) { + WebGUI::Error::InvalidParam->throw(error=>"Need an addressBookId."); + } + my $addressBook = $session->db->quickHashRef('select * from addressBook where addressBookId=?', [$addressBookId]); + if ($addressBook->{addressBookId} eq "") { + WebGUI::Error::ObjectNotFound->throw(error=>"No such address book.", id=>$addressBookId); + } + my $self = register $class; + my $id = id $self; + $session{ $id } = $session; + $properties{ $id } = $addressBook; + return $self; +} + +#------------------------------------------------------------------- + +=head2 update ( properties ) + +Sets properties in the addressBook + +=head3 properties + +A hash reference that contains one of the following: + +=head4 lastShipId + +The last addressId used for shipping. + +=head4 lastPayId + +The last addressId used for payment. + +=head4 userId + +Assign the user that owns this address book. + +=head4 + +Assign the session that owns this adress book. Will automatically be set to "" if a user owns it. + +=cut + +sub update { + my ($self, $newProperties) = @_; + my $id = id $self; + $properties{$id}{lastShipId} = $newProperties->{lastShipId} || $properties{$id}{lastShipId}; + $properties{$id}{lastPayId} = $newProperties->{lastPayId} || $properties{$id}{lastPayId}; + $properties{$id}{userId} = (exists $newProperties->{userId}) ? $newProperties->{userId} : $properties{$id}{userId}; + $properties{$id}{sessionId} = (exists $newProperties->{sessionId}) ? $newProperties->{sessionId} : $properties{$id}{sessionId}; + if ($properties{$id}{userId} ne "") { + $properties{$id}{sessionId} = ""; + } + $self->session->db->setRow("addressBook","addressBookId",$properties{$id}); +} + + +1; + diff --git a/lib/WebGUI/Shop/Cart.pm b/lib/WebGUI/Shop/Cart.pm index 6e50f317e..484fa6362 100644 --- a/lib/WebGUI/Shop/Cart.pm +++ b/lib/WebGUI/Shop/Cart.pm @@ -49,9 +49,6 @@ A reference to a subclass of WebGUI::Asset::Sku. sub addItem { my ($self, $sku) = @_; - unless (defined $sku && $sku->isa("WebGUI::Asset::Sku")) { - WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Asset::Sku", got=>(ref $sku), error=>"Need a SKU item."); - } my $item = WebGUI::Shop::CartItem->create( $self, $sku); return $item; } @@ -306,7 +303,7 @@ sub www_view { extras=>q|onclick="this.form.method.value='removeItem';this.form.itemId.value='|.$item->getId.q|';this.form.submit;"|}), shippingAddress => "todo", shipToButton => WebGUI::Form::submit($session, {value=>$i18n->get("ship to button"), - extras=>q|onclick="this.form.shop.value='ship';this.form.method.value='viewAddressbook';this.form.itemId.value='|.$item->getId.q|';this.form.submit;"|}), + extras=>q|onclick="this.form.shop.value='address';this.form.method.value='view';this.form.itemId.value='|.$item->getId.q|';this.form.submit;"|}), ); push(@items, \%properties); } @@ -325,11 +322,11 @@ sub www_view { continueShoppingButton => WebGUI::Form::submit($session, {value=>$i18n->get("continue shopping button"), extras=>q|onclick="this.form.method.value='continueShopping';this.form.submit;"|}), chooseShippingButton => WebGUI::Form::submit($session, {value=>$i18n->get("choose shipping button"), - extras=>q|onclick="this.form.shop.value='ship';this.form.method.value='viewAddressbook';this.form.submit;"|}), + extras=>q|onclick="this.form.shop.value='address';this.form.method.value='view';this.form.submit;"|}), shipppingAddress => "todo", shippingOptions => "todo", shipToButton => WebGUI::Form::submit($session, {value=>$i18n->get("ship to button"), - extras=>q|onclick="this.form.shop.value='ship';this.form.method.value='viewAddressbook';this.form.submit;"|}), + extras=>q|onclick="this.form.shop.value='address';this.form.method.value='view';this.form.submit;"|}), hasShippingAddress => "todo", couponField => WebGUI::Form::text($session, {name=>"couponCode", value=>"", size=>20}), couponDiscount => "todo", diff --git a/lib/WebGUI/Shop/CartItem.pm b/lib/WebGUI/Shop/CartItem.pm index 970e124dd..43bc36cd7 100644 --- a/lib/WebGUI/Shop/CartItem.pm +++ b/lib/WebGUI/Shop/CartItem.pm @@ -150,9 +150,9 @@ sub incrementQuantity { #------------------------------------------------------------------- -=head2 new ( session, cart, itemId ) +=head2 new ( cart, itemId ) -Constructor. Instanciates a cart based upon a cartId. +Constructor. Instanciates a cart item based upon itemId. =head3 cart @@ -174,7 +174,7 @@ sub new { } my $item = $cart->session->db->quickHashRef('select * from cartItems where itemId=?', [$itemId]); if ($item->{itemId} eq "") { - WebGUI::Error::ObjectNotFound->throw(error=>"Item not in cart.", id=>$itemId); + WebGUI::Error::ObjectNotFound->throw(error=>"Item not found.", id=>$itemId); } if ($item->{cartId} ne $cart->getId) { WebGUI::Error::ObjectNotFound->throw(error=>"Item not in this cart.", id=>$itemId);