@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update the Phortune cart/invoice workflow for policy changes

Summary:
Depends on D20734. Ref T13366. This makes the cart/order flow work under the new policy scheme with no "grantAuthority()" calls.

It prepares for a "Void Invoice" action, although the action doesn't actually do anything yet.

Test Plan: With and without merchant authority, viewed and paid invoices and went through the other invoice interaction workflows.

Maniphest Tasks: T13366

Differential Revision: https://secure.phabricator.com/D20735

+298 -562
+2 -4
src/__phutil_library_map__.php
··· 5279 5279 'PhortuneCartTransactionQuery' => 'applications/phortune/query/PhortuneCartTransactionQuery.php', 5280 5280 'PhortuneCartUpdateController' => 'applications/phortune/controller/cart/PhortuneCartUpdateController.php', 5281 5281 'PhortuneCartViewController' => 'applications/phortune/controller/cart/PhortuneCartViewController.php', 5282 + 'PhortuneCartVoidController' => 'applications/phortune/controller/cart/PhortuneCartVoidController.php', 5282 5283 'PhortuneCharge' => 'applications/phortune/storage/PhortuneCharge.php', 5283 5284 'PhortuneChargePHIDType' => 'applications/phortune/phid/PhortuneChargePHIDType.php', 5284 5285 'PhortuneChargeQuery' => 'applications/phortune/query/PhortuneChargeQuery.php', ··· 5372 5373 'PhortuneSubscription' => 'applications/phortune/storage/PhortuneSubscription.php', 5373 5374 'PhortuneSubscriptionAutopayTransaction' => 'applications/phortune/xaction/subscription/PhortuneSubscriptionAutopayTransaction.php', 5374 5375 'PhortuneSubscriptionCart' => 'applications/phortune/cart/PhortuneSubscriptionCart.php', 5375 - 'PhortuneSubscriptionEditController' => 'applications/phortune/controller/subscription/PhortuneSubscriptionEditController.php', 5376 5376 'PhortuneSubscriptionEditor' => 'applications/phortune/editor/PhortuneSubscriptionEditor.php', 5377 5377 'PhortuneSubscriptionImplementation' => 'applications/phortune/subscription/PhortuneSubscriptionImplementation.php', 5378 - 'PhortuneSubscriptionListController' => 'applications/phortune/controller/subscription/PhortuneSubscriptionListController.php', 5379 5378 'PhortuneSubscriptionPHIDType' => 'applications/phortune/phid/PhortuneSubscriptionPHIDType.php', 5380 5379 'PhortuneSubscriptionPolicyCodex' => 'applications/phortune/codex/PhortuneSubscriptionPolicyCodex.php', 5381 5380 'PhortuneSubscriptionProduct' => 'applications/phortune/product/PhortuneSubscriptionProduct.php', ··· 11861 11860 'PhortuneCartTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 11862 11861 'PhortuneCartUpdateController' => 'PhortuneCartController', 11863 11862 'PhortuneCartViewController' => 'PhortuneCartController', 11863 + 'PhortuneCartVoidController' => 'PhortuneCartController', 11864 11864 'PhortuneCharge' => array( 11865 11865 'PhortuneDAO', 11866 11866 'PhabricatorPolicyInterface', ··· 11984 11984 ), 11985 11985 'PhortuneSubscriptionAutopayTransaction' => 'PhortuneSubscriptionTransactionType', 11986 11986 'PhortuneSubscriptionCart' => 'PhortuneCartImplementation', 11987 - 'PhortuneSubscriptionEditController' => 'PhortuneController', 11988 11987 'PhortuneSubscriptionEditor' => 'PhabricatorApplicationTransactionEditor', 11989 11988 'PhortuneSubscriptionImplementation' => 'Phobject', 11990 - 'PhortuneSubscriptionListController' => 'PhortuneController', 11991 11989 'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType', 11992 11990 'PhortuneSubscriptionPolicyCodex' => 'PhabricatorPolicyCodex', 11993 11991 'PhortuneSubscriptionProduct' => 'PhortuneProductImplementation',
+2
src/applications/phortune/application/PhabricatorPhortuneApplication.php
··· 43 43 'checkout/' => 'PhortuneCartCheckoutController', 44 44 '(?P<action>print)/' => 'PhortuneCartViewController', 45 45 '(?P<action>cancel|refund)/' => 'PhortuneCartCancelController', 46 + 'accept/' => 'PhortuneCartAcceptController', 47 + 'void/' => 'PhortuneCartVoidController', 46 48 'update/' => 'PhortuneCartUpdateController', 47 49 ), 48 50 'account/' => array(
-62
src/applications/phortune/controller/PhortuneController.php
··· 2 2 3 3 abstract class PhortuneController extends PhabricatorController { 4 4 5 - protected function addAccountCrumb( 6 - $crumbs, 7 - PhortuneAccount $account, 8 - $link = true) { 9 - 10 - $name = $account->getName(); 11 - $href = null; 12 - 13 - if ($link) { 14 - $href = $this->getApplicationURI($account->getID().'/'); 15 - $crumbs->addTextCrumb($name, $href); 16 - } else { 17 - $crumbs->addTextCrumb($name); 18 - } 19 - } 20 - 21 - protected function addMerchantCrumb( 22 - $crumbs, 23 - PhortuneMerchant $merchant, 24 - $link = true) { 25 - 26 - $name = $merchant->getName(); 27 - $href = null; 28 - 29 - $crumbs->addTextCrumb( 30 - pht('Merchants'), 31 - $this->getApplicationURI('merchant/')); 32 - 33 - if ($link) { 34 - $href = $this->getApplicationURI('merchant/'.$merchant->getID().'/'); 35 - $crumbs->addTextCrumb($name, $href); 36 - } else { 37 - $crumbs->addTextCrumb($name); 38 - } 39 - } 40 - 41 5 private function loadEnabledProvidersForMerchant(PhortuneMerchant $merchant) { 42 6 $viewer = $this->getRequest()->getUser(); 43 7 ··· 82 46 } 83 47 84 48 return $providers; 85 - } 86 - 87 - protected function loadMerchantAuthority() { 88 - $request = $this->getRequest(); 89 - $viewer = $this->getViewer(); 90 - 91 - $is_merchant = (bool)$request->getURIData('merchantID'); 92 - if (!$is_merchant) { 93 - return null; 94 - } 95 - 96 - $merchant = id(new PhortuneMerchantQuery()) 97 - ->setViewer($viewer) 98 - ->withIDs(array($request->getURIData('merchantID'))) 99 - ->requireCapabilities( 100 - array( 101 - PhabricatorPolicyCapability::CAN_VIEW, 102 - PhabricatorPolicyCapability::CAN_EDIT, 103 - )) 104 - ->executeOne(); 105 - if (!$merchant) { 106 - return null; 107 - } 108 - 109 - $viewer->grantAuthority($merchant); 110 - return $merchant; 111 49 } 112 50 113 51 }
+2 -13
src/applications/phortune/controller/account/PhortuneAccountChargesController.php
··· 12 12 $title = $account->getName(); 13 13 14 14 $crumbs = $this->buildApplicationCrumbs() 15 - ->addTextCrumb(pht('Order History')) 15 + ->addTextCrumb(pht('Orders')) 16 16 ->setBorder(true); 17 17 18 18 $header = $this->buildHeaderView(); ··· 46 46 ->setLimit(100) 47 47 ->execute(); 48 48 49 - $phids = array(); 50 - foreach ($charges as $charge) { 51 - $phids[] = $charge->getProviderPHID(); 52 - $phids[] = $charge->getCartPHID(); 53 - $phids[] = $charge->getMerchantPHID(); 54 - $phids[] = $charge->getPaymentMethodPHID(); 55 - } 56 - 57 - $handles = $this->loadViewerHandles($phids); 58 - 59 49 $charges_uri = $account->getChargeListURI(); 60 50 61 51 $table = id(new PhortuneChargeTableView()) 62 52 ->setUser($viewer) 63 - ->setCharges($charges) 64 - ->setHandles($handles); 53 + ->setCharges($charges); 65 54 66 55 $header = id(new PHUIHeaderView()) 67 56 ->setHeader(pht('Recent Charges'))
+1 -1
src/applications/phortune/controller/account/PhortuneAccountOrdersController.php
··· 12 12 $title = $account->getName(); 13 13 14 14 $crumbs = $this->buildApplicationCrumbs() 15 - ->addTextCrumb(pht('Order History')) 15 + ->addTextCrumb(pht('Orders')) 16 16 ->setBorder(true); 17 17 18 18 $header = $this->buildHeaderView();
+2 -2
src/applications/phortune/controller/account/PhortuneAccountProfileController.php
··· 66 66 67 67 $nav->addFilter( 68 68 'orders', 69 - pht('Order History'), 69 + pht('Orders'), 70 70 $account->getOrdersURI(), 71 71 'fa-shopping-bag'); 72 72 73 73 $nav->addFilter( 74 74 'charges', 75 - pht('Charge History'), 75 + pht('Charges'), 76 76 $account->getChargesURI(), 77 77 'fa-calculator'); 78 78
+10 -18
src/applications/phortune/controller/cart/PhortuneCartAcceptController.php
··· 3 3 final class PhortuneCartAcceptController 4 4 extends PhortuneCartController { 5 5 6 - public function handleRequest(AphrontRequest $request) { 7 - $viewer = $request->getViewer(); 8 - $id = $request->getURIData('id'); 6 + protected function shouldRequireAccountAuthority() { 7 + return false; 8 + } 9 9 10 - // You must control the merchant to accept orders. 11 - $authority = $this->loadMerchantAuthority(); 12 - if (!$authority) { 13 - return new Aphront404Response(); 14 - } 10 + protected function shouldRequireMerchantAuthority() { 11 + return true; 12 + } 15 13 16 - $cart = id(new PhortuneCartQuery()) 17 - ->setViewer($viewer) 18 - ->withIDs(array($id)) 19 - ->withMerchantPHIDs(array($authority->getPHID())) 20 - ->needPurchases(true) 21 - ->executeOne(); 22 - if (!$cart) { 23 - return new Aphront404Response(); 24 - } 14 + protected function handleCartRequest(AphrontRequest $request) { 15 + $viewer = $request->getViewer(); 16 + $cart = $this->getCart(); 25 17 26 - $cancel_uri = $cart->getDetailURI($authority); 18 + $cancel_uri = $cart->getDetailURI(); 27 19 28 20 if ($cart->getStatus() !== PhortuneCart::STATUS_REVIEW) { 29 21 return $this->newDialog()
+13 -18
src/applications/phortune/controller/cart/PhortuneCartCancelController.php
··· 3 3 final class PhortuneCartCancelController 4 4 extends PhortuneCartController { 5 5 6 - public function handleRequest(AphrontRequest $request) { 6 + protected function shouldRequireAccountAuthority() { 7 + return false; 8 + } 9 + 10 + protected function shouldRequireMerchantAuthority() { 11 + return false; 12 + } 13 + 14 + protected function handleCartRequest(AphrontRequest $request) { 7 15 $viewer = $request->getViewer(); 8 16 $id = $request->getURIData('id'); 9 17 $action = $request->getURIData('action'); 10 18 11 - $authority = $this->loadMerchantAuthority(); 12 - 13 - $cart_query = id(new PhortuneCartQuery()) 14 - ->setViewer($viewer) 15 - ->withIDs(array($id)) 16 - ->needPurchases(true); 17 - 18 - if ($authority) { 19 - $cart_query->withMerchantPHIDs(array($authority->getPHID())); 20 - } 21 - 22 - $cart = $cart_query->executeOne(); 23 - if (!$cart) { 24 - return new Aphront404Response(); 25 - } 19 + $cart = $this->getCart(); 20 + $authority = $this->getMerchantAuthority(); 26 21 27 22 switch ($action) { 28 23 case 'cancel': ··· 45 40 return new Aphront404Response(); 46 41 } 47 42 48 - $cancel_uri = $cart->getDetailURI($authority); 43 + $cancel_uri = $cart->getDetailURI(); 49 44 $merchant = $cart->getMerchant(); 50 45 51 46 try { ··· 60 55 return $this->newDialog() 61 56 ->setTitle($title) 62 57 ->appendChild($ex->getMessage()) 63 - ->addCancelButton($cancel_uri); 58 + ->addCancelButton($cancel_uri, pht('Rats')); 64 59 } 65 60 66 61 $charges = id(new PhortuneChargeQuery())
+14 -12
src/applications/phortune/controller/cart/PhortuneCartCheckoutController.php
··· 3 3 final class PhortuneCartCheckoutController 4 4 extends PhortuneCartController { 5 5 6 - public function handleRequest(AphrontRequest $request) { 6 + protected function shouldRequireAccountAuthority() { 7 + return true; 8 + } 9 + 10 + protected function shouldRequireMerchantAuthority() { 11 + return false; 12 + } 13 + 14 + protected function handleCartRequest(AphrontRequest $request) { 7 15 $viewer = $request->getViewer(); 8 - $id = $request->getURIData('id'); 9 - 10 - $cart = id(new PhortuneCartQuery()) 11 - ->setViewer($viewer) 12 - ->withIDs(array($id)) 13 - ->needPurchases(true) 14 - ->executeOne(); 15 - if (!$cart) { 16 - return new Aphront404Response(); 17 - } 16 + $cart = $this->getCart(); 18 17 19 18 $cancel_uri = $cart->getCancelURI(); 20 19 $merchant = $cart->getMerchant(); ··· 139 138 'cartID' => $cart->getID(), 140 139 ); 141 140 142 - $payment_method_uri = $this->getApplicationURI("{$account_id}/card/new/"); 141 + $payment_method_uri = urisprintf( 142 + 'account/%d/methods/new/', 143 + $account->getID()); 144 + $payment_method_uri = $this->getApplicationURI($payment_method_uri); 143 145 $payment_method_uri = new PhutilURI($payment_method_uri, $params); 144 146 145 147 $form = id(new AphrontFormView())
+71
src/applications/phortune/controller/cart/PhortuneCartController.php
··· 3 3 abstract class PhortuneCartController 4 4 extends PhortuneController { 5 5 6 + private $cart; 7 + private $merchantAuthority; 8 + 9 + abstract protected function shouldRequireAccountAuthority(); 10 + abstract protected function shouldRequireMerchantAuthority(); 11 + abstract protected function handleCartRequest(AphrontRequest $request); 12 + 13 + final public function handleRequest(AphrontRequest $request) { 14 + $viewer = $this->getViewer(); 15 + 16 + if ($this->shouldRequireAccountAuthority()) { 17 + $capabilities = array( 18 + PhabricatorPolicyCapability::CAN_VIEW, 19 + PhabricatorPolicyCapability::CAN_EDIT, 20 + ); 21 + } else { 22 + $capabilities = array( 23 + PhabricatorPolicyCapability::CAN_VIEW, 24 + ); 25 + } 26 + 27 + $cart = id(new PhortuneCartQuery()) 28 + ->setViewer($viewer) 29 + ->withIDs(array($request->getURIData('id'))) 30 + ->needPurchases(true) 31 + ->requireCapabilities($capabilities) 32 + ->executeOne(); 33 + if (!$cart) { 34 + return new Aphront404Response(); 35 + } 36 + 37 + if ($this->shouldRequireMerchantAuthority()) { 38 + PhabricatorPolicyFilter::requireCapability( 39 + $viewer, 40 + $cart->getMerchant(), 41 + PhabricatorPolicyCapability::CAN_EDIT); 42 + } 43 + 44 + $this->cart = $cart; 45 + 46 + $can_edit = PhortuneMerchantQuery::canViewersEditMerchants( 47 + array($viewer->getPHID()), 48 + array($cart->getMerchantPHID())); 49 + if ($can_edit) { 50 + $this->merchantAuthority = $cart->getMerchant(); 51 + } else { 52 + $this->merchantAuthority = null; 53 + } 54 + 55 + return $this->handleCartRequest($request); 56 + } 57 + 58 + final protected function getCart() { 59 + return $this->cart; 60 + } 61 + 62 + final protected function getMerchantAuthority() { 63 + return $this->merchantAuthority; 64 + } 65 + 66 + final protected function hasMerchantAuthority() { 67 + return (bool)$this->merchantAuthority; 68 + } 69 + 70 + final protected function hasAccountAuthority() { 71 + return (bool)PhabricatorPolicyFilter::hasCapability( 72 + $this->getViewer(), 73 + $this->getCart(), 74 + PhabricatorPolicyCapability::CAN_EDIT); 75 + } 76 + 6 77 protected function buildCartContentTable(PhortuneCart $cart) { 7 78 8 79 $rows = array();
+12 -17
src/applications/phortune/controller/cart/PhortuneCartUpdateController.php
··· 3 3 final class PhortuneCartUpdateController 4 4 extends PhortuneCartController { 5 5 6 - public function handleRequest(AphrontRequest $request) { 6 + protected function shouldRequireAccountAuthority() { 7 + return false; 8 + } 9 + 10 + protected function shouldRequireMerchantAuthority() { 11 + return false; 12 + } 13 + 14 + protected function handleCartRequest(AphrontRequest $request) { 7 15 $viewer = $request->getViewer(); 8 16 $id = $request->getURIData('id'); 9 17 10 - $authority = $this->loadMerchantAuthority(); 11 - 12 - $cart_query = id(new PhortuneCartQuery()) 13 - ->setViewer($viewer) 14 - ->withIDs(array($id)) 15 - ->needPurchases(true); 16 - 17 - if ($authority) { 18 - $cart_query->withMerchantPHIDs(array($authority->getPHID())); 19 - } 20 - 21 - $cart = $cart_query->executeOne(); 22 - if (!$cart) { 23 - return new Aphront404Response(); 24 - } 18 + $cart = $this->getCart(); 19 + $authority = $this->getMerchantAuthority(); 25 20 26 21 $charges = id(new PhortuneChargeQuery()) 27 22 ->setViewer($viewer) ··· 60 55 } 61 56 62 57 return id(new AphrontRedirectResponse()) 63 - ->setURI($cart->getDetailURI($authority)); 58 + ->setURI($cart->getDetailURI()); 64 59 } 65 60 66 61 }
+76 -92
src/applications/phortune/controller/cart/PhortuneCartViewController.php
··· 5 5 6 6 private $action = null; 7 7 8 - public function handleRequest(AphrontRequest $request) { 9 - $viewer = $request->getViewer(); 10 - $id = $request->getURIData('id'); 11 - $this->action = $request->getURIData('action'); 8 + protected function shouldRequireAccountAuthority() { 9 + return false; 10 + } 12 11 13 - $authority = $this->loadMerchantAuthority(); 14 - require_celerity_resource('phortune-css'); 12 + protected function shouldRequireMerchantAuthority() { 13 + return false; 14 + } 15 15 16 - $query = id(new PhortuneCartQuery()) 17 - ->setViewer($viewer) 18 - ->withIDs(array($id)) 19 - ->needPurchases(true); 20 - 21 - if ($authority) { 22 - $query->withMerchantPHIDs(array($authority->getPHID())); 23 - } 16 + protected function handleCartRequest(AphrontRequest $request) { 17 + $viewer = $request->getViewer(); 18 + $cart = $this->getCart(); 19 + $authority = $this->getMerchantAuthority(); 20 + $can_edit = $this->hasAccountAuthority(); 24 21 25 - $cart = $query->executeOne(); 26 - if (!$cart) { 27 - return new Aphront404Response(); 28 - } 22 + $this->action = $request->getURIData('action'); 29 23 30 24 $cart_table = $this->buildCartContentTable($cart); 31 25 32 - $can_edit = PhabricatorPolicyFilter::hasCapability( 33 - $viewer, 34 - $cart, 35 - PhabricatorPolicyCapability::CAN_EDIT); 36 - 37 26 $errors = array(); 38 27 $error_view = null; 39 28 $resume_uri = null; 40 29 switch ($cart->getStatus()) { 41 30 case PhortuneCart::STATUS_READY: 42 - if ($authority && $cart->getIsInvoice()) { 43 - // We arrived here by following the ad-hoc invoice workflow, and 44 - // are acting with merchant authority. 45 - 46 - $checkout_uri = PhabricatorEnv::getURI($cart->getCheckoutURI()); 47 - 48 - $invoice_message = array( 49 - pht( 50 - 'Manual invoices do not automatically notify recipients yet. '. 51 - 'Send the payer this checkout link:'), 52 - ' ', 53 - phutil_tag( 54 - 'a', 55 - array( 56 - 'href' => $checkout_uri, 57 - ), 58 - $checkout_uri), 59 - ); 60 - 31 + if ($cart->getIsInvoice()) { 61 32 $error_view = id(new PHUIInfoView()) 62 - ->setSeverity(PHUIInfoView::SEVERITY_WARNING) 63 - ->setErrors(array($invoice_message)); 33 + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) 34 + ->appendChild(pht('This invoice is ready for payment.')); 64 35 } 65 36 break; 66 37 case PhortuneCart::STATUS_PURCHASING: ··· 133 104 $header = id(new PHUIHeaderView()) 134 105 ->setUser($viewer) 135 106 ->setHeader($cart->getName()) 136 - ->setHeaderIcon('fa-shopping-cart'); 107 + ->setHeaderIcon('fa-shopping-bag'); 137 108 138 109 if ($cart->getStatus() == PhortuneCart::STATUS_PURCHASED) { 139 110 $done_uri = $cart->getDoneURI(); ··· 160 131 ->needCarts(true) 161 132 ->execute(); 162 133 163 - $phids = array(); 164 - foreach ($charges as $charge) { 165 - $phids[] = $charge->getProviderPHID(); 166 - $phids[] = $charge->getCartPHID(); 167 - $phids[] = $charge->getMerchantPHID(); 168 - $phids[] = $charge->getPaymentMethodPHID(); 169 - } 170 - $handles = $this->loadViewerHandles($phids); 171 - 172 134 $charges_table = id(new PhortuneChargeTableView()) 173 135 ->setUser($viewer) 174 - ->setHandles($handles) 175 136 ->setCharges($charges) 176 137 ->setShowOrder(false); 177 138 ··· 182 143 183 144 $account = $cart->getAccount(); 184 145 185 - $crumbs = $this->buildApplicationCrumbs(); 186 - if ($authority) { 187 - $this->addMerchantCrumb($crumbs, $authority); 188 - } else { 189 - $this->addAccountCrumb($crumbs, $cart->getAccount()); 190 - } 191 - $crumbs->addTextCrumb(pht('Cart %d', $cart->getID())); 192 - $crumbs->setBorder(true); 146 + $crumbs = $this->buildApplicationCrumbs() 147 + ->addTextCrumb($account->getName(), $account->getURI()) 148 + ->addTextCrumb(pht('Orders'), $account->getOrdersURI()) 149 + ->addTextCrumb(pht('Cart %d', $cart->getID())) 150 + ->setBorder(true); 151 + 152 + require_celerity_resource('phortune-css'); 193 153 194 154 if (!$this->action) { 195 155 $class = 'phortune-cart-page'; ··· 267 227 if ($crumbs) { 268 228 $page->setCrumbs($crumbs); 269 229 } 230 + 270 231 return $page; 271 232 } 272 233 ··· 318 279 $viewer = $this->getViewer(); 319 280 $id = $cart->getID(); 320 281 $curtain = $this->newCurtainView($cart); 282 + $status = $cart->getStatus(); 283 + 284 + $is_ready = ($status === PhortuneCart::STATUS_READY); 321 285 322 286 $can_cancel = ($can_edit && $cart->canCancelOrder()); 287 + $can_checkout = ($can_edit && $is_ready); 288 + $can_accept = ($status === PhortuneCart::STATUS_REVIEW); 289 + $can_refund = ($authority && $cart->canRefundOrder()); 290 + $can_void = ($authority && $cart->canVoidOrder()); 323 291 324 - if ($authority) { 325 - $prefix = 'merchant/'.$authority->getID().'/'; 326 - } else { 327 - $prefix = ''; 328 - } 292 + $cancel_uri = $this->getApplicationURI("cart/{$id}/cancel/"); 293 + $refund_uri = $this->getApplicationURI("cart/{$id}/refund/"); 294 + $update_uri = $this->getApplicationURI("cart/{$id}/update/"); 295 + $accept_uri = $this->getApplicationURI("cart/{$id}/accept/"); 296 + $print_uri = $this->getApplicationURI("cart/{$id}/print/"); 297 + $checkout_uri = $cart->getCheckoutURI(); 298 + $void_uri = $this->getApplicationURI("cart/{$id}/void/"); 329 299 330 - $cancel_uri = $this->getApplicationURI("{$prefix}cart/{$id}/cancel/"); 331 - $refund_uri = $this->getApplicationURI("{$prefix}cart/{$id}/refund/"); 332 - $update_uri = $this->getApplicationURI("{$prefix}cart/{$id}/update/"); 333 - $accept_uri = $this->getApplicationURI("{$prefix}cart/{$id}/accept/"); 334 - $print_uri = $this->getApplicationURI("{$prefix}cart/{$id}/print/"); 300 + $curtain->addAction( 301 + id(new PhabricatorActionView()) 302 + ->setName(pht('Pay Now')) 303 + ->setIcon('fa-credit-card') 304 + ->setDisabled(!$can_checkout) 305 + ->setWorkflow(!$can_checkout) 306 + ->setHref($checkout_uri)); 335 307 336 308 $curtain->addAction( 337 309 id(new PhabricatorActionView()) ··· 341 313 ->setWorkflow(true) 342 314 ->setHref($cancel_uri)); 343 315 344 - if ($authority) { 345 - if ($cart->getStatus() == PhortuneCart::STATUS_REVIEW) { 346 - $curtain->addAction( 347 - id(new PhabricatorActionView()) 348 - ->setName(pht('Accept Order')) 349 - ->setIcon('fa-check') 350 - ->setWorkflow(true) 351 - ->setHref($accept_uri)); 352 - } 353 - 354 - $curtain->addAction( 355 - id(new PhabricatorActionView()) 356 - ->setName(pht('Refund Order')) 357 - ->setIcon('fa-reply') 358 - ->setWorkflow(true) 359 - ->setHref($refund_uri)); 360 - } 361 - 362 316 $curtain->addAction( 363 317 id(new PhabricatorActionView()) 364 318 ->setName(pht('Update Status')) ··· 369 323 $curtain->addAction( 370 324 id(new PhabricatorActionView()) 371 325 ->setName(pht('Continue Checkout')) 372 - ->setIcon('fa-shopping-cart') 326 + ->setIcon('fa-shopping-bag') 373 327 ->setHref($resume_uri)); 374 328 } 375 329 ··· 379 333 ->setHref($print_uri) 380 334 ->setOpenInNewWindow(true) 381 335 ->setIcon('fa-print')); 336 + 337 + if ($authority) { 338 + $curtain->addAction( 339 + id(new PhabricatorActionView()) 340 + ->setType(PhabricatorActionView::TYPE_DIVIDER)); 341 + 342 + $curtain->addAction( 343 + id(new PhabricatorActionView()) 344 + ->setName(pht('Accept Order')) 345 + ->setIcon('fa-check') 346 + ->setWorkflow(true) 347 + ->setDisabled(!$can_accept) 348 + ->setHref($accept_uri)); 349 + 350 + $curtain->addAction( 351 + id(new PhabricatorActionView()) 352 + ->setName(pht('Refund Order')) 353 + ->setIcon('fa-reply') 354 + ->setWorkflow(true) 355 + ->setDisabled(!$can_refund) 356 + ->setHref($refund_uri)); 357 + 358 + $curtain->addAction( 359 + id(new PhabricatorActionView()) 360 + ->setName(pht('Void Invoice')) 361 + ->setIcon('fa-times') 362 + ->setWorkflow(true) 363 + ->setDisabled(!$can_void) 364 + ->setHref($void_uri)); 365 + } 382 366 383 367 return $curtain; 384 368 }
+43
src/applications/phortune/controller/cart/PhortuneCartVoidController.php
··· 1 + <?php 2 + 3 + final class PhortuneCartVoidController 4 + extends PhortuneCartController { 5 + 6 + protected function shouldRequireAccountAuthority() { 7 + return false; 8 + } 9 + 10 + protected function shouldRequireMerchantAuthority() { 11 + return true; 12 + } 13 + 14 + protected function handleCartRequest(AphrontRequest $request) { 15 + $viewer = $request->getViewer(); 16 + $cart = $this->getCart(); 17 + 18 + $cancel_uri = $cart->getDetailURI(); 19 + 20 + try { 21 + $title = pht('Unable to Void Invoice'); 22 + $cart->assertCanVoidOrder(); 23 + } catch (Exception $ex) { 24 + return $this->newDialog() 25 + ->setTitle($title) 26 + ->appendChild($ex->getMessage()) 27 + ->addCancelButton($cancel_uri); 28 + } 29 + 30 + if ($request->isFormPost()) { 31 + return id(new AphrontRedirectResponse())->setURI($cancel_uri); 32 + } 33 + 34 + return $this->newDialog() 35 + ->setTitle(pht('Void Invoice?')) 36 + ->appendParagraph( 37 + pht( 38 + 'Really void this invoice? The customer will no longer be asked '. 39 + 'to submit payment for it.')) 40 + ->addCancelButton($cancel_uri) 41 + ->addSubmitButton(pht('Void Invoice')); 42 + } 43 + }
-185
src/applications/phortune/controller/subscription/PhortuneSubscriptionEditController.php
··· 1 - <?php 2 - 3 - final class PhortuneSubscriptionEditController extends PhortuneController { 4 - 5 - public function handleRequest(AphrontRequest $request) { 6 - $viewer = $this->getViewer(); 7 - $added = $request->getBool('added'); 8 - 9 - $subscription = id(new PhortuneSubscriptionQuery()) 10 - ->setViewer($viewer) 11 - ->withIDs(array($request->getURIData('id'))) 12 - ->requireCapabilities( 13 - array( 14 - PhabricatorPolicyCapability::CAN_VIEW, 15 - PhabricatorPolicyCapability::CAN_EDIT, 16 - )) 17 - ->executeOne(); 18 - if (!$subscription) { 19 - return new Aphront404Response(); 20 - } 21 - 22 - id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession( 23 - $viewer, 24 - $request, 25 - $subscription->getURI()); 26 - $merchant = $subscription->getMerchant(); 27 - $account = $subscription->getAccount(); 28 - 29 - $title = pht('Subscription: %s', $subscription->getSubscriptionName()); 30 - 31 - $header = id(new PHUIHeaderView()) 32 - ->setHeader($subscription->getSubscriptionName()); 33 - 34 - $view_uri = $subscription->getURI(); 35 - 36 - $valid_methods = id(new PhortunePaymentMethodQuery()) 37 - ->setViewer($viewer) 38 - ->withAccountPHIDs(array($account->getPHID())) 39 - ->withStatuses( 40 - array( 41 - PhortunePaymentMethod::STATUS_ACTIVE, 42 - )) 43 - ->withMerchantPHIDs(array($merchant->getPHID())) 44 - ->requireCapabilities( 45 - array( 46 - PhabricatorPolicyCapability::CAN_VIEW, 47 - PhabricatorPolicyCapability::CAN_EDIT, 48 - )) 49 - ->execute(); 50 - $valid_methods = mpull($valid_methods, null, 'getPHID'); 51 - 52 - $current_phid = $subscription->getDefaultPaymentMethodPHID(); 53 - 54 - $e_method = null; 55 - if ($current_phid && empty($valid_methods[$current_phid])) { 56 - $e_method = pht('Needs Update'); 57 - } 58 - 59 - $errors = array(); 60 - if ($request->isFormPost()) { 61 - 62 - $default_method_phid = $request->getStr('defaultPaymentMethodPHID'); 63 - if (!$default_method_phid) { 64 - $default_method_phid = null; 65 - $e_method = null; 66 - } else if (empty($valid_methods[$default_method_phid])) { 67 - $e_method = pht('Invalid'); 68 - if ($default_method_phid == $current_phid) { 69 - $errors[] = pht( 70 - 'This subscription is configured to autopay with a payment method '. 71 - 'that has been deleted. Choose a valid payment method or disable '. 72 - 'autopay.'); 73 - } else { 74 - $errors[] = pht('You must select a valid default payment method.'); 75 - } 76 - } 77 - 78 - // TODO: We should use transactions here, and move the validation logic 79 - // inside the Editor. 80 - 81 - if (!$errors) { 82 - $subscription->setDefaultPaymentMethodPHID($default_method_phid); 83 - $subscription->save(); 84 - 85 - return id(new AphrontRedirectResponse()) 86 - ->setURI($view_uri); 87 - } 88 - } 89 - 90 - // Add the option to disable autopay. 91 - $disable_options = array( 92 - '' => pht('(Disable Autopay)'), 93 - ); 94 - 95 - // Don't require the user to make a valid selection if the current method 96 - // has become invalid. 97 - if ($current_phid && empty($valid_methods[$current_phid])) { 98 - $current_options = array( 99 - $current_phid => pht('<Deleted Payment Method>'), 100 - ); 101 - } else { 102 - $current_options = array(); 103 - } 104 - 105 - // Add any available options. 106 - $valid_options = mpull($valid_methods, 'getFullDisplayName', 'getPHID'); 107 - 108 - $options = $disable_options + $current_options + $valid_options; 109 - 110 - $crumbs = $this->buildApplicationCrumbs(); 111 - $this->addAccountCrumb($crumbs, $account); 112 - $crumbs->addTextCrumb( 113 - pht('Subscription %d', $subscription->getID()), 114 - $view_uri); 115 - $crumbs->addTextCrumb(pht('Edit')); 116 - $crumbs->setBorder(true); 117 - 118 - 119 - $uri = $this->getApplicationURI($account->getID().'/card/new/'); 120 - $uri = new PhutilURI($uri); 121 - $uri->replaceQueryParam('merchantID', $merchant->getID()); 122 - $uri->replaceQueryParam('subscriptionID', $subscription->getID()); 123 - 124 - $add_method_button = phutil_tag( 125 - 'a', 126 - array( 127 - 'href' => $uri, 128 - 'class' => 'button button-grey', 129 - ), 130 - pht('Add Payment Method...')); 131 - 132 - $radio = id(new AphrontFormRadioButtonControl()) 133 - ->setName('defaultPaymentMethodPHID') 134 - ->setLabel(pht('Autopay With')) 135 - ->setValue($current_phid) 136 - ->setError($e_method); 137 - 138 - foreach ($options as $key => $value) { 139 - $radio->addButton($key, $value, null); 140 - } 141 - 142 - $form = id(new AphrontFormView()) 143 - ->setUser($viewer) 144 - ->appendChild($radio) 145 - ->appendChild( 146 - id(new AphrontFormMarkupControl()) 147 - ->setValue($add_method_button)) 148 - ->appendChild( 149 - id(new AphrontFormSubmitControl()) 150 - ->setValue(pht('Save Changes')) 151 - ->addCancelButton($view_uri)); 152 - 153 - $box = id(new PHUIObjectBoxView()) 154 - ->setUser($viewer) 155 - ->setHeaderText(pht('Subscription')) 156 - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) 157 - ->setFormErrors($errors) 158 - ->appendChild($form); 159 - 160 - if ($added) { 161 - $info_view = id(new PHUIInfoView()) 162 - ->setSeverity(PHUIInfoView::SEVERITY_SUCCESS) 163 - ->appendChild(pht('Payment method has been successfully added.')); 164 - $box->setInfoView($info_view); 165 - } 166 - 167 - $header = id(new PHUIHeaderView()) 168 - ->setHeader(pht('Edit %s', $subscription->getSubscriptionName())) 169 - ->setHeaderIcon('fa-pencil'); 170 - 171 - $view = id(new PHUITwoColumnView()) 172 - ->setHeader($header) 173 - ->setFooter(array( 174 - $box, 175 - )); 176 - 177 - return $this->newPage() 178 - ->setTitle($title) 179 - ->setCrumbs($crumbs) 180 - ->appendChild($view); 181 - 182 - } 183 - 184 - 185 - }
-99
src/applications/phortune/controller/subscription/PhortuneSubscriptionListController.php
··· 1 - <?php 2 - 3 - final class PhortuneSubscriptionListController 4 - extends PhortuneController { 5 - 6 - private $merchant; 7 - private $account; 8 - 9 - public function handleRequest(AphrontRequest $request) { 10 - $viewer = $request->getViewer(); 11 - $querykey = $request->getURIData('queryKey'); 12 - $merchant_id = $request->getURIData('merchantID'); 13 - $account_id = $request->getURIData('accountID'); 14 - 15 - $engine = new PhortuneSubscriptionSearchEngine(); 16 - 17 - if ($merchant_id) { 18 - $merchant = id(new PhortuneMerchantQuery()) 19 - ->setViewer($viewer) 20 - ->withIDs(array($merchant_id)) 21 - ->requireCapabilities( 22 - array( 23 - PhabricatorPolicyCapability::CAN_VIEW, 24 - PhabricatorPolicyCapability::CAN_EDIT, 25 - )) 26 - ->executeOne(); 27 - if (!$merchant) { 28 - return new Aphront404Response(); 29 - } 30 - $this->merchant = $merchant; 31 - $viewer->grantAuthority($merchant); 32 - $engine->setMerchant($merchant); 33 - } else if ($account_id) { 34 - $account = id(new PhortuneAccountQuery()) 35 - ->setViewer($viewer) 36 - ->withIDs(array($account_id)) 37 - ->requireCapabilities( 38 - array( 39 - PhabricatorPolicyCapability::CAN_VIEW, 40 - PhabricatorPolicyCapability::CAN_EDIT, 41 - )) 42 - ->executeOne(); 43 - if (!$account) { 44 - return new Aphront404Response(); 45 - } 46 - $this->account = $account; 47 - $engine->setAccount($account); 48 - } else { 49 - return new Aphront404Response(); 50 - } 51 - 52 - $controller = id(new PhabricatorApplicationSearchController()) 53 - ->setQueryKey($querykey) 54 - ->setSearchEngine($engine) 55 - ->setNavigation($this->buildSideNavView()); 56 - 57 - return $this->delegateToController($controller); 58 - } 59 - 60 - public function buildSideNavView() { 61 - $viewer = $this->getViewer(); 62 - 63 - $nav = new AphrontSideNavFilterView(); 64 - $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 65 - 66 - id(new PhortuneSubscriptionSearchEngine()) 67 - ->setViewer($viewer) 68 - ->addNavigationItems($nav->getMenu()); 69 - 70 - $nav->selectFilter(null); 71 - 72 - return $nav; 73 - } 74 - 75 - protected function buildApplicationCrumbs() { 76 - $crumbs = parent::buildApplicationCrumbs(); 77 - 78 - $merchant = $this->merchant; 79 - if ($merchant) { 80 - $id = $merchant->getID(); 81 - $this->addMerchantCrumb($crumbs, $merchant); 82 - $crumbs->addTextCrumb( 83 - pht('Subscriptions'), 84 - $this->getApplicationURI("merchant/subscriptions/{$id}/")); 85 - } 86 - 87 - $account = $this->account; 88 - if ($account) { 89 - $id = $account->getID(); 90 - $this->addAccountCrumb($crumbs, $account); 91 - $crumbs->addTextCrumb( 92 - pht('Subscriptions'), 93 - $this->getApplicationURI("{$id}/subscription/")); 94 - } 95 - 96 - return $crumbs; 97 - } 98 - 99 - }
+1 -1
src/applications/phortune/query/PhortuneCartSearchEngine.php
··· 105 105 $merchant = $this->getMerchant(); 106 106 $account = $this->getAccount(); 107 107 if ($merchant) { 108 - return '/phortune/merchant/orders/'.$merchant->getID().'/'.$path; 108 + return $merchant->getOrderListURI($path); 109 109 } else if ($account) { 110 110 return $account->getOrderListURI($path); 111 111 } else {
+2 -17
src/applications/phortune/query/PhortuneChargeSearchEngine.php
··· 62 62 protected function getURI($path) { 63 63 $account = $this->getAccount(); 64 64 if ($account) { 65 - return '/phortune/'.$account->getID().'/charge/'; 65 + return $account->getChargeListURI($path); 66 66 } else { 67 67 return '/phortune/charge/'.$path; 68 68 } ··· 89 89 return parent::buildSavedQueryFromBuiltin($query_key); 90 90 } 91 91 92 - protected function getRequiredHandlePHIDsForResultList( 93 - array $charges, 94 - PhabricatorSavedQuery $query) { 95 - 96 - $phids = array(); 97 - foreach ($charges as $charge) { 98 - $phids[] = $charge->getProviderPHID(); 99 - $phids[] = $charge->getCartPHID(); 100 - $phids[] = $charge->getMerchantPHID(); 101 - $phids[] = $charge->getPaymentMethodPHID(); 102 - } 103 - 104 - return $phids; 105 - } 106 92 107 93 protected function renderResultList( 108 94 array $charges, ··· 114 100 115 101 $table = id(new PhortuneChargeTableView()) 116 102 ->setUser($viewer) 117 - ->setCharges($charges) 118 - ->setHandles($handles); 103 + ->setCharges($charges); 119 104 120 105 $result = new PhabricatorApplicationSearchResultView(); 121 106 $result->setTable($table);
+2 -2
src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php
··· 96 96 $merchant = $this->getMerchant(); 97 97 $account = $this->getAccount(); 98 98 if ($merchant) { 99 - return '/phortune/merchant/'.$merchant->getID().'/subscription/'.$path; 99 + return $merchant->getSubscriptionListURI($path); 100 100 } else if ($account) { 101 - return '/phortune/'.$account->getID().'/subscription/'; 101 + return $account->getSubscriptionListURI($path); 102 102 } else { 103 103 return '/phortune/subscription/'.$path; 104 104 }
+34 -7
src/applications/phortune/storage/PhortuneCart.php
··· 471 471 return $this->getImplementation()->getDescription($this); 472 472 } 473 473 474 - public function getDetailURI(PhortuneMerchant $authority = null) { 475 - if ($authority) { 476 - $prefix = 'merchant/'.$authority->getID().'/'; 477 - } else { 478 - $prefix = ''; 479 - } 480 - return '/phortune/'.$prefix.'cart/'.$this->getID().'/'; 474 + public function getDetailURI() { 475 + return urisprintf( 476 + '/phortune/cart/%d/', 477 + $this->getID()); 481 478 } 482 479 483 480 public function getCheckoutURI() { ··· 496 493 public function canRefundOrder() { 497 494 try { 498 495 $this->assertCanRefundOrder(); 496 + return true; 497 + } catch (Exception $ex) { 498 + return false; 499 + } 500 + } 501 + 502 + public function canVoidOrder() { 503 + try { 504 + $this->assertCanVoidOrder(); 499 505 return true; 500 506 } catch (Exception $ex) { 501 507 return false; ··· 533 539 534 540 return $this->getImplementation()->assertCanRefundOrder($this); 535 541 } 542 + 543 + public function assertCanVoidOrder() { 544 + if (!$this->getIsInvoice()) { 545 + throw new Exception( 546 + pht( 547 + 'This order can not be voided because it is not an invoice.')); 548 + } 549 + 550 + switch ($this->getStatus()) { 551 + case self::STATUS_READY: 552 + break; 553 + default: 554 + throw new Exception( 555 + pht( 556 + 'This order can not be voided because it is not ready for '. 557 + 'payment.')); 558 + } 559 + 560 + return null; 561 + } 562 + 536 563 537 564 protected function getConfiguration() { 538 565 return array(
+11 -12
src/applications/phortune/view/PhortuneChargeTableView.php
··· 3 3 final class PhortuneChargeTableView extends AphrontView { 4 4 5 5 private $charges; 6 - private $handles; 7 6 private $showOrder; 8 7 9 8 public function setShowOrder($show_order) { ··· 15 14 return $this->showOrder; 16 15 } 17 16 18 - public function setHandles(array $handles) { 19 - $this->handles = $handles; 20 - return $this; 21 - } 22 - 23 - public function getHandles() { 24 - return $this->handles; 25 - } 26 - 27 17 public function setCharges(array $charges) { 28 18 $this->charges = $charges; 29 19 return $this; ··· 35 25 36 26 public function render() { 37 27 $charges = $this->getCharges(); 38 - $handles = $this->getHandles(); 39 - $viewer = $this->getUser(); 28 + $viewer = $this->getViewer(); 29 + 30 + $phids = array(); 31 + foreach ($charges as $charge) { 32 + $phids[] = $charge->getCartPHID(); 33 + $phids[] = $charge->getProviderPHID(); 34 + $phids[] = $charge->getPaymentMethodPHID(); 35 + $phids[] = $charge->getMerchantPHID(); 36 + } 37 + 38 + $handles = $viewer->loadHandles($phids); 40 39 41 40 $rows = array(); 42 41 foreach ($charges as $charge) {