Commit | Line | Data |
---|---|---|
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 | ||
4df23f2a | 12 | use Civi\Api4\LineItem; |
58315149 | 13 | use Civi\Api4\PriceField; |
60e5cf34 | 14 | use Civi\Api4\PriceFieldValue; |
812b2c0c | 15 | use Civi\Api4\PriceSet; |
58315149 | 16 | |
7aa78908 | 17 | /** |
18 | * | |
19 | * @package CRM | |
20 | * @copyright CiviCRM LLC https://civicrm.org/licensing | |
20df462f | 21 | * |
7aa78908 | 22 | * Order class. |
23 | * | |
24 | * This class is intended to become the object to manage orders, including via Order.api. | |
25 | * | |
26 | * As of writing it is in the process of having appropriate functions built up. | |
20df462f | 27 | * It should **NOT** be accessed directly outside of tested core methods as it |
28 | * may change. | |
812b2c0c EM |
29 | * |
30 | * @internal | |
7aa78908 | 31 | */ |
32 | class CRM_Financial_BAO_Order { | |
33 | ||
34 | /** | |
35 | * Price set id. | |
36 | * | |
37 | * @var int | |
38 | */ | |
39 | protected $priceSetID; | |
40 | ||
41 | /** | |
42 | * Selected price items in the format we see in forms. | |
43 | * | |
44 | * ie. | |
45 | * [price_3 => 4, price_10 => 7] | |
46 | * is equivalent to 'option_value 4 for radio price field 3 and | |
47 | * a quantity of 7 for text price field 10. | |
48 | * | |
49 | * @var array | |
50 | */ | |
51 | protected $priceSelection = []; | |
52 | ||
53 | /** | |
54 | * Override for financial type id. | |
55 | * | |
56 | * Used when the financial type id is to be overridden for all line items | |
57 | * (as can happen in backoffice forms) | |
58 | * | |
59 | * @var int | |
60 | */ | |
61 | protected $overrideFinancialTypeID; | |
62 | ||
4df23f2a EM |
63 | /** |
64 | * Overridable financial type id. | |
65 | * | |
66 | * When this is set only this financial type will be overridden. | |
67 | * | |
68 | * This is relevant to repeat transactions where we want to | |
69 | * override the type on the line items if it a different financial type has | |
70 | * been saved against the recurring contribution. However, it the line item | |
71 | * financial type differs from the contribution financial type then we | |
72 | * treat this as deliberately uncoupled and don't flow through changes | |
73 | * in financial type down to the line items. | |
74 | * | |
75 | * This is covered in testRepeatTransactionUpdatedFinancialTypeAndNotEquals. | |
76 | * | |
77 | * @var int | |
78 | */ | |
79 | protected $overridableFinancialTypeID; | |
80 | ||
81 | /** | |
82 | * Get overridable financial type id. | |
83 | * | |
84 | * If only one financial type id can be overridden at the line item level | |
85 | * then get it here, otherwise NULL. | |
86 | * | |
87 | * @return int|null | |
88 | */ | |
89 | public function getOverridableFinancialTypeID(): ?int { | |
90 | return $this->overridableFinancialTypeID; | |
91 | } | |
92 | ||
93 | /** | |
94 | * Set overridable financial type id. | |
95 | * | |
96 | * If only one financial type id can be overridden at the line item level | |
97 | * then set it here. | |
98 | * | |
99 | * @param int|null $overridableFinancialTypeID | |
100 | */ | |
101 | public function setOverridableFinancialTypeID(?int $overridableFinancialTypeID): void { | |
102 | $this->overridableFinancialTypeID = $overridableFinancialTypeID; | |
103 | } | |
104 | ||
ca44bb7e EM |
105 | /** |
106 | * Financial type id to use for any lines where is is not provided. | |
107 | * | |
108 | * @var int | |
109 | */ | |
110 | protected $defaultFinancialTypeID; | |
111 | ||
4df23f2a EM |
112 | /** |
113 | * ID of a contribution to be used as a template. | |
114 | * | |
115 | * @var int | |
116 | */ | |
117 | protected $templateContributionID; | |
118 | ||
119 | /** | |
120 | * Should we permit the line item financial type to be overridden when there is more than one line. | |
121 | * | |
122 | * Historically the answer is 'yes' for v3 order api and 'no' for repeattransaction | |
123 | * and backoffice forms. | |
124 | * | |
125 | * @var bool | |
126 | */ | |
e5e212b5 | 127 | protected $isPermitOverrideFinancialTypeForMultipleLines = FALSE; |
4df23f2a EM |
128 | |
129 | /** | |
130 | * @return bool | |
131 | */ | |
132 | public function isPermitOverrideFinancialTypeForMultipleLines(): bool { | |
133 | return $this->isPermitOverrideFinancialTypeForMultipleLines; | |
134 | } | |
135 | ||
136 | /** | |
137 | * @param bool $isPermitOverrideFinancialTypeForMultipleLines | |
138 | */ | |
139 | public function setIsPermitOverrideFinancialTypeForMultipleLines(bool $isPermitOverrideFinancialTypeForMultipleLines): void { | |
140 | $this->isPermitOverrideFinancialTypeForMultipleLines = $isPermitOverrideFinancialTypeForMultipleLines; | |
141 | } | |
142 | ||
143 | /** | |
144 | * Number of line items. | |
145 | * | |
146 | * @var int | |
147 | */ | |
148 | protected $lineItemCount; | |
149 | ||
150 | /** | |
151 | * @return int | |
152 | */ | |
153 | public function getLineItemCount(): int { | |
154 | if (!isset($this->lineItemCount)) { | |
155 | $this->lineItemCount = count($this->getPriceOptions()) || count($this->lineItems); | |
156 | } | |
157 | return $this->lineItemCount; | |
158 | } | |
159 | ||
160 | /** | |
161 | * @param int $lineItemCount | |
162 | */ | |
163 | public function setLineItemCount(int $lineItemCount): void { | |
164 | $this->lineItemCount = $lineItemCount; | |
165 | } | |
166 | ||
167 | /** | |
168 | * @return int|null | |
169 | */ | |
170 | public function getTemplateContributionID(): ?int { | |
171 | return $this->templateContributionID; | |
172 | } | |
173 | ||
174 | /** | |
175 | * @param int $templateContributionID | |
176 | */ | |
177 | public function setTemplateContributionID(int $templateContributionID): void { | |
178 | $this->templateContributionID = $templateContributionID; | |
179 | } | |
180 | ||
ca44bb7e EM |
181 | /** |
182 | * @return int | |
183 | */ | |
184 | public function getDefaultFinancialTypeID(): int { | |
185 | return $this->defaultFinancialTypeID; | |
186 | } | |
187 | ||
188 | /** | |
189 | * Set the default financial type id to be used when the line has none. | |
190 | * | |
191 | * @param int|null $defaultFinancialTypeID | |
192 | */ | |
193 | public function setDefaultFinancialTypeID(?int $defaultFinancialTypeID): void { | |
194 | $this->defaultFinancialTypeID = $defaultFinancialTypeID; | |
195 | } | |
196 | ||
7aa78908 | 197 | /** |
198 | * Override for the total amount of the order. | |
199 | * | |
200 | * When there is a single line item the order total may be overriden. | |
201 | * | |
202 | * @var float | |
203 | */ | |
204 | protected $overrideTotalAmount; | |
205 | ||
206 | /** | |
207 | * Line items in the order. | |
208 | * | |
209 | * @var array | |
210 | */ | |
211 | protected $lineItems = []; | |
212 | ||
ca1b238b EM |
213 | /** |
214 | * Array of entities ordered. | |
215 | * | |
216 | * @var array | |
217 | */ | |
218 | protected $entityParameters = []; | |
219 | ||
220 | /** | |
221 | * Default price sets for component. | |
222 | * | |
223 | * @var array | |
224 | */ | |
225 | protected $defaultPriceSets = []; | |
226 | ||
227 | /** | |
228 | * Cache of the default price field. | |
229 | * | |
230 | * @var array | |
231 | */ | |
232 | protected $defaultPriceField; | |
233 | ||
60e5cf34 EM |
234 | /** |
235 | * Cache of the default price field value ID. | |
236 | * | |
237 | * @var array | |
238 | */ | |
239 | protected $defaultPriceFieldValueID; | |
240 | ||
ca1b238b EM |
241 | /** |
242 | * Get parameters for the entities bought as part of this order. | |
243 | * | |
244 | * @return array | |
245 | * | |
246 | * @internal core tested code only. | |
247 | * | |
248 | */ | |
249 | public function getEntitiesToCreate(): array { | |
250 | $entities = []; | |
251 | foreach ($this->entityParameters as $entityToCreate) { | |
252 | if (in_array($entityToCreate['entity'], ['participant', 'membership'], TRUE)) { | |
253 | $entities[] = $entityToCreate; | |
254 | } | |
255 | } | |
256 | return $entities; | |
257 | } | |
258 | ||
259 | /** | |
260 | * Set parameters for the entities bought as part of this order. | |
261 | * | |
262 | * @param array $entityParameters | |
263 | * @param int|string $key indexing reference | |
264 | * | |
265 | * @internal core tested code only. | |
266 | * | |
267 | */ | |
268 | public function setEntityParameters(array $entityParameters, $key): void { | |
269 | $this->entityParameters[$key] = $entityParameters; | |
270 | } | |
271 | ||
272 | /** | |
273 | * Add a line item to an entity. | |
274 | * | |
275 | * The v3 api supports more than on line item being stored against a given | |
276 | * set of entity parameters. There is some doubt as to whether this is a | |
277 | * good thing that should be supported in v4 or something that 'seemed | |
278 | * like a good idea at the time' - but this allows the lines to be added from the | |
279 | * v3 api. | |
280 | * | |
281 | * @param string $lineIndex | |
282 | * @param string $entityKey | |
283 | */ | |
284 | public function addLineItemToEntityParameters(string $lineIndex, string $entityKey): void { | |
285 | $this->entityParameters[$entityKey]['entity'] = $this->getLineItemEntity($lineIndex); | |
286 | $this->entityParameters[$entityKey]['line_references'][] = $lineIndex; | |
287 | } | |
288 | ||
7aa78908 | 289 | /** |
290 | * Metadata for price fields. | |
291 | * | |
292 | * @var array | |
293 | */ | |
294 | protected $priceFieldMetadata = []; | |
295 | ||
8193ed74 EM |
296 | /** |
297 | * Metadata for price field values. | |
298 | * | |
299 | * @var array | |
300 | */ | |
301 | protected $priceFieldValueMetadata = []; | |
302 | ||
be2e79c8 | 303 | /** |
304 | * Metadata for price sets. | |
305 | * | |
306 | * @var array | |
307 | */ | |
308 | protected $priceSetMetadata = []; | |
309 | ||
20df462f | 310 | /** |
311 | * Get form object. | |
312 | * | |
812b2c0c EM |
313 | * @internal use in tested core code only. |
314 | * | |
20df462f | 315 | * @return \CRM_Core_Form|NULL |
316 | */ | |
317 | public function getForm(): ?CRM_Core_Form { | |
318 | return $this->form; | |
319 | } | |
320 | ||
321 | /** | |
322 | * Set form object. | |
323 | * | |
812b2c0c EM |
324 | * @internal use in tested core code only. |
325 | * | |
2024d5b9 | 326 | * @param \CRM_Core_Form|null $form |
20df462f | 327 | */ |
328 | public function setForm(?CRM_Core_Form $form): void { | |
329 | $this->form = $form; | |
330 | } | |
331 | ||
332 | /** | |
333 | * The serialize & unserialize functions are to prevent the form being serialized & stored. | |
334 | * | |
335 | * The form could be potentially large & circular. | |
336 | * | |
337 | * We simply serialize the values needed to re-serialize the form. | |
338 | * | |
339 | * @return array | |
340 | */ | |
341 | public function _serialize(): array { | |
342 | return [ | |
343 | 'OverrideTotalAmount' => $this->getOverrideTotalAmount(), | |
344 | 'OverrideFinancialType' => $this->getOverrideFinancialTypeID(), | |
345 | 'PriceSelection' => $this->getPriceSelection(), | |
346 | ]; | |
347 | } | |
348 | ||
349 | /** | |
350 | * Re-instantiate the the class with non-calculated variables. | |
351 | * | |
352 | * @param array $data | |
353 | */ | |
354 | public function _unserialize(array $data): void { | |
355 | foreach ($data as $key => $value) { | |
356 | $fn = 'set' . $key; | |
357 | $this->$fn($value); | |
358 | } | |
359 | } | |
360 | ||
361 | /** | |
362 | * Form object - if present the buildAmount hook will be called. | |
363 | * | |
364 | * @var \CRM_Member_Form_Membership|\CRM_Member_Form_MembershipRenewal | |
365 | */ | |
366 | protected $form; | |
367 | ||
7aa78908 | 368 | /** |
369 | * Get Set override for total amount of the order. | |
370 | * | |
812b2c0c EM |
371 | * @internal use in tested core code only. |
372 | * | |
7aa78908 | 373 | * @return float|false |
374 | */ | |
375 | public function getOverrideTotalAmount() { | |
77274636 EM |
376 | // The override amount is only valid for quick config price sets where more |
377 | // than one field has not been selected. | |
4df23f2a | 378 | if (!$this->overrideTotalAmount || $this->getLineItemCount() > 1) { |
7aa78908 | 379 | return FALSE; |
380 | } | |
77274636 | 381 | return $this->overrideTotalAmount; |
7aa78908 | 382 | } |
383 | ||
4df23f2a EM |
384 | /** |
385 | * Is the line item financial type to be overridden. | |
386 | * | |
387 | * We have a tested scenario for repeatcontribution where the line item | |
388 | * does not match the top level financial type for the order. In this case | |
389 | * any financial type override has been determined to NOT apply to the line items. | |
390 | * | |
391 | * This is locked in via testRepeatTransactionUpdatedFinancialTypeAndNotEquals. | |
392 | * | |
393 | * @param int $financialTypeID | |
394 | * | |
395 | * @return bool | |
396 | */ | |
397 | public function isOverrideLineItemFinancialType(int $financialTypeID) { | |
398 | if (!$this->getOverrideFinancialTypeID()) { | |
399 | return FALSE; | |
400 | } | |
401 | if (!$this->getOverridableFinancialTypeID()) { | |
402 | return TRUE; | |
403 | } | |
404 | return $this->getOverridableFinancialTypeID() === $financialTypeID; | |
405 | } | |
406 | ||
7aa78908 | 407 | /** |
408 | * Set override for total amount. | |
409 | * | |
812b2c0c EM |
410 | * @internal use in tested core code only. |
411 | * | |
4df23f2a | 412 | * @param float|null $overrideTotalAmount |
7aa78908 | 413 | */ |
4df23f2a | 414 | public function setOverrideTotalAmount(?float $overrideTotalAmount): void { |
77274636 | 415 | $this->overrideTotalAmount = $overrideTotalAmount; |
7aa78908 | 416 | } |
417 | ||
418 | /** | |
419 | * Get override for total amount. | |
420 | * | |
812b2c0c EM |
421 | * @internal use in tested core code only. |
422 | * | |
7aa78908 | 423 | * @return int| FALSE |
424 | */ | |
425 | public function getOverrideFinancialTypeID() { | |
4df23f2a EM |
426 | // We don't permit overrides if there is more than one line. |
427 | // The reason for this constraint may be more historical since | |
428 | // the case could be made that if it is set it should be used and | |
429 | // we have built out the tax calculations a lot now. | |
430 | if (!$this->isPermitOverrideFinancialTypeForMultipleLines() && $this->getLineItemCount() > 1) { | |
7aa78908 | 431 | return FALSE; |
432 | } | |
433 | return $this->overrideFinancialTypeID ?? FALSE; | |
434 | } | |
435 | ||
436 | /** | |
437 | * Set override for financial type ID. | |
438 | * | |
812b2c0c EM |
439 | * @internal use in tested core code only. |
440 | * | |
4df23f2a | 441 | * @param int|null $overrideFinancialTypeID |
7aa78908 | 442 | */ |
4df23f2a | 443 | public function setOverrideFinancialTypeID(?int $overrideFinancialTypeID): void { |
7aa78908 | 444 | $this->overrideFinancialTypeID = $overrideFinancialTypeID; |
445 | } | |
446 | ||
447 | /** | |
448 | * Getter for price set id. | |
449 | * | |
812b2c0c EM |
450 | * @internal use in tested core code only. |
451 | * | |
7aa78908 | 452 | * @return int |
812b2c0c EM |
453 | * |
454 | * @throws \API_Exception | |
7aa78908 | 455 | */ |
456 | public function getPriceSetID(): int { | |
812b2c0c EM |
457 | if (!$this->priceSetID) { |
458 | foreach ($this->getPriceOptions() as $fieldID => $valueID) { | |
459 | $this->setPriceSetIDFromSelectedField($fieldID); | |
460 | } | |
461 | } | |
7aa78908 | 462 | return $this->priceSetID; |
463 | } | |
464 | ||
465 | /** | |
466 | * Setter for price set id. | |
467 | * | |
812b2c0c EM |
468 | * @internal use in tested core code only. |
469 | * | |
7aa78908 | 470 | * @param int $priceSetID |
471 | */ | |
472 | public function setPriceSetID(int $priceSetID) { | |
473 | $this->priceSetID = $priceSetID; | |
474 | } | |
475 | ||
812b2c0c EM |
476 | /** |
477 | * Set price set id to the default. | |
478 | * | |
479 | * @param string $component [membership|contribution] | |
480 | * | |
481 | * @throws \API_Exception | |
482 | * @internal use in tested core code only. | |
483 | */ | |
484 | public function setPriceSetToDefault(string $component): void { | |
ca1b238b | 485 | $this->priceSetID = $this->getDefaultPriceSetForComponent($component); |
812b2c0c EM |
486 | } |
487 | ||
d6c86ce5 EM |
488 | /** |
489 | * Set price set ID based on the contribution page id. | |
490 | * | |
812b2c0c EM |
491 | * @internal use in tested core code only. |
492 | * | |
d6c86ce5 EM |
493 | * @param int $contributionPageID |
494 | * | |
d6c86ce5 EM |
495 | */ |
496 | public function setPriceSetIDByContributionPageID(int $contributionPageID): void { | |
497 | $this->setPriceSetIDByEntity('contribution_page', $contributionPageID); | |
498 | } | |
499 | ||
500 | /** | |
501 | * Set price set ID based on the event id. | |
502 | * | |
812b2c0c EM |
503 | * @internal use in tested core code only. |
504 | * | |
d6c86ce5 EM |
505 | * @param int $eventID |
506 | * | |
507 | * @throws \CiviCRM_API3_Exception | |
508 | */ | |
509 | public function setPriceSetIDByEventPageID(int $eventID): void { | |
510 | $this->setPriceSetIDByEntity('event', $eventID); | |
511 | } | |
512 | ||
513 | /** | |
514 | * Set the price set id based on looking up the entity. | |
812b2c0c EM |
515 | * |
516 | * @internal use in tested core code only. | |
517 | * | |
d6c86ce5 EM |
518 | * @param string $entity |
519 | * @param int $id | |
520 | * | |
521 | */ | |
522 | protected function setPriceSetIDByEntity(string $entity, int $id): void { | |
523 | $this->priceSetID = CRM_Price_BAO_PriceSet::getFor('civicrm_' . $entity, $id); | |
524 | } | |
525 | ||
7aa78908 | 526 | /** |
527 | * Getter for price selection. | |
528 | * | |
812b2c0c EM |
529 | * @internal use in tested core code only. |
530 | * | |
7aa78908 | 531 | * @return array |
532 | */ | |
533 | public function getPriceSelection(): array { | |
534 | return $this->priceSelection; | |
535 | } | |
536 | ||
537 | /** | |
538 | * Setter for price selection. | |
539 | * | |
812b2c0c EM |
540 | * @internal use in tested core code only. |
541 | * | |
7aa78908 | 542 | * @param array $priceSelection |
543 | */ | |
544 | public function setPriceSelection(array $priceSelection) { | |
545 | $this->priceSelection = $priceSelection; | |
546 | } | |
547 | ||
548 | /** | |
549 | * Price options the simplified price fields selections. | |
550 | * | |
551 | * ie. the 'price_' is stripped off the key name and the field ID | |
552 | * is cast to an integer. | |
553 | * | |
812b2c0c EM |
554 | * @internal use in tested core code only. |
555 | * | |
7aa78908 | 556 | * @return array |
557 | */ | |
558 | public function getPriceOptions():array { | |
559 | $priceOptions = []; | |
560 | foreach ($this->getPriceSelection() as $fieldName => $value) { | |
561 | $fieldID = substr($fieldName, 6); | |
562 | $priceOptions[(int) $fieldID] = $value; | |
563 | } | |
564 | return $priceOptions; | |
565 | } | |
566 | ||
567 | /** | |
568 | * Get the metadata for the given field. | |
569 | * | |
812b2c0c EM |
570 | * @internal use in tested core code only. |
571 | * | |
7aa78908 | 572 | * @param int $id |
573 | * | |
574 | * @return array | |
7aa78908 | 575 | */ |
576 | public function getPriceFieldSpec(int $id) :array { | |
2408aa47 | 577 | return $this->getPriceFieldsMetadata()[$id] ?? $this->getPriceFieldMetadata($id); |
de668aa8 | 578 | } |
579 | ||
8193ed74 EM |
580 | /** |
581 | * Get the metadata for the given field value. | |
582 | * | |
583 | * @internal use in tested core code only. | |
584 | * | |
585 | * @param int $id | |
586 | * | |
587 | * @return array | |
588 | */ | |
589 | public function getPriceFieldValueSpec(int $id) :array { | |
590 | if (!isset($this->priceFieldValueMetadata[$id])) { | |
591 | $this->priceFieldValueMetadata[$id] = PriceFieldValue::get(FALSE)->addWhere('id', '=', $id)->execute()->first(); | |
592 | } | |
593 | return $this->priceFieldValueMetadata[$id]; | |
594 | } | |
595 | ||
de668aa8 | 596 | /** |
597 | * Get the metadata for the fields in the price set. | |
598 | * | |
812b2c0c EM |
599 | * @internal use in tested core code only. |
600 | * | |
de668aa8 | 601 | * @return array |
602 | */ | |
603 | public function getPriceFieldsMetadata(): array { | |
604 | if (empty($this->priceFieldMetadata)) { | |
be2e79c8 | 605 | $this->getPriceSetMetadata(); |
7aa78908 | 606 | } |
de668aa8 | 607 | return $this->priceFieldMetadata; |
7aa78908 | 608 | } |
609 | ||
2408aa47 EM |
610 | /** |
611 | * Get the metadata for the given price field. | |
612 | * | |
613 | * Note this uses a different method to getPriceFieldMetadata. | |
614 | * | |
615 | * There is an assumption in the code currently that all purchases | |
616 | * are within a single price set. However, discussions have been around | |
617 | * the idea that when form-builder supports contributions price sets will | |
618 | * not be used as form-builder in itself is a configuration unit. | |
619 | * | |
620 | * Currently there are couple of unit tests that mix & match & rather than | |
621 | * updating the tests to avoid notices when orders are loaded for receipting, | |
622 | * the migration to this new method is starting.... | |
623 | * | |
624 | * @param int $id | |
625 | * | |
626 | * @return array | |
627 | */ | |
628 | public function getPriceFieldMetadata(int $id): array { | |
629 | return CRM_Price_BAO_PriceField::getPriceField($id); | |
630 | } | |
631 | ||
812b2c0c EM |
632 | /** |
633 | * Set the metadata for the order. | |
634 | * | |
635 | * @param array $metadata | |
636 | */ | |
637 | protected function setPriceFieldMetadata($metadata) { | |
638 | $this->priceFieldMetadata = $metadata; | |
639 | if ($this->getForm()) { | |
640 | CRM_Utils_Hook::buildAmount($this->form->getFormContext(), $this->form, $this->priceFieldMetadata); | |
641 | } | |
642 | } | |
643 | ||
be2e79c8 | 644 | /** |
645 | * Get the metadata for the fields in the price set. | |
646 | * | |
812b2c0c EM |
647 | * @internal use in tested core code only. |
648 | * | |
be2e79c8 | 649 | * @return array |
650 | */ | |
651 | public function getPriceSetMetadata(): array { | |
652 | if (empty($this->priceSetMetadata)) { | |
653 | $priceSetMetadata = CRM_Price_BAO_PriceSet::getCachedPriceSetDetail($this->getPriceSetID()); | |
812b2c0c | 654 | $this->setPriceFieldMetadata($priceSetMetadata['fields']); |
be2e79c8 | 655 | unset($priceSetMetadata['fields']); |
656 | $this->priceSetMetadata = $priceSetMetadata; | |
657 | } | |
658 | return $this->priceSetMetadata; | |
659 | } | |
660 | ||
661 | /** | |
662 | * Get the financial type id for the order. | |
663 | * | |
812b2c0c EM |
664 | * @internal use in tested core code only. |
665 | * | |
be2e79c8 | 666 | * This may differ to the line items.... |
667 | * | |
668 | * @return int | |
669 | */ | |
670 | public function getFinancialTypeID(): int { | |
671 | return (int) $this->getOverrideFinancialTypeID() ?: $this->getPriceSetMetadata()['financial_type_id']; | |
672 | } | |
673 | ||
7aa78908 | 674 | /** |
812b2c0c EM |
675 | * Set the price field selection from an array of params containing price |
676 | * fields. | |
7aa78908 | 677 | * |
812b2c0c EM |
678 | * This function takes the sort of 'anything & everything' parameters that |
679 | * come in from the form layer and filters them before assigning them to the | |
680 | * priceSelection property. | |
7aa78908 | 681 | * |
682 | * @param array $input | |
812b2c0c EM |
683 | * |
684 | * @throws \API_Exception | |
7aa78908 | 685 | */ |
de668aa8 | 686 | public function setPriceSelectionFromUnfilteredInput(array $input): void { |
7aa78908 | 687 | foreach ($input as $fieldName => $value) { |
688 | if (strpos($fieldName, 'price_') === 0) { | |
689 | $fieldID = substr($fieldName, 6); | |
690 | if (is_numeric($fieldID)) { | |
691 | $this->priceSelection[$fieldName] = $value; | |
692 | } | |
693 | } | |
694 | } | |
812b2c0c EM |
695 | if (empty($this->priceSelection) && isset($input['total_amount']) |
696 | && is_numeric($input['total_amount']) && !empty($input['financial_type_id'])) { | |
ca1b238b | 697 | $this->priceSelection['price_' . $this->getDefaultPriceFieldID()] = $input['total_amount']; |
812b2c0c EM |
698 | $this->setOverrideFinancialTypeID($input['financial_type_id']); |
699 | } | |
700 | } | |
701 | ||
702 | /** | |
703 | * Get the id of the price field to use when just an amount is provided. | |
704 | * | |
705 | * @throws \API_Exception | |
ca1b238b EM |
706 | * |
707 | * @return int | |
812b2c0c | 708 | */ |
ca1b238b EM |
709 | public function getDefaultPriceFieldID():int { |
710 | if (!$this->defaultPriceField) { | |
711 | $this->defaultPriceField = PriceField::get(FALSE) | |
712 | ->addWhere('name', '=', 'contribution_amount') | |
713 | ->addWhere('price_set_id.name', '=', 'default_contribution_amount') | |
714 | ->execute()->first(); | |
715 | } | |
716 | return $this->defaultPriceField['id']; | |
7aa78908 | 717 | } |
718 | ||
60e5cf34 EM |
719 | /** |
720 | * Get the id of the price field to use when just an amount is provided. | |
721 | * | |
722 | * @throws \API_Exception | |
723 | * | |
724 | * @return int | |
725 | */ | |
726 | public function getDefaultPriceFieldValueID():int { | |
727 | if (!$this->defaultPriceFieldValueID) { | |
728 | $this->defaultPriceFieldValueID = PriceFieldValue::get(FALSE) | |
729 | ->addWhere('name', '=', 'contribution_amount') | |
730 | ->addWhere('price_field_id.name', '=', 'contribution_amount') | |
731 | ->execute()->first()['id']; | |
732 | } | |
733 | return $this->defaultPriceFieldValueID; | |
734 | } | |
735 | ||
7aa78908 | 736 | /** |
737 | * Get line items. | |
738 | * | |
739 | * return array | |
740 | * | |
741 | * @throws \CiviCRM_API3_Exception | |
742 | */ | |
743 | public function getLineItems():array { | |
744 | if (empty($this->lineItems)) { | |
745 | $this->lineItems = $this->calculateLineItems(); | |
746 | } | |
747 | return $this->lineItems; | |
748 | } | |
749 | ||
77274636 EM |
750 | /** |
751 | * Get line items in a 'traditional' indexing format. | |
752 | * | |
753 | * This ensures the line items are indexed by | |
754 | * price field id - as required by the contribution BAO. | |
755 | * | |
756 | * @throws \CiviCRM_API3_Exception | |
757 | */ | |
758 | public function getPriceFieldIndexedLineItems(): array { | |
759 | $lines = []; | |
760 | foreach ($this->getLineItems() as $item) { | |
761 | $lines[$item['price_field_id']] = $item; | |
762 | } | |
763 | return $lines; | |
764 | } | |
765 | ||
e84c5dc2 | 766 | /** |
767 | * Get line items that specifically relate to memberships. | |
768 | * | |
769 | * return array | |
770 | * | |
771 | * @throws \CiviCRM_API3_Exception | |
772 | */ | |
773 | public function getMembershipLineItems():array { | |
774 | $lines = $this->getLineItems(); | |
775 | foreach ($lines as $index => $line) { | |
776 | if (empty($line['membership_type_id'])) { | |
777 | unset($lines[$index]); | |
778 | continue; | |
779 | } | |
780 | if (empty($line['membership_num_terms'])) { | |
781 | $lines[$index]['membership_num_terms'] = 1; | |
782 | } | |
783 | } | |
784 | return $lines; | |
785 | } | |
786 | ||
aba748e5 | 787 | /** |
788 | * Get an array of all membership types included in the order. | |
789 | * | |
790 | * @return array | |
791 | * | |
792 | * @throws \CiviCRM_API3_Exception | |
793 | */ | |
794 | public function getMembershipTypes(): array { | |
795 | $types = []; | |
796 | foreach ($this->getMembershipLineItems() as $line) { | |
797 | $types[$line['membership_type_id']] = CRM_Member_BAO_MembershipType::getMembershipType((int) $line['membership_type_id']); | |
798 | } | |
799 | return $types; | |
800 | } | |
801 | ||
802 | /** | |
803 | * Get an array of all membership types included in the order. | |
804 | * | |
805 | * @return array | |
806 | * | |
807 | * @throws \CiviCRM_API3_Exception | |
808 | */ | |
809 | public function getRenewableMembershipTypes(): array { | |
810 | $types = []; | |
811 | foreach ($this->getMembershipTypes() as $id => $type) { | |
812 | if (!empty($type['auto_renew'])) { | |
813 | $types[$id] = $type; | |
814 | } | |
815 | } | |
816 | return $types; | |
817 | } | |
818 | ||
7aa78908 | 819 | /** |
820 | * @return array | |
4df23f2a EM |
821 | * |
822 | * @throws \API_Exception | |
7aa78908 | 823 | */ |
824 | protected function calculateLineItems(): array { | |
825 | $lineItems = []; | |
826 | $params = $this->getPriceSelection(); | |
827 | if ($this->getOverrideTotalAmount() !== FALSE) { | |
828 | // We need to do this to keep getLine from doing weird stuff but the goal | |
829 | // is to ditch getLine next round of refactoring | |
830 | // and make the code more sane. | |
831 | $params['total_amount'] = $this->getOverrideTotalAmount(); | |
832 | } | |
833 | ||
812b2c0c EM |
834 | // Dummy value to prevent e-notice in getLine. We calculate tax in this class. |
835 | $params['financial_type_id'] = 0; | |
4df23f2a EM |
836 | if ($this->getTemplateContributionID()) { |
837 | $lineItems = $this->getLinesFromTemplateContribution(); | |
2408aa47 EM |
838 | // Set the price set ID from the first line item (we need to set this here |
839 | // to prevent a loop later when we retrieve the price field metadata to | |
840 | // set the 'title' (as accessed from workflow message templates). | |
841 | $this->setPriceSetID($lineItems[0]['price_field_id.price_set_id']); | |
4df23f2a EM |
842 | } |
843 | else { | |
844 | foreach ($this->getPriceOptions() as $fieldID => $valueID) { | |
cb74e389 EM |
845 | if ($valueID !== '') { |
846 | $this->setPriceSetIDFromSelectedField($fieldID); | |
847 | $throwAwayArray = []; | |
848 | // @todo - still using getLine for now but better to bring it to this class & do a better job. | |
849 | $newLines = CRM_Price_BAO_PriceSet::getLine($params, $throwAwayArray, $this->getPriceSetID(), $this->getPriceFieldSpec($fieldID), $fieldID)[1]; | |
850 | foreach ($newLines as $newLine) { | |
851 | $lineItems[$newLine['price_field_value_id']] = $newLine; | |
852 | } | |
4df23f2a | 853 | } |
58315149 | 854 | } |
7aa78908 | 855 | } |
4df23f2a EM |
856 | // Set the line item count here because it is needed to determine whether |
857 | // we can use overrides and would not be set yet if we have loaded them from | |
858 | // a template contribution. | |
859 | $this->setLineItemCount(count($lineItems)); | |
7aa78908 | 860 | |
7aa78908 | 861 | foreach ($lineItems as &$lineItem) { |
4df23f2a EM |
862 | // Set the price set id if not set above. Note that the above |
863 | // requires it for line retrieval but we want to fix that as it | |
864 | // should not be required at that point. | |
865 | $this->setPriceSetIDFromSelectedField($lineItem['price_field_id']); | |
7aa78908 | 866 | // Set any pre-calculation to zero as we will calculate. |
867 | $lineItem['tax_amount'] = 0; | |
4df23f2a | 868 | if ($this->isOverrideLineItemFinancialType($lineItem['financial_type_id']) !== FALSE) { |
7aa78908 | 869 | $lineItem['financial_type_id'] = $this->getOverrideFinancialTypeID(); |
870 | } | |
0a859bb8 | 871 | $lineItem['tax_rate'] = $taxRate = $this->getTaxRate((int) $lineItem['financial_type_id']); |
7aa78908 | 872 | if ($this->getOverrideTotalAmount() !== FALSE) { |
77274636 | 873 | $this->addTotalsToLineBasedOnOverrideTotal((int) $lineItem['financial_type_id'], $lineItem); |
7aa78908 | 874 | } |
875 | elseif ($taxRate) { | |
876 | $lineItem['tax_amount'] = ($taxRate / 100) * $lineItem['line_total']; | |
877 | } | |
2408aa47 | 878 | $lineItem['title'] = $this->getLineItemTitle($lineItem); |
7aa78908 | 879 | } |
880 | return $lineItems; | |
881 | } | |
882 | ||
883 | /** | |
58315149 | 884 | * Get the total amount for the order. |
7aa78908 | 885 | * |
886 | * @return float | |
887 | * | |
888 | * @throws \CiviCRM_API3_Exception | |
889 | */ | |
890 | public function getTotalTaxAmount() :float { | |
891 | $amount = 0.0; | |
892 | foreach ($this->getLineItems() as $lineItem) { | |
893 | $amount += $lineItem['tax_amount'] ?? 0.0; | |
894 | } | |
895 | return $amount; | |
896 | } | |
897 | ||
58315149 | 898 | /** |
765d02a3 | 899 | * Get the total amount for the order. |
58315149 | 900 | * |
901 | * @return float | |
902 | * | |
903 | * @throws \CiviCRM_API3_Exception | |
904 | */ | |
905 | public function getTotalAmount() :float { | |
906 | $amount = 0.0; | |
907 | foreach ($this->getLineItems() as $lineItem) { | |
53c8b1be | 908 | $amount += ($lineItem['line_total'] ?? 0.0) + ($lineItem['tax_amount'] ?? 0.0); |
58315149 | 909 | } |
910 | return $amount; | |
911 | } | |
912 | ||
765d02a3 EM |
913 | /** |
914 | * Get the total amount relating to memberships for the order. | |
915 | * | |
916 | * @return float | |
917 | * | |
918 | * @throws \CiviCRM_API3_Exception | |
919 | */ | |
920 | public function getMembershipTotalAmount() :float { | |
921 | $amount = 0.0; | |
922 | foreach ($this->getMembershipLineItems() as $lineItem) { | |
923 | $amount += ($lineItem['line_total'] ?? 0.0) + ($lineItem['tax_amount'] ?? 0.0); | |
924 | } | |
925 | return $amount; | |
926 | } | |
927 | ||
dd118b15 | 928 | /** |
929 | * Get the tax rate for the given financial type. | |
930 | * | |
931 | * @param int $financialTypeID | |
932 | * | |
933 | * @return float | |
934 | */ | |
935 | public function getTaxRate(int $financialTypeID) { | |
936 | $taxRates = CRM_Core_PseudoConstant::getTaxRates(); | |
937 | if (!isset($taxRates[$financialTypeID])) { | |
938 | return 0; | |
939 | } | |
940 | return $taxRates[$financialTypeID]; | |
941 | } | |
942 | ||
812b2c0c EM |
943 | /** |
944 | * @param $fieldID | |
945 | * | |
946 | * @throws \API_Exception | |
947 | */ | |
948 | protected function setPriceSetIDFromSelectedField($fieldID): void { | |
949 | if (!isset($this->priceSetID)) { | |
950 | $this->setPriceSetID(PriceField::get(FALSE) | |
951 | ->addSelect('price_set_id') | |
952 | ->addWhere('id', '=', $fieldID) | |
953 | ->execute() | |
954 | ->first()['price_set_id']); | |
955 | } | |
956 | } | |
957 | ||
ca44bb7e EM |
958 | /** |
959 | * Set the line item. | |
960 | * | |
961 | * This function augments the line item where possible. The calling code | |
962 | * should not attempt to set taxes. This function allows minimal values | |
963 | * to be passed for the default price sets - ie if only membership_type_id is | |
964 | * specified the price_field_id and price_value_id will be determined. | |
965 | * | |
966 | * @param array $lineItem | |
967 | * @param int|string $index | |
968 | * | |
969 | * @throws \API_Exception | |
970 | * @internal tested core code usage only. | |
971 | * @internal use in tested core code only. | |
972 | * | |
973 | */ | |
974 | public function setLineItem(array $lineItem, $index): void { | |
77274636 EM |
975 | if (!isset($this->priceSetID)) { |
976 | if (!empty($lineItem['price_field_id'])) { | |
977 | $this->setPriceSetIDFromSelectedField($lineItem['price_field_id']); | |
978 | } | |
979 | else { | |
980 | // we are using either the default membership or default contribution | |
981 | // If membership type is passed in we use the default price field. | |
982 | $component = !empty($lineItem['membership_type_id']) ? 'membership' : 'contribution'; | |
983 | $this->setPriceSetToDefault($component); | |
984 | } | |
ca44bb7e EM |
985 | } |
986 | if (!isset($lineItem['financial_type_id'])) { | |
987 | $lineItem['financial_type_id'] = $this->getDefaultFinancialTypeID(); | |
988 | } | |
989 | if (!is_numeric($lineItem['financial_type_id'])) { | |
990 | $lineItem['financial_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', $lineItem['financial_type_id']); | |
991 | } | |
77274636 EM |
992 | if ($this->getOverrideTotalAmount()) { |
993 | $this->addTotalsToLineBasedOnOverrideTotal((int) $lineItem['financial_type_id'], $lineItem); | |
994 | } | |
995 | else { | |
8193ed74 EM |
996 | $lineItem['tax_rate'] = $this->getTaxRate($lineItem['financial_type_id']); |
997 | $lineItem['tax_amount'] = ($lineItem['tax_rate'] / 100) * $lineItem['line_total']; | |
77274636 | 998 | } |
ca44bb7e EM |
999 | if (!empty($lineItem['membership_type_id'])) { |
1000 | $lineItem['entity_table'] = 'civicrm_membership'; | |
1001 | if (empty($lineItem['price_field_id']) && empty($lineItem['price_field_value_id'])) { | |
ca44bb7e EM |
1002 | $lineItem = $this->fillMembershipLine($lineItem); |
1003 | } | |
1004 | } | |
ca1b238b EM |
1005 | if ($this->getPriceSetID() === $this->getDefaultPriceSetForComponent('contribution')) { |
1006 | $this->fillDefaultContributionLine($lineItem); | |
1007 | } | |
8193ed74 EM |
1008 | if (empty($lineItem['label'])) { |
1009 | $lineItem['label'] = PriceFieldValue::get(FALSE)->addWhere('id', '=', (int) $lineItem['price_field_value_id'])->addSelect('label')->execute()->first()['label']; | |
1010 | } | |
1011 | if (empty($lineItem['price_field_id']) && !empty($lineItem['membership_type_id'])) { | |
1012 | // We have to 'guess' the price field since the calling code hasn't | |
1013 | // passed it in (which it really should but ... history). | |
1014 | foreach ($this->priceFieldMetadata as $pricefield) { | |
1015 | foreach ($pricefield['options'] ?? [] as $option) { | |
1016 | if ((int) $option['membership_type_id'] === $lineItem['membership_type_id']) { | |
1017 | $lineItem['price_field_id'] = $pricefield['id']; | |
1018 | $lineItem['price_field_value_id'] = $option['id']; | |
1019 | } | |
1020 | } | |
1021 | } | |
1022 | } | |
1023 | if (empty($lineItem['title'])) { | |
2408aa47 | 1024 | $lineItem['title'] = $this->getLineItemTitle($lineItem); |
8193ed74 | 1025 | } |
ca44bb7e EM |
1026 | $this->lineItems[$index] = $lineItem; |
1027 | } | |
1028 | ||
1029 | /** | |
1030 | * Set a value on a line item. | |
1031 | * | |
1032 | * @internal only use in core tested code. | |
1033 | * | |
1034 | * @param string $name | |
1035 | * @param mixed $value | |
1036 | * @param string|int $index | |
1037 | */ | |
1038 | public function setLineItemValue(string $name, $value, $index): void { | |
1039 | $this->lineItems[$index][$name] = $value; | |
1040 | } | |
1041 | ||
1042 | /** | |
1043 | * @param int|string $index | |
1044 | * | |
1045 | * @return string | |
1046 | */ | |
1047 | public function getLineItemEntity($index):string { | |
1048 | // @todo - ensure entity_table is set in setLineItem, go back to enotices here. | |
1049 | return str_replace('civicrm_', '', ($this->lineItems[$index]['entity_table'] ?? 'contribution')); | |
1050 | } | |
1051 | ||
1052 | /** | |
1053 | * Get the ordered line item. | |
1054 | * | |
1055 | * @param string|int $index | |
1056 | * | |
1057 | * @return array | |
1058 | */ | |
1059 | public function getLineItem($index): array { | |
1060 | return $this->lineItems[$index]; | |
1061 | } | |
1062 | ||
1063 | /** | |
1064 | * Fills in additional data for the membership line. | |
1065 | * | |
1066 | * The minimum requirement is the membership_type_id and that priceSetID is set. | |
1067 | * | |
1068 | * @param array $lineItem | |
1069 | * | |
1070 | * @return array | |
1071 | */ | |
1072 | protected function fillMembershipLine(array $lineItem): array { | |
1073 | $fields = $this->getPriceFieldsMetadata(); | |
77274636 EM |
1074 | foreach ($fields as $field) { |
1075 | if (!isset($lineItem['price_field_value_id'])) { | |
1076 | foreach ($field['options'] as $option) { | |
1077 | if ((int) $option['membership_type_id'] === (int) $lineItem['membership_type_id']) { | |
1078 | $lineItem['price_field_id'] = $field['id']; | |
8193ed74 | 1079 | $lineItem['price_field_id.label'] = $field['label']; |
77274636 EM |
1080 | $lineItem['price_field_value_id'] = $option['id']; |
1081 | $lineItem['qty'] = 1; | |
1082 | } | |
ca44bb7e EM |
1083 | } |
1084 | } | |
77274636 EM |
1085 | if (isset($lineItem['price_field_value_id'], $field['options'][$lineItem['price_field_value_id']])) { |
1086 | $option = $field['options'][$lineItem['price_field_value_id']]; | |
1087 | } | |
ca44bb7e | 1088 | } |
ca44bb7e EM |
1089 | $lineItem['unit_price'] = $lineItem['line_total'] ?? $option['amount']; |
1090 | $lineItem['label'] = $lineItem['label'] ?? $option['label']; | |
1091 | $lineItem['field_title'] = $lineItem['field_title'] ?? $option['label']; | |
1092 | $lineItem['financial_type_id'] = $lineItem['financial_type_id'] ?: ($this->getDefaultFinancialTypeID() ?? $option['financial_type_id']); | |
1093 | return $lineItem; | |
1094 | } | |
1095 | ||
77274636 EM |
1096 | /** |
1097 | * Add total_amount and tax_amount to the line from the override total. | |
1098 | * | |
1099 | * @param int $financialTypeID | |
1100 | * @param array $lineItem | |
1101 | * | |
1102 | * @return void | |
1103 | */ | |
1104 | protected function addTotalsToLineBasedOnOverrideTotal(int $financialTypeID, array &$lineItem): void { | |
0a859bb8 | 1105 | $lineItem['tax_rate'] = $taxRate = $this->getTaxRate($financialTypeID); |
77274636 EM |
1106 | if ($taxRate) { |
1107 | // Total is tax inclusive. | |
1108 | $lineItem['tax_amount'] = ($taxRate / 100) * $this->getOverrideTotalAmount() / (1 + ($taxRate / 100)); | |
e66cf74b | 1109 | $lineItem['line_total'] = $this->getOverrideTotalAmount() - $lineItem['tax_amount']; |
77274636 EM |
1110 | } |
1111 | else { | |
e66cf74b DRJ |
1112 | $lineItem['line_total'] = $this->getOverrideTotalAmount(); |
1113 | } | |
1114 | if (!empty($lineItem['qty'])) { | |
1115 | $lineItem['unit_price'] = $lineItem['line_total'] / $lineItem['qty']; | |
1116 | } | |
1117 | else { | |
1118 | $lineItem['unit_price'] = $lineItem['line_total']; | |
77274636 EM |
1119 | } |
1120 | } | |
1121 | ||
4df23f2a EM |
1122 | /** |
1123 | * Get the line items from a template. | |
1124 | * | |
1125 | * @return \Civi\Api4\Generic\Result | |
1126 | * | |
1127 | * @throws \API_Exception | |
1128 | */ | |
1129 | protected function getLinesFromTemplateContribution(): array { | |
1130 | $lines = $this->getLinesForContribution(); | |
1131 | foreach ($lines as &$line) { | |
1132 | // The apiv4 insists on adding id - so let it get all the details | |
1133 | // and we will filter out those that are not part of a template here. | |
1134 | unset($line['id'], $line['contribution_id']); | |
1135 | } | |
1136 | return $lines; | |
1137 | } | |
1138 | ||
502822e7 EM |
1139 | /** |
1140 | * Get the constructed line items formatted for the v3 Order api. | |
1141 | * | |
1142 | * @return array | |
1143 | * | |
1144 | * @internal core tested code only. | |
1145 | * | |
1146 | * @throws \CiviCRM_API3_Exception | |
1147 | */ | |
1148 | public function getLineItemForV3OrderApi(): array { | |
1149 | $lineItems = []; | |
1150 | foreach ($this->getLineItems() as $key => $line) { | |
1151 | $lineItems[] = [ | |
1152 | 'line_item' => [$line['price_field_value_id'] => $line], | |
1153 | 'params' => $this->entityParameters[$key] ?? [], | |
1154 | ]; | |
1155 | } | |
1156 | return $lineItems; | |
1157 | } | |
1158 | ||
4df23f2a EM |
1159 | /** |
1160 | * @return array | |
1161 | * @throws \API_Exception | |
1162 | * @throws \Civi\API\Exception\UnauthorizedException | |
1163 | */ | |
1164 | protected function getLinesForContribution(): array { | |
1165 | return (array) LineItem::get(FALSE) | |
1166 | ->addWhere('contribution_id', '=', $this->getTemplateContributionID()) | |
1167 | ->setSelect([ | |
1168 | 'contribution_id', | |
1169 | 'entity_id', | |
1170 | 'entity_table', | |
1171 | 'price_field_id', | |
8193ed74 | 1172 | 'price_field_id.label', |
691345e9 | 1173 | 'price_field_id.price_set_id', |
4df23f2a EM |
1174 | 'price_field_value_id', |
1175 | 'financial_type_id', | |
1176 | 'label', | |
1177 | 'qty', | |
1178 | 'unit_price', | |
1179 | 'line_total', | |
1180 | 'tax_amount', | |
1181 | 'non_deductible_amount', | |
1182 | 'participant_count', | |
1183 | 'membership_num_terms', | |
1184 | ]) | |
1185 | ->execute(); | |
1186 | } | |
1187 | ||
ca1b238b EM |
1188 | /** |
1189 | * Get the default price set id for the given component. | |
1190 | * | |
1191 | * @param string $component | |
1192 | * | |
1193 | * @return int | |
1194 | * @throws \API_Exception | |
1195 | */ | |
1196 | protected function getDefaultPriceSetForComponent(string $component): int { | |
1197 | if (!isset($this->defaultPriceSets[$component])) { | |
1198 | $this->defaultPriceSets[$component] = PriceSet::get(FALSE) | |
1199 | ->addWhere('name', '=', ($component === 'membership' ? 'default_membership_type_amount' : 'default_contribution_amount')) | |
1200 | ->execute() | |
1201 | ->first()['id']; | |
1202 | } | |
1203 | return $this->defaultPriceSets[$component]; | |
1204 | } | |
1205 | ||
1206 | /** | |
1207 | * Fill in values for a default contribution line item. | |
1208 | * | |
1209 | * @param array $lineItem | |
1210 | * | |
1211 | * @throws \API_Exception | |
1212 | */ | |
1213 | protected function fillDefaultContributionLine(array &$lineItem): void { | |
1214 | $defaults = [ | |
1215 | 'qty' => 1, | |
1216 | 'price_field_id' => $this->getDefaultPriceFieldID(), | |
8193ed74 | 1217 | 'price_field_id.label' => $this->defaultPriceField['label'], |
60e5cf34 | 1218 | 'price_field_value_id' => $this->getDefaultPriceFieldValueID(), |
ca1b238b EM |
1219 | 'entity_table' => 'civicrm_contribution', |
1220 | 'unit_price' => $lineItem['line_total'], | |
1221 | 'label' => ts('Contribution Amount'), | |
1222 | ]; | |
1223 | $lineItem = array_merge($defaults, $lineItem); | |
1224 | } | |
1225 | ||
2408aa47 EM |
1226 | /** |
1227 | * Get a 'title' for the line item. | |
1228 | * | |
1229 | * This descriptor is used in message templates. It could conceivably | |
1230 | * by used elsewhere but if so determination would likely move to the api. | |
1231 | * | |
1232 | * @param array $lineItem | |
1233 | * | |
1234 | * @return string | |
1235 | */ | |
1236 | private function getLineItemTitle(array $lineItem): string { | |
1237 | // Title is used in output for workflow templates. | |
1238 | $htmlType = $this->getPriceFieldSpec($lineItem['price_field_id'])['html_type'] ?? NULL; | |
1239 | $lineItemTitle = (!$htmlType || $htmlType === 'Text') ? $lineItem['label'] : $this->getPriceFieldSpec($lineItem['price_field_id'])['label'] . ' - ' . $lineItem['label']; | |
1240 | if (!empty($lineItem['price_field_value_id'])) { | |
1241 | $description = $this->priceFieldValueMetadata[$lineItem['price_field_value_id']]['description'] ?? ''; | |
1242 | if ($description) { | |
1243 | $lineItemTitle .= ' ' . CRM_Utils_String::ellipsify($description, 30); | |
1244 | } | |
1245 | } | |
1246 | return $lineItemTitle; | |
1247 | } | |
1248 | ||
7aa78908 | 1249 | } |