webgui/lib/WebGUI/Shop/Vendor.pm

704 lines
22 KiB
Perl

package WebGUI::Shop::Vendor;
use strict;
use Class::InsideOut qw{ :std };
use WebGUI::Shop::Admin;
use WebGUI::Exception::Shop;
use WebGUI::International;
use WebGUI::Utility qw{ isIn };
use List::Util qw{ sum };
use JSON qw{ encode_json };
=head1 NAME
Package WebGUI::Shop::Vendor
=head1 DESCRIPTION
Keeps track of vendors that sell merchandise in the store.
=head1 SYNOPSIS
use WebGUI::Shop::Vendor;
my $vendor = WebGUI::Shop::Vendor->new($session, $vendord);
=head1 METHODS
These subroutines are available from this package:
=cut
readonly session => my %session;
readonly properties => my %properties;
#-------------------------------------------------------------------
=head2 create ( session, properties )
Constructor. Creates a new vendor.
=head3 session
A reference to the current session.
=head3 properties
A hash reference containing the properties for this vendor. See update() for details.
=cut
sub create {
my ($class, $session, $properties) = @_;
unless (defined $session && $session->isa("WebGUI::Session")) {
WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Session", got=>(ref $session), error=>"Need a session.");
}
my $id = $session->id->generate;
$session->db->write("insert into vendor (vendorId, dateCreated) values (?, now())",[$id]);
my $self = $class->new($session, $id);
$self->update($properties);
return $self;
}
#-------------------------------------------------------------------
=head2 delete ()
Deletes this vendor.
=cut
sub delete {
my ($self) = @_;
$self->session->db->deleteRow("vendor","vendorId",$self->getId);
}
#-------------------------------------------------------------------
=head2 get ( [ property ] )
Returns a duplicated hash reference of this objectÕs data. See update() for details.
=head3 property
Any field returns the value of a field rather than the hash reference.
=head3 Additional properties
=head4 dateCreated
The date this vendor was created in the system.
=head4 vendorId
The id of this vendor from the database. Use getId() instead.
=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("vendorId");
}
#-------------------------------------------------------------------
=head2 getPayoutTotals ( )
Returns a hash ref, containing the payout details for this vendor. The keys in the hash are:
=head3 paid
The amount of money already transfered to the vendor.
=head3 scheduled
The amount of money scheduled to be transfered to the vendor.
=head3 notPaid
The amount of money that is yet to be scheduled for payment to the vendor.
=head3 total
The sum of these three values.
=cut
sub getPayoutTotals {
my $self = shift;
my %totals = $self->session->db->buildHash(
'select vendorPayoutStatus, sum(vendorPayoutAmount) as amount from transactionItem as t1, transaction as t2 '
.'where t1.transactionId = t2.transactionId and t2.isSuccessful <> 0 and vendorId=? group by vendorPayoutStatus ',
[ $self->getId ]
);
# Format the payout categories and calc the total those.
%totals =
map { lcfirst $_ => sprintf '%.2f', $totals{ $_ } }
qw( Paid Scheduled NotPaid );
$totals{ total } = sprintf '%.2f', sum values %totals;
return \%totals;
}
#-------------------------------------------------------------------
=head2 getVendors ( session, options )
Class method. Returns an array reference of WebGUI::Shop::Vendor objects.
=head3 session
A reference to the current session.
=head3 options
A hash reference of optional flags.
=head4 asHashRef
A boolean indicating that the vendors should be returned as a hash reference of id/names rather than an array of objects.
=cut
sub getVendors {
my ($class, $session, $options) = @_;
my $vendorList = $session->db->buildHashRef("select vendorId,name from vendor order by name");
if ($options->{asHashRef}) {
return $vendorList;
}
my @vendors = ();
foreach my $id (keys %{$vendorList}) {
push @vendors, $class->new($session, $id);
}
return \@vendors;
}
#-------------------------------------------------------------------
=head2 isVendorInfoComplete ( )
Returns a boolean indicating whether the payoutinformation entered by the vendor is complete.
=cut
sub isVendorInfoComplete {
my $self = shift;
my $complete =
defined $self->get( 'name' )
&& defined $self->get( 'userId' )
&& defined $self->get( 'preferredPaymentType' )
&& defined $self->get( 'paymentInformation' );
return $complete
}
#-------------------------------------------------------------------
=head2 new ( session, vendorId )
Constructor. Returns a WebGUI::Shop::Vendor object.
=head3 session
A reference to the current session. If the session variable is not passed, then an WebGUI::Error::InvalidObject
Exception will be thrown.
=head3 vendorId
A unique id for a vendor that already exists in the database. If the vendorId is not passed
in, then a WebGUI::Error::InvalidParam Exception will be thrown. If the requested Id cannot
be found in the database, then a WebGUI::Error::ObjectNotFound exception will be thrown.
=cut
sub new {
my ($class, $session, $vendorId) = @_;
unless (defined $session && $session->isa("WebGUI::Session")) {
WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Session", got=>(ref $session), error=>"Need a session.");
}
unless (defined $vendorId) {
WebGUI::Error::InvalidParam->throw( param=>$vendorId, error=>"Need a vendorId.");
}
my $vendor = $session->db->quickHashRef("select * from vendor where vendorId=?",[$vendorId]);
if ($vendor->{vendorId} eq "") {
WebGUI::Error::ObjectNotFound->throw(error=>"Vendor not found.", id=>$vendorId);
}
my $self = register $class;
my $id = id $self;
$session{ $id } = $session;
$properties{ $id } = $vendor;
return $self;
}
#-------------------------------------------------------------------
=head2 newByUserId ( session, [userId] )
Constructor.
=head3 session
A reference to the current session.
=head3 userId
A unique userId. Will pull from the session if not specified.
=cut
sub newByUserId {
my ($class, $session, $userId) = @_;
unless (defined $session && $session->isa("WebGUI::Session")) {
WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Session", got=>(ref $session), error=>"Need a session.");
}
$userId ||= $session->user->userId;
unless (defined $userId) {
WebGUI::Error::InvalidParam->throw( param=>$userId, error=>"Need a userId.");
}
return $class->new($session, $session->db->quickScalar("select vendorId from vendor where userId=?",[$userId]));
}
#-------------------------------------------------------------------
=head2 session ()
Returns a reference to the current session.
=cut
#-------------------------------------------------------------------
=head2 update ( properties )
Sets properties of the vendor
=head3 properties
A hash reference that contains one of the following:
=head4 name
The name of the vendor.
=head4 userId
The unique GUID of the vendor.
=head4 url
The vendor's url.
=head4 paymentInformation
????
=head4 preferredPaymentType
????
=cut
sub update {
my ($self, $newProperties) = @_;
my $id = id $self;
my @fields = (qw(name userId url paymentInformation preferredPaymentType));
foreach my $field (@fields) {
$properties{$id}{$field} = (exists $newProperties->{$field}) ? $newProperties->{$field} : $properties{$id}{$field};
}
$self->session->db->setRow("vendor","vendorId",$properties{$id});
}
#-------------------------------------------------------------------
=head2 www_delete ( )
Deletes a vendor.
=cut
sub www_delete {
my ($class, $session) = @_;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
my $self = $class->new($session, $session->form->get("vendorId"));
if (defined $self) {
$self->delete;
}
return $class->www_manage($session);
}
#-------------------------------------------------------------------
=head2 www_edit ( )
Displays an edit form for a vendor.
=cut
sub www_edit {
my ($class, $session) = @_;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
# get properties
my $self = eval{$class->new($session, $session->form->get("vendorId"))};
my $properties = {};
if (!WebGUI::Error->caught && defined $self) {
$properties = $self->get;
}
# draw form
my $i18n = WebGUI::International->new($session, "Shop");
my $f = WebGUI::HTMLForm->new($session);
$f->hidden(name=>'shop',value=>'vendor');
$f->hidden(name=>'method',value=>'editSave');
$f->hidden(name=>'vendorId',value=>$properties->{vendorId});
$f->readOnly(label=>$i18n->get('date created'),value=>$properties->{dateCreated});
$f->text(name=>'name', label=>$i18n->get('name'),value=>$properties->{name});
$f->user(name=>'userId',label=>$i18n->get('username'),value=>$properties->{userId},defaultValue=>3);
$f->url(name=>'url', label=>$i18n->get('company url'),value=>$properties->{url});
$f->text(name=>'preferredPaymentType', label=>$i18n->get('Preferred Payment Type'),value=>$properties->{preferredPaymentType});
$f->textarea(name=>'paymentInformation', label=>$i18n->get('Payment Information'),value=>$properties->{paymentInformation});
$f->submit();
# Wrap in admin console
my $console = $admin->getAdminConsole;
return $console->render($f->print, $i18n->get("vendors"));
}
#-------------------------------------------------------------------
=head2 www_editSave ( )
Saves the results of www_edit()
=cut
sub www_editSave {
my ($class, $session) = @_;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
my $form = $session->form;
my $properties = {
name => $form->get("name","text"),
preferredPaymentType => $form->get("preferredPaymentType","text"),
paymentInformation => $form->get("paymentInformation","textarea"),
userId => $form->get("userId","user",'3'),
url => $form->get("url","url"),
};
my $self = eval{$class->new($session, $form->get("vendorId"))};
if (!WebGUI::Error->caught && defined $self) {
$self->update($properties);
}
else {
$class->create($session, $properties);
}
return $class->www_manage($session);
}
#-------------------------------------------------------------------
=head2 www_manage ( )
Displays the list of vendors.
=cut
sub www_manage {
my ($class, $session) = @_;
my $admin = WebGUI::Shop::Admin->new($session);
my $i18n = WebGUI::International->new($session, "Shop");
return $session->privilege->adminOnly() unless ($admin->canManage);
# Button for adding a vendor
my $output = WebGUI::Form::formHeader($session)
.WebGUI::Form::hidden($session, { name => "shop", value => "vendor" })
.WebGUI::Form::hidden($session, { name => "method", value => "edit" })
.WebGUI::Form::submit($session, { value => $i18n->get("add a vendor") })
.WebGUI::Form::formFooter($session);
# Add a row with edit/delete buttons for each
foreach my $vendor (@{$class->getVendors($session)}) {
$output .= '<div style="clear: both;">'
# Delete button
.WebGUI::Form::formHeader($session, {extras=>'style="float: left;"' })
.WebGUI::Form::hidden($session, { name => "shop", value => "vendor" })
.WebGUI::Form::hidden($session, { name => "method", value => "delete" })
.WebGUI::Form::hidden($session, { name => "vendorId", value => $vendor->getId })
.WebGUI::Form::submit($session, { value => $i18n->get("delete"), extras => 'class="backwardButton"' })
.WebGUI::Form::formFooter($session)
# Edit button
.WebGUI::Form::formHeader($session, {extras=>'style="float: left;"' })
.WebGUI::Form::hidden($session, { name => "shop", value => "vendor" })
.WebGUI::Form::hidden($session, { name => "method", value => "edit" })
.WebGUI::Form::hidden($session, { name => "vendorId", value => $vendor->getId })
.WebGUI::Form::submit($session, { value => $i18n->get("edit"), extras => 'class="normalButton"' })
.WebGUI::Form::formFooter($session)
# Append name
.' '. $vendor->get("name")
.'</div>';
}
# Wrap in admin console
my $console = $admin->getAdminConsole;
return $console->render($output, $i18n->get("vendors"));
}
#-------------------------------------------------------------------
=head2 www_managePayouts ( )
Displays the payout manager.
=cut
sub www_managePayouts {
my $class = shift;
my $session = shift;
my $style = $session->style;
my $url = $session->url;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
# Load the required YUI stuff.
$style->setLink($url->extras('yui/build/paginator/assets/skins/sam/paginator.css'), {type=>'text/css', rel=>'stylesheet'});
$style->setLink($url->extras('yui/build/datatable/assets/skins/sam/datatable.css'), {type=>'text/css', rel=>'stylesheet'});
$style->setLink($url->extras('yui/build/button/assets/skins/sam/button.css'), {type=>'text/css', rel=>'stylesheet'});
$style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/element/element-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/connection/connection-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/json/json-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/paginator/paginator-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/datasource/datasource-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/datatable/datatable-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/button/button-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui-webgui/build/i18n/i18n.js'), {type=>'text/javascript'});
$style->setScript($url->extras('VendorPayout/vendorPayout.js'), {type=>'text/javascript'});
# Add css for scheduled payout highlighting
$style->setRawHeadTags(<<CSS);
<style type="text/css">
.yui-skin-sam .yui-dt tr.scheduled,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-asc,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-desc,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-asc,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-desc {
background-color : #080;
color : #fff;
}
</style>
CSS
my $output = q{<div id="vendorPayoutContainer" class="yui-skin-sam"></div>}
.q{<script type="text/javascript">var vp = new WebGUI.VendorPayout( 'vendorPayoutContainer' );</script>};
my $console = WebGUI::Shop::Admin->new($session)->getAdminConsole;
my $i18n = WebGUI::International->new($session, 'Shop');
return $console->render($output, $i18n->get('vendor payouts'));
}
#-------------------------------------------------------------------
=head2 www_payoutDataAsJSON ( )
Returns a JSON string containing paginated payout data for a specific vendor.
The following form params should be passed:
=head3 vendorId
The vendorId of the vendor you want the payout data for.
=head3 results
The number of results to be returned. Defaults to 100.
=head3 startIndex
The index of the record at which the payout data should start.
=cut
sub www_payoutDataAsJSON {
my $class = shift;
my $session = shift;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
my $vendorId = $session->form->process('vendorId');
my $startIndex = $session->form->process('startIndex');
my $rowsPerPage = $session->form->process('results') || 100;
my $pageNumber = int( $startIndex / $rowsPerPage ) + 1;
my $sql =
"select t1.* from transactionItem as t1 join transaction as t2 on t1.transactionId=t2.transactionId "
." where t2.isSuccessful <> 0 and vendorId=? and vendorPayoutAmount > 0 and vendorPayoutStatus <> 'Paid' order by t2.orderNumber";
my $placeholders = [ $vendorId ];
my $paginator = WebGUI::Paginator->new( $session, '', $rowsPerPage, '', $pageNumber );
$paginator->setDataByQuery( $sql, undef, 0, $placeholders );
my $data = {
totalRecords => $paginator->getRowCount,
results => $paginator->getPageData,
};
$session->http->setMimeType( 'application/json' );
return JSON::to_json( $data );
}
#-------------------------------------------------------------------
=head2 www_setPayoutStatus ( )
Sets the vendorPayoutStatus flag for each transaction passed by the form param 'itemId'. The new status is passed
by the form param 'status'. Status can either be 'NotPaid' or 'Scheduled' and may only be applied on items that do
not have their vendorPayoutStatus set to 'Paid'.
Returns the status to which the item(s) are set.
=cut
sub www_setPayoutStatus {
my $class = shift;
my $session = shift;
my ( $form, $db ) = $session->quick( qw{ form db } );
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
my $status = $form->process('status');
return "error: wrong status [$status]" unless isIn( $status, qw{ NotPaid Scheduled } );
my @itemIds;
if ( $form->process( 'all' ) ) {
@itemIds = $session->db->buildArray( 'select itemId from transactionItem where vendorPayoutStatus = ?' , [
( $status eq 'NotPaid' ) ? 'Scheduled' : 'NotPaid'
] );
}
else {
@itemIds = $form->process('itemId');
}
foreach my $itemId (@itemIds) {
my $item = WebGUI::Shop::TransactionItem->newByDynamicTransaction( $session, $itemId );
return "error: invalid transactionItemId [$itemId]" unless $item;
return "error: cannot change status of a Paid item" if $item->get('vendorPayoutStatus') eq 'Paid';
$item->update({ vendorPayoutStatus => $status });
}
$session->http->setMimeType( 'text/plain' );
return $status;
}
#-------------------------------------------------------------------
=head2 www_submitScheduledPayouts ()
Sets the vendorPayoutStatus flag of scheduled payments to 'Paid'.
NOTE: This method does no payments at all. In the future this method should trigger some automated payout
mechanism.
=cut
sub www_submitScheduledPayouts {
my $class = shift;
my $session = shift;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
$session->db->write(
q{ update transactionItem set vendorPayoutStatus = 'Paid' where vendorPayoutStatus = 'Scheduled' }
);
return $class->www_managePayouts( $session );
}
#-------------------------------------------------------------------
=head2 www_vendorTotalsAsJSON ( )
Returns a JSON string containing all vendors and their payout details. The following
form parameters can be passed:
=head3 vendorId
If passed, the results will include only the totals of this vendor.
=cut
sub www_vendorTotalsAsJSON {
my $class = shift;
my $session = shift;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
my $vendorId = $session->form->process('vendorId');
my ($vendorPayoutData, @placeholders);
my @sql;
push @sql, ' select vendorId, vendorPayoutStatus, sum(vendorPayoutAmount) as total ';
push @sql, ' from transactionItem as t1, transaction as t2 ';
push @sql, ' where t1.transactionId=t2.transactionId and isSuccessful <> 0 ';
push @sql, ' and vendorId=? ' if $vendorId;
push @sql, ' group by vendorId, vendorPayoutStatus ';
push @placeholders, $vendorId if $vendorId;
my $sth = $session->db->read( join( ' ', @sql) , \@placeholders );
while (my $row = $sth->hashRef) {
$vendorPayoutData->{ $row->{vendorId} }->{ $row->{vendorPayoutStatus} } = $row->{total};
}
$sth->finish;
my @dataset;
foreach my $vendorId (keys %{ $vendorPayoutData }) {
my $vendor = WebGUI::Shop::Vendor->new( $session, $vendorId );
my $dataset = {
%{ $vendor->get },
%{ $vendorPayoutData->{ $vendorId } },
};
my $user = WebGUI::User->new($session, $vendor->get('userId'));
$dataset->{name} .= ' ('.$user->username.')';
push @dataset, $dataset;
}
$session->http->setMimeType( 'application/json' );
return JSON::to_json( { vendors => \@dataset } );
}
1;