This chapter shows how to write SKUs, subclasses of WebGUI::Asset::Sku, which are assets that tie into the WebGUI Shop. Creating your own SKUs provides your customers a custom shopping experience.
WebGUI Shop comes with many SKUs that you can use as examples to guide you in writing your own SKUs. The Event Manager has four SKUs of its own: EMSTicket, EMSBadge, EMSRibbon, and EMSToken. In addition, there are the Product and Subscription SKUs, which can be used to generically sell any kind of products or recurring memberships. There are also some specialty SKUs, specifically the Donation, Ad Sales, Thingy Record and Flat Discount Coupon. It's wise to examine these SKUs before developing your own to get a feel for the effort involved, and to give you ideas of how to build your own.
The rich SKU API provides the power and flexibility needed to develop your own subclasses.
The most important method in all SKUs is the addToCart() method, because this is where the transaction begins. In some cases you may not need to modify the addToCart() method for your SKU, but in most cases you will probably either modify it or, at the very least, write a wrapper around it that calls it.
The main instance where you would want to modify the addToCart() method is if you have some book keeping functions to perform. For example, if this is a product with an inventory, you'll likely want to subtract the item out of inventory so that it can't be oversold.
The base addToCart() method accepts a hash reference of configuration properties and then calls the methods necessary to put this product into the cart. The configuration properties are used if you need to keep track of how the product is configured. For example, if it's a donation, you could put the donation amount in the properties. If it's a configurable product, like a computer, then you could store all of those configuration options in there. Whenever the cart, or other parts of the shop, interact with this SKU, it will restore this SKU with these options using the applyOptions() and getOptions() methods.
The addToCart() method is generally never called by any WebGUI subsystems. It's there for you to write a wrapper around and to call. Generally, you might create a www_addToCart() method that would be called by a user clicking on a button. How that happens is up to you.
The SKU provides a series of event handlers which help manage the purchase process. There are some that are specific to the type of SKU that you're building. Others are useful in all SKUs: onCompletePurchase(), onRefund(), and onRemoveFromCart().
onCompletePurchase() is called when a transaction has finalized and payment has been made. Use onCompletePurchase() to handle book keeping tasks like giving privileges, accounting for inventory, etc.
onRefund() is called when a shop manager clicks the Refund button in the transaction manager. Use onRefund() to take away privileges, restock inventory that was previously purchased, etc.
onRemoveFromCart() is called when an item is removed from the shopping cart before purchase. Use onRemoveFromCart() to restock items that you took out of inventory with the addToCart() method.
In a sale it all comes down to the numbers, so there are several methods that help you manage the numbers for each SKU.
The getMaxAllowedInCart() method should be overridden in the event you're selling a unique item (for instance a ticket that has been customized to a user).
The getPrice() method should be overridden to return the price of the SKU, and should calculate the price in the case of a configurable product.
The getQuantityAvailable() method should be overridden if there are a limited number of the SKU available for sale.
There are a couple of methods that should be overridden to help visually integrate your SKU with the Shop.
getConfiguredTitle() should return a descriptive title, such as “Red XL T-Shirt” rather than just “T-Shirt”.
getThumbnailUrl() should return the URL to a thumbnail of a picture of the SKU.
The most common archetype of a SKU is a product: a physical good set up for sale. There is a generic Product SKU, that comes with WebGUI, that can handle the needs of most users. However, there are many scenarios in which using a SKU to do just the right job makes sense. That's where you come in to write a new product SKU.
There are a few special methods that you'll need to know when writing this type of SKU.
The getWeight() method should be overridden to reflect the item's shipping weight.
The isShippingRequired() method should be overridden to return 1 if the good is non-digital (a book rather than a PDF), or if the item will be picked up rather than shipped (like will-call tickets at a box office).
The onAdjustQuantityInCart() method is an event handler, which is called when the user updates the quantity of an item in the cart. Use onAdjustQuantityInCart() to adjust inventory levels so that you don't over-sell an item.
This shows how to write a SKU as a product archetype through the example of selling a book. Books have several properties that are unique to each book. A normal Product SKU could do the job, but not as well as a SKU written specifically for selling books. This example uses the special properties of ISBN, Author, and Edition.
As with any asset, start by creating the definition(). Add the author, ISBN, and edition properties, as well as the weight and price of the book.
sub definition {
my ( $class, $session, $definition ) = @_;
tie my %properties, 'Tie::IxHash', (
isbn => {
tab => "properties",
fieldType => "text",
defaultValue => undef,
label => 'ISBN',
hoverHelp => 'The International Standard Book Number.',
},
author => {
tab => "properties",
fieldType => "text",
defaultValue => undef,
label => 'Author',
hoverHelp => 'Author\'s name or pen name.',
},
edition => {
tab => "properties",
fieldType => "text",
defaultValue => 'First Edition',
label => 'Edition',
hoverHelp => 'The printing number or name of the book.',
},
price => {
tab => "shop",
fieldType => "float",
defaultValue => 0,
label => 'Price',
hoverHelp => 'The amount this book sells for.',
},
weight => {
tab => "shop",
fieldType => "float",
defaultValue => 0,
label => 'Weight',
hoverHelp => 'How much in lbs does this book weigh?',
},
);
push @{$definition}, {
assetName => 'Book',
icon => 'assets.gif',
autoGenerateForms => 1,
tableName => 'Book',
className => 'WebGUI::Asset::Sku::Book',
properties => \%properties
};
return $class->SUPER::definition($session, $definition);
}
Because you're building a product archetype, fill out the getPrice() and getWeight() methods. Note that the price formatted is returned using sprintf. This saves you from having to do it everywhere that you want to display the price.
sub getPrice {
my ( $self ) = @_;
return sprintf "%.2f", $self->get('price');
}
sub getWeight {
my ( $self ) = @_;
return $self->get('weight');
}
Then, define your view() method. Normally, you would want to template this, but to keep it simple this example has left out that step. Instead, all the properties are displayed using a standard HTML Form.
sub view {
my ( $self ) = @_;
my $session = $self->session;
my $f = WebGUI::HTMLForm->new($session, action=>$self->getUrl);
$f->readOnly(
label => 'Title',
value => $self->get('title'),
);
$f->readOnly(
label => 'Edition',
value => $self->get('edition'),
);
$f->readOnly(
label => 'Description',
value => $self->get('description'),
);
$f->readOnly(
label => 'Author',
value => $self->get('author'),
);
$f->readOnly(
label => 'ISBN',
value => $self->get('isbn'),
);
$f->readOnly(
label => 'Price',
value => $self->getPrice,
);
$f->hidden(
name => "func",
value => "buy",
);
$f->submit( value => 'Add To Cart');
return $f->print;
}
In view() you defined a reference to a www_buy() method, so you must also create that. This is the wrapper that will call addToCart() for you.
sub www_buy {
my ( $self ) = @_;
my $session = $self->session;
return $session->privilege->noAccess() unless ($self->canView);
$self->addToCart;
return $self->getParent->www_view;
}
Note that you want www_buy() to follow the same view privileges as all other assets. If you can't view it, you shouldn't be able to buy it. In this way you can make some books available to only certain groups of purchasers.
That gives you a final class that looks similar to the one below. This is also uploaded to the add-ons area of webgui.org, so you can work directly from this code if you plan on making something similar.
package WebGUI::Asset::Sku::Book;
use strict;
use Tie::IxHash;
use base 'WebGUI::Asset::Sku';
use WebGUI::Utility;
use WebGUI::HTMLForm;
=head1 NAME
Package WebGUI::Asset::Sku::Book
=head1 DESCRIPTION
This sku allows you to sell books on your site with complete information.
=head1 SYNOPSIS
use WebGUI::Asset::Sku::Book;
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 definition ( session, definition )
Adds isbn, author, and edition fields.
=head3 session
=head3 definition
A hash reference passed in from a subclass definition.
=cut
sub definition {
my ( $class, $session, $definition ) = @_;
tie my %properties, 'Tie::IxHash', (
isbn => {
tab => "properties",
fieldType => "text",
defaultValue => undef,
label => 'ISBN',
hoverHelp => 'The International Standard Book Number.',
},
author => {
tab => "properties",
fieldType => "text",
defaultValue => undef,
label => 'Author',
hoverHelp => 'Author\'s name or pen name.',
},
edition => {
tab => "properties",
fieldType => "text",
defaultValue => 'First Edition',
label => 'Edition',
hoverHelp => 'The printing number or name of the book.',
},
price => {
tab => "shop",
fieldType => "float",
defaultValue => 0,
label => 'Price',
hoverHelp => 'The amount this book sells for.',
},
weight => {
tab => "shop",
fieldType => "float",
defaultValue => 0,
label => 'Weight',
hoverHelp => 'How much in lbs does this book weigh?',
},
);
push @{$definition}, {
assetName => 'Book',
icon => 'assets.gif',
autoGenerateForms => 1,
tableName => 'Book',
className => 'WebGUI::Asset::Sku::Book',
properties => \%properties
};
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 getPrice ()
Returns the value of the price field formatted as currency.
=cut
sub getPrice {
my ( $self ) = @_;
return sprintf "%.2f", $self->get('price');
}
#-------------------------------------------------------------------
=head2 getWeight ()
Returns the value of the price field formatted as currency.
=cut
sub getWeight {
my ( $self ) = @_;
return $self->get('weight');
}
#-------------------------------------------------------------------
=head2 indexContent ( )
Adding search keywords. See WebGUI::Asset::indexContent() for additonal details.
=cut
sub indexContent {
my ( $self ) = @_;
my $indexer = $self->SUPER::indexContent;
$indexer->addKeywords($self->get('author'), $self->get('isbn'), $self->get('edition'));
}
#-------------------------------------------------------------------
=head2 view ( )
method called by the container www_view method.
=cut
sub view {
my ( $self ) = @_;
my $session = $self->session;
my $f = WebGUI::HTMLForm->new($session, action=>$self->getUrl);
$f->readOnly(
label => 'Title',
value => $self->get('title'),
);
$f->readOnly(
label => 'Edition',
value => $self->get('edition'),
);
$f->readOnly(
label => 'Description',
value => $self->get('description'),
);
$f->readOnly(
label => 'Author',
value => $self->get('author'),
);
$f->readOnly(
label => 'ISBN',
value => $self->get('isbn'),
);
$f->readOnly(
label => 'Price',
value => $self->getPrice,
);
$f->hidden(
name => "func",
value => "buy",
);
$f->submit( value => 'Add To Cart');
return $f->print;
}
#-------------------------------------------------------------------
=head2 www_buy ()
Adds the book to the cart.
=cut
sub www_buy {
my ( $self ) = @_;
my $session = $self->session;
return $session->privilege->noAccess() unless ($self->canView);
$self->addToCart;
return $self->getParent->www_view;
}
1;
Recurring SKUs are often thought of as subscriptions or memberships. You pay every so often to see a support board, to read some articles about your favorite sports team, or to pay your dues to a club. In many of these cases the Subscription SKU that comes with WebGUI will serve the purpose perfectly. However, you may want to collect some data along with the subscription, so you'd create your own recurring SKU.
There are a few special methods you need to be aware of when writing a recurring SKU archetype.
The getRecurInterval() method should be overridden so that the Shop knows how often to process the recurring transaction.
The isRecurring() method should be overridden to return 1 so that the Shop knows this is a recurring SKU.
The onCancelRecurring() method is an event handler, which is called when a user or administrator cancels a recurring transaction. Use this to make privilege adjustments, send out emails, etc. However, be careful not to take away a user's privileges too early. This cancels the next transaction, but if you're working with a subscription, and that subscription has not already expired, then the user needs to maintain its current privileges, and lose them when the next transaction fires.
In clubs and associations, it's often necessary to collect dues and members' information. You may want to collect these simultaneously the first time, and then allow your members to update their data periodically thereafter. A great way to achieve this is to display a form of the data you want to capture with the add to cart button. For this example, data is collected and placed in the user's profile. That way the user can use the profile system to update the data going forward.
Start with the definition() method, which defines the memberGroupId (the group that holds your members) and the price fields.
sub definition {
my ( $class, $session, $definition ) = @_;
tie my %properties, 'Tie::IxHash', (
memberGroupId => {
tab => "properties",
fieldType => "group",
defaultValue => '3',
label => 'Member Group',
hoverHelp => 'The group holding your membership.',
},
price => {
tab => "shop",
fieldType => "float",
defaultValue => 0,
label => 'Price',
hoverHelp => 'The annual fee for this membership.',
},
);
push @{$definition}, {
assetName => 'Member Dues',
icon => 'assets.gif',
autoGenerateForms => 1,
tableName => 'MemberDues',
className => 'WebGUI::Asset::Sku::MemberDues',
properties => \%properties
};
return $class->SUPER::definition($session, $definition);
}
Then, you have to publish your price.
sub getPrice {
my ( $self ) = @_;
return sprintf "%.2f", $self->get('price');
}
Because this is a recurring SKU, you have to override the appropriate method to let the Shop know that. By defining isRecurring to return 1, getMaxAllowedInCart() will automatically also return 1. In this case, you're building a SKU that recurs annually, so you can hard code getRecurInterval(). In some cases you may want to make this a select box property so that the user can select the recur interval.
sub getRecurInterval {
return 'Yearly';
}
sub isRecurring {
return 1;
}
You need to set up the privileges on each recurrence, but you only want to update the membership data on the first recurrence. Do that with the onCompletePurchase() method.
sub onCompletePurchase {
my ($self, $item) = @_;
my $session = $self->session;
my $transaction = $item->transaction;
my $group = WebGUI::Group->new($session, $self->get('memberGroupId'));
$group->addUsers([$transaction->get('userId')],60*60*24*365);
if ($transaction->isFirst) { # this is the first instance of this transaction
my $user = WebGUI::User->new($session, $transaction->get('userId'));
my $options = $self->getOptions;
$user->update( {
email => $options->{email},
firstName => $options->{firstName},
lastName => $options->{lastName},
workPhone => $options->{workPhone},
);
}
}
Then, you can build out the view() method with your registration form.
sub view {
my ( $self ) = @_;
my $session = $self->session;
my ( $user ) = $session->quick(qw{ user });
my $f = WebGUI::HTMLForm->new($session, action=>$self->getUrl);
$f->readOnly(
label => 'Price',
value => $self->getPrice,
);
$f->hidden(
name => "func",
value => "buy",
);
$f->text(
name => 'firstName',
defaultValue => $user->profileField('firstName'),
label => 'First Name',
);
$f->text(
name => 'lastName',
defaultValue => $user->profileField('lastName'),
label => 'Last Name',
);
$f->email(
name => 'email',
defaultValue => $user->profileField('email'),
label => 'Email Address',
);
$f->phone(
name => 'workPhone',
defaultValue => $user->profileField('workPhone'),
label => 'Telephone Number',
);
$f->submit( value => 'Add To Cart');
my $output = sprintf '<h3>%s</h3><p>%s</p><p>%s</p>',
$self->getTitle, $self->get('description'), $f->print;
return $output;
}
Just like in the Book product, you have to create the www_buy() method, but this time you have some configuration options to store with the item. Note the use of that in the addToCart() method.
sub www_buy {
my ( $self ) = @_;
my $session = $self->session;
my ( $form ) = $session->quick(qw{ form });
return $session->privilege->noAccess() unless ($self->canView);
$self->addToCart({
workPhone => $form->get('workPhone','phone'),
email => $form->get('email','email'),
firstName => $form->get('firstName','text'),
lastName => $form->get('lastName','text'),
});
return $self->getParent->www_view;
}
All of the methods together give you an asset that looks like this:
package WebGUI::Asset::Sku::MemberDues;
use strict;
use Tie::IxHash;
use base 'WebGUI::Asset::Sku';
use WebGUI::Utility;
use WebGUI::HTMLForm;
use WebGUI::Group;
=head1 NAME
Package WebGUI::Asset::Sku::MemberDues
=head1 DESCRIPTION
This sku allows you to register new members to your organization.
=head1 SYNOPSIS
use WebGUI::Asset::Sku::MemberDues;
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 definition ( session, definition )
Adds memberGroupId and price fields.
=head3 session
=head3 definition
A hash reference passed in from a subclass definition.
=cut
sub definition {
my ( $class, $session, $definition ) = @_;
tie my %properties, 'Tie::IxHash', (
memberGroupId => {
tab => "properties",
fieldType => "group",
defaultValue => '3',
label => 'Member Group',
hoverHelp => 'The group holding your membership.',
},
price => {
tab => "shop",
fieldType => "float",
defaultValue => 0,
label => 'Price',
hoverHelp => 'The annual fee for this membership.',
},
);
push @{$definition}, {
assetName => 'Member Dues',
icon => 'assets.gif',
autoGenerateForms => 1,
tableName => 'MemberDues',
className => 'WebGUI::Asset::Sku::MemberDues',
properties => \%properties
};
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 getPrice ()
Returns the value of the price field formatted as currency.
=cut
sub getPrice {
my ( $self ) = @_;
return sprintf "%.2f", $self->get('price');
}
#-------------------------------------------------------------------
=head2 getRecurInterval ( )
Returns 'Yearly'.
=cut
sub getRecurInterval {
return 'Yearly';
}
#-------------------------------------------------------------------
=head2 isRecurring ( )
Returns 1.
=cut
sub isRecurring {
return 1;
}
sub onCompletePurchase {
my ($self, $item) = @_;
my $session = $self->session;
my $transaction = $item->transaction;
my $group = WebGUI::Group->new($session, $self->get('memberGroupId'));
$group->addUsers([$transaction->get('userId')],60*60*24*365);
if ($transaction->isFirst) { # this is the first instance of this transaction
my $user = WebGUI::User->new($session, $transaction->get('userId'));
my $options = $self->getOptions;
$user->update( {
email => $options->{email},
firstName => $options->{firstName},
lastName => $options->{lastName},
workPhone => $options->{workPhone},
);
}
}
#-------------------------------------------------------------------
sub view {
my ( $self ) = @_;
my $session = $self->session;
my ( $user ) = $session->quick(qw{ user });
my $f = WebGUI::HTMLForm->new($session, action=>$self->getUrl);
$f->readOnly(
label => 'Price',
value => $self->getPrice,
);
$f->hidden(
name => "func",
value => "buy",
);
$f->text(
name => 'firstName',
defaultValue => $user->profileField('firstName'),
label => 'First Name',
);
$f->text(
name => 'lastName',
defaultValue => $user->profileField('lastName'),
label => 'Last Name',
);
$f->email(
name => 'email',
defaultValue => $user->profileField('email'),
label => 'Email Address',
);
$f->phone(
name => 'workPhone',
defaultValue => $user->profileField('workPhone'),
label => 'Telephone Number',
);
$f->submit( value => 'Add To Cart');
my $output = sprintf '<h3>%s</h3><p>%s</p><p>%s</p>',
$self->getTitle, $self->get('description'), $f->print;
return $output;
}
#-------------------------------------------------------------------
=head2 www_buy ()
Adds the book to the cart.
=cut
sub www_buy {
my ( $self ) = @_;
my $session = $self->session;
my ( $form ) = $session->quick(qw{ form });
return $session->privilege->noAccess() unless ($self->canView);
$self->addToCart({
workPhone => $form->get('workPhone','phone'),
email => $form->get('email','email'),
firstName => $form->get('firstName','text'),
lastName => $form->get('lastName','text'),
});
return $self->getParent->www_view;
}
1;
Another powerful aspect of SKUs is that they know what else is in the cart and can adjust the price accordingly. This allows SKUs to be used as coupons. For example, you might detect that the user has placed an order for 10 packs of tube socks in the cart. You just happen to be running a special on tube socks, which gives the user a 20% discount if s/he buys 5 or more packages. Your coupon can look at the cart, determine if the user has met the criteria, and then return a negative price, thus creating a discount.
There are a couple of methods you should be aware of when creating a SKU as coupon archetype.
The getCart() method gives you a reference to the shopping cart (WebGUI::Shop::Cart) so that you can see what else is in the cart, to make your judgments about discounts to give.
You also need to override isCoupon() to return a 1, because some coupons may want to know if the user has other coupons in the cart, and only allow one coupon per cart.
Let's say that you have an online shop, which sells training materials for realtors, and that you also run a realtor association from your web site. You may want to give a discount to all the realtors that also belong to your association. So, create a coupon that your members can add to their carts that gives them their association discount.
Similar to the Member Dues example, start out your definition() method with a memberGroupId property. This time, instead of price, you have a discount field.
sub definition {
my ( $class, $session, $definition ) = @_;
tie my %properties, 'Tie::IxHash', (
memberGroupId => {
tab => "properties",
fieldType => "group",
defaultValue => '3',
label => 'Member Group',
hoverHelp => 'The group holding your membership.',
},
discount => {
tab => "shop",
fieldType => "integer",
defaultValue => 10,
label => 'Percentage Discount',
hoverHelp => 'The amount of discount in percent.',
},
);
push @{$definition}, {
assetName => 'Member Discount',
icon => 'assets.gif',
autoGenerateForms => 1,
tableName => 'MemberDiscount',
className => 'WebGUI::Asset::Sku::MemberDiscount',
properties => \%properties
};
return $class->SUPER::definition($session, $definition);
}
With the coupon archetype most of the work happens in the getPrice() method. Only give the discount if the user is a member of your group. Also note that when checking the items in the cart you skip yourself. If you didn't do this you would create an infinite loop.
sub getPrice {
my ( $self ) = @_;
my $session = $self->session;
my ( $user ) = $session->quick(qw{ user });
my $discount = 0;
if ($user->isInGroup($self->get('memberGroupId'))) {
foreach my $item (@{$self->getCart->getItems}) {
next if ($item->get('assetId') eq $self->getId); # prevent infinite loop
$discount += $item->sku->getPrice * $item->get('quantity') * $self->get('discount') * -1 / 100;
}
}
return sprintf "%.2f", $discount;
}
Because this is a coupon you have to tell the Shop that.
sub isCoupon {
return 1;
}
And your view() method looks very simple this time.
sub view {
my ( $self ) = @_;
my $session = $self->session;
my $f = WebGUI::HTMLForm->new($session, action=>$self->getUrl);
$f->hidden(
name => "func",
value => "addToCart",
);
$f->submit( value => 'Add To Cart');
my $output = sprintf '<h3>%s</h3><p>%s</p><p>%s</p>',
$self->getTitle, $self->get('description'), $f->print;
return $output;
}
But the www_addToCart() method looks pretty similar to the www_buy() method in the Book example.
sub www_addToCart {
my ( $self ) = @_;
return $self->session->privilege->noAccess() unless ($self->canView);
$self->addToCart;
return $self->getParent->www_view;
}
If you put all that together, you get an asset that looks similar to this:
package WebGUI::Asset::Sku::MemberDiscount;
use strict;
use Tie::IxHash;
use base 'WebGUI::Asset::Sku';
use WebGUI::Utility;
use WebGUI::HTMLForm;
=head1 NAME
Package WebGUI::Asset::Sku::MemberDiscount
=head1 DESCRIPTION
This sku gives members of a specific group a discount.
=head1 SYNOPSIS
use WebGUI::Asset::Sku::MemberDiscount;
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 definition ( session, definition )
Adds memberGroupId and discount fields.
=head3 session
=head3 definition
A hash reference passed in from a subclass definition.
=cut
sub definition {
my ( $class, $session, $definition ) = @_;
tie my %properties, 'Tie::IxHash', (
memberGroupId => {
tab => "properties",
fieldType => "group",
defaultValue => '3',
label => 'Member Group',
hoverHelp => 'The group holding your membership.',
},
discount => {
tab => "shop",
fieldType => "integer",
defaultValue => 10,
label => 'Percentage Discount',
hoverHelp => 'The amount of discount in percent.',
},
);
push @{$definition}, {
assetName => 'Member Discount',
icon => 'assets.gif',
autoGenerateForms => 1,
tableName => 'MemberDiscount',
className => 'WebGUI::Asset::Sku::MemberDiscount',
properties => \%properties
};
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 getPrice ()
Returns the discount formatted as currency.
=cut
sub getPrice {
my ( $self ) = @_;
my $session = $self->session;
my ( $user ) = $session->quick(qw{ user });
my $discount = 0;
if ($user->isInGroup($self->get('memberGroupId'))) {
foreach my $item (@{$self->getCart->getItems}) {
next if ($item->get('assetId') eq $self->getId); # prevent infinite loop
$discount += $item->sku->getPrice * $item->get('quantity') * $self->get('discount') * -1 / 100;
}
}
return sprintf "%.2f", $discount;
}
#-------------------------------------------------------------------
=head2 isCoupon ()
Returns 1.
=cut
sub isCoupon {
return 1;
}
#-------------------------------------------------------------------
=head2 view ( )
method called by the container www_view method.
=cut
sub view {
my ( $self ) = @_;
my $session = $self->session;
my $f = WebGUI::HTMLForm->new($session, action=>$self->getUrl);
$f->hidden(
name => "func",
value => "addToCart",
);
$f->submit( value => 'Add To Cart');
my $output = sprintf '<h3>%s</h3><p>%s</p><p>%s</p>',
$self->getTitle, $self->get('description'), $f->print;
return $output;
}
#-------------------------------------------------------------------
=head2 www_addToCart ()
Adds the coupon to the cart.
=cut
sub www_addToCart {
my ( $self ) = @_;
return $self->session->privilege->noAccess() unless ($self->canView);
$self->addToCart;
return $self->getParent->www_view;
}
1;
Keywords: ad sales API badge donation event flat discount coupon product ribbon shelf shop keeping unit SKU subcription thingy record ticket token