Jump to content
beemertec

customer_discount_v1.1 rounding error

Recommended Posts

I installed customer_discount_v1.1 in my heavily modded cart. It was a simple install and works great with one exception. When it calulates the discount, it always rounds up to the nearest penny, but the total is apparently rounding properly. This causes an error when I import to QB.

 

As an example, I have an order:

Sub-Total: $948.70 FedEx (Ground Home Delivery

---Approx. delivery time THREE Days): $75.86 VIP Discount: $142.31 Total: $882.26

 

The actual discount is $948.70 x 15% = $142.305. As you can see, the discount module rounded up instead of down. $948.70 + $75.86 -$142.31 =$882.25 not $882.26.

 

Although this problem could theoretically be fixed in a couple of ways, my preference would be to modify the ot_customer_discount.php file to correct the rounding error.

 

Unfortunatley, I do not know what I need to change to correct it.

 

Here is the code:

<?php
/*
$Id: ot_customer_discount.php,v 1.1 2002/07/22 07:36:01 amk Exp $
osCommerce, Open Source E-Commerce Solutions
http://www.oscommerce.com
Copyright (c) 2002 osCommerce
Released under the GNU General Public License
*/
class ot_customer_discount {
var $title, $output;
function ot_customer_discount() {
 $this->code = 'ot_customer_discount';
 $this->title = MODULE_CUSTOMER_DISCOUNT_TITLE;
 $this->description = MODULE_CUSTOMER_DISCOUNT_DESCRIPTION;
 $this->enabled = MODULE_CUSTOMER_DISCOUNT_STATUS;
 $this->sort_order = MODULE_CUSTOMER_DISCOUNT_SORT_ORDER;
 $this->include_shipping = MODULE_CUSTOMER_DISCOUNT_INC_SHIPPING;
 $this->include_tax = MODULE_CUSTOMER_DISCOUNT_INC_TAX;
 $this->calculate_tax = MODULE_CUSTOMER_DISCOUNT_CALC_TAX;
 $this->output = array();
}
 function process()
 {
		 global $order, $currencies;	
		 if ($this->calculate_tax == 'true') {
			 $tod_amount = $this->calculate_discount($order->info['tax']);
		 }
		 $od_amount = $this->calculate_discount($this->get_order_total());
		 if ($od_amount>0)
		 {
				 $this->deduction = $od_amount+$tod_amount;
				 $this->output[] = array('title' => $this->title . ':',
										 'text' => '<b>' . $currencies->format($od_amount) . '</b>',
										 'value' => $od_amount);
				 $order->info['total'] = $order->info['total'] - $od_amount - $tod_amount;
				 $order->info['tax'] = $order->info['tax'] - $tod_amount;
		 }
 }
function calculate_discount($amount) {
global $customer_id;
$od_amount=0;
$query = tep_db_query("select customer_discount from " . TABLE_CUSTOMERS . " where customers_id = '" . $customer_id . "'");
$query_result = tep_db_fetch_array($query);
if ($query_result['customer_discount'] > 0) {
 $od_amount = (round($amount*10)/10)*($query_result['customer_discount']/100);
}
return $od_amount;
}

function get_order_total() {
global $order;
$order_total = $order->info['total'];
if ($this->include_tax == 'false') $order_total=$order_total-$order->info['tax'];
if ($this->include_shipping == 'false') $order_total=$order_total-$order->info['shipping_cost'];
return $order_total;
}

function check() {
 if (!isset($this->check)) {
 $check_query = tep_db_query("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_CUSTOMER_DISCOUNT_STATUS'");
 $this->check = tep_db_num_rows($check_query);
 }
 return $this->check;
}
function keys() {
 return array('MODULE_CUSTOMER_DISCOUNT_STATUS', 'MODULE_CUSTOMER_DISCOUNT_SORT_ORDER', 'MODULE_CUSTOMER_DISCOUNT_INC_SHIPPING', 'MODULE_CUSTOMER_DISCOUNT_INC_TAX', 'MODULE_CUSTOMER_DISCOUNT_CALC_TAX');
}
function install() {
 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Display Total', 'MODULE_CUSTOMER_DISCOUNT_STATUS', 'true', 'Do you want to enable the Customer specific order discount module?', '6', '1','tep_cfg_select_option(array(\'true\', \'false\'), ', now())");
 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Sort Order', 'MODULE_CUSTOMER_DISCOUNT_SORT_ORDER', '999', 'Sort order of display.', '6', '2', now())");
 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function ,date_added) values ('Include Shipping', 'MODULE_CUSTOMER_DISCOUNT_INC_SHIPPING', 'true', 'Include Shipping in calculation', '6', '5', 'tep_cfg_select_option(array(\'true\', \'false\'), ', now())");
 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function ,date_added) values ('Include Tax', 'MODULE_CUSTOMER_DISCOUNT_INC_TAX', 'true', 'Include Tax in calculation.', '6', '6','tep_cfg_select_option(array(\'true\', \'false\'), ', now())");
 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function ,date_added) values ('Calculate Tax', 'MODULE_CUSTOMER_DISCOUNT_CALC_TAX', 'false', 'Re-calculate Tax on discounted amount.', '6', '5','tep_cfg_select_option(array(\'true\', \'false\'), ', now())");
}
function remove() {
 $keys = '';
 $keys_array = $this->keys();
 for ($i=0; $i<sizeof($keys_array); $i++) {
 $keys .= "'" . $keys_array[$i] . "',";
 }
 $keys = substr($keys, 0, -1);
 tep_db_query("delete from " . TABLE_CONFIGURATION . " where configuration_key in (" . $keys . ")");
}
}
?>

 

