1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-17 20:32:41 +01:00

Add a basic invoice view for printing to Phortune

Summary: Makes a more complete PDF looking invoice form for printing in Phortune.

Test Plan: Make an invoice, click print view, print.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Differential Revision: https://secure.phabricator.com/D16762
This commit is contained in:
Chad Little 2016-10-29 17:45:10 -07:00
parent 1993005651
commit 5fd79479ec
12 changed files with 419 additions and 40 deletions

View file

@ -89,6 +89,7 @@ return array(
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
'rsrc/css/application/phortune/phortune-credit-card-form.css' => '8391eb02',
'rsrc/css/application/phortune/phortune-invoice.css' => '476055e2',
'rsrc/css/application/phortune/phortune.css' => '5b99dae0',
'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad',
'rsrc/css/application/phriction/phriction-document-css.css' => '4282e4ad',
@ -840,6 +841,7 @@ return array(
'phortune-credit-card-form' => '2290aeef',
'phortune-credit-card-form-css' => '8391eb02',
'phortune-css' => '5b99dae0',
'phortune-invoice-css' => '476055e2',
'phrequent-css' => 'ffc185ad',
'phriction-document-css' => '4282e4ad',
'phui-action-panel-css' => '91c7b835',

View file

@ -0,0 +1,5 @@
ALTER TABLE {$NAMESPACE}_phortune.phortune_merchant
ADD invoiceEmail VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;
ALTER TABLE {$NAMESPACE}_phortune.phortune_merchant
ADD invoiceFooter LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL;

View file

@ -4174,6 +4174,7 @@ phutil_register_library_map(array(
'PhortuneCurrencyTestCase' => 'applications/phortune/currency/__tests__/PhortuneCurrencyTestCase.php',
'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php',
'PhortuneInvoiceView' => 'applications/phortune/view/PhortuneInvoiceView.php',
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php',
'PhortuneMemberHasMerchantEdgeType' => 'applications/phortune/edge/PhortuneMemberHasMerchantEdgeType.php',
@ -9422,6 +9423,7 @@ phutil_register_library_map(array(
'PhortuneCurrencyTestCase' => 'PhabricatorTestCase',
'PhortuneDAO' => 'PhabricatorLiskDAO',
'PhortuneErrCode' => 'PhortuneConstants',
'PhortuneInvoiceView' => 'AphrontTagView',
'PhortuneLandingController' => 'PhortuneController',
'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType',
'PhortuneMemberHasMerchantEdgeType' => 'PhabricatorEdgeType',

View file

@ -61,6 +61,7 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
'cart/(?P<id>\d+)/' => array(
'' => 'PhortuneCartViewController',
'checkout/' => 'PhortuneCartCheckoutController',
'(?P<action>print)/' => 'PhortuneCartViewController',
'(?P<action>cancel|refund)/' => 'PhortuneCartCancelController',
'update/' => 'PhortuneCartUpdateController',
),

View file

@ -3,9 +3,12 @@
final class PhortuneCartViewController
extends PhortuneCartController {
private $action = null;
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$this->action = $request->getURIData('action');
$authority = $this->loadMerchantAuthority();
require_celerity_resource('phortune-css');
@ -129,7 +132,7 @@ final class PhortuneCartViewController
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader(pht('Order Detail'))
->setHeader($cart->getName())
->setHeaderIcon('fa-shopping-cart');
if ($cart->getStatus() == PhortuneCart::STATUS_PURCHASED) {
@ -188,36 +191,75 @@ final class PhortuneCartViewController
$crumbs->addTextCrumb(pht('Cart %d', $cart->getID()));
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
$cart,
new PhortuneCartTransactionQuery());
$timeline
->setShouldTerminate(true);
if (!$this->action) {
$class = 'phortune-cart-page';
$timeline = $this->buildTransactionTimeline(
$cart,
new PhortuneCartTransactionQuery());
$timeline
->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$error_view,
$details,
$cart_box,
$description,
$charges,
$timeline,
));
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$error_view,
$details,
$cart_box,
$description,
$charges,
$timeline,
));
return $this->newPage()
} else {
$class = 'phortune-invoice-view';
$crumbs = null;
$merchant_phid = $cart->getMerchantPHID();
$buyer_phid = $cart->getAuthorPHID();
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withPHIDs(array($merchant_phid))
->needProfileImage(true)
->executeOne();
$buyer = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($buyer_phid))
->needProfileImage(true)
->executeOne();
// TODO: Add account "Contact" info
$merchant_contact = new PHUIRemarkupView(
$viewer, $merchant->getContactInfo());
$description = null;
$view = id(new PhortuneInvoiceView())
->setMerchantName($merchant->getName())
->setMerchantLogo($merchant->getProfileImageURI())
->setMerchantContact($merchant_contact)
->setMerchantFooter($merchant->getInvoiceFooter())
->setAccountName($buyer->getRealName())
->setStatus($error_view)
->setContent(array(
$description,
$details,
$cart_box,
$charges,
));
}
$page = $this->newPage()
->setTitle(pht('Cart %d', $cart->getID()))
->setCrumbs($crumbs)
->addClass('phortune-cart-page')
->addClass($class)
->appendChild($view);
if ($crumbs) {
$page->setCrumbs($crumbs);
}
return $page;
}
private function buildDetailsView(PhortuneCart $cart) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($cart);
@ -229,9 +271,10 @@ final class PhortuneCartViewController
$cart->getMerchantPHID(),
));
$view->addProperty(
pht('Order Name'),
$cart->getName());
if ($this->action == 'print') {
$view->addProperty(pht('Order Name'), $cart->getName());
}
$view->addProperty(
pht('Account'),
$handles[$cart->getAccountPHID()]->renderLink());
@ -276,7 +319,7 @@ final class PhortuneCartViewController
$refund_uri = $this->getApplicationURI("{$prefix}cart/{$id}/refund/");
$update_uri = $this->getApplicationURI("{$prefix}cart/{$id}/update/");
$accept_uri = $this->getApplicationURI("{$prefix}cart/{$id}/accept/");
$print_uri = $this->getApplicationURI("{$prefix}cart/{$id}/?__print__=1");
$print_uri = $this->getApplicationURI("{$prefix}cart/{$id}/print/");
$curtain->addAction(
id(new PhabricatorActionView())

View file

@ -129,6 +129,13 @@ final class PhortuneMerchantViewController
$view->addProperty(pht('Status'), $status_view);
$invoice_from = $merchant->getInvoiceEmail();
if (!$invoice_from) {
$invoice_from = pht('No email address set');
$invoice_from = phutil_tag('em', array(), $invoice_from);
}
$view->addProperty(pht('Invoice From'), $invoice_from);
$description = $merchant->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
@ -147,6 +154,15 @@ final class PhortuneMerchantViewController
$view->addTextContent($contact_info);
}
$footer_info = $merchant->getInvoiceFooter();
if (strlen($footer_info)) {
$footer_info = new PHUIRemarkupView($viewer, $footer_info);
$view->addSectionHeader(
pht('Invoice Footer'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($footer_info);
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)

View file

@ -84,6 +84,21 @@ final class PhortuneMerchantEditEngine
->setTransactionType(PhortuneMerchantTransaction::TYPE_NAME)
->setValue($object->getName()),
id(new PhabricatorUsersEditField())
->setKey('members')
->setAliases(array('memberPHIDs'))
->setLabel(pht('Members'))
->setUseEdgeTransactions(true)
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue(
'edge:type',
PhortuneMerchantHasMemberEdgeType::EDGECONST)
->setDescription(pht('Initial merchant members.'))
->setConduitDescription(pht('Set merchant members.'))
->setConduitTypeDescription(pht('New list of members.'))
->setInitialValue($object->getMemberPHIDs())
->setValue($member_phids),
id(new PhabricatorRemarkupEditField())
->setKey('description')
->setLabel(pht('Description'))
@ -100,20 +115,22 @@ final class PhortuneMerchantEditEngine
->setTransactionType(PhortuneMerchantTransaction::TYPE_CONTACTINFO)
->setValue($object->getContactInfo()),
id(new PhabricatorUsersEditField())
->setKey('members')
->setAliases(array('memberPHIDs'))
->setLabel(pht('Members'))
->setUseEdgeTransactions(true)
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue(
'edge:type',
PhortuneMerchantHasMemberEdgeType::EDGECONST)
->setDescription(pht('Initial merchant members.'))
->setConduitDescription(pht('Set merchant members.'))
->setConduitTypeDescription(pht('New list of members.'))
->setInitialValue($object->getMemberPHIDs())
->setValue($member_phids),
id(new PhabricatorTextEditField())
->setKey('invoiceEmail')
->setLabel(pht('Invoice From Email'))
->setDescription(pht('Email address invoices are sent from.'))
->setConduitTypeDescription(
pht('Email address invoices are sent from.'))
->setTransactionType(PhortuneMerchantTransaction::TYPE_INVOICEEMAIL)
->setValue($object->getInvoiceEmail()),
id(new PhabricatorRemarkupEditField())
->setKey('invoiceFooter')
->setLabel(pht('Invoice Footer'))
->setDescription(pht('Footer on invoice forms.'))
->setConduitTypeDescription(pht('Footer on invoice forms.'))
->setTransactionType(PhortuneMerchantTransaction::TYPE_INVOICEFOOTER)
->setValue($object->getInvoiceFooter()),
);
}

