diff --git a/resources/sql/autopatches/20141005.phortuneproduct.sql b/resources/sql/autopatches/20141005.phortuneproduct.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20141005.phortuneproduct.sql @@ -0,0 +1,22 @@ +DROP TABLE {$NAMESPACE}_phortune.phortune_producttransaction; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_product + DROP productName; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_product + DROP priceAsCurrency; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_product + ADD productClassKey BINARY(12) NOT NULL; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_product + ADD productClass VARCHAR(128) NOT NULL COLLATE utf8_bin; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_product + ADD productRefKey BINARY(12) NOT NULL; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_product + ADD productRef VARCHAR(128) NOT NULL COLLATE utf8_bin; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_product + ADD UNIQUE KEY `key_product` (productClassKey, productRefKey); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -669,6 +669,7 @@ 'FundBackerEditor' => 'applications/fund/editor/FundBackerEditor.php', 'FundBackerListController' => 'applications/fund/controller/FundBackerListController.php', 'FundBackerPHIDType' => 'applications/fund/phid/FundBackerPHIDType.php', + 'FundBackerProduct' => 'applications/fund/phortune/FundBackerProduct.php', 'FundBackerQuery' => 'applications/fund/query/FundBackerQuery.php', 'FundBackerSearchEngine' => 'applications/fund/query/FundBackerSearchEngine.php', 'FundBackerTransaction' => 'applications/fund/storage/FundBackerTransaction.php', @@ -2580,13 +2581,9 @@ 'PhortunePaymentProviderTestCase' => 'applications/phortune/provider/__tests__/PhortunePaymentProviderTestCase.php', 'PhortunePaypalPaymentProvider' => 'applications/phortune/provider/PhortunePaypalPaymentProvider.php', 'PhortuneProduct' => 'applications/phortune/storage/PhortuneProduct.php', - 'PhortuneProductEditController' => 'applications/phortune/controller/PhortuneProductEditController.php', - 'PhortuneProductEditor' => 'applications/phortune/editor/PhortuneProductEditor.php', + 'PhortuneProductImplementation' => 'applications/phortune/product/PhortuneProductImplementation.php', 'PhortuneProductListController' => 'applications/phortune/controller/PhortuneProductListController.php', - 'PhortuneProductPurchaseController' => 'applications/phortune/controller/PhortuneProductPurchaseController.php', 'PhortuneProductQuery' => 'applications/phortune/query/PhortuneProductQuery.php', - 'PhortuneProductTransaction' => 'applications/phortune/storage/PhortuneProductTransaction.php', - 'PhortuneProductTransactionQuery' => 'applications/phortune/query/PhortuneProductTransactionQuery.php', 'PhortuneProductViewController' => 'applications/phortune/controller/PhortuneProductViewController.php', 'PhortuneProviderController' => 'applications/phortune/controller/PhortuneProviderController.php', 'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.php', @@ -3520,6 +3517,7 @@ 'FundBackerEditor' => 'PhabricatorApplicationTransactionEditor', 'FundBackerListController' => 'FundController', 'FundBackerPHIDType' => 'PhabricatorPHIDType', + 'FundBackerProduct' => 'PhortuneProductImplementation', 'FundBackerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'FundBackerSearchEngine' => 'PhabricatorApplicationSearchEngine', 'FundBackerTransaction' => 'PhabricatorApplicationTransaction', @@ -5616,13 +5614,8 @@ 'PhortuneDAO', 'PhabricatorPolicyInterface', ), - 'PhortuneProductEditController' => 'PhabricatorController', - 'PhortuneProductEditor' => 'PhabricatorApplicationTransactionEditor', 'PhortuneProductListController' => 'PhabricatorController', - 'PhortuneProductPurchaseController' => 'PhortuneController', 'PhortuneProductQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'PhortuneProductTransaction' => 'PhabricatorApplicationTransaction', - 'PhortuneProductTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhortuneProductViewController' => 'PhortuneController', 'PhortuneProviderController' => 'PhortuneController', 'PhortunePurchase' => array( diff --git a/src/applications/fund/controller/FundInitiativeBackController.php b/src/applications/fund/controller/FundInitiativeBackController.php --- a/src/applications/fund/controller/FundInitiativeBackController.php +++ b/src/applications/fund/controller/FundInitiativeBackController.php @@ -60,7 +60,19 @@ ->setAmountAsCurrency($currency) ->save(); - // TODO: Here, we'd create a purchase and cart. + $product = id(new PhortuneProductQuery()) + ->setViewer($viewer) + ->withClassAndRef('FundBackerProduct', $initiative->getPHID()) + ->executeOne(); + + $account = PhortuneAccountQuery::loadActiveAccountForUser( + $viewer, + PhabricatorContentSource::newFromRequest($request)); + + $cart = $account->newCart($viewer); + + $purchase = $cart->newPurchase($viewer, $product); + $purchase->setBasePriceAsCurrency($currency)->save(); $xactions = array(); @@ -74,9 +86,8 @@ $editor->applyTransactions($backer, $xactions); - // TODO: Here, we'd ship the user into Phortune. - - return id(new AphrontRedirectResponse())->setURI($initiative_uri); + return id(new AphrontRedirectResponse()) + ->setURI($cart->getCheckoutURI()); } } diff --git a/src/applications/fund/phortune/FundBackerProduct.php b/src/applications/fund/phortune/FundBackerProduct.php new file mode 100644 --- /dev/null +++ b/src/applications/fund/phortune/FundBackerProduct.php @@ -0,0 +1,64 @@ +getInitiativePHID(); + } + + public function getName(PhortuneProduct $product) { + return pht('Back Initiative %s', $this->initiativePHID); + } + + public function getPriceAsCurrency(PhortuneProduct $product) { + return PhortuneCurrency::newEmptyCurrency(); + } + + public function setInitiativePHID($initiative_phid) { + $this->initiativePHID = $initiative_phid; + return $this; + } + + public function getInitiativePHID() { + return $this->initiativePHID; + } + + public function setInitiative(FundInitiative $initiative) { + $this->initiative = $initiative; + return $this; + } + + public function getInitiative() { + return $this->initiative; + } + + public function loadImplementationsForRefs( + PhabricatorUser $viewer, + array $refs) { + + $initiatives = id(new FundInitiativeQuery()) + ->setViewer($viewer) + ->withPHIDs($refs) + ->execute(); + $initiatives = mpull($initiatives, null, 'getPHID'); + + $objects = array(); + foreach ($refs as $ref) { + $object = id(new FundBackerProduct()) + ->setInitiativePHID($ref); + + $initiative = idx($initiatives, $ref); + if ($initiative) { + $object->setInitiative($initiative); + } + + $objects[] = $object; + } + + return $objects; + } + +} diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php --- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php +++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php @@ -58,9 +58,6 @@ 'view/(?P\d+)/' => 'PhortuneProductViewController', 'edit/(?:(?P\d+)/)?' => 'PhortuneProductEditController', ), - 'purchase/(?P\d+)/' => array( - '' => 'PhortunePurchaseViewController', - ), 'provider/(?P[^/]+)/(?P[^/]+)/' => 'PhortuneProviderController', ), diff --git a/src/applications/phortune/controller/PhortuneController.php b/src/applications/phortune/controller/PhortuneController.php --- a/src/applications/phortune/controller/PhortuneController.php +++ b/src/applications/phortune/controller/PhortuneController.php @@ -3,53 +3,9 @@ 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(); - - $xactions = array(); - $xactions[] = id(new PhortuneAccountTransaction()) - ->setTransactionType(PhortuneAccountTransaction::TYPE_NAME) - ->setNewValue(pht('Account (%s)', $user->getUserName())); - - $xactions[] = id(new PhortuneAccountTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) - ->setMetadataValue( - 'edge:type', - PhabricatorEdgeConfig::TYPE_ACCOUNT_HAS_MEMBER) - ->setNewValue( - array( - '=' => array($user->getPHID() => $user->getPHID()), - )); - - $account = id(new PhortuneAccount()) - ->attachMemberPHIDs(array()); - - $editor = id(new PhortuneAccountEditor()) - ->setActor($user) - ->setContentSourceFromRequest($request); - - // We create an account for you the first time you visit Phortune. - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - - $editor->applyTransactions($account, $xactions); - - unset($unguarded); - - return $account; + return PhortuneAccountQuery::loadActiveAccountForUser( + $user, + PhabricatorContentSource::newFromRequest($this->getRequest())); } protected function buildChargesTable(array $charges, $show_cart = true) { diff --git a/src/applications/phortune/controller/PhortuneProductEditController.php b/src/applications/phortune/controller/PhortuneProductEditController.php deleted file mode 100644 --- a/src/applications/phortune/controller/PhortuneProductEditController.php +++ /dev/null @@ -1,133 +0,0 @@ -productID = idx($data, 'id'); - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); - - if ($this->productID) { - $product = id(new PhortuneProductQuery()) - ->setViewer($user) - ->withIDs(array($this->productID)) - ->executeOne(); - if (!$product) { - return new Aphront404Response(); - } - - $is_create = false; - $cancel_uri = $this->getApplicationURI( - 'product/view/'.$this->productID.'/'); - } else { - $product = PhortuneProduct::initializeNewProduct(); - $is_create = true; - $cancel_uri = $this->getApplicationURI('product/'); - } - - $v_name = $product->getProductName(); - $v_price = $product->getPriceAsCurrency()->formatForDisplay(); - $display_price = $v_price; - - $e_name = true; - $e_price = true; - $errors = array(); - - if ($request->isFormPost()) { - $v_name = $request->getStr('name'); - if (!strlen($v_name)) { - $e_name = pht('Required'); - $errors[] = pht('Product must have a name.'); - } else { - $e_name = null; - } - - $display_price = $request->getStr('price'); - try { - $v_price = PhortuneCurrency::newFromUserInput($user, $display_price) - ->serializeForStorage(); - $e_price = null; - } catch (Exception $ex) { - $errors[] = pht('Price should be formatted as: $1.23'); - $e_price = pht('Invalid'); - } - - if (!$errors) { - $xactions = array(); - - $xactions[] = id(new PhortuneProductTransaction()) - ->setTransactionType(PhortuneProductTransaction::TYPE_NAME) - ->setNewValue($v_name); - - $xactions[] = id(new PhortuneProductTransaction()) - ->setTransactionType(PhortuneProductTransaction::TYPE_PRICE) - ->setNewValue($v_price); - - $editor = id(new PhortuneProductEditor()) - ->setActor($user) - ->setContinueOnNoEffect(true) - ->setContentSourceFromRequest($request); - - $editor->applyTransactions($product, $xactions); - - return id(new AphrontRedirectResponse())->setURI( - $this->getApplicationURI('product/view/'.$product->getID().'/')); - } - } - - if ($errors) { - $errors = id(new AphrontErrorView()) - ->setErrors($errors); - } - - $form = id(new AphrontFormView()) - ->setUser($user) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Name')) - ->setName('name') - ->setValue($v_name) - ->setError($e_name)) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Price')) - ->setName('price') - ->setValue($display_price) - ->setError($e_price)) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue( - $is_create - ? pht('Create Product') - : pht('Save Product')) - ->addCancelButton($cancel_uri)); - - $title = pht('Edit Product'); - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb( - pht('Products'), - $this->getApplicationURI('product/')); - $crumbs->addTextCrumb( - $is_create ? pht('Create') : pht('Edit'), - $request->getRequestURI()); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Edit Product')) - ->appendChild($form); - - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); - } - -} diff --git a/src/applications/phortune/controller/PhortuneProductPurchaseController.php b/src/applications/phortune/controller/PhortuneProductPurchaseController.php deleted file mode 100644 --- a/src/applications/phortune/controller/PhortuneProductPurchaseController.php +++ /dev/null @@ -1,71 +0,0 @@ -accountID = $data['accountID']; - $this->productID = $data['productID']; - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); - - $account = id(new PhortuneAccountQuery()) - ->setViewer($user) - ->withIDs(array($this->accountID)) - ->executeOne(); - if (!$account) { - return new Aphront404Response(); - } - - $account_uri = $this->getApplicationURI($account->getID().'/'); - - $product = id(new PhortuneProductQuery()) - ->setViewer($user) - ->withIDs(array($this->productID)) - ->executeOne(); - if (!$product) { - return new Aphront404Response(); - } - - if ($request->isFormPost()) { - // TODO: Use ApplicationTransations. - - $cart = new PhortuneCart(); - $cart->openTransaction(); - - $cart->setStatus(PhortuneCart::STATUS_READY); - $cart->setAccountPHID($account->getPHID()); - $cart->setAuthorPHID($user->getPHID()); - $cart->save(); - - $purchase = new PhortunePurchase(); - $purchase->setProductPHID($product->getPHID()); - $purchase->setAccountPHID($account->getPHID()); - $purchase->setAuthorPHID($user->getPHID()); - $purchase->setCartPHID($cart->getPHID()); - $purchase->setBasePriceAsCurrency($product->getPriceAsCurrency()); - $purchase->setQuantity(1); - - $purchase->setStatus(PhortunePurchase::STATUS_PENDING); - $purchase->save(); - - $cart->saveTransaction(); - - $cart_id = $cart->getID(); - $cart_uri = $this->getApplicationURI('/cart/'.$cart_id.'/checkout/'); - return id(new AphrontRedirectResponse())->setURI($cart_uri); - } - - return $this->newDialog() - ->setTitle(pht('Purchase Product')) - ->appendParagraph(pht('Really purchase this stuff?')) - ->addSubmitButton(pht('Checkout')) - ->addCancelButton($account_uri); - } -} diff --git a/src/applications/phortune/controller/PhortuneProductViewController.php b/src/applications/phortune/controller/PhortuneProductViewController.php --- a/src/applications/phortune/controller/PhortuneProductViewController.php +++ b/src/applications/phortune/controller/PhortuneProductViewController.php @@ -34,19 +34,7 @@ $actions = id(new PhabricatorActionListView()) ->setUser($user) - ->setObjectURI($request->getRequestURI()) - ->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Edit Product')) - ->setHref($edit_uri) - ->setIcon('fa-pencil')) - ->addAction( - id(new PhabricatorActionView()) - ->setUser($user) - ->setName(pht('Purchase')) - ->setHref($cart_uri) - ->setIcon('fa-shopping-cart') - ->setWorkflow(true)); + ->setObjectURI($request->getRequestURI()); $crumbs = $this->buildApplicationCrumbs(); $crumbs->setActionList($actions); @@ -64,20 +52,6 @@ pht('Price'), $product->getPriceAsCurrency()->formatForDisplay()); - $xactions = id(new PhortuneProductTransactionQuery()) - ->setViewer($user) - ->withObjectPHIDs(array($product->getPHID())) - ->execute(); - - $engine = id(new PhabricatorMarkupEngine()) - ->setViewer($user); - - $xaction_view = id(new PhabricatorApplicationTransactionView()) - ->setUser($user) - ->setObjectPHID($product->getPHID()) - ->setTransactions($xactions) - ->setMarkupEngine($engine); - $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties); @@ -86,7 +60,6 @@ array( $crumbs, $object_box, - $xaction_view, ), array( 'title' => $title, diff --git a/src/applications/phortune/editor/PhortuneProductEditor.php b/src/applications/phortune/editor/PhortuneProductEditor.php deleted file mode 100644 --- a/src/applications/phortune/editor/PhortuneProductEditor.php +++ /dev/null @@ -1,74 +0,0 @@ -getTransactionType()) { - case PhortuneProductTransaction::TYPE_NAME: - return $object->getProductName(); - case PhortuneProductTransaction::TYPE_PRICE: - return $object->getPriceAsCurrency()->serializeForStorage(); - } - return parent::getCustomTransactionOldValue($object, $xaction); - } - - protected function getCustomTransactionNewValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - switch ($xaction->getTransactionType()) { - case PhortuneProductTransaction::TYPE_NAME: - case PhortuneProductTransaction::TYPE_PRICE: - return $xaction->getNewValue(); - } - return parent::getCustomTransactionNewValue($object, $xaction); - } - - protected function applyCustomInternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - switch ($xaction->getTransactionType()) { - case PhortuneProductTransaction::TYPE_NAME: - $object->setProductName($xaction->getNewValue()); - return; - case PhortuneProductTransaction::TYPE_PRICE: - $object->setPriceAsCurrency( - PhortuneCurrency::newFromString($xaction->getNewValue())); - return; - } - return parent::applyCustomInternalTransaction($object, $xaction); - } - - protected function applyCustomExternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - switch ($xaction->getTransactionType()) { - case PhortuneProductTransaction::TYPE_NAME: - case PhortuneProductTransaction::TYPE_PRICE: - return; - } - return parent::applyCustomExternalTransaction($object, $xaction); - } - -} diff --git a/src/applications/phortune/product/PhortuneProductImplementation.php b/src/applications/phortune/product/PhortuneProductImplementation.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/product/PhortuneProductImplementation.php @@ -0,0 +1,13 @@ +setViewer($user) + ->withMemberPHIDs(array($user->getPHID())) + ->execute(); + + if (!$accounts) { + return PhortuneAccount::createNewAccount($user, $content_source); + } else if (count($accounts) == 1) { + return head($accounts); + } else { + throw new Exception('TODO: No account selection yet.'); + } + } + public function withIDs(array $ids) { $this->ids = $ids; return $this; diff --git a/src/applications/phortune/query/PhortuneProductQuery.php b/src/applications/phortune/query/PhortuneProductQuery.php --- a/src/applications/phortune/query/PhortuneProductQuery.php +++ b/src/applications/phortune/query/PhortuneProductQuery.php @@ -5,6 +5,7 @@ private $ids; private $phids; + private $refMap; public function withIDs(array $ids) { $this->ids = $ids; @@ -16,6 +17,11 @@ return $this; } + public function withClassAndRef($class, $ref) { + $this->refMap = array($class => array($ref)); + return $this; + } + protected function loadPage() { $table = new PhortuneProduct(); $conn = $table->establishConnection('r'); @@ -28,26 +34,80 @@ $this->buildOrderClause($conn), $this->buildLimitClause($conn)); - return $table->loadAllFromArray($rows); + $page = $table->loadAllFromArray($rows); + + // NOTE: We're loading product implementations here, but also creating any + // products which do not yet exist. + + $class_map = mgroup($page, 'getProductClass'); + if ($this->refMap) { + $class_map += array_fill_keys(array_keys($this->refMap), array()); + } + + foreach ($class_map as $class => $products) { + $refs = mpull($products, null, 'getProductRef'); + if (isset($this->refMap[$class])) { + $refs += array_fill_keys($this->refMap[$class], null); + } + + $implementations = newv($class, array())->loadImplementationsForRefs( + $this->getViewer(), + array_keys($refs)); + $implementations = mpull($implementations, null, 'getRef'); + + foreach ($implementations as $ref => $implementation) { + $product = idx($refs, $ref); + if ($product === null) { + // If this product does not exist yet, create it and add it to the + // result page. + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + $product = PhortuneProduct::initializeNewProduct() + ->setProductClass($class) + ->setProductRef($ref) + ->save(); + unset($unguarded); + + $page[] = $product; + } + + $product->attachImplementation($implementation); + } + } + + return $page; } private function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } + if ($this->refMap !== null) { + $sql = array(); + foreach ($this->refMap as $class => $refs) { + foreach ($refs as $ref) { + $sql[] = qsprintf( + $conn, + '(productClassKey = %s AND productRefKey = %s)', + PhabricatorHash::digestForIndex($class), + PhabricatorHash::digestForIndex($ref)); + } + } + $where[] = implode(' OR ', $sql); + } + $where[] = $this->buildPagingClause($conn); return $this->formatWhereClause($where); diff --git a/src/applications/phortune/query/PhortuneProductTransactionQuery.php b/src/applications/phortune/query/PhortuneProductTransactionQuery.php deleted file mode 100644 --- a/src/applications/phortune/query/PhortuneProductTransactionQuery.php +++ /dev/null @@ -1,10 +0,0 @@ -memberPHIDs = array(); + + return $account; + } + + public static function createNewAccount( + PhabricatorUser $actor, + PhabricatorContentSource $content_source) { + + $account = PhortuneAccount::initializeNewAccount($actor); + + $xactions = array(); + $xactions[] = id(new PhortuneAccountTransaction()) + ->setTransactionType(PhortuneAccountTransaction::TYPE_NAME) + ->setNewValue(pht('Account (%s)', $actor->getUserName())); + + $xactions[] = id(new PhortuneAccountTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue( + 'edge:type', + PhabricatorEdgeConfig::TYPE_ACCOUNT_HAS_MEMBER) + ->setNewValue( + array( + '=' => array($actor->getPHID() => $actor->getPHID()), + )); + + $editor = id(new PhortuneAccountEditor()) + ->setActor($actor) + ->setContentSource($content_source); + + // We create an account for you the first time you visit Phortune. + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + + $editor->applyTransactions($account, $xactions); + + unset($unguarded); + + return $account; + } + + public function newCart(PhabricatorUser $actor) { + return PhortuneCart::initializeNewCart($actor, $this) + ->save(); + } + public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, diff --git a/src/applications/phortune/storage/PhortuneCart.php b/src/applications/phortune/storage/PhortuneCart.php --- a/src/applications/phortune/storage/PhortuneCart.php +++ b/src/applications/phortune/storage/PhortuneCart.php @@ -15,6 +15,38 @@ private $account = self::ATTACHABLE; private $purchases = self::ATTACHABLE; + public static function initializeNewCart( + PhabricatorUser $actor, + PhortuneAccount $account) { + $cart = id(new PhortuneCart()) + ->setAuthorPHID($actor->getPHID()) + ->setStatus(self::STATUS_READY) + ->setAccountPHID($account->getPHID()); + + $cart->account = $account; + $cart->purchases = array(); + + return $cart; + } + + public function newPurchase( + PhabricatorUser $actor, + PhortuneProduct $product) { + + $purchase = PhortunePurchase::initializeNewPurchase($actor, $product) + ->setAccountPHID($this->getAccount()->getPHID()) + ->setCartPHID($this->getPHID()) + ->save(); + + $this->purchases[] = $purchase; + + return $purchase; + } + + public function getCheckoutURI() { + return '/phortune/cart/'.$this->getID().'/checkout/'; + } + public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, diff --git a/src/applications/phortune/storage/PhortuneProduct.php b/src/applications/phortune/storage/PhortuneProduct.php --- a/src/applications/phortune/storage/PhortuneProduct.php +++ b/src/applications/phortune/storage/PhortuneProduct.php @@ -6,9 +6,13 @@ final class PhortuneProduct extends PhortuneDAO implements PhabricatorPolicyInterface { - protected $productName; - protected $priceAsCurrency; - protected $metadata; + protected $productClassKey; + protected $productClass; + protected $productRefKey; + protected $productRef; + protected $metadata = array(); + + private $implementation = self::ATTACHABLE; public function getConfiguration() { return array( @@ -16,15 +20,17 @@ self::CONFIG_SERIALIZATION => array( 'metadata' => self::SERIALIZATION_JSON, ), - self::CONFIG_APPLICATION_SERIALIZERS => array( - 'priceAsCurrency' => new PhortuneCurrencySerializer(), - ), self::CONFIG_COLUMN_SCHEMA => array( - 'productName' => 'text255', - 'status' => 'text64', - 'priceAsCurrency' => 'text64', - 'billingIntervalInMonths' => 'uint32?', - 'trialPeriodInDays' => 'uint32?', + 'productClassKey' => 'bytes12', + 'productClass' => 'text128', + 'productRefKey' => 'bytes12', + 'productRef' => 'text128', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_product' => array( + 'columns' => array('productClassKey', 'productRefKey'), + 'unique' => true, + ), ), ) + parent::getConfiguration(); } @@ -35,8 +41,33 @@ } public static function initializeNewProduct() { - return id(new PhortuneProduct()) - ->setPriceAsCurrency(PhortuneCurrency::newEmptyCurrency()); + return id(new PhortuneProduct()); + } + + public function attachImplementation(PhortuneProductImplementation $impl) { + $this->implementation = $impl; + } + + public function getImplementation() { + return $this->assertAttached($this->implementation); + } + + public function save() { + $this->productClassKey = PhabricatorHash::digestForIndex( + $this->productClass); + + $this->productRefKey = PhabricatorHash::digestForIndex( + $this->productRef); + + return parent::save(); + } + + public function getPriceAsCurrency() { + return $this->getImplementation()->getPriceAsCurrency($this); + } + + public function getProductName() { + return $this->getImplementation()->getName($this); } diff --git a/src/applications/phortune/storage/PhortuneProductTransaction.php b/src/applications/phortune/storage/PhortuneProductTransaction.php deleted file mode 100644 --- a/src/applications/phortune/storage/PhortuneProductTransaction.php +++ /dev/null @@ -1,63 +0,0 @@ -getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_NAME: - if ($old === null) { - return pht( - '%s created this product.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s renamed this product from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - break; - case self::TYPE_PRICE: - if ($old === null) { - return pht( - '%s set product price to %s.', - $this->renderHandleLink($author_phid), - PhortuneCurrency::newFromString($new) - ->formatForDisplay()); - } else { - return pht( - '%s changed product price from %s to %s.', - $this->renderHandleLink($author_phid), - PhortuneCurrency::newFromString($old) - ->formatForDisplay(), - PhortuneCurrency::newFromString($new) - ->formatForDisplay()); - } - break; - } - - return parent::getTitle(); - } - -} diff --git a/src/applications/phortune/storage/PhortunePurchase.php b/src/applications/phortune/storage/PhortunePurchase.php --- a/src/applications/phortune/storage/PhortunePurchase.php +++ b/src/applications/phortune/storage/PhortunePurchase.php @@ -20,10 +20,21 @@ protected $basePriceAsCurrency; protected $quantity; protected $status; - protected $metadata; + protected $metadata = array(); private $cart = self::ATTACHABLE; + public static function initializeNewPurchase( + PhabricatorUser $actor, + PhortuneProduct $product) { + return id(new PhortunePurchase()) + ->setAuthorPHID($actor->getPHID()) + ->setProductPHID($product->getPHID()) + ->setQuantity(1) + ->setStatus(self::STATUS_PENDING) + ->setBasePriceAsCurrency($product->getPriceAsCurrency()); + } + public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true,