3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * This class is intended to become the object to manage orders, including via Order.api.
21 * As of writing it is in the process of having appropriate functions built up.
23 class CRM_Financial_BAO_Order
{
30 protected $priceSetID;
33 * Selected price items in the format we see in forms.
36 * [price_3 => 4, price_10 => 7]
37 * is equivalent to 'option_value 4 for radio price field 3 and
38 * a quantity of 7 for text price field 10.
42 protected $priceSelection = [];
45 * Override for financial type id.
47 * Used when the financial type id is to be overridden for all line items
48 * (as can happen in backoffice forms)
52 protected $overrideFinancialTypeID;
55 * Override for the total amount of the order.
57 * When there is a single line item the order total may be overriden.
61 protected $overrideTotalAmount;
64 * Line items in the order.
68 protected $lineItems = [];
71 * Metadata for price fields.
75 protected $priceFieldMetadata = [];
78 * Get Set override for total amount of the order.
82 public function getOverrideTotalAmount() {
83 if (count($this->getPriceOptions()) !== 1) {
86 return $this->overrideTotalAmount ??
FALSE;
90 * Set override for total amount.
92 * @param float $overrideTotalAmount
94 public function setOverrideTotalAmount(float $overrideTotalAmount) {
95 $this->overrideTotalAmount
= $overrideTotalAmount;
99 * Get override for total amount.
103 public function getOverrideFinancialTypeID() {
104 if (count($this->getPriceOptions()) !== 1) {
107 return $this->overrideFinancialTypeID ??
FALSE;
111 * Set override for financial type ID.
113 * @param int $overrideFinancialTypeID
115 public function setOverrideFinancialTypeID(int $overrideFinancialTypeID) {
116 $this->overrideFinancialTypeID
= $overrideFinancialTypeID;
120 * Getter for price set id.
124 public function getPriceSetID(): int {
125 return $this->priceSetID
;
129 * Setter for price set id.
131 * @param int $priceSetID
133 public function setPriceSetID(int $priceSetID) {
134 $this->priceSetID
= $priceSetID;
138 * Getter for price selection.
142 public function getPriceSelection(): array {
143 return $this->priceSelection
;
147 * Setter for price selection.
149 * @param array $priceSelection
151 public function setPriceSelection(array $priceSelection) {
152 $this->priceSelection
= $priceSelection;
156 * Price options the simplified price fields selections.
158 * ie. the 'price_' is stripped off the key name and the field ID
159 * is cast to an integer.
163 public function getPriceOptions():array {
165 foreach ($this->getPriceSelection() as $fieldName => $value) {
166 $fieldID = substr($fieldName, 6);
167 $priceOptions[(int) $fieldID] = $value;
169 return $priceOptions;
173 * Get the metadata for the given field.
178 * @throws \CiviCRM_API3_Exception
180 public function getPriceFieldSpec(int $id) :array {
181 if (!isset($this->priceFieldMetadata
[$id])) {
182 $this->priceFieldMetadata
= CRM_Price_BAO_PriceSet
::getCachedPriceSetDetail($this->getPriceSetID())['fields'];
184 return $this->priceFieldMetadata
[$id];
188 * Set the price field selection from an array of params containing price fields.
190 * This function takes the sort of 'anything & everything' parameters that come in from the
191 * form layer and filters them before assigning them to the priceSelection property.
193 * @param array $input
195 public function setPriceSelectionFromUnfilteredInput(array $input) {
196 foreach ($input as $fieldName => $value) {
197 if (strpos($fieldName, 'price_') === 0) {
198 $fieldID = substr($fieldName, 6);
199 if (is_numeric($fieldID)) {
200 $this->priceSelection
[$fieldName] = $value;
211 * @throws \CiviCRM_API3_Exception
213 public function getLineItems():array {
214 if (empty($this->lineItems
)) {
215 $this->lineItems
= $this->calculateLineItems();
217 return $this->lineItems
;
222 * @throws \CiviCRM_API3_Exception
224 protected function calculateLineItems(): array {
226 $params = $this->getPriceSelection();
227 if ($this->getOverrideTotalAmount() !== FALSE) {
228 // We need to do this to keep getLine from doing weird stuff but the goal
229 // is to ditch getLine next round of refactoring
230 // and make the code more sane.
231 $params['total_amount'] = $this->getOverrideTotalAmount();
234 foreach ($this->getPriceOptions() as $fieldID => $valueID) {
235 $throwAwayArray = [];
236 // @todo - still using getLine for now but better to bring it to this class & do a better job.
237 $lineItems[$valueID] = CRM_Price_BAO_PriceSet
::getLine($params, $throwAwayArray, $this->getPriceSetID(), $this->getPriceFieldSpec($fieldID), $fieldID, 0)[1][$valueID];
240 $taxRates = CRM_Core_PseudoConstant
::getTaxRates();
241 foreach ($lineItems as &$lineItem) {
242 // Set any pre-calculation to zero as we will calculate.
243 $lineItem['tax_amount'] = 0;
244 if ($this->getOverrideFinancialTypeID() !== FALSE) {
245 $lineItem['financial_type_id'] = $this->getOverrideFinancialTypeID();
247 $taxRate = $taxRates[$lineItem['financial_type_id']] ??
0;
248 if ($this->getOverrideTotalAmount() !== FALSE) {
250 // Total is tax inclusive.
251 $lineItem['tax_amount'] = ($taxRate / 100) * $this->getOverrideTotalAmount();
252 $lineItem['line_total'] = $lineItem['unit_price'] = $this->getOverrideTotalAmount() - $lineItem['tax_amount'];
255 $lineItem['line_total'] = $lineItem['unit_price'] = $this->getOverrideTotalAmount();
259 $lineItem['tax_amount'] = ($taxRate / 100) * $lineItem['line_total'];
266 * Get the total tax amount for the order.
270 * @throws \CiviCRM_API3_Exception
272 public function getTotalTaxAmount() :float {
274 foreach ($this->getLineItems() as $lineItem) {
275 $amount +
= $lineItem['tax_amount'] ??
0.0;