Fix rounding issues in reports (#1336)

This commit is contained in:
FrancescoUK
2017-07-02 15:32:06 +01:00
parent 005193e3e0
commit 5ea0324bc0
4 changed files with 76 additions and 67 deletions

View File

@@ -17,23 +17,34 @@ function current_language()
function currency_side()
{
$config = get_instance()->config;
$fmt = new \NumberFormatter($config->item('number_locale'), \NumberFormatter::CURRENCY);
$fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $config->item('currency_symbol'));
return !preg_match('/^¤/', $fmt->getPattern());
}
function quantity_decimals()
{
$config = get_instance()->config;
return $config->item('quantity_decimals') ? $config->item('quantity_decimals') : 0;
}
function totals_decimals()
{
$config = get_instance()->config;
return $config->item('currency_decimals') ? $config->item('currency_decimals') : 0;
}
function tax_decimals()
{
$config = get_instance()->config;
return $config->item('tax_decimals') ? $config->item('tax_decimals') : 0;
}
function to_currency($number)
{
return to_decimals($number, 'currency_decimals', \NumberFormatter::CURRENCY);
@@ -74,11 +85,12 @@ function to_decimals($number, $decimals, $type=\NumberFormatter::DECIMAL)
$fmt = new \NumberFormatter($config->item('number_locale'), $type);
$fmt->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $config->item($decimals));
$fmt->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $config->item($decimals));
if (empty($config->item('thousands_separator')))
if(empty($config->item('thousands_separator')))
{
$fmt->setAttribute(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '');
}
$fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $config->item('currency_symbol'));
return $fmt->format($number);
}
@@ -96,6 +108,7 @@ function parse_decimals($number)
{
$fmt->setAttribute(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '');
}
return $fmt->parse($number);
}

View File

