mirror of
https://github.com/Dolibarr/dolibarr.git
synced 2025-02-20 13:46:52 +01:00
New: [ task #286 ] Enhance rounding function of prices to allow round of
sum instead of sum of rounding
This commit is contained in:
parent
33ed654745
commit
eef039e02f
|
|
@ -6,8 +6,8 @@ English Dolibarr ChangeLog
|
|||
For users:
|
||||
- New: [ task #877 ] Reorganize menus.
|
||||
- New: [ task #858 ] Holiday module: note on manual holiday assignation.
|
||||
- New: [ task #892 ] Add hidden option in thirdparty customer/supplier module to
|
||||
hide non active companies in select_company method.
|
||||
- New: [ task #892 ] Add hidden option in thirdparty customer/supplier module to hide non active
|
||||
companies in select_company method.
|
||||
- New: [ task #531 ] Add a workload field on tasks.
|
||||
- New: Add graph of bank account input/output into input-output report page.
|
||||
- New: Add script export-bank-receipts.php
|
||||
|
|
@ -24,7 +24,8 @@ For users:
|
|||
- New: [ task #928 ] Add extrafield feature on invoice lines.
|
||||
- New: Add option ADHERENT_LOGIN_NOT_REQUIRED.
|
||||
- New: Add a cron module to define scheduled jobs.
|
||||
- New: Add new graphical boxes (customer invoices per month).
|
||||
- New: Add new graphical boxes (customer invoices and orders per month).
|
||||
- New: [ task #286 ] Enhance rounding function of prices to allow round of sum instead of sum of rounding.
|
||||
- Qual: Implement same rule for return value of all command line scripts (0 when success, <>0 if error).
|
||||
|
||||
For translators:
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ abstract class CommonObject
|
|||
public $firstname;
|
||||
public $civility_id;
|
||||
public $import_key;
|
||||
|
||||
|
||||
public $array_options=array();
|
||||
|
||||
public $linkedObjectsIds;
|
||||
|
|
@ -53,7 +53,7 @@ abstract class CommonObject
|
|||
|
||||
/**
|
||||
* Method to output saved errors
|
||||
*
|
||||
*
|
||||
* @return string String with errors
|
||||
*/
|
||||
function errorsToString()
|
||||
|
|
@ -1464,15 +1464,18 @@ abstract class CommonObject
|
|||
}
|
||||
|
||||
/**
|
||||
* Update total_ht, total_ttc and total_vat for an object (sum of lines)
|
||||
* Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
|
||||
* Must be called at end of methods addline, updateline.
|
||||
*
|
||||
* @param int $exclspec Exclude special product (product_type=9)
|
||||
* @param int $roundingadjust -1=Use default method (MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND or 0), 0=Use total of rounding, 1=Use rounding of total
|
||||
* @param int $roundingadjust -1=Use default method (MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND if defined, or 0), 0=Force use total of rounding, 1=Force use rounding of total
|
||||
* @param int $nodatabaseupdate 1=Do not update database. Update only properties of object.
|
||||
* @return int <0 if KO, >0 if OK
|
||||
*/
|
||||
function update_price($exclspec=0,$roundingadjust=-1,$nodatabaseupdate=0)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
|
||||
|
||||
if ($roundingadjust < 0 && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $roundingadjust=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
|
||||
|
|
@ -1486,7 +1489,7 @@ abstract class CommonObject
|
|||
$fieldlocaltax2='total_localtax2';
|
||||
if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='tva';
|
||||
|
||||
$sql = 'SELECT qty, total_ht, '.$fieldtva.' as total_tva, total_ttc, '.$fieldlocaltax1.' as total_localtax1, '.$fieldlocaltax2.' as total_localtax2,';
|
||||
$sql = 'SELECT rowid, qty, total_ht, '.$fieldtva.' as total_tva, total_ttc, '.$fieldlocaltax1.' as total_localtax1, '.$fieldlocaltax2.' as total_localtax2,';
|
||||
$sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type';
|
||||
$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
|
||||
$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
|
||||
|
|
@ -1496,6 +1499,7 @@ abstract class CommonObject
|
|||
if ($this->table_element_line == 'contratdet') $product_field=''; // contratdet table has no product_type field
|
||||
if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
|
||||
}
|
||||
$sql.= ' ORDER by rowid'; // We want to be sure to always use same order of line to not change lines differently when option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND is used
|
||||
|
||||
dol_syslog(get_class($this)."::update_price sql=".$sql);
|
||||
$resql = $this->db->query($sql);
|
||||
|
|
@ -1506,8 +1510,9 @@ abstract class CommonObject
|
|||
$this->total_localtax1 = 0;
|
||||
$this->total_localtax2 = 0;
|
||||
$this->total_ttc = 0;
|
||||
$vatrates = array();
|
||||
$vatrates_alllines = array();
|
||||
$total_ht_by_vats = array();
|
||||
$total_tva_by_vats = array();
|
||||
$total_ttc_by_vats = array();
|
||||
|
||||
$num = $this->db->num_rows($resql);
|
||||
$i = 0;
|
||||
|
|
@ -1515,42 +1520,34 @@ abstract class CommonObject
|
|||
{
|
||||
$obj = $this->db->fetch_object($resql);
|
||||
|
||||
$this->total_ht += $obj->total_ht;
|
||||
$this->total_ht += $obj->total_ht; // The only field visible at line level
|
||||
$this->total_tva += $obj->total_tva;
|
||||
$this->total_localtax1 += $obj->total_localtax1;
|
||||
$this->total_localtax2 += $obj->total_localtax2;
|
||||
$this->total_ttc += $obj->total_ttc;
|
||||
$total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
|
||||
$total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
|
||||
$total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
|
||||
|
||||
// Check if there is a global invoice tax for this vat rate
|
||||
// FIXME: We should have no database access into this function. Also localtax 7 seems to have problem so i add condition to avoid it into standard usage without loosing it.
|
||||
if (! empty($conf->global->MAIN_USE_LOCALTAX_TYPE_7))
|
||||
if ($roundingadjust) // Check if we need adjustement onto line for vat
|
||||
{
|
||||
if ($this->total_localtax1 == 0)
|
||||
{
|
||||
// Search to know if there is a localtax of type 7
|
||||
// TODO : store local taxes types into object lines and remove this. We should use here $obj->localtax1_type but it is not yet filled into database, so we search into table of vat rate
|
||||
global $mysoc;
|
||||
$localtax1_array=getLocalTaxesFromRate($vatrate,1,$mysoc);
|
||||
if (empty($obj->localtax1_type))
|
||||
{
|
||||
$obj->localtax1_type = $localtax1_array[0];
|
||||
$obj->localtax1_tx = $localtax1_array[1];
|
||||
}
|
||||
//end TODO
|
||||
}
|
||||
if ($this->total_localtax2 == 0)
|
||||
{
|
||||
// Search to know if there is a localtax of type 7
|
||||
// TODO : store local taxes types into object lines and remove this. We should use here $obj->localtax1_type but it is not yet filled into database, so we search into table of vat rate
|
||||
global $mysoc;
|
||||
$localtax2_array=getLocalTaxesFromRate($vatrate,2,$mysoc);
|
||||
if (empty($obj->localtax2_type))
|
||||
{
|
||||
$obj->localtax2_type = $localtax2_array[0];
|
||||
$obj->localtax2_tx = $localtax2_array[1];
|
||||
}
|
||||
//end TODO
|
||||
}
|
||||
$tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
|
||||
$diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
|
||||
//print 'Line '.$i.' rowid='.$obj->rowid.' vat_rate='.$obj->vatrate.' total_ht='.$obj->total_ht.' total_tva='.$obj->total_tva.' total_ttc='.$obj->total_ttc.' total_ht_by_vats='.$total_ht_by_vats[$obj->vatrate].' total_tva_by_vats='.$total_tva_by_vats[$obj->vatrate].' (new calculation = '.$tmpvat.') total_ttc_by_vats='.$total_ttc_by_vats[$obj->vatrate].($diff?" => DIFF":"")."<br>\n";
|
||||
if ($diff)
|
||||
{
|
||||
if ($diff > 0.1) { dol_print_error('','A rounding difference was detected but is to high to be corrected'); exit; }
|
||||
$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".($obj->total_tva - $diff).", total_ttc = ".($obj->total_ttc - $diff)." WHERE rowid = ".$obj->rowid;
|
||||
//print 'We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". Run sqlfix = ".$sqlfix."<br>\n";
|
||||
dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". Run sqlfix = ".$sqlfix);
|
||||
$resqlfix=$this->db->query($sqlfix);
|
||||
if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
|
||||
$this->total_tva -= $diff;
|
||||
$this->total_ttc -= $diff;
|
||||
$total_tva_by_vats[$obj->vatrate] -= $diff;
|
||||
$total_ttc_by_vats[$obj->vatrate] -= $diff;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$i++;
|
||||
|
|
@ -1567,6 +1564,7 @@ abstract class CommonObject
|
|||
$fieldlocaltax1='localtax1';
|
||||
$fieldlocaltax2='localtax2';
|
||||
$fieldttc='total_ttc';
|
||||
// Specific code for backward compatibility with old field names
|
||||
if ($this->element == 'facture' || $this->element == 'facturerec') $fieldht='total';
|
||||
if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
|
||||
if ($this->element == 'propal') $fieldttc='total';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
/* Copyright (C) 2012 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
/* Copyright (C) 2012-2013 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -157,9 +157,9 @@ class FactureTestRounding extends PHPUnit_Framework_TestCase
|
|||
|
||||
/**
|
||||
* testFactureRoundingCreate2
|
||||
*
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
*
|
||||
* @depends testFactureRoundingCreate1
|
||||
* Test according to page http://wiki.dolibarr.org/index.php/Draft:VAT_calculation_and_rounding#Standard_usage
|
||||
*/
|
||||
|
|
@ -194,5 +194,133 @@ class FactureTestRounding extends PHPUnit_Framework_TestCase
|
|||
//$this->assertEquals($newlocalobject->total_ttc, 2.73);
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* testFactureAddLine1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFactureAddLine1()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf=$this->savconf;
|
||||
$user=$this->savuser;
|
||||
$langs=$this->savlangs;
|
||||
$db=$this->savdb;
|
||||
|
||||
// With option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND = 0
|
||||
$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND=0;
|
||||
$localobject1a=new Facture($this->savdb);
|
||||
$localobject1a->initAsSpecimen('nolines');
|
||||
$facid=$localobject1a->create($user);
|
||||
$localobject1a->addline($facid, 'Line 1', 6.36, 15, 21); // This include update_price
|
||||
print __METHOD__." id=".$facid." total_ttc=".$localobject1a->total_ttc."\n";
|
||||
$this->assertEquals( 95.40, $localobject1a->total_ht);
|
||||
$this->assertEquals( 20.03, $localobject1a->total_tva);
|
||||
$this->assertEquals(115.43, $localobject1a->total_ttc);
|
||||
|
||||
// With option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND = 1
|
||||
$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND=1;
|
||||
$localobject1b=new Facture($this->savdb);
|
||||
$localobject1b->initAsSpecimen('nolines');
|
||||
$facid=$localobject1b->create($user);
|
||||
$localobject1b->addline($facid, 'Line 1', 6.36, 15, 21); // This include update_price
|
||||
print __METHOD__." id=".$facid." total_ttc=".$localobject1b->total_ttc."\n";
|
||||
$this->assertEquals( 95.40, $localobject1b->total_ht, 'testFactureAddLine1 total_ht');
|
||||
$this->assertEquals( 20.03, $localobject1b->total_tva, 'testFactureAddLine1 total_tva');
|
||||
$this->assertEquals(115.43, $localobject1b->total_ttc, 'testFactureAddLine1 total_ttc');
|
||||
}
|
||||
|
||||
/**
|
||||
* testFactureAddLine2
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @depends testFactureAddLine1
|
||||
* The depends says test is run only if previous is ok
|
||||
*/
|
||||
public function testFactureAddLine2()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf=$this->savconf;
|
||||
$user=$this->savuser;
|
||||
$langs=$this->savlangs;
|
||||
$db=$this->savdb;
|
||||
|
||||
// With option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND = 0
|
||||
$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND=0;
|
||||
$localobject2=new Facture($this->savdb);
|
||||
$localobject2->initAsSpecimen('nolines');
|
||||
$facid=$localobject2->create($user);
|
||||
$localobject2->addline($facid, 'Line 1', 6.36, 5, 21);
|
||||
$localobject2->addline($facid, 'Line 2', 6.36, 5, 21);
|
||||
$localobject2->addline($facid, 'Line 3', 6.36, 5, 21);
|
||||
print __METHOD__." id=".$facid." total_ttc=".$localobject2->total_ttc."\n";
|
||||
$this->assertEquals( 95.40, $localobject2->total_ht);
|
||||
$this->assertEquals( 20.04, $localobject2->total_tva);
|
||||
$this->assertEquals(115.44, $localobject2->total_ttc);
|
||||
|
||||
// With option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND = 1
|
||||
$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND=1;
|
||||
$localobject2=new Facture($this->savdb);
|
||||
$localobject2->initAsSpecimen('nolines');
|
||||
$facid=$localobject2->create($user);
|
||||
$localobject2->addline($facid, 'Line 1', 6.36, 5, 21);
|
||||
$localobject2->addline($facid, 'Line 2', 6.36, 5, 21);
|
||||
$localobject2->addline($facid, 'Line 3', 6.36, 5, 21);
|
||||
print __METHOD__." id=".$facid." total_ttc=".$localobject2->total_ttc."\n";
|
||||
$this->assertEquals( 95.40, $localobject2->total_ht);
|
||||
$this->assertEquals( 20.03, $localobject2->total_tva);
|
||||
$this->assertEquals(115.43, $localobject2->total_ttc);
|
||||
}
|
||||
|
||||
/**
|
||||
* testFactureAddLine3
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @depends testFactureAddLine2
|
||||
* The depends says test is run only if previous is ok
|
||||
*/
|
||||
public function testFactureAddLine3()
|
||||
{
|
||||
global $conf,$user,$langs,$db;
|
||||
$conf=$this->savconf;
|
||||
$user=$this->savuser;
|
||||
$langs=$this->savlangs;
|
||||
$db=$this->savdb;
|
||||
|
||||
// With option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND = 0
|
||||
$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND=0;
|
||||
$localobject3=new Facture($this->savdb);
|
||||
$localobject3->initAsSpecimen('nolines');
|
||||
$facid=$localobject3->create($user);
|
||||
$localobject3->addline($facid, 'Line 1', 6.36, 3, 21);
|
||||
$localobject3->addline($facid, 'Line 2', 6.36, 3, 21);
|
||||
$localobject3->addline($facid, 'Line 3', 6.36, 3, 21);
|
||||
$localobject3->addline($facid, 'Line 4', 6.36, 3, 21);
|
||||
$localobject3->addline($facid, 'Line 5', 6.36, 3, 21);
|
||||
print __METHOD__." id=".$facid." total_ttc=".$localobject3->total_ttc."\n";
|
||||
$this->assertEquals( 95.40, $localobject3->total_ht);
|
||||
$this->assertEquals( 20.05, $localobject3->total_tva);
|
||||
$this->assertEquals(115.45, $localobject3->total_ttc);
|
||||
|
||||
// With option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND = 1
|
||||
$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND=1;
|
||||
$localobject3=new Facture($this->savdb);
|
||||
$localobject3->initAsSpecimen('nolines');
|
||||
$facid=$localobject3->create($user);
|
||||
$localobject3->addline($facid, 'Line 1', 6.36, 3, 21);
|
||||
$localobject3->addline($facid, 'Line 2', 6.36, 3, 21);
|
||||
$localobject3->addline($facid, 'Line 3', 6.36, 3, 21);
|
||||
$localobject3->addline($facid, 'Line 4', 6.36, 3, 21);
|
||||
$localobject3->addline($facid, 'Line 5', 6.36, 3, 21);
|
||||
print __METHOD__." id=".$facid." total_ttc=".$localobject3->total_ttc."\n";
|
||||
$this->assertEquals( 95.40, $localobject3->total_ht);
|
||||
$this->assertEquals( 20.03, $localobject3->total_tva);
|
||||
$this->assertEquals(115.43, $localobject3->total_ttc);
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
Loading…
Reference in New Issue
Block a user