Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate guess work in processPriceSet #20678

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions CRM/Contribute/BAO/Contribution.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,9 @@ public static function add(&$params) {
$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');
}

Expand Down Expand Up @@ -4401,9 +4403,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
126 changes: 126 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,107 @@ 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 {
// @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;
}

}
3 changes: 3 additions & 0 deletions CRM/Price/BAO/LineItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,9 @@ public static function processPriceSet($entityId, $lineItems, $contributionDetai
$line['entity_id'] = $entityId;
}
if (!empty($line['membership_type_id'])) {
if (($line['entity_table'] ?? '') !== 'civicrm_membership') {
CRM_Core_Error::deprecatedWarning('entity table should be already set');
}
$line['entity_table'] = 'civicrm_membership';
}
if (!empty($contributionDetails->id)) {
Expand Down
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