diff --git a/ChangeLog b/ChangeLog index 2910fd07178..a805ef6786a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -46,16 +46,17 @@ For users: - New: [ task #1016 ] Can define a specific numbering for deposits - New: [ task #918 ] Stock replenishment - Fix: [ bug #992 ] Proforma invoices don't have a separated numeric count -- New : Add pdf link into supplier invoice list and supplier order list -- New : Genrate auto the PDF for supplier invoice -- New : Add category into filter webservice thirdparty method getListOfThirdParties -- New : Allow to define margin or mark rate during quoting, ordering, invoicing -- New : User permissions on margin module -- New : Add ref supplier into muscadet model - +- New: Add pdf link into supplier invoice list and supplier order list +- New: Genrate auto the PDF for supplier invoice +- New: Add category into filter webservice thirdparty method getListOfThirdParties +- New: Allow to define margin or mark rate during quoting, ordering, invoicing +- New: User permissions on margin module +- New: Add ref supplier into muscadet model +- New: Can use tag {mm} before {yy} even when there is a reset into numbering masks. + For translators: - Qual: Normalized sort order of all languages files with english reference files. -- New: Add language code files for South Africa. +- New: Add language code files for South Africa, France new caledonia. - New: Translate the email to change password. For developers: @@ -98,7 +99,7 @@ Goal is to fix old compatibility code that does not match hook specifications: All content added must be tagged by a '
' with css class="login_block_elem" 3) Some methods object->addline used a first parameter that was object->id, some not. Of course -this was not a good pratice, since object->id is already known so no need to provide it as +this was not a good pratice, since object->id is already known, there is no need to provide id as parameter. All methods addline in this case were modified to remove this parameter. diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index fac395ac9ca..dfe83f28fa2 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -549,6 +549,7 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m $maskcounter=$reg[1]; $maskraz=-1; $maskoffset=0; + $resetEveryMonth=false; if (dol_strlen($maskcounter) < 3) return 'CounterMustHaveMoreThan3Digits'; // Extract value for third party mask counter @@ -622,21 +623,32 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m if ($maskraz > 12) return 'ErrorBadMaskBadRazMonth'; // Define posy, posm and reg - if ($maskraz > 0) + if ($maskraz > 1) // if reset is not first month, we need month and year into mask { - if (! preg_match('/^(.*)\{(y+)\}\{(m+)\}/i',$maskwithonlyymcode) - && ! preg_match('/^(.*)\{(m+)\}\{(y+)\}/i',$maskwithonlyymcode)) return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask'; if (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i',$maskwithonlyymcode,$reg)) { $posy=2; $posm=3; } elseif (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i',$maskwithonlyymcode,$reg)) { $posy=3; $posm=2; } + else return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask'; + if (dol_strlen($reg[$posy]) < 2) return 'ErrorCantUseRazWithYearOnOneDigit'; } - else + else // if reset is for a specific month in year, we need year { - if (! preg_match('/^(.*)\{(y+)\}/i',$maskwithonlyymcode)) return 'ErrorCantUseRazIfNoYearInMask'; - if (preg_match('/^(.*)\{(y+)\}/i',$maskwithonlyymcode,$reg)) { $posy=2; $posm=0; } + if (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i',$maskwithonlyymcode,$reg)) { $posy=3; $posm=2; } + else if (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i',$maskwithonlyymcode,$reg)) { $posy=2; $posm=3; } + else if (preg_match('/^(.*)\{(y+)\}/i',$maskwithonlyymcode,$reg)) { $posy=2; $posm=0; } + else return 'ErrorCantUseRazIfNoYearInMask'; } - //print "x".$maskwithonlyymcode." ".$maskraz." ".$posy." ".$posm; - //var_dump($reg); + // Define length + $yearlen = $posy?dol_strlen($reg[$posy]):0; + $monthlen = $posm?dol_strlen($reg[$posm]):0; + // Define pos + $yearpos = (dol_strlen($reg[1])+1); + $monthpos = ($yearpos+$yearlen); + if ($posy == 3 && $posm == 2) { // if month is before year + $monthpos = (dol_strlen($reg[1])+1); + $yearpos = ($monthpos+$monthlen); + } + //print "xxx ".$maskwithonlyymcode." maskraz=".$maskraz." posy=".$posy." yearlen=".$yearlen." yearpos=".$yearpos." posm=".$posm." monthlen=".$monthlen." monthpos=".$monthpos." yearoffsettype=".$yearoffsettype." resetEveryMonth=".$resetEveryMonth."\n"; // Define $yearcomp and $monthcomp (that will be use in the select where to search max number) $monthcomp=$maskraz; @@ -662,7 +674,6 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m // For backward compatibility else if (date("m",$date) < $maskraz && empty($resetEveryMonth)) { $yearoffset=-1; } // If current month lower that month of return to zero, year is previous year - $yearlen = dol_strlen($reg[$posy]); if ($yearlen == 4) $yearcomp=sprintf("%04d",date("Y",$date)+$yearoffset); elseif ($yearlen == 2) $yearcomp=sprintf("%02d",date("y",$date)+$yearoffset); elseif ($yearlen == 1) $yearcomp=substr(date("y",$date),2,1)+$yearoffset; @@ -671,14 +682,6 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m if ($yearlen == 4) $yearcomp1=sprintf("%04d",date("Y",$date)+$yearoffset+1); elseif ($yearlen == 2) $yearcomp1=sprintf("%02d",date("y",$date)+$yearoffset+1); - $monthlen = dol_strlen($reg[$posm]); - $yearpos = (dol_strlen($reg[1])+1); - $monthpos = ($yearpos+$yearlen); - if ($posy == 3) { - $monthpos = (dol_strlen($reg[1])+1); - $yearpos = ($monthpos+$monthlen); - } - $sqlwhere.="("; $sqlwhere.=" (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$yearcomp."'"; $sqlwhere.=" AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") >= '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')"; @@ -687,20 +690,14 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m $sqlwhere.=" AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") < '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."') "; $sqlwhere.=')'; } - else if($resetEveryMonth) { - $monthlen = dol_strlen($reg[$posm]); - $yearpos = (dol_strlen($reg[1])+1); - $monthpos = ($yearpos+$yearlen); - if ($posy == 3) { - $monthpos = (dol_strlen($reg[1])+1); - $yearpos = ($monthpos+$monthlen); - } - $sqlwhere.=" SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$yearcomp."'"; - $sqlwhere.=" AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") = '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."' "; + else if ($resetEveryMonth) + { + $sqlwhere.="(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$yearcomp."'"; + $sqlwhere.=" AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") = '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')"; } else // reset is done on january { - $sqlwhere.='(SUBSTRING('.$field.', '.(dol_strlen($reg[1])+1).', '.dol_strlen($reg[2]).") = '".$yearcomp."')"; + $sqlwhere.='(SUBSTRING('.$field.', '.$yearpos.', '.$yearlen.") = '".$yearcomp."')"; } } //print "sqlwhere=".$sqlwhere." yearcomp=".$yearcomp."
\n"; // sqlwhere and yearcomp defined only if we ask a reset @@ -1451,46 +1448,46 @@ function dolGetElementUrl($objectid,$objecttype,$withpicto=0,$option='') // To work with non standard path if ($objecttype == 'facture' || $objecttype == 'invoice') { - $classpath = 'compta/facture/class'; - $module='facture'; + $classpath = 'compta/facture/class'; + $module='facture'; $subelement='facture'; } if ($objecttype == 'commande' || $objecttype == 'order') { - $classpath = 'commande/class'; - $module='commande'; + $classpath = 'commande/class'; + $module='commande'; $subelement='commande'; } if ($objecttype == 'propal') { $classpath = 'comm/propal/class'; } if ($objecttype == 'shipping') { - $classpath = 'expedition/class'; - $subelement = 'expedition'; + $classpath = 'expedition/class'; + $subelement = 'expedition'; $module = 'expedition_bon'; } if ($objecttype == 'delivery') { - $classpath = 'livraison/class'; - $subelement = 'livraison'; + $classpath = 'livraison/class'; + $subelement = 'livraison'; $module = 'livraison_bon'; } if ($objecttype == 'contract') { - $classpath = 'contrat/class'; - $module='contrat'; + $classpath = 'contrat/class'; + $module='contrat'; $subelement='contrat'; } if ($objecttype == 'member') { - $classpath = 'adherents/class'; - $module='adherent'; + $classpath = 'adherents/class'; + $module='adherent'; $subelement='adherent'; } if ($objecttype == 'cabinetmed_cons') { - $classpath = 'cabinetmed/class'; - $module='cabinetmed'; + $classpath = 'cabinetmed/class'; + $module='cabinetmed'; $subelement='cabinetmedcons'; } if ($objecttype == 'fichinter') { - $classpath = 'fichinter/class'; - $module='ficheinter'; + $classpath = 'fichinter/class'; + $module='ficheinter'; $subelement='fichinter'; } @@ -1498,13 +1495,13 @@ function dolGetElementUrl($objectid,$objecttype,$withpicto=0,$option='') $classfile = strtolower($subelement); $classname = ucfirst($subelement); if ($objecttype == 'invoice_supplier') { - $classfile = 'fournisseur.facture'; + $classfile = 'fournisseur.facture'; $classname='FactureFournisseur'; $classpath = 'fourn/class'; $module='fournisseur'; } if ($objecttype == 'order_supplier') { - $classfile = 'fournisseur.commande'; + $classfile = 'fournisseur.commande'; $classname='CommandeFournisseur'; $classpath = 'fourn/class'; $module='fournisseur'; diff --git a/test/phpunit/NumberingModulesTest.php b/test/phpunit/NumberingModulesTest.php index 62e79d4f9d5..66110c1f683 100644 --- a/test/phpunit/NumberingModulesTest.php +++ b/test/phpunit/NumberingModulesTest.php @@ -146,7 +146,7 @@ class NumberingModulesTest extends PHPUnit_Framework_TestCase $this->assertEquals('1915-0001', $result); // counter must start to 1 $result=$localobject->is_erasable(); print __METHOD__." is_erasable=".$result."\n"; - $this->assertEquals(1, $result); // Can be deleted + $this->assertEquals(1, $result, 'Test for {yyyy}-{0000}, 1st invoice'); // Can be deleted $localobject2=new Facture($this->savdb); $localobject2->initAsSpecimen(); @@ -159,13 +159,13 @@ class NumberingModulesTest extends PHPUnit_Framework_TestCase $result2=$localobject2->create($user,1); $result3=$localobject2->validate($user, $result); // create invoice by forcing ref print __METHOD__." result=".$result."\n"; - $this->assertEquals('1916-0002', $result); // counter must not be reseted so be 2 + $this->assertEquals('1916-0002', $result); // counter must be now 2 (not reseted) $result=$localobject2->is_erasable(); print __METHOD__." is_erasable=".$result."\n"; $this->assertEquals(1, $result); // Can be deleted $result=$localobject->is_erasable(); print __METHOD__." is_erasable=".$result."\n"; - $this->assertEquals(0, $result); // Case 1 can not be deleted (case 2 is more recent) + $this->assertEquals(0, $result, 'Test for {yyyy}-{0000} that is_erasable is 0 for 1st invoice'); // 1 can no more be deleted (2 is more recent // Now we try with a reset $conf->global->FACTURE_MERCURE_MASK_CREDIT='{yyyy}-{0000@1}'; @@ -176,10 +176,26 @@ class NumberingModulesTest extends PHPUnit_Framework_TestCase $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1910); // we use year 1910 to be sure to not have existing invoice for this year $numbering=new mod_facture_mercure(); $result=$numbering->getNextValue($mysoc, $localobject); + $result2=$localobject->create($user,1); + $result3=$localobject->validate($user, $result); print __METHOD__." result=".$result."\n"; - $this->assertEquals('1910-0001', $result); // counter must start to 1 + $this->assertEquals('1910-0001', $result, 'Test for {yyyy}-{0000@1} 1st invoice'); // counter must start to 1 + $localobject2=new Facture($this->savdb); + $localobject2->initAsSpecimen(); + $localobject2->date=dol_mktime(12, 0, 0, 1, 1, 1910); // we use same year for second invoice (and there is a reset required) + $numbering=new mod_facture_mercure(); + $result=$numbering->getNextValue($mysoc, $localobject2); + print __METHOD__." result=".$result."\n"; + $this->assertEquals('1910-0002', $result, 'Test for {yyyy}-{0000@1} 2nd invoice, same day'); // counter must be now 2 + $localobject3=new Facture($this->savdb); + $localobject3->initAsSpecimen(); + $localobject3->date=dol_mktime(12, 0, 0, 1, 1, 1911); // we use next year for third invoice (and there is a reset required) + $numbering=new mod_facture_mercure(); + $result=$numbering->getNextValue($mysoc, $localobject3); + print __METHOD__." result=".$result."\n"; + $this->assertEquals('1911-0001', $result, 'Test for {yyyy}-{0000@1} 3nd invoice, same day'); // counter must be now 1 - // Same mask but we add month + // Same but we add month after year $conf->global->FACTURE_MERCURE_MASK_CREDIT='{yyyy}{mm}-{0000@1}'; $conf->global->FACTURE_MERCURE_MASK_INVOICE='{yyyy}{mm}-{0000@1}'; $localobject=new Facture($this->savdb); @@ -190,11 +206,10 @@ class NumberingModulesTest extends PHPUnit_Framework_TestCase $result2=$localobject->create($user,1); $result3=$localobject->validate($user, $result); print __METHOD__." result=".$result."\n"; - $this->assertEquals('192001-0001', $result); // counter must start to 1 + $this->assertEquals('192001-0001', $result, 'Test for {yyyy}{mm}-{0000@1} 1st invoice'); // counter must start to 1 $result=$localobject->is_erasable(); print __METHOD__." is_erasable=".$result."\n"; $this->assertEquals(1, $result); // Can be deleted - $localobject2=new Facture($this->savdb); $localobject2->initAsSpecimen(); $localobject2->date=dol_mktime(12, 0, 0, 1, 1, 1921); // we use following year for second invoice (and there is a reset required) @@ -211,6 +226,44 @@ class NumberingModulesTest extends PHPUnit_Framework_TestCase print __METHOD__." is_erasable=".$result."\n"; $this->assertEquals(1, $result); // Case 1 can be deleted (because there was a reset for case 2) + // Same but we add month before year and use a year on 2 digits + $conf->global->FACTURE_MERCURE_MASK_CREDIT='[mm}{yy}-{0000@1}'; + $conf->global->FACTURE_MERCURE_MASK_INVOICE='{mm}{yy}-{0000@1}'; + $localobject=new Facture($this->savdb); + $localobject->initAsSpecimen(); + $localobject->date=dol_mktime(12, 0, 0, 1, 1, 1925); // we use year 1925 to be sure to not have existing invoice for this year + $numbering=new mod_facture_mercure(); + $result=$numbering->getNextValue($mysoc, $localobject); + $result2=$localobject->create($user,1); + $result3=$localobject->validate($user, $result); + print __METHOD__." result=".$result."\n"; + $this->assertEquals('0125-0001', $result, 'Test for {mm}{yy}-{0000@1} 1st invoice'); // counter must start to 1 + $result=$localobject->is_erasable(); // This call get getNextNumRef with param 'last' + print __METHOD__." is_erasable=".$result."\n"; + $this->assertEquals(1, $result); // Can be deleted + $localobject2=new Facture($this->savdb); + $localobject2->initAsSpecimen(); + $localobject2->date=dol_mktime(12, 0, 0, 1, 1, 1925); // we use same year 1925 for second invoice (and there is a reset required) + $numbering=new mod_facture_mercure(); + $result=$numbering->getNextValue($mysoc, $localobject2); + $result2=$localobject2->create($user,1); + $result3=$localobject2->validate($user, $result); + print __METHOD__." result=".$result."\n"; + $this->assertEquals('0125-0002', $result, 'Test for {mm}{yy}-{0000@1} 2st invoice'); // counter must be now 2 + $result=$localobject2->is_erasable(); + print __METHOD__." is_erasable=".$result."\n"; + $this->assertEquals(1, $result); // Can be deleted + $result=$localobject->is_erasable(); + print __METHOD__." is_erasable=".$result."\n"; + $this->assertEquals(0, $result); // Case 1 can not be deleted (because there is an invoice 2) + $localobject3=new Facture($this->savdb); + $localobject3->initAsSpecimen(); + $localobject3->date=dol_mktime(12, 0, 0, 1, 1, 1926); // we use following year for third invoice (and there is a reset required) + $numbering=new mod_facture_mercure(); + $result=$numbering->getNextValue($mysoc, $localobject3); + print __METHOD__." result=".$result."\n"; + $this->assertEquals('0126-0001', $result, 'Test for {mm}{yy}-{0000@1} 3rd invoice'); // counter must be now 1 + // Try an offset when an invoice already exists $conf->global->FACTURE_MERCURE_MASK_CREDIT='{yyyy}{mm}-{0000+9990}'; $conf->global->FACTURE_MERCURE_MASK_INVOICE='{yyyy}{mm}-{0000+9990}';