Move determination of priceSetID to the internal order class
authorEileen McNaughton <emcnaughton@wikimedia.org>
Sat, 19 Jun 2021 02:36:55 +0000 (14:36 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Sat, 19 Jun 2021 07:50:51 +0000 (19:50 +1200)
This moves some functionality from the form to where we can start to use if from the order api.

I also added more 'internal' commenting. This is implicitly the case but
I think it would be tempting to use this class and the comments at the top
of the class saying not to could be missed

CRM/Financial/BAO/Order.php
CRM/Member/Form.php
tests/phpunit/CRM/Contribute/Form/ContributionTest.php

index 960aa1a0ad003cdf06e81433818afcef6541cddc..9f5df990e5aa9422573a7cd9fd1b7e9787507998 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 use Civi\Api4\PriceField;
+use Civi\Api4\PriceSet;
 
 /**
  *
@@ -23,6 +24,8 @@ use Civi\Api4\PriceField;
  * As of writing it is in the process of having appropriate functions built up.
  * It should **NOT** be accessed directly outside of tested core methods as it
  * may change.
+ *
+ * @internal
  */
 class CRM_Financial_BAO_Order {
 
@@ -88,6 +91,8 @@ class CRM_Financial_BAO_Order {
   /**
    * Get form object.
    *
+   * @internal use in tested core code only.
+   *
    * @return \CRM_Core_Form|NULL
    */
   public function getForm(): ?CRM_Core_Form {
@@ -97,6 +102,8 @@ class CRM_Financial_BAO_Order {
   /**
    * Set form object.
    *
+   * @internal use in tested core code only.
+   *
    * @param \CRM_Core_Form|NULL $form
    */
   public function setForm(?CRM_Core_Form $form): void {
@@ -142,6 +149,8 @@ class CRM_Financial_BAO_Order {
   /**
    * Get Set override for total amount of the order.
    *
+   * @internal use in tested core code only.
+   *
    * @return float|false
    */
   public function getOverrideTotalAmount() {
@@ -154,15 +163,21 @@ class CRM_Financial_BAO_Order {
   /**
    * Set override for total amount.
    *
+   * @internal use in tested core code only.
+   *
    * @param float $overrideTotalAmount
    */
   public function setOverrideTotalAmount(float $overrideTotalAmount): void {
-    $this->overrideTotalAmount = $overrideTotalAmount;
+    if ($this->supportsOverrideAmount()) {
+      $this->overrideTotalAmount = $overrideTotalAmount;
+    }
   }
 
   /**
    * Get override for total amount.
    *
+   * @internal use in tested core code only.
+   *
    * @return int| FALSE
    */
   public function getOverrideFinancialTypeID() {
@@ -175,6 +190,8 @@ class CRM_Financial_BAO_Order {
   /**
    * Set override for financial type ID.
    *
+   * @internal use in tested core code only.
+   *
    * @param int $overrideFinancialTypeID
    */
   public function setOverrideFinancialTypeID(int $overrideFinancialTypeID) {
@@ -184,24 +201,63 @@ class CRM_Financial_BAO_Order {
   /**
    * Getter for price set id.
    *
+   * @internal use in tested core code only.
+   *
    * @return int
+   *
+   * @throws \API_Exception
    */
   public function getPriceSetID(): int {
+    if (!$this->priceSetID) {
+      foreach ($this->getPriceOptions() as $fieldID => $valueID) {
+        $this->setPriceSetIDFromSelectedField($fieldID);
+      }
+    }
     return $this->priceSetID;
   }
 
   /**
    * Setter for price set id.
    *
+   * @internal use in tested core code only.
+   *
    * @param int $priceSetID
    */
   public function setPriceSetID(int $priceSetID) {
     $this->priceSetID = $priceSetID;
   }
 
+  /**
+   * Set price set id to the default.
+   *
+   * @param string $component [membership|contribution]
+   *
+   * @throws \API_Exception
+   * @internal use in tested core code only.
+   */
+  public function setPriceSetToDefault(string $component): void {
+    $this->priceSetID = PriceSet::get(FALSE)
+      ->addWhere('name', '=', ($component === 'membership' ? 'default_membership_type_amount' : 'default_contribution_amount'))
+      ->execute()
+      ->first()['id'];
+  }
+
+  /**
+   * Is overriding the total amount valid for this price set.
+   *
+   * @internal tested. core code use only.
+   *
+   * @return bool
+   */
+  public function supportsOverrideAmount(): bool {
+    return (bool) $this->getPriceSetMetadata()['is_quick_config'];
+  }
+
   /**
    * Set price set ID based on the contribution page id.
    *
+   * @internal use in tested core code only.
+   *
    * @param int $contributionPageID
    *
    * @throws \CiviCRM_API3_Exception
@@ -213,6 +269,8 @@ class CRM_Financial_BAO_Order {
   /**
    * Set price set ID based on the event id.
    *
+   * @internal use in tested core code only.
+   *
    * @param int $eventID
    *
    * @throws \CiviCRM_API3_Exception
@@ -223,6 +281,9 @@ class CRM_Financial_BAO_Order {
 
   /**
    * Set the price set id based on looking up the entity.
+   *
+   * @internal use in tested core code only.
+   *
    * @param string $entity
    * @param int $id
    *
@@ -234,6 +295,8 @@ class CRM_Financial_BAO_Order {
   /**
    * Getter for price selection.
    *
+   * @internal use in tested core code only.
+   *
    * @return array
    */
   public function getPriceSelection(): array {
@@ -243,6 +306,8 @@ class CRM_Financial_BAO_Order {
   /**
    * Setter for price selection.
    *
+   * @internal use in tested core code only.
+   *
    * @param array $priceSelection
    */
   public function setPriceSelection(array $priceSelection) {
@@ -255,6 +320,8 @@ class CRM_Financial_BAO_Order {
    * ie. the 'price_' is stripped off the key name and the field ID
    * is cast to an integer.
    *
+   * @internal use in tested core code only.
+   *
    * @return array
    */
   public function getPriceOptions():array {
@@ -269,6 +336,8 @@ class CRM_Financial_BAO_Order {
   /**
    * Get the metadata for the given field.
    *
+   * @internal use in tested core code only.
+   *
    * @param int $id
    *
    * @return array
@@ -280,27 +349,40 @@ class CRM_Financial_BAO_Order {
   /**
    * Get the metadata for the fields in the price set.
    *
+   * @internal use in tested core code only.
+   *
    * @return array
    */
   public function getPriceFieldsMetadata(): array {
     if (empty($this->priceFieldMetadata)) {
       $this->getPriceSetMetadata();
-      if ($this->getForm()) {
-        CRM_Utils_Hook::buildAmount($this->form->getFormContext(), $this->form, $this->priceFieldMetadata);
-      }
     }
     return $this->priceFieldMetadata;
   }
 
+  /**
+   * Set the metadata for the order.
+   *
+   * @param array $metadata
+   */
+  protected function setPriceFieldMetadata($metadata) {
+    $this->priceFieldMetadata = $metadata;
+    if ($this->getForm()) {
+      CRM_Utils_Hook::buildAmount($this->form->getFormContext(), $this->form, $this->priceFieldMetadata);
+    }
+  }
+
   /**
    * Get the metadata for the fields in the price set.
    *
+   * @internal use in tested core code only.
+   *
    * @return array
    */
   public function getPriceSetMetadata(): array {
     if (empty($this->priceSetMetadata)) {
       $priceSetMetadata = CRM_Price_BAO_PriceSet::getCachedPriceSetDetail($this->getPriceSetID());
-      $this->priceFieldMetadata = $priceSetMetadata['fields'];
+      $this->setPriceFieldMetadata($priceSetMetadata['fields']);
       unset($priceSetMetadata['fields']);
       $this->priceSetMetadata = $priceSetMetadata;
     }
@@ -310,6 +392,8 @@ class CRM_Financial_BAO_Order {
   /**
    * Get the financial type id for the order.
    *
+   * @internal use in tested core code only.
+   *
    * This may differ to the line items....
    *
    * @return int
@@ -319,12 +403,16 @@ class CRM_Financial_BAO_Order {
   }
 
   /**
-   * Set the price field selection from an array of params containing price fields.
+   * Set the price field selection from an array of params containing price
+   * fields.
    *
-   * This function takes the sort of 'anything & everything' parameters that come in from the
-   * form layer and filters them before assigning them to the priceSelection property.
+   * This function takes the sort of 'anything & everything' parameters that
+   * come in from the form layer and filters them before assigning them to the
+   * priceSelection property.
    *
    * @param array $input
+   *
+   * @throws \API_Exception
    */
   public function setPriceSelectionFromUnfilteredInput(array $input): void {
     foreach ($input as $fieldName => $value) {
@@ -335,6 +423,23 @@ class CRM_Financial_BAO_Order {
         }
       }
     }
+    if (empty($this->priceSelection) && isset($input['total_amount'])
+      && is_numeric($input['total_amount']) && !empty($input['financial_type_id'])) {
+      $this->priceSelection['price_' . $this->getDefaultPriceField()] = $input['total_amount'];
+      $this->setOverrideFinancialTypeID($input['financial_type_id']);
+    }
+  }
+
+  /**
+   * Get the id of the price field to use when just an amount is provided.
+   *
+   * @throws \API_Exception
+   */
+  public function getDefaultPriceField() {
+    return PriceField::get(FALSE)
+      ->addWhere('name', '=', 'contribution_amount')
+      ->addWhere('price_set.name', '=', 'default_contribution_amount')
+      ->execute()->first()['id'];
   }
 
   /**
@@ -418,10 +523,10 @@ class CRM_Financial_BAO_Order {
       $params['total_amount'] = $this->getOverrideTotalAmount();
     }
 
+    // Dummy value to prevent e-notice in getLine. We calculate tax in this class.
+    $params['financial_type_id'] = 0;
     foreach ($this->getPriceOptions() as $fieldID => $valueID) {
-      if (!isset($this->priceSetID)) {
-        $this->setPriceSetID(PriceField::get()->addSelect('price_set_id')->addWhere('id', '=', $fieldID)->execute()->first()['price_set_id']);
-      }
+      $this->setPriceSetIDFromSelectedField($fieldID);
       $throwAwayArray = [];
       // @todo - still using getLine for now but better to bring it to this class & do a better job.
       $newLines = CRM_Price_BAO_PriceSet::getLine($params, $throwAwayArray, $this->getPriceSetID(), $this->getPriceFieldSpec($fieldID), $fieldID)[1];
@@ -514,4 +619,19 @@ class CRM_Financial_BAO_Order {
     return $taxRates[$financialTypeID];
   }
 
+  /**
+   * @param $fieldID
+   *
+   * @throws \API_Exception
+   */
+  protected function setPriceSetIDFromSelectedField($fieldID): void {
+    if (!isset($this->priceSetID)) {
+      $this->setPriceSetID(PriceField::get(FALSE)
+        ->addSelect('price_set_id')
+        ->addWhere('id', '=', $fieldID)
+        ->execute()
+        ->first()['price_set_id']);
+    }
+  }
+
 }
index b5a5d5403fda592abd33007f16d6e15effb8eac1..ea71c00f11c6efc4720a59d33541be6d0b251b40 100644 (file)
@@ -490,6 +490,7 @@ class CRM_Member_Form extends CRM_Contribute_Form_AbstractEditPayment {
    * @param array $formValues
    *
    * @return array
+   * @throws \API_Exception
    */
   protected function setPriceSetParameters(array $formValues): array {
     // process price set and get total amount and line items.
@@ -498,12 +499,9 @@ class CRM_Member_Form extends CRM_Contribute_Form_AbstractEditPayment {
     $priceSetDetails = $this->getPriceSetDetails($formValues);
     $this->_priceSet = $priceSetDetails[$this->_priceSetId];
     $this->order = new CRM_Financial_BAO_Order();
-    $this->order->setPriceSelectionFromUnfilteredInput($formValues);
-    $this->order->setPriceSetID($this->getPriceSetID($formValues));
     $this->order->setForm($this);
-    if ($priceSetDetails[$this->order->getPriceSetID()]['is_quick_config'] && isset($formValues['total_amount'])) {
-      // Amount overrides only permitted on quick config.
-      // Possibly Order object should enforce this...
+    $this->order->setPriceSelectionFromUnfilteredInput($formValues);
+    if (isset($formValues['total_amount'])) {
       $this->order->setOverrideTotalAmount((float) $formValues['total_amount']);
     }
     $this->order->setOverrideFinancialTypeID((int) $formValues['financial_type_id']);
index bb5aae45a26ad777b7a9baec8f436723f422a27c..5983e3cdb62968391c773469aef63bb6d5c3a204 100644 (file)
@@ -2120,7 +2120,7 @@ Price Field - Price Field 1        1   $ 100.00      $ 100.00
     ];
 
     $form = new CRM_Contribute_Form_Contribution();
-    $this->assertSame([], $form->formRule($fields, [], $form));
+    $this->assertSame([], $form::formRule($fields, [], $form));
   }
 
   /**