From 1d6aeaf2d41311c2d1bb6d1c797d2be195efb755 Mon Sep 17 00:00:00 2001 From: Martin Kamerbeek Date: Sat, 7 May 2005 16:37:01 +0000 Subject: [PATCH] Diverse bug and typo fixes to the recurring billing system. Also added some POD --- docs/upgrades/upgrade_6.6.0-6.6.1.sql | 2 +- lib/WebGUI/Commerce/Item.pm | 14 +++ lib/WebGUI/Commerce/Item/Subscription.pm | 2 +- lib/WebGUI/Commerce/Payment.pm | 24 ++++ lib/WebGUI/Commerce/Payment/ITransact.pm | 5 +- lib/WebGUI/Commerce/Shipping.pm | 139 ++++++++++++++++++++++- lib/WebGUI/Commerce/ShoppingCart.pm | 53 ++++++++- lib/WebGUI/Commerce/Transaction.pm | 112 +++++++++++++++++- lib/WebGUI/Operation.pm | 1 + lib/WebGUI/Operation/Commerce.pm | 2 +- sbin/Hourly/ProcessRecurringPayments.pm | 7 +- 11 files changed, 341 insertions(+), 20 deletions(-) diff --git a/docs/upgrades/upgrade_6.6.0-6.6.1.sql b/docs/upgrades/upgrade_6.6.0-6.6.1.sql index abcf540a0..e5319bbc7 100644 --- a/docs/upgrades/upgrade_6.6.0-6.6.1.sql +++ b/docs/upgrades/upgrade_6.6.0-6.6.1.sql @@ -1,2 +1,2 @@ insert into webguiVersion values ('6.6.1','upgrade',unix_timestamp()); - +insert into settings values ('commerceSendDailyReportTo', ''); diff --git a/lib/WebGUI/Commerce/Item.pm b/lib/WebGUI/Commerce/Item.pm index ef16ce74b..1ec98c0d4 100644 --- a/lib/WebGUI/Commerce/Item.pm +++ b/lib/WebGUI/Commerce/Item.pm @@ -36,6 +36,12 @@ These methods are available from this class: #------------------------------------------------------------------- +=head2 available ( ) + +Returns a boolean indicating that the item is available or not. + +=cut + sub available { return 1; } @@ -117,6 +123,14 @@ sub name { return WebGUI::ErrorHandler::fatal('The name method of WebGUI::Commerce::Item must be overridden.'); } +#------------------------------------------------------------------- + +=head2 needsShipping ( ) + +Return a boolean indicating whether the item needs to be shipped or not. Defaults to false. + +=cut + sub needsShipping { return 0; } diff --git a/lib/WebGUI/Commerce/Item/Subscription.pm b/lib/WebGUI/Commerce/Item/Subscription.pm index fa06f3e15..ff5f67d08 100644 --- a/lib/WebGUI/Commerce/Item/Subscription.pm +++ b/lib/WebGUI/Commerce/Item/Subscription.pm @@ -18,7 +18,7 @@ sub duration { #------------------------------------------------------------------- sub handler { - $_[0]->{_subscription}->apply; + $_[0]->{_subscription}->apply($_[1]); } #------------------------------------------------------------------- diff --git a/lib/WebGUI/Commerce/Payment.pm b/lib/WebGUI/Commerce/Payment.pm index 82e49cd4f..c45c0411e 100644 --- a/lib/WebGUI/Commerce/Payment.pm +++ b/lib/WebGUI/Commerce/Payment.pm @@ -436,11 +436,35 @@ sub recurringPeriodValues { } #------------------------------------------------------------------- + +=head2 shippingCost ( amount ) + +This sets the shippingcost involved with the transaction. Your plugin must override this +method. + +=head3 amount + +The amaount of money that's being charged for shipping. + +=cut + sub shippingCost { return WebGUI::ErrorHandler::fatal("You must override the shippingCost method in the payment plugin."); } #------------------------------------------------------------------- + +=head2 shippingDescription ( message ) + +This method sets the description for the shipping cost of the transaction. You must overload +this method if you are writing a custom plugin. + +=head3 message + +The description of the shiping cost. + +=cut + sub shippingDescription { return WebGUI::ErrorHandler::fatal("You must override the shippingDescription method in the payment plugin."); } diff --git a/lib/WebGUI/Commerce/Payment/ITransact.pm b/lib/WebGUI/Commerce/Payment/ITransact.pm index aa7021820..78ba0244e 100644 --- a/lib/WebGUI/Commerce/Payment/ITransact.pm +++ b/lib/WebGUI/Commerce/Payment/ITransact.pm @@ -11,6 +11,7 @@ use WebGUI::International; use LWP::UserAgent; use XML::Simple; use HTTP::Cookies; +use WebGUI::SQL; our @ISA = qw(WebGUI::Commerce::Payment); @@ -523,7 +524,7 @@ sub getRecurringPaymentStatus { $recurringId = shift; $term = shift || 1; - my %resolve = { + my %resolve = ( weekly => 7*3600*24, biweekly => 14*3600*24, fourweekly => 28*3600*24, @@ -531,7 +532,7 @@ sub getRecurringPaymentStatus { quarterly => 91*3600*24, halfyearly => 182*3600*24, yearly => 365*3600*24 - }; + ); my $transactionData = WebGUI::SQL->quickHashRef("select * from ITransact_recurringStatus where gatewayId=".quote($recurringId)); diff --git a/lib/WebGUI/Commerce/Shipping.pm b/lib/WebGUI/Commerce/Shipping.pm index 91b6e2169..4a3ff234b 100644 --- a/lib/WebGUI/Commerce/Shipping.pm +++ b/lib/WebGUI/Commerce/Shipping.pm @@ -5,11 +5,27 @@ use WebGUI::SQL; use WebGUI::HTMLForm; use WebGUI::Commerce::ShoppingCart; +#------------------------------------------------------------------- + +=head2 calc + +Returns the calculated shipping cost. Your plugin must override this method. + +=cut sub calc { return WebGUI::ErrorHanlder::fatal('The calc method must be overriden.'); }; +#------------------------------------------------------------------- + +=head2 description + +Returns a description of the shipping configuration. Defaults to the name of your plugin +if you do not overload this method. + +=cut + sub description { return $_[0]->name; } @@ -70,10 +86,28 @@ sub get { return $_[0]->{_properties}{$_[1]}; } +#------------------------------------------------------------------- + +=head2 getOptions + +Returns a hash containing the parameters of a user configurable shipping method. If +your shipping plugin has an options form you should overload this method. + +=cut + sub getOptions { return {}; } +#------------------------------------------------------------------- + +=head2 getShippingItems + +Returns an arrayref containing the items, marked for shipping. If no items are set +using setShippingOptions it this method will default to the shopping cart of the user. + +=cut + sub getShippingItems { my ($normal, $recurring, @allItems, @items, $self); $self = shift; @@ -142,6 +176,12 @@ sub init { #------------------------------------------------------------------- +=head2 getShoppingCart + +Returns a WebGUI::Commerce::ShoppingCart object of the current user. + +=cut + sub getShoppingCart { return $_[0]->{_shoppingCart}; }; @@ -151,7 +191,7 @@ sub getShoppingCart { =head2 load ( namespace ) A convienient method to load a plugin. It handles all error checking and stuff for you. -This is a SUPER class method only and shoud NOT be overridden. +This is a SUPER class method only and should NOT be overridden. =head3 namespace @@ -199,6 +239,16 @@ sub namespace { return $_[0]->{_namespace}; } +#------------------------------------------------------------------- + +=head2 optionsOk + +Indicates whether the options loaded into the plugin (by using either setOptions or processOptionsForm) +are correct. If your plugin is able of being configured by an options form you must overload this method. +Defaults to true. + +=cut + sub optionsOk { return 1; } @@ -232,12 +282,46 @@ sub prepend { return "~Shipping~".$self->namespace."~".$name; } +#------------------------------------------------------------------- + +=head2 processOptionsForm + +Processes the submitted form variables from the optionsForm and stores them +into the plugin. You only need to overload this method if your plugin is capable +of using user configurable options. + +=cut + sub processOptionsForm { } +#------------------------------------------------------------------- + +=head2 setOptions ( options ) + +Stores the supplied option hash into the plugin object. + +=head3 options + +Hashref containing the options. + +=cut + sub setOptions { } +#------------------------------------------------------------------- + +=head2 setShippingItems ( items ) + +Sets the items the shipping is to be calculated for. + +=head3 items + +Arrayref containing the items. + +=cut + sub setShippingItems { my ($self, $items); $self = shift; @@ -246,18 +330,69 @@ sub setShippingItems { $self->{_shippingItems} = $items; } +#------------------------------------------------------------------- + +=head2 supportsTracking + +Returns a boolean indicating whether this plugin supports tracking of the shipment. +Overload this method if your plugin does. Defaults to false. + +=cut + sub supportsTracking { return 0; } +#------------------------------------------------------------------- + +=head2 trackingInfo + +Returns a message containing information about the shipment tracking (ie. where the +package is or something like that). If your plugin support these tracking, you probably +want to overload this method. Defaults to "". + +=cut + sub trackingInfo { - return {}; + return ""; } +#------------------------------------------------------------------- + +=head2 trackingNumber + +Returns the tracking ID supplied by the shipment company. If your plugin supports tracking +you'll have to overload this method. Defaults to undef. + +=cut + sub trackingNumber { return undef; } +#------------------------------------------------------------------- + +=head2 trackingUrl + +Returns the URL where the user can go to either fill in the tracking number or view the tracking +info of his package. Overload this method if your plugin supports tracking. Defaults to undef. + +=cut + +sub trackingUrl { + return undef; +} + +#------------------------------------------------------------------- + +=head2 optionsOk + +This method returns a boolean indicating wheter the supplied options (loaded into the plugin +by either setOptions or processOptionsForm) are valid. Overload if your plugin support configuration +options. Defaults to true. + +=cut + sub optionsOk { return 1; }; diff --git a/lib/WebGUI/Commerce/ShoppingCart.pm b/lib/WebGUI/Commerce/ShoppingCart.pm index 516b550c8..4e5932242 100644 --- a/lib/WebGUI/Commerce/ShoppingCart.pm +++ b/lib/WebGUI/Commerce/ShoppingCart.pm @@ -20,7 +20,12 @@ shopping cart is tied to the sessionId and, thus, expires when the sessionId exp $shoppingCart = WebGUI::Commerce::ShoppingCart->new; $shoppingCart->add('myItemId', 'myItem', 3); -$shoppingCart->empty; +$shoppingCart->setQuantity('myItemId', 'myItem', 2); + +$shoppingCart->delete('myItemId', 'myItem'); # These two lines are equivalent; +$shoppingCart->setQuantity('myItemId', 'myItem', 0); # + +$shoppingCart->empty; # Remove contents from cart ($normal, $recurring) = $shoppingCart->getItems; $normal->[0]->{quantity} # quantity of first normal item @@ -76,6 +81,21 @@ sub add { } #------------------------------------------------------------------- + +=head2 delete ( itemId, itemType ) + +Deletes the item identified by the passed parameters from the cart. + +=head3 itemId + +The id of the item to delete. + +=head3 itemType + +the type (namespace) of the item to delete. + +=cut + sub delete { my ($self, $itemId, $itemType); @@ -90,6 +110,32 @@ sub delete { } #------------------------------------------------------------------- + +=head2 setQuantity ( itemId, itemType, quantity ) + +Sets the quantity of an item (identified by itemId and itemType) in the shopping +cart. When quantity is set to zero or a negative number, the item will be deleted +from the cart. + +This method only operates on items that are already in the cart. You cannot use it +to add new items to the cart. In order to that use the add method. + +Generates a fatal error when the quantity is not a number. + +=head3 itemId + +The is of item you want to set the quantity for. + +=head3 itemType + +The type (namespace) of the item. + +=head3 quantity + +The quantity you want to set the item to. + +=cut + sub setQuantity { my ($self, $itemId, $itemType, $quantity); $self = shift; @@ -111,7 +157,7 @@ sub setQuantity { =head2 empty ( ) -Invoking this method will putrge all content from the shopping cart. +Invoking this method will purge all content from the shopping cart. =cut @@ -155,9 +201,6 @@ The total price of this item. Ie. totalPrice = quantity * price. The instanciated plugin of this item. See WebGUI::Commerce::Item for a detailed API. -For example: - - =cut sub getItems { diff --git a/lib/WebGUI/Commerce/Transaction.pm b/lib/WebGUI/Commerce/Transaction.pm index 85caa3927..331d84eda 100644 --- a/lib/WebGUI/Commerce/Transaction.pm +++ b/lib/WebGUI/Commerce/Transaction.pm @@ -117,6 +117,26 @@ sub deleteItem { } +#------------------------------------------------------------------- + +=head2 deleteItem ( itemId, itemType ) + +Deletes an item from a transaction. This will purge the record from the database, and +updates the amount of the transaction. It doesn't change the shipping cost however. + +Also if you want to credit the user (you'll probably want to) for the amount of the +removed items, you must do this yourself. + +=head3 itemId + +The id of the item you want to remove. + +=head3 itemType + +The type of the item you want to remove. + +=cut + #------------------------------------------------------------------- sub deleteItem { my ($self, $itemId, $itemType, $amount, @items); @@ -243,7 +263,7 @@ sub getByGatewayId { $gatewayId = shift; $paymentGateway = shift; - ($transactionId) = WebGUI::SQL->quickArray("select transaction Id from transaction where gatewayId=".quote($gatewayId). + ($transactionId) = WebGUI::SQL->quickArray("select transactionId from transaction where gatewayId=".quote($gatewayId). " and gateway=".quote($paymentGateway)); return WebGUI::Commerce::Transaction->new($transactionId) if $transactionId; @@ -264,6 +284,36 @@ sub getItems { } #------------------------------------------------------------------- + +=head2 getTransactions ( constraints ) + +Returns an array consisting of WebGUI::Commerce::Transaction objects complying to +the passed constraints. + +=head3 constraints + +A hashref containing the contrains by which the transactions are selected. These can be: + + * initStart + Epoch that specifies the lower bounds on the initialisation date. + + * initStop + Epoch that specifies the upper bound on the initialisation date. + + * completionStart + Epoch specifying the lower bound on the completion date. + + * completionStop + Epoch specifying the upper bound on the completion date. + + * status + The status of the transaction. Can be: Pending, Completed or Canceled + + * shippingStatus + The shipping status of the transaction. Can be: NotShipped, Shipped or Delivered + +=cut + sub getTransactions { my ($self, $criteria, @constraints, $sql, @transactionIds, @transactions); @@ -272,8 +322,8 @@ sub getTransactions { push (@constraints, 'initDate >= '.quote($criteria->{initStart})) if (defined $criteria->{initStart}); push (@constraints, 'initDate <= '.quote($criteria->{initStop})) if (defined $criteria->{initStop}); - push (@constraints, 'initDate >= '.quote($criteria->{completionStart})) if (defined $criteria->{completionStart}); - push (@constraints, 'initDate >= '.quote($criteria->{completionStop})) if (defined $criteria->{completionStop}); + push (@constraints, 'completionDate >= '.quote($criteria->{completionStart})) if (defined $criteria->{completionStart}); + push (@constraints, 'completionDate <= '.quote($criteria->{completionStop})) if (defined $criteria->{completionStop}); push (@constraints, 'status='.quote($criteria->{paymentStatus})) if (defined $criteria->{paymentStatus}); push (@constraints, 'shippingStatus='.quote($criteria->{shippingStatus})) if (defined $criteria->{shippingStatus}); @@ -407,6 +457,17 @@ sub pendingTransactions { } #------------------------------------------------------------------- + +=head2 shippingCost ( [amount] ) + +Returns the shipping cost for this transaction. If amount is supplied the sipping cost will +be set to that value. + +=head3 amount +If supplied the shipping cost of the transaction will be set to this value. + +=cut + sub shippingCost { my ($self, $shippingCost); $self = shift; @@ -421,6 +482,17 @@ sub shippingCost { } #------------------------------------------------------------------- + +=head2 shippingMethod ( [ method ] ) + +Returns the shipping method for this transaction. If amount is supplied the shipping method will +be set to it. + +=head3 method +If supplied the shipping method of the transaction will be set to this value. + +=cut + sub shippingMethod { my ($self, $shippingMethod); $self = shift; @@ -435,6 +507,18 @@ sub shippingMethod { } #------------------------------------------------------------------- + +=head2 shippingOptions ( [ options ] ) + +Returns the shipping options for this transaction. If options is supplied the shipping options will +be set to it. + +=head3 options +If supplied the shipping options of the transaction will be set to this value. This should probably +be some serialized datastructure. + +=cut + sub shippingOptions { my ($self, $shippingOptions); $self = shift; @@ -449,6 +533,17 @@ sub shippingOptions { } #------------------------------------------------------------------- + +=head2 shippingStatus ( [ status ] ) + +Returns the shipping status for this transaction. If status is supplied the shipping status will +be set to it. + +=head3 status +If supplied the shipping status of the transaction will be set to this value. + +=cut + sub shippingStatus { my ($self, $shippingStatus); $self = shift; @@ -489,6 +584,17 @@ sub status { #------------------------------------------------------------------- + +=head2 trackingNumber ( [ number ] ) + +Returns the tracking number of the shipped transaction. If numer is supplied the tracking number will +be set to it. + +=head3 number +If supplied the tracking number of the transaction will be set to this value. + +=cut + sub trackingNumber { my ($self, $trackingNumber); $self = shift; diff --git a/lib/WebGUI/Operation.pm b/lib/WebGUI/Operation.pm index ba49fc5f0..eac44f05b 100644 --- a/lib/WebGUI/Operation.pm +++ b/lib/WebGUI/Operation.pm @@ -192,6 +192,7 @@ sub getOperations { 'deleteSubscriptionCodes' => 'WebGUI::Operation::Subscription', 'addToCart' => 'WebGUI::Operation::Commerce', + 'confirmRecurringTransaction' => 'WebGUI::Operation::Commerce', 'checkout' => 'WebGUI::Operation::Commerce', 'checkoutConfirm' => 'WebGUI::Operation::Commerce', 'checkoutSubmit' => 'WebGUI::Operation::Commerce', diff --git a/lib/WebGUI/Operation/Commerce.pm b/lib/WebGUI/Operation/Commerce.pm index f1e0db519..d26c0d3d6 100644 --- a/lib/WebGUI/Operation/Commerce.pm +++ b/lib/WebGUI/Operation/Commerce.pm @@ -327,8 +327,8 @@ sub www_completePendingTransaction { #------------------------------------------------------------------- sub www_confirmRecurringTransaction { my($plugin, %var); + $plugin = WebGUI::Commerce::Payment->load($session{form}{gateway}); - if ($plugin) { $plugin->confirmRecurringTransaction; } diff --git a/sbin/Hourly/ProcessRecurringPayments.pm b/sbin/Hourly/ProcessRecurringPayments.pm index 689ea7782..0f78e4ab7 100644 --- a/sbin/Hourly/ProcessRecurringPayments.pm +++ b/sbin/Hourly/ProcessRecurringPayments.pm @@ -20,9 +20,6 @@ sub _getDuration { return addToDate(0,1,0,0) if $duration eq 'Yearly'; } - - - sub process { my @recurringTransactions = WebGUI::SQL->buildArray("select transactionId from transaction where recurring=1 and status='Completed'"); @@ -34,7 +31,7 @@ sub process { my $time = time; $time -= $transaction->get('initDate'); my $term = int($time / _getDuration($item->duration)) + 1; - + if ($term > $transaction->lastPayedTerm) { my $payment = WebGUI::Commerce::Payment->load($transaction->gateway); @@ -52,7 +49,7 @@ sub process { } elsif ($status->{resultCode} eq '0') { $output .= "OK"; push (@ok, $output); - $item->apply unless ($term == 1); + $item->handler($transaction->get(userId)) unless ($term == 1); $transaction->lastPayedTerm($term); } else { $output .= "PAYMENT FAILED: ".$status->{resultCode};