X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=api%2Fv3%2FOrder.php;h=e0529d385c37c31666e0218e8f86dc48108ba6ac;hb=880600455bd157486e6324bb8b581fad7bd0a2b8;hp=5a9ac4855318aaee699c6451af17f5ae8afa66cd;hpb=2aa65a0f0a19bedf95829fa6582e39465717f48b;p=civicrm-core.git diff --git a/api/v3/Order.php b/api/v3/Order.php index 5a9ac48553..e0529d385c 100644 --- a/api/v3/Order.php +++ b/api/v3/Order.php @@ -16,6 +16,8 @@ * @package CiviCRM_APIv3 */ +use Civi\Api4\Membership; + /** * Retrieve a set of Order. * @@ -73,62 +75,91 @@ function _civicrm_api3_order_get_spec(array &$params) { */ function civicrm_api3_order_create(array $params): array { civicrm_api3_verify_one_mandatory($params, NULL, ['line_items', 'total_amount']); - + if (empty($params['skipCleanMoney'])) { + // We have to do this for v3 api - sadly. For v4 it will be no more. + foreach (['total_amount', 'net_amount', 'fee_amount', 'non_deductible_amount'] as $field) { + if (isset($params[$field])) { + $params[$field] = CRM_Utils_Rule::cleanMoney($params[$field]); + } + } + $params['skipCleanMoney'] = TRUE; + } $params['contribution_status_id'] = 'Pending'; $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 $index => $lineItems) { + if (!empty($lineItems['params'])) { + $order->setEntityParameters($lineItems['params'], $index); + } foreach ($lineItems['line_item'] as $innerIndex => $lineItem) { $lineIndex = $index . '+' . $innerIndex; $order->setLineItem($lineItem, $lineIndex); + $order->addLineItemToEntityParameters($lineIndex, $index); } + } + } + else { + $order->setPriceSetToDefault('contribution'); + $order->setLineItem([ + // Historically total_amount in this case could be tax + // inclusive if tax is also supplied. + // This is inconsistent with the contribution api.... + 'line_total' => ((float) $params['total_amount'] - (float) ($params['tax_amount'] ?? 0)), + 'financial_type_id' => (int) $params['financial_type_id'], + ], 0); + } + // Only check the amount if line items are set because that is what we have historically + // done and total amount is historically only inclusive of tax_amount IF + // tax amount is also passed in it seems + if (isset($params['total_amount']) && !empty($params['line_items'])) { + $currency = $params['currency'] ?? CRM_Core_Config::singleton()->defaultCurrency; + if (!CRM_Utils_Money::equals($params['total_amount'], $order->getTotalAmount(), $currency)) { + throw new CRM_Contribute_Exception_CheckLineItemsException(); + } + } + $params['total_amount'] = $order->getTotalAmount(); - $entityParams = $lineItems['params'] ?? []; - $entity = $order->getLineItemEntity($lineIndex); - - if ($entityParams) { - $supportedEntity = TRUE; - switch ($entity) { - case 'participant': - if (isset($entityParams['participant_status_id']) - && (!CRM_Event_BAO_ParticipantStatusType::getIsValidStatusForClass($entityParams['participant_status_id'], 'Pending'))) { - throw new CiviCRM_API3_Exception('Creating a participant via the Order API with a non "pending" status is not supported'); - } - $entityParams['participant_status_id'] = $entityParams['participant_status_id'] ?? 'Pending from incomplete transaction'; - $entityParams['status_id'] = $entityParams['participant_status_id']; - $params['contribution_mode'] = 'participant'; - break; + foreach ($order->getEntitiesToCreate() as $entityParams) { + if ($entityParams['entity'] === 'participant') { + if (isset($entityParams['participant_status_id']) + && (!CRM_Event_BAO_ParticipantStatusType::getIsValidStatusForClass($entityParams['participant_status_id'], 'Pending'))) { + throw new CiviCRM_API3_Exception('Creating a participant via the Order API with a non "pending" status is not supported'); + } + $entityParams['participant_status_id'] = $entityParams['participant_status_id'] ?? 'Pending from incomplete transaction'; + $entityParams['status_id'] = $entityParams['participant_status_id']; + $entityParams['skipLineItem'] = TRUE; + $entityResult = civicrm_api3('Participant', 'create', $entityParams); + // @todo - once membership is cleaned up & financial validation tests are extended + // we can look at removing this - some weird handling in removeFinancialAccounts + $params['contribution_mode'] = 'participant'; + $params['participant_id'] = $entityResult['id']; + foreach ($entityParams['line_references'] as $lineIndex) { + $order->setLineItemValue('entity_id', $entityResult['id'], $lineIndex); + } + } - case 'membership': - $entityParams['status_id'] = 'Pending'; - break; + if ($entityParams['entity'] === 'membership') { + if (empty($entityParams['id'])) { + $entityParams['status_id:name'] = 'Pending'; + } + if (!empty($params['contribution_recur_id'])) { + $entityParams['contribution_recur_id'] = $params['contribution_recur_id']; + } + // At this stage we need to get this passed through. + $entityParams['version'] = 4; + _order_create_wrangle_membership_params($entityParams); - default: - // Don't create any related entities. We might want to support eg. Pledge one day? - $supportedEntity = FALSE; - break; - } - if ($supportedEntity) { - $entityParams['skipLineItem'] = TRUE; - $entityResult = civicrm_api3($entity, 'create', $entityParams); - $params[$entity . '_id'] = $entityResult['id']; - foreach ($lineItems['line_item'] as $innerIndex => $lineItem) { - $lineIndex = $index . '+' . $innerIndex; - $order->setLineItemValue('entity_id', $entityResult['id'], $lineIndex); - } + $membershipID = Membership::save($params['check_permissions'] ?? FALSE)->setRecords([$entityParams])->execute()->first()['id']; + foreach ($entityParams['line_references'] as $lineIndex) { + $order->setLineItemValue('entity_id', $membershipID, $lineIndex); } } - } - $priceSetID = $order->getPriceSetID(); - $params['line_item'][$priceSetID] = $order->getLineItems(); - } - else { - $order->setPriceSetToDefault('contribution'); } + $params['line_item'][$order->getPriceSetID()] = $order->getLineItems(); + $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... @@ -143,7 +174,7 @@ function civicrm_api3_order_create(array $params): array { } $contribution = civicrm_api3('Contribution', 'create', $contributionParams); - $contribution['values'][$contribution['id']]['line_item'] = $order->getLineItems(); + $contribution['values'][$contribution['id']]['line_item'] = array_values($order->getLineItems()); return civicrm_api3_create_success($contribution['values'] ?? [], $params, 'Order', 'create'); } @@ -260,3 +291,29 @@ function _civicrm_api3_order_delete_spec(array &$params) { ]; $params['id']['api.aliases'] = ['contribution_id']; } + +/** + * Handle possibility of v3 style params. + * + * We used to call v3 Membership.create. Now we call v4. + * This converts membership input parameters. + * + * @param array $membershipParams + * + * @throws \API_Exception + */ +function _order_create_wrangle_membership_params(array &$membershipParams) { + $fields = Membership::getFields(FALSE)->execute()->indexBy('name'); + foreach ($fields as $fieldName => $field) { + $customFieldName = 'custom_' . ($field['custom_field_id'] ?? NULL); + if ($field['type'] === ['Custom'] && isset($membershipParams[$customFieldName])) { + $membershipParams[$field['custom_group'] . '.' . $field['custom_field']] = $membershipParams[$customFieldName]; + unset($membershipParams[$customFieldName]); + } + + if (!empty($membershipParams[$fieldName]) && $field['data_type'] === 'Integer' && !is_numeric($membershipParams[$fieldName])) { + $membershipParams[$field['name'] . ':name'] = $membershipParams[$fieldName]; + unset($membershipParams[$field['name']]); + } + } +}