dev/core#1409 Remove net_amount from Addtional Payment form
authoreileen <emcnaughton@wikimedia.org>
Wed, 20 Nov 2019 04:54:22 +0000 (17:54 +1300)
committerSeamus Lee <seamuslee001@gmail.com>
Wed, 20 Nov 2019 20:23:12 +0000 (07:23 +1100)
This is causing a validation problem. We used to have an issue on the contribution form which we eventually
resolved by removing net_amount as it's best calculated anyway

In order to make this changed I had to ensure Payment.create adds the net_amount & had
to do a couple of tweaks for the test to pass

Backport additional test functions and update API to get tests to pass

CRM/Contribute/Form/AdditionalPayment.php
CRM/Core/BAO/FinancialTrxn.php
api/v3/Payment.php
templates/CRM/Contribute/Form/AdditionalPayment.tpl
tests/phpunit/CiviTest/CiviUnitTestCase.php
tests/phpunit/api/v3/PaymentTest.php
tests/phpunit/api/v3/SyntaxConformanceTest.php

index 68912379641bf641404bd51626f3316a07ddae5e..edd43400b91eab2e0b11692976580a06767eef1e 100644 (file)
@@ -242,7 +242,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract
     $js = NULL;
     // render backoffice payment fields only on offline mode
     if (!$this->_mode) {
-      $js = ['onclick' => "return verify( );"];
+      $js = ['onclick' => 'return verify( );'];
 
       $this->add('select', 'payment_instrument_id',
         ts('Payment Method'),
@@ -258,11 +258,6 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract
         $attributes['fee_amount']
       );
       $this->addRule('fee_amount', ts('Please enter a valid monetary value for Fee Amount.'), 'money');
-
-      $this->add('text', 'net_amount', ts('Net Amount'),
-        $attributes['net_amount']
-      );
-      $this->addRule('net_amount', ts('Please enter a valid monetary value for Net Amount.'), 'money');
     }
 
     $buttonName = $this->_refund ? 'Record Refund' : 'Record Payment';
@@ -299,10 +294,7 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract
     if ($self->_paymentType == 'refund' && $fields['total_amount'] != abs($self->_refund)) {
       $errors['total_amount'] = ts('Refund amount must equal refund due amount.');
     }
-    $netAmt = (float) $fields['total_amount'] - (float) CRM_Utils_Array::value('fee_amount', $fields, 0);
-    if (!empty($fields['net_amount']) && $netAmt != $fields['net_amount']) {
-      $errors['net_amount'] = ts('Net amount should be equal to the difference between payment amount and fee amount.');
-    }
+
     if ($self->_paymentProcessor['id'] === 0 && empty($fields['payment_instrument_id'])) {
       $errors['payment_instrument_id'] = ts('Payment method is a required field');
     }