Any help would be much appreciated.

Share this post


Link to post
Share on other sites

osC seems to have this problem whenever it applies a discount or tax that is a percentage of the base amount. In this case, I surmise that $od_amount is 142.3050. When this "half cent" is subtracted from the base, it will produce 882.2550, which rounds to 882.26 (as you saw), while the presentation of the discount amount itself is also rounded up to 143.31, leaving the discounted amount at 882.26. While that's technically true, it does not match the math on the rounded amounts. That much you've figured out.

 

I suspect that the best approach would be to round the discount amount ($od_amount) to the same number of decimal digits as the currency rounding routine uses (the number is in the currency description table). It would be done between

$od_amount = $this->calculate_discount($this->get_order_total());

and

if ($od_amount>0)

giving you $od_amount = 142.31 instead of 142.3050. Then the downstream math should work out.

 

I don't have the code available at the moment, but if you look in currencies->format you will see a call to something like tep_round() with the currency decimal digits specified somewhere. In your case, as long as you're using only dollars, you could take $currences->format($od_amount), and strip off the '$' to get a new $od_amount, but that's not safe in general. If would be better to copy that code to do the rounding, and thus get $od_amount with 2 decimal places (or whatever your currency demands) before you do anything else. Plus, if the discount rounds down to 0, it will be treated as 0.

Share this post


Link to post
Share on other sites

Thanks MrPhil,

 

I looked around my other ot_ files based on what you said.

 

After

function calculate_discount($amount) {
   global $customer_id;
   $od_amount=0;
   $query = tep_db_query("select customer_discount from " . TABLE_CUSTOMERS . " where customers_id = '" . $customer_id . "'");
   $query_result = tep_db_fetch_array($query);
   if ($query_result['customer_discount'] > 0) {
  $od_amount = (round($amount*10)/10)*($query_result['customer_discount']/100);
   }

I replaced

return $od_amount;

with

return tep_round($od_amount,2);

 

That seems to have fixed it. I will check it over the course of a few days. If it works, I will update the add-on.

Share this post


Link to post
Share on other sites

If you want to do the rounding in the calculate_discount method, rather than after it, that is certainly permissible.

 

Remember that 2 decimal places for currency, while common, may not be universal. For your own store, if you will only use dollars, hard coding it to 2 places will be OK, but for others using different currencies, you should use the "number of decimal places" setting in the currencies table.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×