From 4356c7dce1f373927e159f33e831ac67a1a24feb Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 6 Feb 2020 11:46:34 +1300 Subject: [PATCH] dev/financial#87 make it possible to add a payment to a fully paid contribution Per previous discussions it's possible to pay more than the contribution total (or refund less than the amount due ) - this change removes the restriction --- CRM/Contribute/BAO/Contribution.php | 24 ++-- CRM/Contribute/Form/AdditionalPayment.php | 113 ++++++++++++------ .../CRM/Contribute/Form/AdditionalPayment.tpl | 2 +- 3 files changed, 91 insertions(+), 48 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 845f9c8631..b4fb3e347d 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -5363,33 +5363,41 @@ LIMIT 1;"; return []; } $actionLinks = []; + $actionLinks[] = [ + 'url' => CRM_Utils_System::url('civicrm/payment', [ + 'action' => 'add', + 'reset' => 1, + 'id' => $id, + 'is_refund' => 0, + ]), + 'title' => ts('Record Payment'), + ]; + if ((int) $balance > 0) { + // @todo - this should be possible even if not > 0 - test & remove this if. + // it is possible to 'overpay' in the real world & we honor that. if (CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) { $actionLinks[] = [ 'url' => CRM_Utils_System::url('civicrm/payment', [ 'action' => 'add', 'reset' => 1, + 'is_refund' => 0, 'id' => $id, 'mode' => 'live', ]), 'title' => ts('Submit Credit Card payment'), ]; } - $actionLinks[] = [ - 'url' => CRM_Utils_System::url('civicrm/payment', [ - 'action' => 'add', - 'reset' => 1, - 'id' => $id, - ]), - 'title' => ts('Record Payment'), - ]; } elseif ((int) $balance < 0) { + // @todo - in the future remove this IF - OK to refund money even when not due since + // ... life. $actionLinks[] = [ 'url' => CRM_Utils_System::url('civicrm/payment', [ 'action' => 'add', 'reset' => 1, 'id' => $id, + 'is_refund' => 1, ]), 'title' => ts('Record Refund'), ]; diff --git a/CRM/Contribute/Form/AdditionalPayment.php b/CRM/Contribute/Form/AdditionalPayment.php index cf387bf944..d60f59ccbd 100644 --- a/CRM/Contribute/Form/AdditionalPayment.php +++ b/CRM/Contribute/Form/AdditionalPayment.php @@ -29,6 +29,8 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract protected $entity = 'Contribution'; + protected $amountDue; + protected $_owed = NULL; protected $_refund = NULL; @@ -59,6 +61,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract * Pre process form. * * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ public function preProcess() { @@ -84,24 +87,18 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract } $paymentDetails = CRM_Contribute_BAO_Contribution::getPaymentInfo($this->_id, $this->_component, FALSE, TRUE); - $paymentAmt = CRM_Contribute_BAO_Contribution::getContributionBalance($this->_contributionId); + $paymentAmt = $this->getAmountDue(); $this->_amtPaid = $paymentDetails['paid']; $this->_amtTotal = $paymentDetails['total']; - if ($paymentAmt < 0) { - $this->_refund = $paymentAmt; - $this->_paymentType = 'refund'; - } - elseif ($paymentAmt > 0) { + if ($paymentAmt >= 0) { $this->_owed = $paymentAmt; - $this->_paymentType = 'owed'; - } - else { - throw new CRM_Core_Exception(ts('No payment information found for this record')); } - if (!empty($this->_mode) && $this->_paymentType == 'refund') { + $this->_paymentType = $this->getPaymentType(); + + if (!empty($this->_mode) && $this->isARefund()) { throw new CRM_Core_Exception(ts('Credit card payment is not for Refund payments use')); } @@ -110,9 +107,11 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract $this->assign('contributionMode', $this->_mode); $this->assign('contactId', $this->_contactID); $this->assign('paymentType', $this->_paymentType); - $this->assign('paymentAmt', abs($paymentAmt)); + $this->assign('paymentAmt', $paymentAmt); + // It's easier to strip the minus sign for display purposes in php than smarty. + $this->assign('absolutePaymentAmount', abs($paymentAmt)); - $this->setPageTitle($this->_refund ? ts('Refund') : ts('Payment')); + $this->setPageTitle($this->isARefund() ? ts('Refund') : ts('Payment')); } /** @@ -138,6 +137,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract /** * @return array + * @throws \CRM_Core_Exception */ public function setDefaultValues() { if ($this->_view == 'transaction' && ($this->_action & CRM_Core_Action::BROWSE)) { @@ -153,10 +153,10 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract $defaults['trxn_date'] = date('Y-m-d H:i:s'); } - if ($this->_refund) { - $defaults['total_amount'] = CRM_Utils_Money::format(abs($this->_refund), NULL, NULL, TRUE); + if ($this->isARefund() && $this->amountDue < 0) { + $defaults['total_amount'] = CRM_Utils_Money::format(abs($this->amountDue), NULL, NULL, TRUE); } - elseif ($this->_owed) { + elseif ($this->_owed && $this->amountDue > 0) { $defaults['total_amount'] = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($this->_owed); } @@ -169,9 +169,10 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract * Build the form object. * * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ public function buildQuickForm() { - if ($this->_view == 'transaction' && ($this->_action & CRM_Core_Action::BROWSE)) { + if ($this->_view === 'transaction' && ($this->_action & CRM_Core_Action::BROWSE)) { $this->addButtons([ [ 'type' => 'cancel', @@ -188,9 +189,8 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract $attributes = CRM_Core_DAO::getAttribute('CRM_Financial_DAO_FinancialTrxn'); - $label = ($this->_refund) ? ts('Refund Amount') : ts('Payment Amount'); $this->addMoney('total_amount', - $label, + $this->isARefund() ? ts('Refund Amount') : ts('Payment Amount'), TRUE, $attributes['total_amount'], TRUE, 'currency', NULL @@ -207,8 +207,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract $this->add('textarea', 'receipt_text', ts('Confirmation Message')); - $dateLabel = ($this->_refund) ? ts('Refund Date') : ts('Date Received'); - $this->addField('trxn_date', ['entity' => 'FinancialTrxn', 'label' => $dateLabel, 'context' => 'Contribution'], FALSE, FALSE); + $this->addField('trxn_date', ['entity' => 'FinancialTrxn', 'label' => $this->isARefund() ? ts('Refund Date') : ts('Date Received'), 'context' => 'Contribution'], FALSE, FALSE); if ($this->_contactId && $this->_id) { if ($this->_component == 'event') { @@ -243,7 +242,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract $this->addRule('fee_amount', ts('Please enter a valid monetary value for Fee Amount.'), 'money'); } - $buttonName = $this->_refund ? ts('Record Refund') : ts('Record Payment'); + $buttonName = $this->isARefund() ? ts('Record Refund') : ts('Record Payment'); $this->addButtons([ [ 'type' => 'upload', @@ -271,13 +270,6 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract */ public static function formRule($fields, $files, $self) { $errors = []; - if ($self->_paymentType == 'owed' && (int) $fields['total_amount'] > (int) $self->_owed) { - $errors['total_amount'] = ts('Payment amount cannot be greater than owed amount'); - } - if ($self->_paymentType == 'refund' && $fields['total_amount'] != abs($self->_refund)) { - $errors['total_amount'] = ts('Refund amount must equal refund due amount.'); - } - if ($self->_paymentProcessor['id'] === 0 && empty($fields['payment_instrument_id'])) { $errors['payment_instrument_id'] = ts('Payment method is a required field'); } @@ -287,12 +279,14 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract /** * Process the form submission. + * + * @throws \CiviCRM_API3_Exception */ public function postProcess() { $submittedValues = $this->controller->exportValues($this->_name); $this->submit($submittedValues); $childTab = 'contribute'; - if ($this->_component == 'event') { + if ($this->_component === 'event') { $childTab = 'participant'; } $session = CRM_Core_Session::singleton(); @@ -307,6 +301,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract * @param array $submittedValues * * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception */ public function submit($submittedValues) { $this->_params = $submittedValues; @@ -314,7 +309,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract $this->_contributorContactID = $this->_contactID; $this->processBillingAddress(); $participantId = NULL; - if ($this->_component == 'event') { + if ($this->_component === 'event') { $participantId = $this->_id; } @@ -326,7 +321,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract // @todo we should clean $ on the form & pass in skipCleanMoney $trxnsData = $this->_params; - if ($this->_paymentType == 'refund') { + if ($this->isARefund()) { $trxnsData['total_amount'] = -$trxnsData['total_amount']; } $trxnsData['participant_id'] = $participantId; @@ -453,6 +448,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract * @param string $entityType * * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception */ public function testSubmit($params, $creditCardMode = NULL, $entityType = 'contribute') { $this->_bltID = 5; @@ -467,18 +463,14 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract $paymentDetails = CRM_Contribute_BAO_Contribution::getPaymentInfo($this->_contributionId, $entityType, FALSE, TRUE); - $paymentAmount = CRM_Contribute_BAO_Contribution::getContributionBalance($this->_contributionId); + $paymentAmount = $this->getAmountDue(); $this->_amtPaid = $paymentDetails['paid']; $this->_amtTotal = $paymentDetails['total']; - if ($paymentAmount < 0) { - $this->_refund = $paymentAmount; - $this->_paymentType = 'refund'; - } - elseif ($paymentAmount > 0) { + if ($paymentAmount > 0) { $this->_owed = $paymentAmount; - $this->_paymentType = 'owed'; } + $this->_paymentType = $this->getPaymentType(); } if (!empty($params['contact_id'])) { @@ -495,4 +487,47 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract $this->submit($params); } + /** + * Get the 'payment type' - ie is it a payment or a refund. + * + * We prefer the url action param but fall back on a guess from the balance. + * + * The refund|owed is not great - perhaps move to positive & negative. + * + * @return string + * @throws \CRM_Core_Exception + */ + protected function getPaymentType():string { + $urlParam = CRM_Utils_Request::retrieve('is_refund', 'Int', $this); + if ($urlParam === 0) { + return 'owed'; + } + if ($urlParam === 1) { + return 'refund'; + } + + return $this->getAmountDue() < 0 ? 'refund' : 'owed'; + } + + /** + * Is the form processing a refund. + * + * @return bool + * + * @throws \CRM_Core_Exception + */ + protected function isARefund() { + return $this->getPaymentType() === 'refund'; + } + + /** + * @return float + */ + protected function getAmountDue(): float { + if (!isset($this->amountDue)) { + $this->amountDue = CRM_Contribute_BAO_Contribution::getContributionBalance($this->_contributionId); + } + return $this->amountDue; + } + } diff --git a/templates/CRM/Contribute/Form/AdditionalPayment.tpl b/templates/CRM/Contribute/Form/AdditionalPayment.tpl index 4ca837f074..3fd811d48c 100644 --- a/templates/CRM/Contribute/Form/AdditionalPayment.tpl +++ b/templates/CRM/Contribute/Form/AdditionalPayment.tpl @@ -48,7 +48,7 @@ {$form.total_amount.label} - {$form.currency.html|crmAddClass:eight} {$form.total_amount.html|crmAddClass:eight}  {if $paymentType EQ 'refund'}{ts}Refund Due{/ts}{else}{ts}Balance Owed{/ts}{/if}: {$paymentAmt|crmMoney} + {$form.currency.html|crmAddClass:eight} {$form.total_amount.html|crmAddClass:eight}  {if $paymentType EQ 'refund' || $paymentAmt < 0}{ts}Refund Due : {$absolutePaymentAmount|crmMoney} {/ts}{else}{ts}Balance Owed{/ts} : {$paymentAmt|crmMoney}{/if} {if $email and $outBound_option != 2} -- 2.25.1