index e8e886d41d0db20c3a25547bea5a8ebc23094b8a..6976048bebce8848149eca2d2472fabb175460bc 100644 (file)
@@ -55,6 +55,14 @@ class CRM_Core_BAO_FinancialTrxn extends CRM_Financial_DAO_FinancialTrxn {
     $trxn = new CRM_Financial_DAO_FinancialTrxn();
     $trxn->copyValues($params);
 
+    if (isset($params['fee_amount']) && is_numeric($params['fee_amount'])) {
+      if (!isset($params['total_amount'])) {
+        $trxn->fetch();
+        $params['total_amount'] = $trxn->total_amount;
+      }
+      $trxn->net_amount = $params['total_amount'] - $params['fee_amount'];
+    }
+
     if (empty($params['id']) && !CRM_Utils_Rule::currencyCode($trxn->currency)) {
       $trxn->currency = CRM_Core_Config::singleton()->defaultCurrency;
     }
index 956b62f86f964aa321f7aa1c5fa9fa562f6392cd..e86c930c7127675006aa5cbe31ecd8e126a1ed1c 100644 (file)
@@ -39,6 +39,8 @@
  *
  * @return array
  *   Array of financial transactions which are payments, if error an array with an error id and error message
+ *
+ * @throws \CiviCRM_API3_Exception
  */
 function civicrm_api3_payment_get($params) {
   $financialTrxn = [];
@@ -50,7 +52,10 @@ function civicrm_api3_payment_get($params) {
   if (isset($params['trxn_id'])) {
     $params['financial_trxn_id.trxn_id'] = $params['trxn_id'];
   }
-  $eft = civicrm_api3('EntityFinancialTrxn', 'get', $params);
+  $eftParams = $params;
+  unset($eftParams['return']);
+  // @todo - why do we fetch EFT params at all?
+  $eft = civicrm_api3('EntityFinancialTrxn', 'get', $eftParams);
   if (!empty($eft['values'])) {
     $eftIds = [];
     foreach ($eft['values'] as $efts) {
@@ -83,9 +88,10 @@ function civicrm_api3_payment_get($params) {
  * @param array $params
  *   Input parameters.
  *
- * @throws API_Exception
  * @return array
  *   Api result array
+ *
+ * @throws \CiviCRM_API3_Exception
  */
 function civicrm_api3_payment_delete($params) {
   return civicrm_api3('FinancialTrxn', 'delete', $params);
@@ -132,7 +138,6 @@ function civicrm_api3_payment_cancel($params) {
  * @return array
  *   Api result array
  *
- * @throws \API_Exception
  * @throws \CRM_Core_Exception
  * @throws \CiviCRM_API3_Exception
  */
@@ -164,12 +169,19 @@ function _civicrm_api3_payment_create_spec(&$params) {
       'api.required' => 1,
       'title' => ts('Contribution ID'),
       'type' => CRM_Utils_Type::T_INT,
+      // We accept order_id as an alias so that we can chain like
+      // civicrm_api3('Order', 'create', ['blah' => 'blah', 'contribution_status_id' => 'Pending', 'api.Payment.create => ['total_amount' => 5]]
+      'api.aliases' => ['order_id'],
     ],
     'total_amount' => [
       'api.required' => 1,
       'title' => ts('Total Payment Amount'),
       'type' => CRM_Utils_Type::T_FLOAT,
     ],
+    'fee_amount' => [
+      'title' => ts('Fee Amount'),
+      'type' => CRM_Utils_Type::T_FLOAT,
+    ],
     'payment_processor_id' => [
       'name' => 'payment_processor_id',
       'type' => CRM_Utils_Type::T_INT,
@@ -324,6 +336,15 @@ function _civicrm_api3_payment_get_spec(&$params) {
       'title' => 'Transaction ID',
       'type' => CRM_Utils_Type::T_STRING,
     ],
+    'trxn_date' => [
+      'title' => ts('Payment Date'),
+      'type' => CRM_Utils_Type::T_TIMESTAMP,
+    ],
+    'financial_trxn_id' => [
+      'title' => ts('Payment ID'),
+      'type' => CRM_Utils_Type::T_INT,
+      'api.aliases' => ['payment_id', 'id'],
+    ],
   ];
 }
 
index 23443f10fd5f261c039193f2143266610b6e29c9..ac7feb83fe8f3e8b05683a2b098ce8dd7aadbcae 100644 (file)
           </tr>
           <tr class="crm-payment-form-block-fee_amount"><td class="label">{$form.fee_amount.label}</td><td{$valueStyle}>{$form.fee_amount.html|crmMoney:$currency:'XXX':'YYY'}<br />
             <span class="description">{ts}Processing fee for this transaction (if applicable).{/ts}</span></td></tr>
-           <tr class="crm-payment-form-block-net_amount"><td class="label">{$form.net_amount.label}</td><td>{$form.net_amount.html|crmMoney:$currency:'':1}<br />
-            <span class="description">{ts}Net value of the payment (Total Amount minus Fee).{/ts}</span></td></tr>
         </table>
       </div>
       {/if}
index 8cc6355d740174084fb3ccc21fbff8027d7ad92c..de021857e24c23a91626afad6ab55c2a31ca61a5 100644 (file)
@@ -3320,4 +3320,60 @@ AND    ( TABLE_NAME LIKE 'civicrm_value_%' )
     return $csv;
   }
 
+  /**
+   * Get parameters to set up a multi-line participant order.
+   *
+   * @return array
+   * @throws \CRM_Core_Exception
+   */
+  protected function getParticipantOrderParams() {
+    $this->_contactId = $this->individualCreate();
+    $event = $this->eventCreate();
+    $this->_eventId = $event['id'];
+    $eventParams = [
+      'id' => $this->_eventId,
+      'financial_type_id' => 4,
+      'is_monetary' => 1,
+    ];
+    $this->callAPISuccess('event', 'create', $eventParams);
+    $priceFields = $this->createPriceSet('event', $this->_eventId);
+    $participantParams = [
+      'financial_type_id' => 4,
+      'event_id' => $this->_eventId,
+      'role_id' => 1,
+      'status_id' => 14,
+      'fee_currency' => 'USD',
+      'contact_id' => $this->_contactId,
+    ];
+    $participant = $this->callAPISuccess('Participant', 'create', $participantParams);
+    $orderParams = [
+      'total_amount' => 300,
+      'currency' => 'USD',
+      'contact_id' => $this->_contactId,
+      'financial_type_id' => 4,
+      'contribution_status_id' => 'Pending',
+      'contribution_mode' => 'participant',
+      'participant_id' => $participant['id'],
+    ];
+    foreach ($priceFields['values'] as $key => $priceField) {
+      $orderParams['line_items'][] = [
+        'line_item' => [
+          [
+            'price_field_id' => $priceField['price_field_id'],
+            'price_field_value_id' => $priceField['id'],
+            'label' => $priceField['label'],
+            'field_title' => $priceField['label'],
+            'qty' => 1,
+            'unit_price' => $priceField['amount'],
+            'line_total' => $priceField['amount'],
+            'financial_type_id' => $priceField['financial_type_id'],
+            'entity_table' => 'civicrm_participant',
+          ],
+        ],
+        'params' => $participant,
+      ];
+    }
+    return $orderParams;
+  }
+
 }
index 6c5c27bd24b9f56c3d342b86726465821fa04419..48f8b61203ea0d3225b309f41ec31d139d6364d5 100644 (file)
@@ -732,6 +732,17 @@ class api_v3_PaymentTest extends CiviUnitTestCase {
     ]);
   }
 
+  /**
+   * Test net amount is set when fee amount is passed in.
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function testNetAmount() {
+    $order = $this->createPendingParticipantOrder();
+    $payment = $this->callAPISuccess('Payment', 'create', ['order_id' => $order['id'], 'total_amount' => 10, 'fee_amount' => .25]);
+    $this->assertEquals('9.75', $this->callAPISuccessGetValue('Payment', ['id' => $payment['id'], 'return' => 'net_amount']));
+  }
+
   /**
    * Test create payment api for pay later contribution with partial payment.
    *
@@ -866,4 +877,15 @@ class api_v3_PaymentTest extends CiviUnitTestCase {
     ]);
   }
 
+  /**
+   * Add participant with contribution
+   *
+   * @return array
+   *
+   * @throws \CRM_Core_Exception
+   */
+  protected function createPendingParticipantOrder() {
+    return $this->callAPISuccess('Order', 'create', $this->getParticipantOrderParams());
+  }
+
 }
index c074d1a31449efc82d3160847a1a32ba2cf961ea..74bb2956915edce6284c52ca686f13f34bbc27df 100644 (file)
@@ -663,6 +663,12 @@ class api_v3_SyntaxConformanceTest extends CiviUnitTestCase {
           'is_primary',
         ],
       ],
+      'FinancialTrxn' => [
+        'cant_update' => [
+          // Altering fee amount will also cause net_amount to be recalculated.
+          'fee_amount',
+        ],
+      ],
       'Navigation' => [
         'cant_update' => [
           // Weight is deliberately altered when this is changed - skip.