View file

@ -18,6 +18,8 @@ final class PhortuneMerchantEditor
$types[] = PhortuneMerchantTransaction::TYPE_DESCRIPTION;
$types[] = PhortuneMerchantTransaction::TYPE_CONTACTINFO;
$types[] = PhortuneMerchantTransaction::TYPE_PICTURE;
$types[] = PhortuneMerchantTransaction::TYPE_INVOICEEMAIL;
$types[] = PhortuneMerchantTransaction::TYPE_INVOICEFOOTER;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDGE;
@ -34,6 +36,10 @@ final class PhortuneMerchantEditor
return $object->getDescription();
case PhortuneMerchantTransaction::TYPE_CONTACTINFO:
return $object->getContactInfo();
case PhortuneMerchantTransaction::TYPE_INVOICEEMAIL:
return $object->getInvoiceEmail();
case PhortuneMerchantTransaction::TYPE_INVOICEFOOTER:
return $object->getInvoiceFooter();
case PhortuneMerchantTransaction::TYPE_PICTURE:
return $object->getProfileImagePHID();
}
@ -49,6 +55,8 @@ final class PhortuneMerchantEditor
case PhortuneMerchantTransaction::TYPE_NAME:
case PhortuneMerchantTransaction::TYPE_DESCRIPTION:
case PhortuneMerchantTransaction::TYPE_CONTACTINFO:
case PhortuneMerchantTransaction::TYPE_INVOICEEMAIL:
case PhortuneMerchantTransaction::TYPE_INVOICEFOOTER:
case PhortuneMerchantTransaction::TYPE_PICTURE:
return $xaction->getNewValue();
}
@ -70,6 +78,12 @@ final class PhortuneMerchantEditor
case PhortuneMerchantTransaction::TYPE_CONTACTINFO:
$object->setContactInfo($xaction->getNewValue());
return;
case PhortuneMerchantTransaction::TYPE_INVOICEEMAIL:
$object->setInvoiceEmail($xaction->getNewValue());
return;
case PhortuneMerchantTransaction::TYPE_INVOICEFOOTER:
$object->setInvoiceFooter($xaction->getNewValue());
return;
case PhortuneMerchantTransaction::TYPE_PICTURE:
$object->setProfileImagePHID($xaction->getNewValue());
return;
@ -86,6 +100,8 @@ final class PhortuneMerchantEditor
case PhortuneMerchantTransaction::TYPE_NAME:
case PhortuneMerchantTransaction::TYPE_DESCRIPTION:
case PhortuneMerchantTransaction::TYPE_CONTACTINFO:
case PhortuneMerchantTransaction::TYPE_INVOICEEMAIL:
case PhortuneMerchantTransaction::TYPE_INVOICEFOOTER:
case PhortuneMerchantTransaction::TYPE_PICTURE:
return;
}
@ -116,6 +132,29 @@ final class PhortuneMerchantEditor
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
case PhortuneMerchantTransaction::TYPE_INVOICEEMAIL:
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhortuneMerchantTransaction::TYPE_INVOICEEMAIL:
$new_email = $xaction->getNewValue();
break;
}
}
if (strlen($new_email)) {
$email = new PhutilEmailAddress($new_email);
$domain = $email->getDomainName();
if (!$domain) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht('%s is not a valid email.', $new_email),
nonempty(last($xactions), null));
$errors[] = $error;
}
}
break;
}

