public static function checkLineItems(&$params) {
$totalAmount = CRM_Utils_Array::value('total_amount', $params);
$lineItemAmount = 0;
+
foreach ($params['line_items'] as &$lineItems) {
foreach ($lineItems['line_item'] as &$item) {
if (empty($item['financial_type_id'])) {
$lineItemAmount += $item['line_total'];
}
}
+
if (!isset($totalAmount)) {
$params['total_amount'] = $lineItemAmount;
}
- elseif ($totalAmount != $lineItemAmount) {
- throw new API_Exception("Line item total doesn't match with total amount.");
+ else {
+ $currency = CRM_Utils_Array::value('currency', $params, '');
+
+ if (empty($currency)) {
+ $currency = CRM_Core_Config::singleton()->defaultCurrency;
+ }
+
+ if (!CRM_Utils_Money::equals($totalAmount, $lineItemAmount, $currency)) {
+ throw new CRM_Contribute_Exception_CheckLineItemsException();
+ }
}
}
if (!empty($params['financial_account_id'])) {
return $params['financial_account_id'];
}
+
$contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus($params['contribution_status_id'], 'name');
$preferredAccountsRelationships = array(
'Refunded' => 'Credit/Contra Revenue Account is',
'Chargeback' => 'Chargeback Account is',
);
+
if (in_array($contributionStatus, array_keys($preferredAccountsRelationships))) {
$financialTypeID = !empty($params['financial_type_id']) ? $params['financial_type_id'] : $params['prevContribution']->financial_type_id;
return CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship(
$preferredAccountsRelationships[$contributionStatus]
);
}
+
return $default;
}
--- /dev/null
+<?php\r
+\r
+/**\r
+ * Class CRM_Contribute_Exception_CheckLineItemsException\r
+ */\r
+class CRM_Contribute_Exception_CheckLineItemsException extends API_Exception {\r
+ const LINE_ITEM_DIFFERRING_TOTAL_EXCEPTON_MSG = "Line item total doesn't match with total amount.";\r
+\r
+ public function __construct($message = self::LINE_ITEM_DIFFERRING_TOTAL_EXCEPTON_MSG, $error_code = 0, array $extraParams = [], $previous = NULL) {\r
+ parent::__construct($message, $error_code, $extraParams, $previous);\r
+ }\r
+\r
+}\r
}
}
+ /**
+ * Tests if two currency values are equal, taking into account the currency's
+ * precision, so that if the difference between the two values is less than
+ * one more order of magnitude for the precision, then the values are
+ * considered as equal. So, if the currency has precision of 2 decimal
+ * points, a difference of more than 0.001 will cause the values to be
+ * considered as different. Anything less than 0.001 will be considered as
+ * equal.
+ *
+ * Eg.
+ *
+ * 1.2312 == 1.2319 with a currency precision of 2 decimal points
+ * 1.2310 != 1.2320 with a currency precision of 2 decimal points
+ * 1.3000 != 1.2000 with a currency precision of 2 decimal points
+ *
+ * @param $value1
+ * @param $value2
+ * @param $currency
+ *
+ * @return bool
+ */
+ public static function equals($value1, $value2, $currency) {
+ $precision = 1 / pow(10, self::getCurrencyPrecision($currency) + 1);
+ $difference = self::subtractCurrencies($value1, $value2, $currency);
+
+ if (abs($difference) > $precision) {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
}
),
),
);
+
try {
CRM_Contribute_BAO_Contribution::checkLineItems($params);
$this->fail("Missed expected exception");
}
- catch (Exception $e) {
- $this->assertEquals("Line item total doesn't match with total amount.", $e->getMessage());
+ catch (CRM_Contribute_Exception_CheckLineItemsException $e) {
+ $this->assertEquals(
+ CRM_Contribute_Exception_CheckLineItemsException::LINE_ITEM_DIFFERRING_TOTAL_EXCEPTON_MSG,
+ $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);
}
+ /**
+ * Tests CRM_Contribute_BAO_Contribution::checkLineItems() method works with
+ * floating point values.
+ */
+ public function testCheckLineItemsWithFloatingPointValues() {
+ $params = array(
+ 'contact_id' => 202,
+ 'receive_date' => date('Y-m-d'),
+ 'total_amount' => 16.67,
+ 'financial_type_id' => 3,
+ 'line_items' => array(
+ array(
+ 'line_item' => array(
+ array(
+ 'entity_table' => 'civicrm_contribution',
+ 'price_field_id' => 8,
+ 'price_field_value_id' => 16,
+ 'label' => 'test 1',
+ 'qty' => 1,
+ 'unit_price' => 14.85,
+ 'line_total' => 14.85,
+ ),
+ array(
+ 'entity_table' => 'civicrm_contribution',
+ 'price_field_id' => 8,
+ 'price_field_value_id' => 17,
+ 'label' => 'Test 2',
+ 'qty' => 1,
+ 'unit_price' => 1.66,
+ 'line_total' => 1.66,
+ 'financial_type_id' => 1,
+ ),
+ array(
+ 'entity_table' => 'civicrm_contribution',
+ 'price_field_id' => 8,
+ 'price_field_value_id' => 17,
+ 'label' => 'Test 2',
+ 'qty' => 1,
+ 'unit_price' => 0.16,
+ 'line_total' => 0.16,
+ 'financial_type_id' => 1,
+ ),
+ ),
+ 'params' => array(),
+ ),
+ ),
+ );
+
+ $foundException = FALSE;
+
+ try {
+ CRM_Contribute_BAO_Contribution::checkLineItems($params);
+ }
+ catch (CRM_Contribute_Exception_CheckLineItemsException $e) {
+ $foundException = TRUE;
+ }
+
+ $this->assertFalse($foundException);
+ }
+
/**
* Test activity amount updation.
*/
$this->assertEquals($expectedResult, CRM_Utils_Money::subtractCurrencies($leftOp, $rightOp, $currency));
}
+ public function testEquals() {
+ $testValue = 0.01;
+
+ for ($i = 0; $i <= 10; $i++) {
+ $equalValues = CRM_Utils_Money::equals($testValue, $testValue + ($i * 0.0001), 'USD');
+ $this->assertTrue($equalValues);
+ }
+
+ $this->assertFalse(CRM_Utils_Money::equals($testValue, $testValue + 0.001000000001, 'USD'));
+ }
+
/**
* @return array
*/