Fix Floating Point Precision Comparison Exception on Order Creation
Overview
When adding Orders, a check is done by CiviCRM core when the contribution is being created to make sure the sum of line item totals is equal to the contribution total amount. This comparison, however, is done comparing floating point values, which may fail falsely, under some circumstances.
This is the line where the comparison is done:
https://github.com/civicrm/civicrm-core/blob/master/CRM/Contribute/BAO/Contribution.php#L5011
This problem is documented in PHP official documentation, and is actually related to the way floating point values are modeled in programming languages in general.
http://php.net/manual/en/language.types.float.php
http://floating-point-gui.de/basic/
This has to be taken into account to make sure the comparison doesn't fail when it shouldn't.
How it Works Currently
- Use API to try to create Order with given sample data.
- Exception is thrown, saying "Line item total doesn't match with total amount".
How it Should Work
- Use API to try to create Order with given sample data.
- Order is created without throwing exception.
Sample Data
data ={
'contact_id': 1051,
'payment_instrument_id': 6,
'line_items': [{
'line_item': {
'0': {
'price_field_value_id': 56,
'price_field_id': 33,
'entity_id': 646,
'tax_amount': 0,
'line_total': 14.85,
'label': 4,
'entity_table': 'civicrm_membership',
'unit_price': 14.85,
'qty': 1}}}, {
'line_item': {
'1': {
'price_field_value_id': 55,
'price_field_id': 32,
'entity_id': 648,
'tax_amount': 0,
'line_total': 1.66,
'label': 50,
'entity_table': 'civicrm_membership',
'unit_price': 1.66,
'qty': 1}}}, {
'line_item': {
'2': {
'price_field_value_id': 49,
'price_field_id': 26,
'entity_id': 647,
'tax_amount': 0,
'line_total': 0.16,
'label': 47,
'entity_table': 'civicrm_membership',
'unit_price': 0.16,
'qty': 1}}}],
'total_amount': 16.67,
'financial_type_id': 2,
'fee_amount': 0,
'payment_processor_id': 5,
'receive_date': '2017-10-16',
'contribution_status_id': 1
}
Failing API Call Example
Calling API using cv:
echo '{"contact_id":45,"payment_instrument_id":6,"line_items":[{"line_item":{"6":{"price_field_value_id":52,"price_field_id":29,"tax_amount":0,"line_total":14.85,"financial_type_id":2,"qty":1,"entity_table":"civicrm_membership","unit_price":14.85,"label":5}}},{"line_item":{"7":{"price_field_value_id":53,"price_field_id":30,"tax_amount":0,"line_total":1.66,"financial_type_id":2,"qty":1,"entity_table":"civicrm_membership","unit_price":1.66,"label":49}}},{"line_item":{"8":{"price_field_value_id":49,"price_field_id":26,"tax_amount":0,"line_total":0.16,"financial_type_id":2,"qty":1,"entity_table":"civicrm_membership","unit_price":0.16,"label":47}}}],"tax_amount":0,"total_amount":16.67,"contribution_recur_id":733,"financial_type_id":2,"fee_amount":0,"payment_processor_id":5,"receive_date":"2017-12-15","contribution_status_id":1}' | cv api Order.create --in=json