View file

@ -9,6 +9,8 @@ final class PhortuneMerchant extends PhortuneDAO
protected $viewPolicy;
protected $description;
protected $contactInfo;
protected $invoiceEmail;
protected $invoiceFooter;
protected $profileImagePHID;
private $memberPHIDs = self::ATTACHABLE;
@ -27,6 +29,8 @@ final class PhortuneMerchant extends PhortuneDAO
'name' => 'text255',
'description' => 'text',
'contactInfo' => 'text',
'invoiceEmail' => 'text255',
'invoiceFooter' => 'text',
'profileImagePHID' => 'phid?',
),
) + parent::getConfiguration();

View file

@ -6,6 +6,8 @@ final class PhortuneMerchantTransaction
const TYPE_NAME = 'merchant:name';
const TYPE_DESCRIPTION = 'merchant:description';
const TYPE_CONTACTINFO = 'merchant:contactinfo';
const TYPE_INVOICEEMAIL = 'merchant:invoiceemail';
const TYPE_INVOICEFOOTER = 'merchant:invoicefooter';
const TYPE_PICTURE = 'merchant:picture';
public function getApplicationName() {
@ -48,6 +50,14 @@ final class PhortuneMerchantTransaction
return pht(
'%s updated the contact information for this merchant.',
$this->renderHandleLink($author_phid));
case self::TYPE_INVOICEEMAIL:
return pht(
'%s updated the invoice email for this merchant.',
$this->renderHandleLink($author_phid));
case self::TYPE_INVOICEFOOTER:
return pht(
'%s updated the invoice footer for this merchant.',
$this->renderHandleLink($author_phid));
}
return parent::getTitle();
@ -58,6 +68,8 @@ final class PhortuneMerchantTransaction
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
case self::TYPE_CONTACTINFO:
case self::TYPE_INVOICEEMAIL:
case self::TYPE_INVOICEFOOTER:
return ($old === null);
}
return parent::shouldHide();
@ -69,6 +81,10 @@ final class PhortuneMerchantTransaction
return ($this->getOldValue() !== null);
case self::TYPE_CONTACTINFO:
return ($this->getOldValue() !== null);
case self::TYPE_INVOICEEMAIL:
return ($this->getOldValue() !== null);
case self::TYPE_INVOICEFOOTER:
return ($this->getOldValue() !== null);
}
return parent::hasChangeDetails();