@@ -328,7 +328,7 @@ class Sale_lib
}
/*
/**
* Returns 'subtotal', 'total', 'cash_total', 'payment_total', 'amount_due', 'cash_amount_due', 'paid_in_full'
* 'subtotal', 'discounted_subtotal', 'tax_exclusive_subtotal'
*/
@@ -359,7 +359,7 @@ class Sale_lib
}
else
{
$subtotal =bcadd($subtotal, $extended_discounted_amount);
$subtotal = bcadd($subtotal, $extended_discounted_amount);
}
}
@@ -367,7 +367,7 @@ class Sale_lib
$totals['total_discount'] = $total_discount;
$totals['subtotal'] = $subtotal;
if ($this->CI->config->item('tax_included'))
if($this->CI->config->item('tax_included'))
{
$totals['total'] = $total;
}
@@ -1137,6 +1137,7 @@ class Sale_lib
return $discounted_extended_amount;
}
public function get_item_total($quantity, $price, $discount_percentage, $include_discount = FALSE)
{
$total = bcmul($quantity, $price);

View File

@@ -30,23 +30,22 @@ class Sale extends CI_Model
)'
);
$decimals = totals_decimals();
$sale_price = 'sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100)';
$tax = 'ROUND(IFNULL(SUM(sales_items_taxes.tax), 0), ' . $decimals . ')';
if($this->config->item('tax_included'))
{
$sale_total = 'SUM(' . $sale_price . ')';
$sale_subtotal = 'SUM(' . $sale_price . ' - sales_items_taxes.tax)';
$sale_total = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ')';
$sale_subtotal = $sale_total . ' - ' . $tax;
}
else
{
$sale_total = 'SUM(' . $sale_price . ' + sales_items_taxes.tax)';
$sale_subtotal = 'SUM(' . $sale_price . ')';
$sale_subtotal = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ')';
$sale_total = $sale_subtotal . ' + ' . $tax;
}
$sale_cost = 'SUM(sales_items.item_cost_price * sales_items.quantity_purchased)';
$decimals = totals_decimals();
// create a temporary table to contain all the sum of taxes per sale item
$this->db->query('CREATE TEMPORARY TABLE IF NOT EXISTS ' . $this->db->dbprefix('sales_items_taxes_temp') .
'(
@@ -80,9 +79,9 @@ class Sale extends CI_Model
MAX(customer_p.email) AS email,
MAX(customer_p.comments) AS comments,
' . "
IFNULL(ROUND($sale_total, $decimals), ROUND($sale_subtotal, $decimals)) AS amount_due,
IFNULL($sale_total, $sale_subtotal) AS amount_due,
MAX(payments.sale_payment_amount) AS amount_tendered,
(MAX(payments.sale_payment_amount) - IFNULL(ROUND($sale_total, $decimals), ROUND($sale_subtotal, $decimals))) AS change_due,
(MAX(payments.sale_payment_amount) - IFNULL($sale_total, $sale_subtotal)) AS change_due,
" . '
MAX(payments.payment_type) AS payment_type
');
@@ -144,23 +143,23 @@ class Sale extends CI_Model
)'
);
$decimals = totals_decimals();
$sale_price = 'sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100)';
$sale_cost = 'ROUND(SUM(sales_items.item_cost_price * sales_items.quantity_purchased), ' . $decimals . ')';
$tax = 'ROUND(IFNULL(SUM(sales_items_taxes.tax), 0), ' . $decimals . ')';
if($this->config->item('tax_included'))
{
$sale_total = 'SUM(' . $sale_price . ')';
$sale_subtotal = 'SUM(' . $sale_price . ' - sales_items_taxes.tax)';
$sale_total = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ')';
$sale_subtotal = $sale_total . ' - ' . $tax;
}
else
{
$sale_total = 'SUM(' . $sale_price . ' + sales_items_taxes.tax)';
$sale_subtotal = 'SUM(' . $sale_price . ')';
$sale_subtotal = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ')';
$sale_total = $sale_subtotal . ' + ' . $tax;
}
$sale_cost = 'SUM(sales_items.item_cost_price * sales_items.quantity_purchased)';
$decimals = totals_decimals();
// create a temporary table to contain all the sum of taxes per sale item
$this->db->query('CREATE TEMPORARY TABLE IF NOT EXISTS ' . $this->db->dbprefix('sales_items_taxes_temp') .
' (INDEX(sale_id), INDEX(item_id))
@@ -189,14 +188,14 @@ class Sale extends CI_Model
MAX(CONCAT(customer_p.first_name, " ", customer_p.last_name)) AS customer_name,
MAX(customer.company_name) AS company_name,
' . "
IFNULL(ROUND($sale_subtotal, $decimals), ROUND($sale_total - IFNULL(SUM(sales_items_taxes.tax), 0), $decimals)) AS subtotal,
IFNULL(ROUND(SUM(sales_items_taxes.tax), $decimals), 0) AS tax,
IFNULL(ROUND($sale_total, $decimals), ROUND($sale_subtotal, $decimals)) AS total,
IFNULL(ROUND($sale_cost, $decimals), 0) AS cost,
IFNULL(ROUND($sale_total - IFNULL(SUM(sales_items_taxes.tax), 0) - $sale_cost, $decimals), ROUND($sale_subtotal - $sale_cost, $decimals)) AS profit,
IFNULL(ROUND($sale_total, $decimals), ROUND($sale_subtotal, $decimals)) AS amount_due,
IFNULL($sale_subtotal, $sale_total) AS subtotal,
$tax AS tax,
IFNULL($sale_total, $sale_subtotal) AS total,
$sale_cost AS cost,
(IFNULL($sale_subtotal, $sale_total) - $sale_cost) AS profit,
IFNULL($sale_total, $sale_subtotal) AS amount_due,
MAX(payments.sale_payment_amount) AS amount_tendered,
(MAX(payments.sale_payment_amount) - IFNULL(ROUND($sale_total, $decimals), ROUND($sale_subtotal, $decimals))) AS change_due,
(MAX(payments.sale_payment_amount) - IFNULL($sale_total, $sale_subtotal)) AS change_due,
" . '
MAX(payments.payment_type) AS payment_type
');
@@ -1082,23 +1081,23 @@ class Sale extends CI_Model
$where = 'sales.sale_id = ' . $this->db->escape($inputs['sale_id']);
}
$decimals = totals_decimals();
$sale_price = 'sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100)';
$sale_cost = 'ROUND(IFNULL(SUM(sales_items.item_cost_price * sales_items.quantity_purchased), 0), ' . $decimals . ')';
$tax = 'ROUND(IFNULL(SUM(sales_items_taxes.tax), 0), ' . $decimals . ')';
if($this->config->item('tax_included'))
{
$sale_total = 'SUM(' . $sale_price . ')';
$sale_subtotal = 'SUM(' . $sale_price . ' - sales_items_taxes.tax)';
$sale_total = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ')';
$sale_subtotal = $sale_total . ' - ' . $tax;
}
else
{
$sale_total = 'SUM(' . $sale_price . ' + sales_items_taxes.tax)';
$sale_subtotal = 'SUM(' . $sale_price . ')';
$sale_subtotal = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ')';
$sale_total = $sale_subtotal . ' + ' . $tax;
}
$sale_cost = 'SUM(sales_items.item_cost_price * sales_items.quantity_purchased)';
$decimals = totals_decimals();
// create a temporary table to contain all the sum of taxes per sale item
$this->db->query('CREATE TEMPORARY TABLE IF NOT EXISTS ' . $this->db->dbprefix('sales_items_taxes_temp') .
' (INDEX(sale_id), INDEX(item_id))
@@ -1166,11 +1165,11 @@ class Sale extends CI_Model
MAX(payments.payment_type) AS payment_type,
MAX(payments.sale_payment_amount) AS sale_payment_amount,
' . "
IFNULL(ROUND($sale_subtotal, $decimals), ROUND($sale_total - IFNULL(SUM(sales_items_taxes.tax), 0), $decimals)) AS subtotal,
IFNULL(ROUND(SUM(sales_items_taxes.tax), $decimals), 0) AS tax,
IFNULL(ROUND($sale_total, $decimals), ROUND($sale_subtotal, $decimals)) AS total,
IFNULL(ROUND($sale_cost, $decimals), 0) AS cost,
IFNULL(ROUND($sale_total - IFNULL(SUM(sales_items_taxes.tax), 0) - $sale_cost, $decimals), ROUND($sale_subtotal - $sale_cost, $decimals)) AS profit
IFNULL($sale_subtotal, $sale_total) AS subtotal,
$tax AS tax,
IFNULL($sale_total, $sale_subtotal) AS total,
$sale_cost AS cost,
(IFNULL($sale_subtotal, $sale_total) - $sale_cost) AS profit
" . '
FROM ' . $this->db->dbprefix('sales_items') . ' AS sales_items
INNER JOIN ' . $this->db->dbprefix('sales') . ' AS sales

View File

@@ -4,9 +4,9 @@ require_once("Report.php");
abstract class Summary_report extends Report
{
/*
Private interface
*/
/**
* Private interface
*/
private function _common_select(array $inputs)
{
@@ -21,23 +21,23 @@ abstract class Summary_report extends Report
$where .= 'sale_time BETWEEN ' . $this->db->escape(rawurldecode($inputs['start_date'])) . ' AND ' . $this->db->escape(rawurldecode($inputs['end_date']));
}
$decimals = totals_decimals();
$sale_price = 'sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100)';
$sale_cost = 'ROUND(IFNULL(SUM(sales_items.item_cost_price * sales_items.quantity_purchased), 0), ' . $decimals . ')';
$tax = 'ROUND(IFNULL(SUM(sales_items_taxes.tax), 0), ' . $decimals . ')';
if($this->config->item('tax_included'))
{
$sale_total = 'SUM(' . $sale_price . ')';
$sale_subtotal = 'SUM(' . $sale_price . ' - sales_items_taxes.tax)';
$sale_total = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ')';
$sale_subtotal = $sale_total . ' - ' . $tax;
}
else
{
$sale_total = 'SUM(' . $sale_price . ' + sales_items_taxes.tax)';
$sale_subtotal = 'SUM(' . $sale_price . ')';
$sale_subtotal = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ')';
$sale_total = $sale_subtotal . ' + ' . $tax;
}
$sale_cost = 'SUM(sales_items.item_cost_price * sales_items.quantity_purchased)';
$decimals = totals_decimals();
// create a temporary table to contain all the sum of taxes per sale item
$this->db->query('CREATE TEMPORARY TABLE IF NOT EXISTS ' . $this->db->dbprefix('sales_items_taxes_temp') .
' (INDEX(sale_id), INDEX(item_id))
@@ -57,11 +57,11 @@ abstract class Summary_report extends Report
);
$this->db->select("
IFNULL(ROUND($sale_subtotal, $decimals), ROUND($sale_total - IFNULL(SUM(sales_items_taxes.tax), 0), $decimals)) AS subtotal,
IFNULL(ROUND(SUM(sales_items_taxes.tax), $decimals), 0) AS tax,
IFNULL(ROUND($sale_total, $decimals), ROUND($sale_subtotal, $decimals)) AS total,
IFNULL(ROUND($sale_cost, $decimals), 0) AS cost,
IFNULL(ROUND($sale_total - IFNULL(SUM(sales_items_taxes.tax), 0) - $sale_cost, $decimals), ROUND($sale_subtotal - $sale_cost, $decimals)) AS profit
IFNULL($sale_subtotal, $sale_total) AS subtotal,
$tax AS tax,
IFNULL($sale_total, $sale_subtotal) AS total,
$sale_cost AS cost,
(IFNULL($sale_subtotal, $sale_total) - $sale_cost) AS profit
");
}
@@ -100,11 +100,9 @@ abstract class Summary_report extends Report
}
}
/*
Protected class interface implemented by derived classes
*/
/**
* Protected class interface implemented by derived classes
*/
abstract protected function _get_data_columns();
@@ -113,10 +111,8 @@ abstract class Summary_report extends Report
protected function _where(array $inputs) { $this->_common_where($inputs); }
protected function _group_order() {}
/*
Public interface implementing the base abstract class, in general it should not be extended unless there is a valid reason
/**
* Public interface implementing the base abstract class, in general it should not be extended unless there is a valid reason
*/
public function getDataColumns()