mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-21 21:10:56 +01:00
Support basic ad-hoc invoices in Phortune
Summary: This allows a merchant to send a user an invoice for something arbitrary, like services rendered. Two major missing parts: - These don't actually get marked as invoices. I'll fix that in the next diff, but it's not entirely trivial because `subscriptionPHID` is currently overloaded to also mean "is invoice". - We don't send email automatically. I don't plan to fix that for now, since all our invoicing needs are covered by personal email. Test Plan: Merchants have a new "new invoice" option: {F376999} This leads to selecting a user and account, and then you can generate the invoice (only one actual "purchase" / line item for the moment). You can add a longer-form remarkup description to contextualize the billable items: {F377001} This sends the invoice and takes you to the merchant order overview screen: {F377002} For now, you copy/paste that link into a nice personal enterprisey business-to-business email; the recipient sees this: {F377003} Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D12478
This commit is contained in:
parent
e0c95bca86
commit
0195e751c6
12 changed files with 436 additions and 18 deletions
|
@ -2817,6 +2817,8 @@ phutil_register_library_map(array(
|
|||
'PhortuneAccountTransaction' => 'applications/phortune/storage/PhortuneAccountTransaction.php',
|
||||
'PhortuneAccountTransactionQuery' => 'applications/phortune/query/PhortuneAccountTransactionQuery.php',
|
||||
'PhortuneAccountViewController' => 'applications/phortune/controller/PhortuneAccountViewController.php',
|
||||
'PhortuneAdHocCart' => 'applications/phortune/cart/PhortuneAdHocCart.php',
|
||||
'PhortuneAdHocProduct' => 'applications/phortune/product/PhortuneAdHocProduct.php',
|
||||
'PhortuneCart' => 'applications/phortune/storage/PhortuneCart.php',
|
||||
'PhortuneCartAcceptController' => 'applications/phortune/controller/PhortuneCartAcceptController.php',
|
||||
'PhortuneCartCancelController' => 'applications/phortune/controller/PhortuneCartCancelController.php',
|
||||
|
@ -2856,6 +2858,7 @@ phutil_register_library_map(array(
|
|||
'PhortuneMerchantEditController' => 'applications/phortune/controller/PhortuneMerchantEditController.php',
|
||||
'PhortuneMerchantEditor' => 'applications/phortune/editor/PhortuneMerchantEditor.php',
|
||||
'PhortuneMerchantHasMemberEdgeType' => 'applications/phortune/edge/PhortuneMerchantHasMemberEdgeType.php',
|
||||
'PhortuneMerchantInvoiceCreateController' => 'applications/phortune/controller/PhortuneMerchantInvoiceCreateController.php',
|
||||
'PhortuneMerchantListController' => 'applications/phortune/controller/PhortuneMerchantListController.php',
|
||||
'PhortuneMerchantPHIDType' => 'applications/phortune/phid/PhortuneMerchantPHIDType.php',
|
||||
'PhortuneMerchantQuery' => 'applications/phortune/query/PhortuneMerchantQuery.php',
|
||||
|
@ -6265,6 +6268,8 @@ phutil_register_library_map(array(
|
|||
'PhortuneAccountTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'PhortuneAccountTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhortuneAccountViewController' => 'PhortuneController',
|
||||
'PhortuneAdHocCart' => 'PhortuneCartImplementation',
|
||||
'PhortuneAdHocProduct' => 'PhortuneProductImplementation',
|
||||
'PhortuneCart' => array(
|
||||
'PhortuneDAO',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
|
@ -6312,6 +6317,7 @@ phutil_register_library_map(array(
|
|||
'PhortuneMerchantEditController' => 'PhortuneMerchantController',
|
||||
'PhortuneMerchantEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhortuneMerchantHasMemberEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhortuneMerchantInvoiceCreateController' => 'PhortuneMerchantController',
|
||||
'PhortuneMerchantListController' => 'PhortuneMerchantController',
|
||||
'PhortuneMerchantPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhortuneMerchantQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
|
|
|
@ -84,19 +84,24 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
|
|||
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantEditController',
|
||||
'orders/(?P<merchantID>\d+)/(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
=> 'PhortuneCartListController',
|
||||
'(?P<merchantID>\d+)/cart/(?P<id>\d+)/' => array(
|
||||
'' => 'PhortuneCartViewController',
|
||||
'(?P<action>cancel|refund)/' => 'PhortuneCartCancelController',
|
||||
'update/' => 'PhortuneCartUpdateController',
|
||||
'accept/' => 'PhortuneCartAcceptController',
|
||||
),
|
||||
'(?P<merchantID>\d+)/subscription/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
=> 'PhortuneSubscriptionListController',
|
||||
'view/(?P<id>\d+)/'
|
||||
=> 'PhortuneSubscriptionViewController',
|
||||
'order/(?P<subscriptionID>\d+)/'
|
||||
=> 'PhortuneCartListController',
|
||||
'(?P<merchantID>\d+)/' => array(
|
||||
'cart/(?P<id>\d+)/' => array(
|
||||
'' => 'PhortuneCartViewController',
|
||||
'(?P<action>cancel|refund)/' => 'PhortuneCartCancelController',
|
||||
'update/' => 'PhortuneCartUpdateController',
|
||||
'accept/' => 'PhortuneCartAcceptController',
|
||||
),
|
||||
'subscription/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
=> 'PhortuneSubscriptionListController',
|
||||
'view/(?P<id>\d+)/'
|
||||
=> 'PhortuneSubscriptionViewController',
|
||||
'order/(?P<subscriptionID>\d+)/'
|
||||
=> 'PhortuneCartListController',
|
||||
),
|
||||
'invoice/' => array(
|
||||
'new/' => 'PhortuneMerchantInvoiceCreateController',
|
||||
),
|
||||
),
|
||||
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
|
||||
),
|
||||
|
|
39
src/applications/phortune/cart/PhortuneAdHocCart.php
Normal file
39
src/applications/phortune/cart/PhortuneAdHocCart.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
final class PhortuneAdHocCart extends PhortuneCartImplementation {
|
||||
|
||||
public function loadImplementationsForCarts(
|
||||
PhabricatorUser $viewer,
|
||||
array $carts) {
|
||||
|
||||
$results = array();
|
||||
foreach ($carts as $key => $cart) {
|
||||
$results[$key] = new PhortuneAdHocCart();
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function getName(PhortuneCart $cart) {
|
||||
return $cart->getMetadataValue('adhoc.title');
|
||||
}
|
||||
|
||||
public function getDescription(PhortuneCart $cart) {
|
||||
return $cart->getMetadataValue('adhoc.description');
|
||||
}
|
||||
|
||||
public function getCancelURI(PhortuneCart $cart) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDoneURI(PhortuneCart $cart) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function willCreateCart(
|
||||
PhabricatorUser $viewer,
|
||||
PhortuneCart $cart) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,10 @@ abstract class PhortuneCartImplementation {
|
|||
abstract public function getCancelURI(PhortuneCart $cart);
|
||||
abstract public function getDoneURI(PhortuneCart $cart);
|
||||
|
||||
public function getDescription(PhortuneCart $cart) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDoneActionName(PhortuneCart $cart) {
|
||||
return pht('Return to Application');
|
||||
}
|
||||
|
|
|
@ -209,6 +209,8 @@ final class PhortuneCartCheckoutController
|
|||
->appendChild($form)
|
||||
->appendChild($provider_form);
|
||||
|
||||
$description_box = $this->renderCartDescription($cart);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(pht('Checkout'));
|
||||
$crumbs->addTextCrumb($title);
|
||||
|
@ -217,6 +219,7 @@ final class PhortuneCartCheckoutController
|
|||
array(
|
||||
$crumbs,
|
||||
$cart_box,
|
||||
$description_box,
|
||||
$payment_box,
|
||||
),
|
||||
array(
|
||||
|
|
|
@ -42,4 +42,26 @@ abstract class PhortuneCartController
|
|||
return $table;
|
||||
}
|
||||
|
||||
protected function renderCartDescription(PhortuneCart $cart) {
|
||||
$description = $cart->getDescription();
|
||||
if (!strlen($description)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$output = PhabricatorMarkupEngine::renderOneObject(
|
||||
id(new PhabricatorMarkupOneOff())
|
||||
->setPreserveLinebreaks(true)
|
||||
->setContent($description),
|
||||
'default',
|
||||
$this->getViewer());
|
||||
|
||||
$box = id(new PHUIBoxView())
|
||||
->addMargin(PHUI::MARGIN_LARGE)
|
||||
->appendChild($output);
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Description'))
|
||||
->appendChild($box);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,11 +15,16 @@ final class PhortuneCartViewController
|
|||
|
||||
$authority = $this->loadMerchantAuthority();
|
||||
|
||||
$cart = id(new PhortuneCartQuery())
|
||||
$query = id(new PhortuneCartQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->id))
|
||||
->needPurchases(true)
|
||||
->executeOne();
|
||||
->needPurchases(true);
|
||||
|
||||
if ($authority) {
|
||||
$query->withMerchantPHIDs(array($authority->getPHID()));
|
||||
}
|
||||
|
||||
$cart = $query->executeOne();
|
||||
if (!$cart) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
@ -35,6 +40,31 @@ final class PhortuneCartViewController
|
|||
$error_view = null;
|
||||
$resume_uri = null;
|
||||
switch ($cart->getStatus()) {
|
||||
case PhortuneCart::STATUS_READY:
|
||||
if ($authority && $request->getStr('invoice')) {
|
||||
// We arrived here by following the ad-hoc invoice workflow, and
|
||||
// are acting with merchant authority.
|
||||
|
||||
$checkout_uri = PhabricatorEnv::getURI($cart->getCheckoutURI());
|
||||
|
||||
$invoice_message = array(
|
||||
pht(
|
||||
'Manual invoices do not automatically notify recipients yet. '.
|
||||
'Send the payer this checkout link:'),
|
||||
' ',
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $checkout_uri,
|
||||
),
|
||||
$checkout_uri),
|
||||
);
|
||||
|
||||
$error_view = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setErrors(array($invoice_message));
|
||||
}
|
||||
break;
|
||||
case PhortuneCart::STATUS_PURCHASING:
|
||||
if ($can_edit) {
|
||||
$resume_uri = $cart->getMetadataValue('provider.checkoutURI');
|
||||
|
@ -86,7 +116,6 @@ final class PhortuneCartViewController
|
|||
$error_view = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
||||
->appendChild(pht('This purchase has been completed.'));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -126,6 +155,8 @@ final class PhortuneCartViewController
|
|||
$cart_box->setInfoView($error_view);
|
||||
}
|
||||
|
||||
$description = $this->renderCartDescription($cart);
|
||||
|
||||
$charges = id(new PhortuneChargeQuery())
|
||||
->setViewer($viewer)
|
||||
->withCartPHIDs(array($cart->getPHID()))
|
||||
|
@ -171,6 +202,7 @@ final class PhortuneCartViewController
|
|||
array(
|
||||
$crumbs,
|
||||
$cart_box,
|
||||
$description,
|
||||
$charges,
|
||||
$timeline,
|
||||
),
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
<?php
|
||||
|
||||
final class PhortuneMerchantInvoiceCreateController
|
||||
extends PhortuneMerchantController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$merchant = $this->loadMerchantAuthority();
|
||||
if (!$merchant) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$merchant_id = $merchant->getID();
|
||||
$cancel_uri = $this->getApplicationURI("/merchant/{$merchant_id}/");
|
||||
|
||||
// Load the user to invoice, or prompt the viewer to select one.
|
||||
$target_user = null;
|
||||
$user_phid = head($request->getArr('userPHID'));
|
||||
if (!$user_phid) {
|
||||
$user_phid = $request->getStr('userPHID');
|
||||
}
|
||||
if ($user_phid) {
|
||||
$target_user = id(new PhabricatorPeopleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($user_phid))
|
||||
->executeOne();
|
||||
}
|
||||
|
||||
if (!$target_user) {
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendRemarkupInstructions(pht('Choose a user to invoice.'))
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setLabel(pht('User'))
|
||||
->setDatasource(new PhabricatorPeopleDatasource())
|
||||
->setName('userPHID')
|
||||
->setLimit(1));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Choose User'))
|
||||
->appendForm($form)
|
||||
->addCancelButton($cancel_uri)
|
||||
->addSubmitButton(pht('Continue'));
|
||||
}
|
||||
|
||||
// Load the account to invoice, or prompt the viewer to select one.
|
||||
$target_account = null;
|
||||
$account_phid = $request->getStr('accountPHID');
|
||||
if ($account_phid) {
|
||||
$target_account = id(new PhortuneAccountQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($account_phid))
|
||||
->withMemberPHIDs(array($target_user->getPHID()))
|
||||
->executeOne();
|
||||
}
|
||||
|
||||
if (!$target_account) {
|
||||
$accounts = PhortuneAccountQuery::loadAccountsForUser(
|
||||
$target_user,
|
||||
PhabricatorContentSource::newFromRequest($request));
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->addHiddenInput('userPHID', $target_user->getPHID())
|
||||
->appendRemarkupInstructions(pht('Choose which account to invoice.'))
|
||||
->appendControl(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel(pht('User'))
|
||||
->setValue($viewer->renderHandle($target_user->getPHID())))
|
||||
->appendControl(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Account'))
|
||||
->setName('accountPHID')
|
||||
->setValue($account_phid)
|
||||
->setOptions(mpull($accounts, 'getName', 'getPHID')));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Choose Account'))
|
||||
->appendForm($form)
|
||||
->addCancelButton($cancel_uri)
|
||||
->addSubmitButton(pht('Continue'));
|
||||
}
|
||||
|
||||
|
||||
// Now we build the actual invoice.
|
||||
$title = pht('New Invoice');
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($merchant->getName());
|
||||
|
||||
$v_title = $request->getStr('title');
|
||||
$e_title = true;
|
||||
|
||||
$v_name = $request->getStr('name');
|
||||
$e_name = true;
|
||||
|
||||
$v_cost = $request->getStr('cost');
|
||||
$e_cost = true;
|
||||
|
||||
$v_desc = $request->getStr('description');
|
||||
|
||||
$v_quantity = 1;
|
||||
$e_quantity = null;
|
||||
|
||||
$errors = array();
|
||||
if ($request->isFormPost() && $request->getStr('invoice')) {
|
||||
$v_quantity = $request->getStr('quantity');
|
||||
|
||||
$e_title = null;
|
||||
$e_name = null;
|
||||
$e_cost = null;
|
||||
$e_quantity = null;
|
||||
|
||||
if (!strlen($v_title)) {
|
||||
$e_title = pht('Required');
|
||||
$errors[] = pht('You must title this invoice.');
|
||||
}
|
||||
|
||||
if (!strlen($v_name)) {
|
||||
$e_name = pht('Required');
|
||||
$errors[] = pht('You must provide a name for this purchase.');
|
||||
}
|
||||
|
||||
if (!strlen($v_cost)) {
|
||||
$e_cost = pht('Required');
|
||||
$errors[] = pht('You must provide a cost for this purchase.');
|
||||
} else {
|
||||
try {
|
||||
$v_currency = PhortuneCurrency::newFromUserInput(
|
||||
$viewer,
|
||||
$v_cost);
|
||||
} catch (Exception $ex) {
|
||||
$errors[] = $ex->getMessage();
|
||||
$e_cost = pht('Invalid');
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)$v_quantity <= 0) {
|
||||
$e_quantity = pht('Invalid');
|
||||
$errors[] = pht('Quantity must be a positive integer.');
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$unique = Filesystem::readRandomCharacters(16);
|
||||
|
||||
$product = id(new PhortuneProductQuery())
|
||||
->setViewer($target_user)
|
||||
->withClassAndRef('PhortuneAdHocProduct', $unique)
|
||||
->executeOne();
|
||||
|
||||
$cart_implementation = new PhortuneAdHocCart();
|
||||
|
||||
$cart = $target_account->newCart(
|
||||
$target_user,
|
||||
$cart_implementation,
|
||||
$merchant);
|
||||
|
||||
$cart
|
||||
->setMetadataValue('adhoc.title', $v_title)
|
||||
->setMetadataValue('adhoc.description', $v_desc);
|
||||
|
||||
$purchase = $cart->newPurchase($target_user, $product)
|
||||
->setBasePriceAsCurrency($v_currency)
|
||||
->setQuantity((int)$v_quantity)
|
||||
->setMetadataValue('adhoc.name', $v_name)
|
||||
->save();
|
||||
|
||||
// TODO: Actually mark these as invoices. Right now, there's no easy
|
||||
// way to do that.
|
||||
|
||||
$cart->activateCart();
|
||||
$cart_id = $cart->getID();
|
||||
|
||||
$uri = "/merchant/{$merchant_id}/cart/{$cart_id}/?invoice=true";
|
||||
$uri = $this->getApplicationURI($uri);
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||
}
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->addHiddenInput('userPHID', $target_user->getPHID())
|
||||
->addHiddenInput('accountPHID', $target_account->getPHID())
|
||||
->addHiddenInput('invoice', true)
|
||||
->appendControl(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel(pht('User'))
|
||||
->setValue($viewer->renderHandle($target_user->getPHID())))
|
||||
->appendControl(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel(pht('Account'))
|
||||
->setValue($viewer->renderHandle($target_account->getPHID())))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Invoice Title'))
|
||||
->setName('title')
|
||||
->setValue($v_title)
|
||||
->setError($e_title))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Purchase Name'))
|
||||
->setName('name')
|
||||
->setValue($v_name)
|
||||
->setError($e_name))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Purchase Cost'))
|
||||
->setName('cost')
|
||||
->setValue($v_cost)
|
||||
->setError($e_cost))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Quantity'))
|
||||
->setName('quantity')
|
||||
->setValue($v_quantity)
|
||||
->setError($e_quantity))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setLabel(pht('Invoice Description'))
|
||||
->setName('description')
|
||||
->setValue($v_desc))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->addCancelButton($cancel_uri)
|
||||
->setValue(pht('Send Invoice')));
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('New Invoice'))
|
||||
->setFormErrors($errors)
|
||||
->appendChild($form);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -192,6 +192,15 @@ final class PhortuneMerchantViewController
|
|||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit));
|
||||
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('New Invoice'))
|
||||
->setIcon('fa-fax')
|
||||
->setHref($this->getApplicationURI("merchant/{$id}/invoice/new/"))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
|
42
src/applications/phortune/product/PhortuneAdHocProduct.php
Normal file
42
src/applications/phortune/product/PhortuneAdHocProduct.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
final class PhortuneAdHocProduct extends PhortuneProductImplementation {
|
||||
|
||||
private $ref;
|
||||
|
||||
public function loadImplementationsForRefs(
|
||||
PhabricatorUser $viewer,
|
||||
array $refs) {
|
||||
|
||||
$results = array();
|
||||
foreach ($refs as $key => $ref) {
|
||||
$product = new PhortuneAdHocProduct();
|
||||
$product->ref = $ref;
|
||||
$results[$key] = $product;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function getRef() {
|
||||
return $this->ref;
|
||||
}
|
||||
|
||||
public function getName(PhortuneProduct $product) {
|
||||
return pht('Ad-Hoc Product');
|
||||
}
|
||||
|
||||
public function getPurchaseName(
|
||||
PhortuneProduct $product,
|
||||
PhortunePurchase $purchase) {
|
||||
|
||||
return coalesce(
|
||||
$purchase->getMetadataValue('adhoc.name'),
|
||||
$this->getName($product));
|
||||
}
|
||||
|
||||
public function getPriceAsCurrency(PhortuneProduct $product) {
|
||||
return PhortuneCurrency::newEmptyCurrency();
|
||||
}
|
||||
|
||||
}
|
|
@ -453,6 +453,10 @@ final class PhortuneCart extends PhortuneDAO
|
|||
return $this->getImplementation()->getCancelURI($this);
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return $this->getImplementation()->getDescription($this);
|
||||
}
|
||||
|
||||
public function getDetailURI(PhortuneMerchant $authority = null) {
|
||||
if ($authority) {
|
||||
$prefix = 'merchant/'.$authority->getID().'/';
|
||||
|
|
|
@ -92,7 +92,14 @@ final class PhortunePurchase extends PhortuneDAO
|
|||
}
|
||||
|
||||
public function getTotalPriceAsCurrency() {
|
||||
return $this->getBasePriceAsCurrency();
|
||||
$base = $this->getBasePriceAsCurrency();
|
||||
|
||||
$price = PhortuneCurrency::newEmptyCurrency();
|
||||
for ($ii = 0; $ii < $this->getQuantity(); $ii++) {
|
||||
$price = $price->add($base);
|
||||
}
|
||||
|
||||
return $price;
|
||||
}
|
||||
|
||||
public function getMetadataValue($key, $default = null) {
|
||||
|
|
Loading…
Reference in a new issue