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}';