View file

@ -0,0 +1,159 @@
<?php
final class PhortuneInvoiceView extends AphrontTagView {
private $merchantName;
private $merchantLogo;
private $merchantContact;
private $merchantFooter;
private $accountName;
private $accountContact;
private $status;
private $content;
public function setMerchantName($name) {
$this->merchantName = $name;
return $this;
}
public function setMerchantLogo($logo) {
$this->merchantLogo = $logo;
return $this;
}
public function setMerchantContact($contact) {
$this->merchantContact = $contact;
return $this;
}
public function setMerchantFooter($footer) {
$this->merchantFooter = $footer;
return $this;
}
public function setAccountName($name) {
$this->accountName = $name;
return $this;
}
public function setAccountContact($contact) {
$this->accountContact = $contact;
return $this;
}
public function setStatus($status) {
$this->status = $status;
return $this;
}
public function setContent($content) {
$this->content = $content;
return $this;
}
protected function getTagAttributes() {
$classes = array();
$classes[] = 'phortune-invoice-view';
return array(
'class' => implode(' ', $classes),
);
}
protected function getTagContent() {
require_celerity_resource('phortune-invoice-css');
$logo = phutil_tag(
'div',
array(
'class' => 'phortune-invoice-logo',
),
phutil_tag(
'img',
array(
'height' => '50',
'width' => '50',
'alt' => $this->merchantName,
'src' => $this->merchantLogo,
)));
$to_title = phutil_tag(
'div',
array(
'class' => 'phortune-mini-header',
),
pht('To:'));
$bill_to = phutil_tag(
'td',
array(
'class' => 'phortune-invoice-to',
'width' => '50%',
),
array(
$to_title,
phutil_tag('strong', array(), $this->accountName),
phutil_tag('br', array()),
$this->accountContact,
));
$from_title = phutil_tag(
'div',
array(
'class' => 'phortune-mini-header',
),
pht('From:'));
$bill_from = phutil_tag(
'td',
array(
'class' => 'phortune-invoice-from',
'width' => '50%',
),
array(
$from_title,
phutil_tag('strong', array(), $this->merchantName),
phutil_tag('br', array()),
$this->merchantContact,
));
$contact = phutil_tag(
'table',
array(
'class' => 'phortune-invoice-contact',
'width' => '100%',
),
phutil_tag(
'tr',
array(),
array(
$bill_to,
$bill_from,
)));
$status = null;
if ($this->status) {
$status = phutil_tag(
'div',
array(
'class' => 'phortune-invoice-status',
),
$this->status);
}
$footer = phutil_tag(
'div',
array(
'class' => 'phortune-invoice-footer',
),
$this->merchantFooter);
return array(
$logo,
$contact,
$status,
$this->content,
$footer,
);
}
}

