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
18 * This class is intended to become the object to manage orders, including via Order.api.
20 * As of writing it is in the process of having appropriate functions built up.
22 class CRM_Financial_BAO_Order
{
29 protected $priceSetID;
32 * Selected price items in the format we see in forms.
35 * [price_3 => 4, price_10 => 7]
36 * is equivalent to 'option_value 4 for radio price field 3 and
37 * a quantity of 7 for text price field 10.
41 protected $priceSelection = [];
44 * Override for financial type id.
46 * Used when the financial type id is to be overridden for all line items
47 * (as can happen in backoffice forms)
51 protected $overrideFinancialTypeID;
54 * Override for the total amount of the order.
56 * When there is a single line item the order total may be overriden.
60 protected $overrideTotalAmount;
63 * Line items in the order.
67 protected $lineItems = [];
70 * Metadata for price fields.
74 protected $priceFieldMetadata = [];
77 * Get Set override for total amount of the order.
81 public function getOverrideTotalAmount() {
82 if (count($this->getPriceOptions()) !== 1) {
85 return $this->overrideTotalAmount ??
FALSE;
89 * Set override for total amount.
91 * @param float $overrideTotalAmount
93 public function setOverrideTotalAmount(float $overrideTotalAmount) {
94 $this->overrideTotalAmount
= $overrideTotalAmount;
98 * Get override for total amount.
102 public function getOverrideFinancialTypeID() {
103 if (count($this->getPriceOptions()) !== 1) {
106 return $this->overrideFinancialTypeID ??
FALSE;
110 * Set override for financial type ID.
112 * @param int $overrideFinancialTypeID
114 public function setOverrideFinancialTypeID(int $overrideFinancialTypeID) {
115 $this->overrideFinancialTypeID
= $overrideFinancialTypeID;
119 * Getter for price set id.
123 public function getPriceSetID(): int {
124 return $this->priceSetID
;
128 * Setter for price set id.
130 * @param int $priceSetID
132 public function setPriceSetID(int $priceSetID) {
133 $this->priceSetID
= $priceSetID;
137 * Getter for price selection.
141 public function getPriceSelection(): array {
142 return $this->priceSelection
;
146 * Setter for price selection.
148 * @param array $priceSelection
150 public function setPriceSelection(array $priceSelection) {
151 $this->priceSelection
= $priceSelection;
155 * Price options the simplified price fields selections.
157 * ie. the 'price_' is stripped off the key name and the field ID
158 * is cast to an integer.
162 public function getPriceOptions():array {
164 foreach ($this->getPriceSelection() as $fieldName => $value) {
165 $fieldID = substr($fieldName, 6);
166 $priceOptions[(int) $fieldID] = $value;
168 return $priceOptions;
172 * Get the metadata for the given field.
177 * @throws \CiviCRM_API3_Exception
179 public function getPriceFieldSpec(int $id) :array {
180 if (!isset($this->priceFieldMetadata
[$id])) {
181 $this->priceFieldMetadata
= CRM_Price_BAO_PriceSet
::getCachedPriceSetDetail($this->getPriceSetID())['fields'];
183 return $this->priceFieldMetadata
[$id];
187 * Set the price field selection from an array of params containing price fields.
189 * This function takes the sort of 'anything & everything' parameters that come in from the
190 * form layer and filters them before assigning them to the priceSelection property.
192 * @param array $input
194 public function setPriceSelectionFromUnfilteredInput(array $input) {
195 foreach ($input as $fieldName => $value) {
196 if (strpos($fieldName, 'price_') === 0) {
197 $fieldID = substr($fieldName, 6);
198 if (is_numeric($fieldID)) {
199 $this->priceSelection
[$fieldName] = $value;
210 * @throws \CiviCRM_API3_Exception
212 public function getLineItems():array {
213 if (empty($this->lineItems
)) {
214 $this->lineItems
= $this->calculateLineItems();
216 return $this->lineItems
;
221 * @throws \CiviCRM_API3_Exception
223 protected function calculateLineItems(): array {
225 $params = $this->getPriceSelection();
226 if ($this->getOverrideTotalAmount() !== FALSE) {
227 // We need to do this to keep getLine from doing weird stuff but the goal
228 // is to ditch getLine next round of refactoring
229 // and make the code more sane.
230 $params['total_amount'] = $this->getOverrideTotalAmount();
233 foreach ($this->getPriceOptions() as $fieldID => $valueID) {
234 $throwAwayArray = [];
235 // @todo - still using getLine for now but better to bring it to this class & do a better job.
236 $lineItems[$valueID] = CRM_Price_BAO_PriceSet
::getLine($params, $throwAwayArray, $this->getPriceSetID(), $this->getPriceFieldSpec($fieldID), $fieldID, 0)[1][$valueID];
239 $taxRates = CRM_Core_PseudoConstant
::getTaxRates();
240 foreach ($lineItems as &$lineItem) {
241 // Set any pre-calculation to zero as we will calculate.
242 $lineItem['tax_amount'] = 0;
243 if ($this->getOverrideFinancialTypeID() !== FALSE) {
244 $lineItem['financial_type_id'] = $this->getOverrideFinancialTypeID();
246 $taxRate = $taxRates[$lineItem['financial_type_id']] ??
0;
247 if ($this->getOverrideTotalAmount() !== FALSE) {
249 // Total is tax inclusive.
250 $lineItem['tax_amount'] = ($taxRate / 100) * $this->getOverrideTotalAmount();
251 $lineItem['line_total'] = $lineItem['unit_price'] = $this->getOverrideTotalAmount() - $lineItem['tax_amount'];
254 $lineItem['line_total'] = $lineItem['unit_price'] = $this->getOverrideTotalAmount();
258 $lineItem['tax_amount'] = ($taxRate / 100) * $lineItem['line_total'];
265 * Get the total tax amount for the order.
269 * @throws \CiviCRM_API3_Exception
271 public function getTotalTaxAmount() :float {
273 foreach ($this->getLineItems() as $lineItem) {
274 $amount +
= $lineItem['tax_amount'] ??
0.0;