7aa78908 |
1 | <?php |
2 | /* |
3 | +--------------------------------------------------------------------+ |
4 | | Copyright CiviCRM LLC. All rights reserved. | |
5 | | | |
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 | +--------------------------------------------------------------------+ |
10 | */ |
11 | |
12 | /** |
13 | * |
14 | * @package CRM |
15 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
7aa78908 |
16 | * Order class. |
17 | * |
18 | * This class is intended to become the object to manage orders, including via Order.api. |
19 | * |
20 | * As of writing it is in the process of having appropriate functions built up. |
21 | */ |
22 | class CRM_Financial_BAO_Order { |
23 | |
24 | /** |
25 | * Price set id. |
26 | * |
27 | * @var int |
28 | */ |
29 | protected $priceSetID; |
30 | |
31 | /** |
32 | * Selected price items in the format we see in forms. |
33 | * |
34 | * ie. |
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. |
38 | * |
39 | * @var array |
40 | */ |
41 | protected $priceSelection = []; |
42 | |
43 | /** |
44 | * Override for financial type id. |
45 | * |
46 | * Used when the financial type id is to be overridden for all line items |
47 | * (as can happen in backoffice forms) |
48 | * |
49 | * @var int |
50 | */ |
51 | protected $overrideFinancialTypeID; |
52 | |
53 | /** |
54 | * Override for the total amount of the order. |
55 | * |
56 | * When there is a single line item the order total may be overriden. |
57 | * |
58 | * @var float |
59 | */ |
60 | protected $overrideTotalAmount; |
61 | |
62 | /** |
63 | * Line items in the order. |
64 | * |
65 | * @var array |
66 | */ |
67 | protected $lineItems = []; |
68 | |
69 | /** |
70 | * Metadata for price fields. |
71 | * |
72 | * @var array |
73 | */ |
74 | protected $priceFieldMetadata = []; |
75 | |
76 | /** |
77 | * Get Set override for total amount of the order. |
78 | * |
79 | * @return float|false |
80 | */ |
81 | public function getOverrideTotalAmount() { |
82 | if (count($this->getPriceOptions()) !== 1) { |
83 | return FALSE; |
84 | } |
85 | return $this->overrideTotalAmount ?? FALSE; |
86 | } |
87 | |
88 | /** |
89 | * Set override for total amount. |
90 | * |
91 | * @param float $overrideTotalAmount |
92 | */ |
93 | public function setOverrideTotalAmount(float $overrideTotalAmount) { |
94 | $this->overrideTotalAmount = $overrideTotalAmount; |
95 | } |
96 | |
97 | /** |
98 | * Get override for total amount. |
99 | * |
100 | * @return int| FALSE |
101 | */ |
102 | public function getOverrideFinancialTypeID() { |
103 | if (count($this->getPriceOptions()) !== 1) { |
104 | return FALSE; |
105 | } |
106 | return $this->overrideFinancialTypeID ?? FALSE; |
107 | } |
108 | |
109 | /** |
110 | * Set override for financial type ID. |
111 | * |
112 | * @param int $overrideFinancialTypeID |
113 | */ |
114 | public function setOverrideFinancialTypeID(int $overrideFinancialTypeID) { |
115 | $this->overrideFinancialTypeID = $overrideFinancialTypeID; |
116 | } |
117 | |
118 | /** |
119 | * Getter for price set id. |
120 | * |
121 | * @return int |
122 | */ |
123 | public function getPriceSetID(): int { |
124 | return $this->priceSetID; |
125 | } |
126 | |
127 | /** |
128 | * Setter for price set id. |
129 | * |
130 | * @param int $priceSetID |
131 | */ |
132 | public function setPriceSetID(int $priceSetID) { |
133 | $this->priceSetID = $priceSetID; |
134 | } |
135 | |
136 | /** |
137 | * Getter for price selection. |
138 | * |
139 | * @return array |
140 | */ |
141 | public function getPriceSelection(): array { |
142 | return $this->priceSelection; |
143 | } |
144 | |
145 | /** |
146 | * Setter for price selection. |
147 | * |
148 | * @param array $priceSelection |
149 | */ |
150 | public function setPriceSelection(array $priceSelection) { |
151 | $this->priceSelection = $priceSelection; |
152 | } |
153 | |
154 | /** |
155 | * Price options the simplified price fields selections. |
156 | * |
157 | * ie. the 'price_' is stripped off the key name and the field ID |
158 | * is cast to an integer. |
159 | * |
160 | * @return array |
161 | */ |
162 | public function getPriceOptions():array { |
163 | $priceOptions = []; |
164 | foreach ($this->getPriceSelection() as $fieldName => $value) { |
165 | $fieldID = substr($fieldName, 6); |
166 | $priceOptions[(int) $fieldID] = $value; |
167 | } |
168 | return $priceOptions; |
169 | } |
170 | |
171 | /** |
172 | * Get the metadata for the given field. |
173 | * |
174 | * @param int $id |
175 | * |
176 | * @return array |
177 | * @throws \CiviCRM_API3_Exception |
178 | */ |
179 | public function getPriceFieldSpec(int $id) :array { |
180 | if (!isset($this->priceFieldMetadata[$id])) { |
181 | $this->priceFieldMetadata = CRM_Price_BAO_PriceSet::getCachedPriceSetDetail($this->getPriceSetID())['fields']; |
182 | } |
183 | return $this->priceFieldMetadata[$id]; |
184 | } |
185 | |
186 | /** |
187 | * Set the price field selection from an array of params containing price fields. |
188 | * |
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. |
191 | * |
192 | * @param array $input |
193 | */ |
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; |
200 | } |
201 | } |
202 | } |
203 | } |
204 | |
205 | /** |
206 | * Get line items. |
207 | * |
208 | * return array |
209 | * |
210 | * @throws \CiviCRM_API3_Exception |
211 | */ |
212 | public function getLineItems():array { |
213 | if (empty($this->lineItems)) { |
214 | $this->lineItems = $this->calculateLineItems(); |
215 | } |
216 | return $this->lineItems; |
217 | } |
218 | |
219 | /** |
220 | * @return array |
221 | * @throws \CiviCRM_API3_Exception |
222 | */ |
223 | protected function calculateLineItems(): array { |
224 | $lineItems = []; |
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(); |
231 | } |
232 | |
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]; |
237 | } |
238 | |
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(); |
245 | } |
246 | $taxRate = $taxRates[$lineItem['financial_type_id']] ?? 0; |
247 | if ($this->getOverrideTotalAmount() !== FALSE) { |
248 | if ($taxRate) { |
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']; |
252 | } |
253 | else { |
254 | $lineItem['line_total'] = $lineItem['unit_price'] = $this->getOverrideTotalAmount(); |
255 | } |
256 | } |
257 | elseif ($taxRate) { |
258 | $lineItem['tax_amount'] = ($taxRate / 100) * $lineItem['line_total']; |
259 | } |
260 | } |
261 | return $lineItems; |
262 | } |
263 | |
264 | /** |
265 | * Get the total tax amount for the order. |
266 | * |
267 | * @return float |
268 | * |
269 | * @throws \CiviCRM_API3_Exception |
270 | */ |
271 | public function getTotalTaxAmount() :float { |
272 | $amount = 0.0; |
273 | foreach ($this->getLineItems() as $lineItem) { |
274 | $amount += $lineItem['tax_amount'] ?? 0.0; |
275 | } |
276 | return $amount; |
277 | } |
278 | |
279 | } |