View file

@ -0,0 +1,75 @@
/**
* @provides phortune-invoice-css
*/
.phortune-invoice-view {
max-width: 800px;
margin: 16px auto;
background: #fff;
}
.phortune-invoice-view .phabricator-main-menu {
display: none;
}
.phortune-invoice-view .phabricator-standard-page-footer {
display: none;
}
.device-desktop .phortune-invoice-view .phui-property-list-key {
width: 16%;
}
.device-desktop .phortune-invoice-view .phui-property-list-value {
width: 80%;
}
.phortune-invoice-logo {
margin-bottom: 24px;
}
.phortune-invoice-logo img {
margin: 0 auto;
}
.phortune-invoice-contact {
margin-bottom: 32px;
}
.phortune-invoice-contact td {
padding: 4px 16px;
}
.phortune-invoice-to {
border-right: 1px solid {$lightblueborder};
}
.phortune-mini-header {
color: {$lightbluetext};
font-weight: bold;
text-transform: uppercase;
margin-bottom: 4px;
letter-spacing: 0.3em;
}
.phortune-invoice-status {
margin-bottom: 24px;
}
.phortune-invoice-status .phui-info-view {
margin: 0;
}
.phortune-invoice-view .phui-box.phui-object-box {
margin-bottom: 24px;
}
.phortune-invoice-footer {
color: {$lightgreytext};
margin: 48px 0 64px;
text-align: center;
}
.phortune-invoice-footer strong {
color: #000;
}