Skip to content

Commit

Permalink
Make Order api easier to use for default price set
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
eileenmcnaughton committed Jun 22, 2021
1 parent 1acdec4 commit 45a2835
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 41 deletions.
3 changes: 0 additions & 3 deletions CRM/Contribute/BAO/Contribution.php
Original file line number Diff line number Diff line change
Expand Up @@ -4401,9 +4401,6 @@ public static function checkLineItems(&$params) {

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);
}
}
Expand Down
125 changes: 125 additions & 0 deletions CRM/Financial/BAO/Order.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,29 @@ class CRM_Financial_BAO_Order {
*/
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.
*
Expand Down Expand Up @@ -634,4 +657,106 @@ protected function setPriceSetIDFromSelectedField($fieldID): void {
}
}

/**
* 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 {
return str_replace('civicrm_', '', $this->lineItems[$index]['entity_table']);
}

/**
* 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;
}

}
43 changes: 19 additions & 24 deletions api/v3/Order.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,20 @@ function civicrm_api3_order_create(array $params): array {
$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) {
Expand All @@ -118,23 +116,20 @@ function civicrm_api3_order_create(array $params): array {
$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...
Expand All @@ -149,7 +144,7 @@ function civicrm_api3_order_create(array $params): array {
}

$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'])) {
Expand Down
2 changes: 0 additions & 2 deletions tests/phpunit/CRM/Contribute/BAO/ContributionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -820,8 +820,6 @@ public function testcheckLineItems() {
$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);
Expand Down
31 changes: 19 additions & 12 deletions tests/phpunit/api/v3/ContributionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4345,24 +4345,31 @@ protected function setUpAutoRenewMembership($generalParams = [], $recurParams =
'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', []);
Expand Down

0 comments on commit 45a2835

Please sign in to comment.