diff --git a/resources/sql/autopatches/20141007.fundtotal.sql b/resources/sql/autopatches/20141007.fundtotal.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20141007.fundtotal.sql @@ -0,0 +1,4 @@ +ALTER TABLE {$NAMESPACE}_fund.fund_initiative + ADD totalAsCurrency VARCHAR(64) NOT NULL COLLATE utf8_bin; + +UPDATE {$NAMESPACE}_fund.fund_initiative SET totalAsCurrency = '0.00 USD'; diff --git a/src/applications/fund/controller/FundInitiativeViewController.php b/src/applications/fund/controller/FundInitiativeViewController.php --- a/src/applications/fund/controller/FundInitiativeViewController.php +++ b/src/applications/fund/controller/FundInitiativeViewController.php @@ -84,12 +84,25 @@ ->setObject($initiative); $owner_phid = $initiative->getOwnerPHID(); - $this->loadHandles(array($owner_phid)); + $merchant_phid = $initiative->getMerchantPHID(); + $this->loadHandles( + array( + $owner_phid, + $merchant_phid, + )); $view->addProperty( pht('Owner'), $this->getHandle($owner_phid)->renderLink()); + $view->addProperty( + pht('Payable To Merchant'), + $this->getHandle($merchant_phid)->renderLink()); + + $view->addProperty( + pht('Total Funding'), + $initiative->getTotalAsCurrency()->formatForDisplay()); + $view->invokeWillRenderEvent(); $description = $initiative->getDescription(); diff --git a/src/applications/fund/editor/FundInitiativeEditor.php b/src/applications/fund/editor/FundInitiativeEditor.php --- a/src/applications/fund/editor/FundInitiativeEditor.php +++ b/src/applications/fund/editor/FundInitiativeEditor.php @@ -78,7 +78,18 @@ $object->setStatus($xaction->getNewValue()); return; case FundInitiativeTransaction::TYPE_BACKER: - // TODO: Calculate total funding / backers / etc. + $backer = id(new FundBackerQuery()) + ->setViewer($this->requireActor()) + ->withPHIDs(array($xaction->getNewValue())) + ->executeOne(); + if (!$backer) { + throw new Exception(pht('No such backer!')); + } + + $backer_amount = $backer->getAmountAsCurrency(); + $total = $object->getTotalAsCurrency()->add($backer_amount); + $object->setTotalAsCurrency($total); + return; case PhabricatorTransactions::TYPE_SUBSCRIBERS: case PhabricatorTransactions::TYPE_EDGE: diff --git a/src/applications/fund/storage/FundInitiative.php b/src/applications/fund/storage/FundInitiative.php --- a/src/applications/fund/storage/FundInitiative.php +++ b/src/applications/fund/storage/FundInitiative.php @@ -18,6 +18,7 @@ protected $viewPolicy; protected $editPolicy; protected $status; + protected $totalAsCurrency; private $projectPHIDs = self::ATTACHABLE; @@ -43,7 +44,8 @@ ->setOwnerPHID($actor->getPHID()) ->setViewPolicy($view_policy) ->setEditPolicy($actor->getPHID()) - ->setStatus(self::STATUS_OPEN); + ->setStatus(self::STATUS_OPEN) + ->setTotalAsCurrency(PhortuneCurrency::newEmptyCurrency()); } public function getConfiguration() { @@ -54,6 +56,10 @@ 'description' => 'text', 'status' => 'text32', 'merchantPHID' => 'phid?', + 'totalAsCurrency' => 'text64', + ), + self::CONFIG_APPLICATION_SERIALIZERS => array( + 'totalAsCurrency' => new PhortuneCurrencySerializer(), ), self::CONFIG_KEY_SCHEMA => array( 'key_status' => array( diff --git a/src/applications/phortune/currency/PhortuneCurrency.php b/src/applications/phortune/currency/PhortuneCurrency.php --- a/src/applications/phortune/currency/PhortuneCurrency.php +++ b/src/applications/phortune/currency/PhortuneCurrency.php @@ -72,26 +72,20 @@ public static function newFromList(array $list) { assert_instances_of($list, 'PhortuneCurrency'); - $total = 0; - $currency = null; + if (!$list) { + return PhortuneCurrency::newEmptyCurrency(); + } + + $total = null; foreach ($list as $item) { - if ($currency === null) { - $currency = $item->getCurrency(); - } else if ($currency === $item->getCurrency()) { - // Adding a value denominated in the same currency, which is - // fine. + if ($total === null) { + $total = $item; } else { - throw new Exception( - pht('Trying to sum a list of unlike currencies.')); + $total = $total->add($item); } - - // TODO: This should check for integer overflows, etc. - $total += $item->getValue(); } - return PhortuneCurrency::newFromValueAndCurrency( - $total, - self::getDefaultCurrency()); + return $total; } public function formatForDisplay() { @@ -132,6 +126,20 @@ throw new Exception("Invalid currency format ('{$string}')."); } + public function add(PhortuneCurrency $other) { + if ($this->currency !== $other->currency) { + throw new Exception(pht('Trying to add unlike currencies!')); + } + + $currency = new PhortuneCurrency(); + + // TODO: This should check for integer overflows, etc. + $currency->value = $this->value + $other->value; + $currency->currency = $this->currency; + + return $currency; + } + /** * Assert that a currency value lies within a range. * diff --git a/src/applications/phortune/currency/__tests__/PhortuneCurrencyTestCase.php b/src/applications/phortune/currency/__tests__/PhortuneCurrencyTestCase.php --- a/src/applications/phortune/currency/__tests__/PhortuneCurrencyTestCase.php +++ b/src/applications/phortune/currency/__tests__/PhortuneCurrencyTestCase.php @@ -19,7 +19,6 @@ } } - public function testCurrencyFormatBareValue() { // NOTE: The PayPal API depends on the behavior of the bare value format! @@ -142,4 +141,22 @@ $this->assertTrue($caught instanceof Exception); } + public function testAddCurrency() { + $cases = array( + array('0.00 USD', '0.00 USD', '$0.00 USD'), + array('1.00 USD', '1.00 USD', '$2.00 USD'), + array('1.23 USD', '9.77 USD', '$11.00 USD'), + ); + + foreach ($cases as $case) { + list($l, $r, $expect) = $case; + + $l = PhortuneCurrency::newFromString($l); + $r = PhortuneCurrency::newFromString($r); + $sum = $l->add($r); + + $this->assertEqual($expect, $sum->formatForDisplay()); + } + } + }