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 .= '
' # 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") .'
'; } # 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(< .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; } CSS my $output = q{
} .q{}; 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;