This changes the order api so that it is not necessary to figure out the details
of the default price set when using it to create memberships.
$params['tax_amount'] = $taxAmount;
$params['total_amount'] = $taxAmount + $lineTotal;
}
- if (isset($params['tax_amount']) && $params['tax_amount'] != $taxAmount && empty($params['skipLineItem'])) {
+ if (isset($params['tax_amount']) && empty($params['skipLineItem'])
+ && !CRM_Utils_Money::equals($params['tax_amount'], $taxAmount, ($params['currency'] ?? Civi::settings()->get('defaultCurrency')))
+ ) {
CRM_Core_Error::deprecatedWarning('passing in incorrect tax amounts is deprecated');
}
foreach ($params['line_items'] as &$lineItems) {
foreach ($lineItems['line_item'] as &$item) {
- if (empty($item['financial_type_id'])) {
- $item['financial_type_id'] = $params['financial_type_id'];
- }
$lineItemAmount += $item['line_total'] + ($item['tax_amount'] ?? 0.00);
}
}
*/
protected $overrideFinancialTypeID;
+ /**
+ * Financial type id to use for any lines where is is not provided.
+ *
+ * @var int
+ */
+ protected $defaultFinancialTypeID;
+
+ /**
+ * @return int
+ */
+ public function getDefaultFinancialTypeID(): int {
+ return $this->defaultFinancialTypeID;
+ }
+
+ /**
+ * Set the default financial type id to be used when the line has none.
+ *
+ * @param int|null $defaultFinancialTypeID
+ */
+ public function setDefaultFinancialTypeID(?int $defaultFinancialTypeID): void {
+ $this->defaultFinancialTypeID = $defaultFinancialTypeID;
+ }
+
/**
* Override for the total amount of the order.
*
}
}
+ /**
+ * Set the line item.
+ *
+ * This function augments the line item where possible. The calling code
+ * should not attempt to set taxes. This function allows minimal values
+ * to be passed for the default price sets - ie if only membership_type_id is
+ * specified the price_field_id and price_value_id will be determined.
+ *
+ * @param array $lineItem
+ * @param int|string $index
+ *
+ * @throws \API_Exception
+ * @internal tested core code usage only.
+ * @internal use in tested core code only.
+ *
+ */
+ public function setLineItem(array $lineItem, $index): void {
+ if (!empty($lineItem['price_field_id']) && !isset($this->priceSetID)) {
+ $this->setPriceSetIDFromSelectedField($lineItem['price_field_id']);
+ }
+ if (!isset($lineItem['financial_type_id'])) {
+ $lineItem['financial_type_id'] = $this->getDefaultFinancialTypeID();
+ }
+ if (!is_numeric($lineItem['financial_type_id'])) {
+ $lineItem['financial_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', $lineItem['financial_type_id']);
+ }
+ $lineItem['tax_amount'] = ($this->getTaxRate($lineItem['financial_type_id']) / 100) * $lineItem['line_total'];
+ if (!empty($lineItem['membership_type_id'])) {
+ $lineItem['entity_table'] = 'civicrm_membership';
+ if (empty($lineItem['price_field_id']) && empty($lineItem['price_field_value_id'])) {
+ // If only the membership type is passed in we use the default price field.
+ if (!isset($this->priceSetID)) {
+ $this->setPriceSetToDefault('membership');
+ }
+ $lineItem = $this->fillMembershipLine($lineItem);
+ }
+ }
+ $this->lineItems[$index] = $lineItem;
+ }
+
+ /**
+ * Set a value on a line item.
+ *
+ * @internal only use in core tested code.
+ *
+ * @param string $name
+ * @param mixed $value
+ * @param string|int $index
+ */
+ public function setLineItemValue(string $name, $value, $index): void {
+ $this->lineItems[$index][$name] = $value;
+ }
+
+ /**
+ * @param int|string $index
+ *
+ * @return string
+ */
+ public function getLineItemEntity($index):string {
+ // @todo - ensure entity_table is set in setLineItem, go back to enotices here.
+ return str_replace('civicrm_', '', ($this->lineItems[$index]['entity_table'] ?? 'contribution'));
+ }
+
+ /**
+ * Get the ordered line item.
+ *
+ * @param string|int $index
+ *
+ * @return array
+ */
+ public function getLineItem($index): array {
+ return $this->lineItems[$index];
+ }
+
+ /**
+ * Fills in additional data for the membership line.
+ *
+ * The minimum requirement is the membership_type_id and that priceSetID is set.
+ *
+ * @param array $lineItem
+ *
+ * @return array
+ */
+ protected function fillMembershipLine(array $lineItem): array {
+ $fields = $this->getPriceFieldsMetadata();
+ $field = reset($fields);
+ if (!isset($lineItem['price_field_value_id'])) {
+ foreach ($field['options'] as $option) {
+ if ((int) $option['membership_type_id'] === (int) $lineItem['membership_type_id']) {
+ $lineItem['price_field_id'] = $field['id'];
+ $lineItem['price_field_value_id'] = $option['id'];
+ $lineItem['qty'] = 1;
+ }
+ }
+ }
+ $option = $field['options'][$lineItem['price_field_value_id']];
+ $lineItem['unit_price'] = $lineItem['line_total'] ?? $option['amount'];
+ $lineItem['label'] = $lineItem['label'] ?? $option['label'];
+ $lineItem['field_title'] = $lineItem['field_title'] ?? $option['label'];
+ $lineItem['financial_type_id'] = $lineItem['financial_type_id'] ?: ($this->getDefaultFinancialTypeID() ?? $option['financial_type_id']);
+ return $lineItem;
+ }
+
}
$entity = NULL;
$entityIds = [];
$params['contribution_status_id'] = 'Pending';
- $priceSetID = NULL;
+ $order = new CRM_Financial_BAO_Order();
+ $order->setDefaultFinancialTypeID($params['financial_type_id'] ?? NULL);
if (!empty($params['line_items']) && is_array($params['line_items'])) {
CRM_Contribute_BAO_Contribution::checkLineItems($params);
- foreach ($params['line_items'] as $lineItems) {
- $entityParams = $lineItems['params'] ?? [];
- if (!empty($entityParams) && !empty($lineItems['line_item'])) {
- $item = reset($lineItems['line_item']);
- if (!empty($item['membership_type_id'])) {
- $entity = 'membership';
- }
- else {
- $entity = str_replace('civicrm_', '', $item['entity_table']);
- }
+ foreach ($params['line_items'] as $index => $lineItems) {
+ foreach ($lineItems['line_item'] as $innerIndex => $lineItem) {
+ $lineIndex = $index . '+' . $innerIndex;
+ $order->setLineItem($lineItem, $lineIndex);
}
+ $entityParams = $lineItems['params'] ?? [];
+ $entity = $order->getLineItemEntity($lineIndex);
+
if ($entityParams) {
$supportedEntity = TRUE;
switch ($entity) {
$entityResult = civicrm_api3($entity, 'create', $entityParams);
$params['contribution_mode'] = $entity;
$entityIds[] = $params[$entity . '_id'] = $entityResult['id'];
- foreach ($lineItems['line_item'] as &$items) {
- $items['entity_id'] = $entityResult['id'];
+ foreach ($lineItems['line_item'] as $innerIndex => $lineItem) {
+ $lineIndex = $index . '+' . $innerIndex;
+ $order->setLineItemValue('entity_id', $entityResult['id'], $lineIndex);
}
}
}
-
- if (empty($priceSetID)) {
- $item = reset($lineItems['line_item']);
- $priceSetID = (int) civicrm_api3('PriceField', 'getvalue', [
- 'return' => 'price_set_id',
- 'id' => $item['price_field_id'],
- ]);
- $params['line_item'][$priceSetID] = [];
- }
- $params['line_item'][$priceSetID] = array_merge($params['line_item'][$priceSetID], $lineItems['line_item']);
}
+ $priceSetID = $order->getPriceSetID();
+ $params['line_item'][$priceSetID] = $order->getLineItems();
}
+ else {
+ $order->setPriceSetToDefault('contribution');
+ }
+
$contributionParams = $params;
// If this is nested we need to set sequential to 0 as sequential handling is done
// in create_success & id will be miscalculated...
}
$contribution = civicrm_api3('Contribution', 'create', $contributionParams);
- $contribution['values'][$contribution['id']]['line_item'] = $params['line_item'][$priceSetID] ?? [];
+ $contribution['values'][$contribution['id']]['line_item'] = $order->getLineItems();
// add payments
if ($entity && !empty($contribution['id'])) {
$e->getMessage()
);
}
-
- $this->assertEquals(3, $params['line_items'][0]['line_item'][0]['financial_type_id']);
$params['total_amount'] = 300;
CRM_Contribute_BAO_Contribution::checkLineItems($params);
'payment_processor_id' => $this->paymentProcessorID,
], $generalParams, $recurParams));
- $this->callAPISuccess('membership', 'create', [
- 'contact_id' => $newContact['id'],
- 'contribution_recur_id' => $contributionRecur['id'],
- 'financial_type_id' => 'Member Dues',
- 'membership_type_id' => $membershipType['id'],
- 'num_terms' => 1,
- 'skipLineItem' => TRUE,
- ]);
-
- CRM_Price_BAO_LineItem::getLineItemArray($this->_params, NULL, 'membership', $membershipType['id']);
- $originalContribution = $this->callAPISuccess('contribution', 'create', array_merge(
+ $originalContribution = $this->callAPISuccess('Order', 'create', array_merge(
$this->_params,
[
'contact_id' => $newContact['id'],
'contribution_recur_id' => $contributionRecur['id'],
'financial_type_id' => 'Member Dues',
- 'contribution_status_id' => 1,
+ 'api.Payment.create' => ['total_amount' => 100, 'payment_instrument_id' => 'Credit card'],
'invoice_id' => 2345,
+ 'line_items' => [
+ [
+ 'line_item' => [
+ [
+ 'membership_type_id' => $membershipType['id'],
+ 'financial_type_id' => 'Member Dues',
+ 'line_total' => $generalParams['total_amount'] ?? 100,
+ ],
+ ],
+ 'params' => [
+ 'contact_id' => $newContact['id'],
+ 'contribution_recur_id' => $contributionRecur['id'],
+ 'membership_type_id' => $membershipType['id'],
+ 'num_terms' => 1,
+ ],
+ ],
+ ],
], $generalParams)
);
$lineItem = $this->callAPISuccess('LineItem', 'getsingle', []);