From 1fd425ac966cf8723d24b0218ccae77ce7438073 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 9 Jun 2008 21:52:59 +0000 Subject: [PATCH] Convert WebGUI::Shop::Products over to be object oriented. Update the tests and Content plugin for the Shop. Add the Products screen to the Shop admin screen. Add exception handling to www_import. Add a status message to the manage products screen. --- lib/WebGUI/Content/Shop.pm | 3 +- lib/WebGUI/Shop/Admin.pm | 1 + lib/WebGUI/Shop/Products.pm | 79 ++++++++++++++++++++++++++++----- lib/WebGUI/i18n/English/Shop.pm | 7 +++ t/Shop/Products.t | 53 +++++++++++----------- 5 files changed, 102 insertions(+), 41 deletions(-) diff --git a/lib/WebGUI/Content/Shop.pm b/lib/WebGUI/Content/Shop.pm index 70019ef61..9ff3e8b92 100644 --- a/lib/WebGUI/Content/Shop.pm +++ b/lib/WebGUI/Content/Shop.pm @@ -200,8 +200,9 @@ sub www_products { my $session = shift; my $output = undef; my $method = "www_".$session->form->get("method"); + my $products = WebGUI::Shop::Products->new($session); if ($method ne "www_" && WebGUI::Shop::Products->can($method)) { - $output = $WebGUI::Shop::Products->$method($session); + $output = $products->$method(); } else { WebGUI::Error::MethodNotFound->throw(error=>"Couldn't call non-existant method $method", method=>$method); diff --git a/lib/WebGUI/Shop/Admin.pm b/lib/WebGUI/Shop/Admin.pm index eb412a7ec..a468579a0 100644 --- a/lib/WebGUI/Shop/Admin.pm +++ b/lib/WebGUI/Shop/Admin.pm @@ -69,6 +69,7 @@ sub getAdminConsole { $ac->addSubmenuItem($url->page("shop=transaction;method=manage"), $i18n->get("transactions")); $ac->addSubmenuItem($url->page("shop=vendor;method=manage"), $i18n->get("vendors")); $ac->addSubmenuItem($url->page("shop=credit;method=manage"), $i18n->get("in shop credit")); + $ac->addSubmenuItem($url->page("shop=products;method=manage"), $i18n->get("products")); return $ac; } diff --git a/lib/WebGUI/Shop/Products.pm b/lib/WebGUI/Shop/Products.pm index bb5585f7b..7e8739522 100644 --- a/lib/WebGUI/Shop/Products.pm +++ b/lib/WebGUI/Shop/Products.pm @@ -2,6 +2,8 @@ package WebGUI::Shop::Products; use strict; +use Class::InsideOut qw{ :std }; + use WebGUI::Text; use WebGUI::Storage; use WebGUI::Exception::Shop; @@ -25,9 +27,11 @@ These subroutines are available from this package: =cut +readonly session => my %session; + #------------------------------------------------------------------- -=head2 exportProducts ( $session ) +=head2 exportProducts ( ) Export all products from the WebGUI system in a CSV file. For details about the file format, see importProducts. @@ -38,7 +42,8 @@ file will be named siteProductData.csv. =cut sub exportProducts { - my $session = shift; + my $self = shift; + my $session = $self->session; my @columns = qw{sku shortdescription price weight quantity}; my $productData = WebGUI::Text::joinCSV(qw{mastersku title}, @columns) . "\n"; @columns = map { $_ eq 'shortdescription' ? 'shortdesc' : $_ } @columns; @@ -60,7 +65,7 @@ sub exportProducts { #------------------------------------------------------------------- -=head2 importProducts ( $session, $filePath ) +=head2 importProducts ( $filePath ) Import products into the WebGUI system. If the master sku of a product exists in the system, it will be updated. If master skus do not exist, @@ -113,8 +118,9 @@ if old data has been deleted and new has been inserted. =cut sub importProducts { - my $session = shift; + my $self = shift; my $filePath = shift; + my $session = $self->session; WebGUI::Error::InvalidParam->throw(error => q{Must provide the path to a file}) unless $filePath; WebGUI::Error::InvalidFile->throw(error => qq{File could not be found}, brokenFile => $filePath) @@ -209,6 +215,24 @@ sub importProducts { #------------------------------------------------------------------- +=head2 new ( $session ) + +Constructor for the WebGUI::Shop::Products. Returns a WebGUI::Shop::Products object. + +=cut + +sub new { + my $class = shift; + my $session = shift; + my $self = {}; + bless $self, $class; + register $self; + $session{ id $self } = $session; + return $self; +} + +#------------------------------------------------------------------- + =head2 www_exportProducts ( ) Export all product SKUs as a CSV file. Returns a WebGUI::Storage @@ -223,7 +247,7 @@ sub www_exportProducts { return $session->privilege->insufficient unless $admin->canManage; my $storage = $self->exportProducts(); - $self->session->http->setRedirect($storage->getUrl($storage->getFiles->[0])); + $session->http->setRedirect($storage->getUrl($storage->getFiles->[0])); return "redirect"; } @@ -237,29 +261,53 @@ or alter existing products. =cut sub www_importProducts { - my $self = shift; + my $self = shift; my $session = $self->session; my $admin = WebGUI::Shop::Admin->new($session); return $session->privilege->insufficient unless $admin->canManage; my $storage = WebGUI::Storage->create($session); my $productFile = $storage->addFileFromFormPost('importFile', 1); - $self->importProducts($storage->getPath($productFile)) if $productFile; - return $self->www_manage; + eval { + $self->importProducts($storage->getPath($productFile)) if $productFile; + }; + my ($exception, $status_message); + if ($exception = Exception::Class->caught('WebGUI::Error::InvalidFile')) { + $status_message = sprintf 'A problem was found with your file: %s, %s', + $exception->brokenFile, + $exception->error; + if ($exception->brokenLine) { + $status_message .= sprintf ' on line %d', $exception->brokenLine; + } + } + elsif ($exception = Exception::Class->caught()) { + $status_message = sprintf 'A problem happened during the import: %s', $exception->error; + } + else { + my $i18n = WebGUI::International->new($session, 'Shop'); + $status_message = $i18n->get('import successful'); + } + return $self->www_manage($status_message); } #------------------------------------------------------------------- -=head2 www_manage ( ) +=head2 www_manage ( $status_message ) User interface to synchronize product data. Provides an interface for exporting all products on the site, and importing sets of products. +=head3 $status_message + +An status message generated when import or export is called that needs to be +displayed back to the user. + =cut sub www_manage { - my $self = shift; - my $session = $self->session; + my $self = shift; + my $status_message = shift; + my $session = $self->session; my $admin = WebGUI::Shop::Admin->new($session); return $session->privilege->insufficient unless $admin->canManage; @@ -277,7 +325,14 @@ sub www_manage { . q{} . WebGUI::Form::formFooter($session); - my $output =sprintf <%s +EODIV + } + + $output .= sprintf <%s%s EODIV diff --git a/lib/WebGUI/i18n/English/Shop.pm b/lib/WebGUI/i18n/English/Shop.pm index 8e5c52f42..e62888123 100644 --- a/lib/WebGUI/i18n/English/Shop.pm +++ b/lib/WebGUI/i18n/English/Shop.pm @@ -908,6 +908,13 @@ our $I18N = { lastUpdated => 1212550978, context => q|Label for taking data out of the Shop (Tax, Product, etc.)|, }, + + 'import successful' => { + message => q|Your products have been imported.|, + lastUpdated => 1213047491, + context => q|Message telling the user the their products have been imported successfully.| + }, + }; 1; diff --git a/t/Shop/Products.t b/t/Shop/Products.t index 20b68f1c6..ab441bc31 100644 --- a/t/Shop/Products.t +++ b/t/Shop/Products.t @@ -44,6 +44,8 @@ my $loaded = use_ok('WebGUI::Shop::Products'); my $storage; my ($e, $failure); +my $products = WebGUI::Shop::Products->new($session); + SKIP: { skip 'Unable to load module WebGUI::Shop::Products', $tests unless $loaded; @@ -56,12 +58,12 @@ SKIP: { my $importNode = WebGUI::Asset::Sku::Product->getProductImportNode($session); - eval { WebGUI::Shop::Products::importProducts($session); }; + eval { $products->importProducts(); }; $e = Exception::Class->caught(); isa_ok($e, 'WebGUI::Error::InvalidParam', 'importProducts: error handling for an undefined path to file'); is($e->error, 'Must provide the path to a file', 'importProducts: error handling for an undefined path to file'); - eval { WebGUI::Shop::Products::importProducts($session, '/path/to/nowhere'); }; + eval { $products->importProducts('/path/to/nowhere'); }; $e = Exception::Class->caught(); isa_ok($e, 'WebGUI::Error::InvalidFile', 'importProducts: error handling for file that does not exist in the filesystem'); is($e->error, 'File could not be found', 'importProducts: error handling for file that does not exist in the filesystem'); @@ -82,7 +84,7 @@ SKIP: { my $originalChmod = (stat $productsFile)[2]; chmod oct(0000), $productsFile; - eval { WebGUI::Shop::Products::importProducts($session, $productsFile); }; + eval { $products->importProducts($productsFile); }; $e = Exception::Class->caught(); isa_ok($e, 'WebGUI::Error::InvalidFile', 'importProducts: error handling for file that cannot be read'); is($e->error, 'File is not readable', 'importProducts: error handling for file that that cannot be read'); @@ -99,8 +101,7 @@ SKIP: { } eval { - $failure = WebGUI::Shop::Products::importProducts( - $session, + $failure = $products->importProducts( WebGUI::Test->getTestCollateralPath('productTables/missingHeaders.csv'), ); }; @@ -117,8 +118,7 @@ SKIP: { ); eval { - $failure = WebGUI::Shop::Products::importProducts( - $session, + $failure = $products->importProducts( WebGUI::Test->getTestCollateralPath('productTables/badHeaders.csv'), ); }; @@ -134,8 +134,7 @@ SKIP: { 'importProducts: error handling for a file with a missing header', ); - my $pass = WebGUI::Shop::Products::importProducts( - $session, + my $pass = $products->importProducts( WebGUI::Test->getTestCollateralPath('productTables/goodProductTable.csv'), ); ok($pass, 'Products imported'); @@ -195,15 +194,15 @@ SKIP: { # ####################################################################### - my $products = WebGUI::Shop::Products::exportProducts($session); - isa_ok($products, 'WebGUI::Storage', 'exportProducts returns a Storage object'); - is(scalar @{ $products->getFiles }, 1, 'The storage contains just 1 file...'); - is(scalar $products->getFiles->[0], 'siteProductData.csv', '...with the correct filename'); - my $productData = $products->getFileContentsAsScalar($products->getFiles->[0]); + my $productsOut = $products->exportProducts(); + isa_ok($productsOut, 'WebGUI::Storage', 'exportProducts returns a Storage object'); + is(scalar @{ $productsOut->getFiles }, 1, 'The storage contains just 1 file...'); + is(scalar $productsOut->getFiles->[0], 'siteProductData.csv', '...with the correct filename'); + my $productData = $productsOut->getFileContentsAsScalar($productsOut->getFiles->[0]); my @productData = split /\n/, $productData; is(scalar @productData, 4, 'productData should have 4 entries, 1 header + 3 data'); is($productData[0], 'mastersku,title,sku,shortdescription,price,weight,quantity', 'header line is okay'); - my @productData = map { [ WebGUI::Text::splitCSV($_) ] } @productData[1..3]; + @productData = map { [ WebGUI::Text::splitCSV($_) ] } @productData[1..3]; my ($sodas, $shirts); foreach my $productData (@productData) { if ($productData->[0] eq 'soda') { @@ -229,17 +228,16 @@ SKIP: { # ####################################################################### - $pass = WebGUI::Shop::Products::importProducts( - $session, + $pass = $products->importProducts( WebGUI::Test->getTestCollateralPath('productTables/secondProductTable.csv'), ); ok($pass, 'Products imported for the second time'); - my $count = $session->db->quickScalar('select count(*) from Product'); + $count = $session->db->quickScalar('select count(*) from Product'); is($count, 3, 'three products were imported'); - my $soda = WebGUI::Asset::Sku->newBySku($session, 'soda'); - my $sodaCollateral = $soda->getAllCollateral('variantsJSON'); + $soda = WebGUI::Asset::Sku->newBySku($session, 'soda'); + $sodaCollateral = $soda->getAllCollateral('variantsJSON'); cmp_deeply( $sodaCollateral, [ @@ -256,7 +254,7 @@ SKIP: { ); $shirt = WebGUI::Asset::Sku->newBySku($session, 't-shirt'); - my $shirtCollateral = $shirt->getAllCollateral('variantsJSON'); + $shirtCollateral = $shirt->getAllCollateral('variantsJSON'); cmp_deeply( $shirtCollateral, [ @@ -304,19 +302,18 @@ SKIP: { # ####################################################################### - $pass = WebGUI::Shop::Products::importProducts( - $session, + $pass = $products->importProducts( WebGUI::Test->getTestCollateralPath('productTables/thirdProductTable.csv'), ); ok($pass, 'Products imported for the third time'); - my $count = $session->db->quickScalar('select count(*) from Product'); + $count = $session->db->quickScalar('select count(*) from Product'); is($count, 3, 'still have 3 products, nothing new added'); - my $soda = WebGUI::Asset::Sku->newBySku($session, 'soda'); + $soda = WebGUI::Asset::Sku->newBySku($session, 'soda'); is($soda->getTitle(), 'Sweet Soda-totally organic', 'Title updated correctly for soda'); $shirt = WebGUI::Asset::Sku->newBySku($session, 't-shirt'); - my $shirtCollateral = $shirt->getAllCollateral('variantsJSON'); + $shirtCollateral = $shirt->getAllCollateral('variantsJSON'); cmp_deeply( $shirtCollateral, [ @@ -340,8 +337,8 @@ SKIP: { 'collateral updated correctly for shirt' ); - my $record = WebGUI::Asset::Sku->newBySku($session, 'classical-records-1'); - my $recordCollateral = $record->getAllCollateral('variantsJSON'); + $record = WebGUI::Asset::Sku->newBySku($session, 'classical-records-1'); + $recordCollateral = $record->getAllCollateral('variantsJSON'); cmp_deeply( $recordCollateral, [