From 2b5c0c4b3bc8d7bee57345a300030388eaa4c95e Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 25 Apr 2013 09:45:07 -0700 Subject: [PATCH] Add "Carts" to Phortune Summary: Although I imagine we aren't really going to have an "add to cart" type storefront, putting this in place makes a lot of other workflows simpler. No storage yet, just allows reasonable construction of a "buy stuff" page. Test Plan: {F41342} Reviewers: chad, btrahan Reviewed By: chad CC: aran Maniphest Tasks: T2787 Differential Revision: https://secure.phabricator.com/D5745 --- src/__phutil_library_map__.php | 4 +- .../phid/PhabricatorPHIDConstants.php | 1 + .../PhortuneAccountBuyController.php | 96 +++++++++++++++++-- .../controller/PhortuneController.php | 15 +++ .../PhortuneProductViewController.php | 15 ++- .../phortune/storage/PhortuneCart.php | 38 ++++++++ .../storage/PhortunePaymentMethod.php | 4 + 7 files changed, 160 insertions(+), 13 deletions(-) create mode 100644 src/applications/phortune/storage/PhortuneCart.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 7ee198dcb7..0f2538fe67 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1580,6 +1580,7 @@ phutil_register_library_map(array( 'PhortuneAccountTransaction' => 'applications/phortune/storage/PhortuneAccountTransaction.php', 'PhortuneAccountTransactionQuery' => 'applications/phortune/query/PhortuneAccountTransactionQuery.php', 'PhortuneAccountViewController' => 'applications/phortune/controller/PhortuneAccountViewController.php', + 'PhortuneCart' => 'applications/phortune/storage/PhortuneCart.php', 'PhortuneCharge' => 'applications/phortune/storage/PhortuneCharge.php', 'PhortuneController' => 'applications/phortune/controller/PhortuneController.php', 'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php', @@ -3293,6 +3294,7 @@ phutil_register_library_map(array( 'PhortuneAccountTransaction' => 'PhabricatorApplicationTransaction', 'PhortuneAccountTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhortuneAccountViewController' => 'PhortuneController', + 'PhortuneCart' => 'PhortuneDAO', 'PhortuneCharge' => 'PhortuneDAO', 'PhortuneController' => 'PhabricatorController', 'PhortuneDAO' => 'PhabricatorLiskDAO', @@ -3318,7 +3320,7 @@ phutil_register_library_map(array( 'PhortuneProductQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhortuneProductTransaction' => 'PhabricatorApplicationTransaction', 'PhortuneProductTransactionQuery' => 'PhabricatorApplicationTransactionQuery', - 'PhortuneProductViewController' => 'PhabricatorController', + 'PhortuneProductViewController' => 'PhortuneController', 'PhortunePurchase' => 'PhortuneDAO', 'PhortuneStripePaymentFormView' => 'AphrontView', 'PhrequentController' => 'PhabricatorController', diff --git a/src/applications/phid/PhabricatorPHIDConstants.php b/src/applications/phid/PhabricatorPHIDConstants.php index 05d0751c61..6ed089f91f 100644 --- a/src/applications/phid/PhabricatorPHIDConstants.php +++ b/src/applications/phid/PhabricatorPHIDConstants.php @@ -38,6 +38,7 @@ final class PhabricatorPHIDConstants { const PHID_TYPE_PRCH = 'PRCH'; const PHID_TYPE_PAYM = 'PAYM'; const PHID_TYPE_CHRG = 'CHRG'; + const PHID_TYPE_CART = 'CART'; const PHID_TYPE_XACT = 'XACT'; const PHID_TYPE_XCMT = 'XCMT'; diff --git a/src/applications/phortune/controller/PhortuneAccountBuyController.php b/src/applications/phortune/controller/PhortuneAccountBuyController.php index ea993556d9..10158a78e7 100644 --- a/src/applications/phortune/controller/PhortuneAccountBuyController.php +++ b/src/applications/phortune/controller/PhortuneAccountBuyController.php @@ -33,7 +33,87 @@ final class PhortuneAccountBuyController return new Aphront404Response(); } - $title = pht('Buy %s', $product->getProductName()); + $purchase = new PhortunePurchase(); + $purchase->setProductPHID($product->getPHID()); + $purchase->setAccountPHID($account->getPHID()); + $purchase->setPurchaseName($product->getProductName()); + $purchase->setBasePriceInCents($product->getPriceInCents()); + $purchase->setQuantity(1); + $purchase->setTotalPriceInCents( + $purchase->getBasePriceInCents() * $purchase->getQuantity()); + $purchase->setStatus(PhortunePurchase::STATUS_PENDING); + + $cart = new PhortuneCart(); + $cart->setAccountPHID($account->getPHID()); + $cart->setOwnerPHID($user->getPHID()); + $cart->attachPurchases( + array( + $purchase, + )); + + $rows = array(); + $total = 0; + foreach ($cart->getPurchases() as $purchase) { + $rows[] = array( + $purchase->getPurchaseName(), + PhortuneUtil::formatCurrency($purchase->getBasePriceInCents()), + $purchase->getQuantity(), + PhortuneUtil::formatCurrency($purchase->getTotalPriceInCents()), + ); + + $total += $purchase->getTotalPriceInCents(); + } + + $rows[] = array( + phutil_tag('strong', array(), pht('Total')), + '', + '', + phutil_tag('strong', array(), PhortuneUtil::formatCurrency($total)), + ); + + $table = new AphrontTableView($rows); + $table->setHeaders( + array( + pht('Item'), + pht('Price'), + pht('Qty.'), + pht('Total'), + )); + + $panel = new AphrontPanelView(); + $panel->setNoBackground(true); + $panel->appendChild($table); + + + $title = pht('Buy Stuff'); + + + $methods = id(new PhortunePaymentMethodQuery()) + ->setViewer($user) + ->withAccountPHIDs(array($account->getPHID())) + ->withStatus(PhortunePaymentMethodQuery::STATUS_OPEN) + ->execute(); + + $method_control = id(new AphrontFormRadioButtonControl()) + ->setLabel(pht('Payment Method')); + + if (!$methods) { + $method_control = id(new AphrontFormStaticControl()) + ->setLabel(pht('Payment Method')) + ->setValue( + phutil_tag('em', array(), pht('No payment methods configured.'))); + } else { + $method_control = id(new AphrontFormRadioButtonControl()) + ->setLabel(pht('Payment Method')) + ->setName('paymentMethodID') + ->setValue($request->getInt('paymentMethodID')); + foreach ($methods as $method) { + $method_control->addButton( + $method->getID(), + $method->getName(), + $method->getDescription()); + } + } $payment_method_uri = $this->getApplicationURI( $account->getID().'/paymentmethod/edit/'); @@ -46,16 +126,9 @@ final class PhortuneAccountBuyController ), pht('Add New Payment Method')); - $form = id(new AphrontFormView()) ->setUser($user) - ->appendChild( - id(new AphrontFormStaticControl()) - ->setLabel(pht('Stuff')) - ->setValue($product->getProductName())) - ->appendChild( - id(new AphrontFormRadioButtonControl()) - ->setLabel(pht('Payment Method'))) + ->appendChild($method_control) ->appendChild( id(new AphrontFormMarkupControl()) ->setValue($new_method)) @@ -64,7 +137,10 @@ final class PhortuneAccountBuyController ->setValue(pht("Dolla Dolla Bill Y'all"))); return $this->buildApplicationPage( - $form, + array( + $panel, + $form, + ), array( 'title' => $title, 'device' => true, diff --git a/src/applications/phortune/controller/PhortuneController.php b/src/applications/phortune/controller/PhortuneController.php index 21c2f395ad..70c0d44e93 100644 --- a/src/applications/phortune/controller/PhortuneController.php +++ b/src/applications/phortune/controller/PhortuneController.php @@ -2,6 +2,21 @@ abstract class PhortuneController extends PhabricatorController { + protected function loadActiveAccount(PhabricatorUser $user) { + $accounts = id(new PhortuneAccountQuery()) + ->setViewer($user) + ->withMemberPHIDs(array($user->getPHID())) + ->execute(); + + if (!$accounts) { + return $this->createUserAccount($user); + } else if (count($accounts) == 1) { + return head($accounts); + } else { + throw new Exception("TODO: No account selection yet."); + } + } + protected function createUserAccount(PhabricatorUser $user) { $request = $this->getRequest(); diff --git a/src/applications/phortune/controller/PhortuneProductViewController.php b/src/applications/phortune/controller/PhortuneProductViewController.php index dbabf8303d..a0a8942e09 100644 --- a/src/applications/phortune/controller/PhortuneProductViewController.php +++ b/src/applications/phortune/controller/PhortuneProductViewController.php @@ -1,6 +1,6 @@ setHeader($product->getProductName()); + $account = $this->loadActiveAccount($user); + $edit_uri = $this->getApplicationURI('product/edit/'.$product->getID().'/'); + $cart_uri = $this->getApplicationURI( + $account->getID().'/buy/'.$product->getID().'/'); $actions = id(new PhabricatorActionListView()) ->setUser($user) @@ -34,7 +38,14 @@ final class PhortuneProductViewController extends PhabricatorController { id(new PhabricatorActionView()) ->setName(pht('Edit Product')) ->setHref($edit_uri) - ->setIcon('edit')); + ->setIcon('edit')) + ->addAction( + id(new PhabricatorActionView()) + ->setUser($user) + ->setName(pht('Purchase')) + ->setHref($cart_uri) + ->setIcon('new') + ->setRenderAsForm(true)); $crumbs = $this->buildApplicationCrumbs(); $crumbs->setActionList($actions); diff --git a/src/applications/phortune/storage/PhortuneCart.php b/src/applications/phortune/storage/PhortuneCart.php new file mode 100644 index 0000000000..48fca94565 --- /dev/null +++ b/src/applications/phortune/storage/PhortuneCart.php @@ -0,0 +1,38 @@ + true, + self::CONFIG_SERIALIZATION => array( + 'metadata' => self::SERIALIZATION_JSON, + ), + ) + parent::getConfiguration(); + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID( + PhabricatorPHIDConstants::PHID_TYPE_CART); + } + + public function attachPurchases(array $purchases) { + assert_instances_of($purchases, 'PhortunePurchase'); + $this->purchases = $purchases; + return $this; + } + + public function getPurchases() { + if ($this->purchases === null) { + throw new Exception("Purchases not attached to cart!"); + } + return $this->purchases; + } + +} diff --git a/src/applications/phortune/storage/PhortunePaymentMethod.php b/src/applications/phortune/storage/PhortunePaymentMethod.php index 4aa0b11d03..5a38c79e9e 100644 --- a/src/applications/phortune/storage/PhortunePaymentMethod.php +++ b/src/applications/phortune/storage/PhortunePaymentMethod.php @@ -46,6 +46,10 @@ final class PhortunePaymentMethod extends PhortuneDAO return $this->account; } + public function getDescription() { + return pht('Expires %s', date('m/y'), $this->getExpiresEpoch()); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */