소스 검색

Tons of fixes and enhancement on recurring invoice generation

Laurent Destailleur 9 년 전
부모
커밋
c78997ed23

+ 88 - 35
htdocs/compta/facture/class/facture-rec.class.php

@@ -52,6 +52,11 @@ class FactureRec extends Facture
 	var $db_table;
 	var $propalid;
 
+	var $date_last_gen;
+	var $next_gen;
+	var $nb_gen_done;
+	var $nb_gen_max;
+	
 	var $rang;
 	var $special_code;
 
@@ -214,17 +219,17 @@ class FactureRec extends Facture
 
 
 	/**
-	 *	Recupere l'objet facture et ses lignes de factures
+	 *	Load object and lines
 	 *
 	 *	@param      int		$rowid       	Id of object to load
-	 * 	@param		string	$ref			Reference of invoice
+	 * 	@param		string	$ref			Reference of recurring invoice
 	 * 	@param		string	$ref_ext		External reference of invoice
 	 * 	@param		int		$ref_int		Internal reference of other object
 	 *	@return     int         			>0 if OK, <0 if KO, 0 if not found
 	 */
 	function fetch($rowid, $ref='', $ref_ext='', $ref_int='')
 	{
-		$sql = 'SELECT f.titre,f.fk_soc,f.amount,f.tva,f.total,f.total_ttc,f.remise_percent,f.remise_absolue,f.remise';
+		$sql = 'SELECT f.rowid, f.titre, f.fk_soc, f.amount, f.tva, f.total, f.total_ttc, f.remise_percent, f.remise_absolue, f.remise';
 		$sql.= ', f.date_lim_reglement as dlr';
 		$sql.= ', f.note_private, f.note_public, f.fk_user_author';
 		$sql.= ', f.fk_mode_reglement, f.fk_cond_reglement, f.fk_projet';
@@ -232,19 +237,18 @@ class FactureRec extends Facture
 		$sql.= ', f.frequency, f.unit_frequency, f.date_when, f.date_last_gen, f.nb_gen_done, f.nb_gen_max, f.usenewprice, f.auto_validate';
 		$sql.= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
 		$sql.= ', c.code as cond_reglement_code, c.libelle as cond_reglement_libelle, c.libelle_facture as cond_reglement_libelle_doc';
-		$sql.= ', el.fk_source';
+		//$sql.= ', el.fk_source';
 		$sql.= ' FROM '.MAIN_DB_PREFIX.'facture_rec as f';
 		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as c ON f.fk_cond_reglement = c.rowid';
 		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON f.fk_mode_reglement = p.id';
-		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = f.rowid AND el.targettype = 'facture'";
-		$sql.= ' WHERE f.rowid='.$rowid;
-		if ($ref)     $sql.= " AND f.titre='".$this->db->escape($ref)."'";
+		//$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = f.rowid AND el.targettype = 'facture'";
+		if ($rowid) $sql.= ' WHERE f.rowid='.$rowid;
+		elseif ($ref) $sql.= " WHERE f.titre='".$this->db->escape($ref)."'";
 		/* This field are not used for template invoice
 		if ($ref_ext) $sql.= " AND f.ref_ext='".$this->db->escape($ref_ext)."'";
 		if ($ref_int) $sql.= " AND f.ref_int='".$this->db->escape($ref_int)."'";
 		*/
 		
-        dol_syslog(get_class($this)."::fetch rowid=".$rowid, LOG_DEBUG);
 		$result = $this->db->query($sql);
 		if ($result)
 		{
@@ -252,7 +256,7 @@ class FactureRec extends Facture
 			{
 				$obj = $this->db->fetch_object($result);
 
-				$this->id                     = $rowid;
+				$this->id                     = $obj->rowid;
 				$this->titre                  = $obj->titre;
 				$this->ref                    = $obj->titre;
 				$this->ref_client             = $obj->ref_client;
@@ -290,8 +294,8 @@ class FactureRec extends Facture
 				$this->special_code			  = $obj->special_code;
 				$this->frequency			  = $obj->frequency;
 				$this->unit_frequency		  = $obj->unit_frequency;
-				$this->date_when			  = $obj->date_when;
-				$this->date_last_gen		  = $obj->date_last_gen;
+				$this->date_when			  = $this->db->jdate($obj->date_when);
+				$this->date_last_gen		  = $this->db->jdate($obj->date_last_gen);
 				$this->nb_gen_done			  = $obj->nb_gen_done;
 				$this->nb_gen_max			  = $obj->nb_gen_max;
 				$this->usenewprice			  = $obj->usenewprice;
@@ -305,14 +309,14 @@ class FactureRec extends Facture
 				$result=$this->fetch_lines();
 				if ($result < 0)
 				{
-					$this->error=$this->db->error();
+					$this->error=$this->db->lasterror();
 					return -3;
 				}
 				return 1;
 			}
 			else
 			{
-				$this->error='Bill with id '.$rowid.' not found sql='.$sql;
+				$this->error='Bill with id '.$rowid.' or ref '.$ref.' not found sql='.$sql;
 				dol_syslog('Facture::Fetch Error '.$this->error, LOG_ERR);
 				return -2;
 			}
@@ -336,7 +340,7 @@ class FactureRec extends Facture
 		$sql.= ' l.remise, l.remise_percent, l.subprice,';
 		$sql.= ' l.total_ht, l.total_tva, l.total_ttc,';
 		$sql.= ' l.rang, l.special_code,';
-		$sql.= ' l.fk_unit,';
+		$sql.= ' l.fk_unit, l.fk_contract_line,';
 		$sql.= ' p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc';
 		$sql.= ' FROM '.MAIN_DB_PREFIX.'facturedet_rec as l';
 		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
@@ -380,7 +384,8 @@ class FactureRec extends Facture
 				$line->rang 			= $objp->rang;
 				$line->special_code 	= $objp->special_code;
 				$line->fk_unit          = $objp->fk_unit;
-
+                $line->fk_contract_line = $objp->fk_contract_line;
+                
 				// Ne plus utiliser
 				$line->price            = $objp->price;
 				$line->remise           = $objp->remise;
@@ -578,53 +583,101 @@ class FactureRec extends Facture
 	function getNextDate()
 	{
 		if (empty($this->date_when)) return false;
-		return dol_time_plus_duree(strtotime($this->date_when), $this->frequency, $this->unit_frequency);
+		return dol_time_plus_duree($this->date_when, $this->frequency, $this->unit_frequency);
 	}
 	
 	/**
-	 *  Create all recurrents invoices 
+	 *  Create all recurrents invoices.
+	 *  A result may also be provided into this->output
 	 * 
-	 *  @return		int		number of created invoices
+	 *  @return	int						0 if OK, < 0 if KO (this function is used also by cron so only 0 is OK) 
 	 */
 	function createRecurringInvoices()
 	{
-		global $db,$user;
+		global $langs, $db, $user;
+		
+		$langs->load("bills");
 		
 		$nb_create=0;
 		
-		$today = date('Y-m-d 23:59:59');
+		$now = dol_now();
+		$tmparray=dol_getdate($now);
+		$today = dol_mktime(23,59,59,$tmparray['mon'],$tmparray['mday'],$tmparray['year']);   // Today is last second of current day
 		
+		dol_syslog("createRecurringInvoices");
 		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'facture_rec';
-		$sql.= ' WHERE date_when IS NOT NULL AND frequency > 0';
-		$sql.= ' AND date_when <= "'.$db->escape($today).'"';
-		$sql.= ' AND nb_gen_done < nb_gen_max';
+		$sql.= ' WHERE frequency > 0';      // A recurring invoice is an invoice with a frequency
+		$sql.= " AND (date_when IS NULL OR date_when <= '".$db->idate($today)."')";
+		$sql.= ' AND (nb_gen_done < nb_gen_max OR nb_gen_max = 0)';
+		//print $sql;exit;
 		
 		$resql = $db->query($sql);
 		if ($resql)
 		{
-			while ($line = $db->fetch_object($resql))
+		    $i=0;
+		    $num = $db->num_rows($resql);
+		    
+		    if ($num) $this->output.=$langs->trans("FoundXQualifiedRecurringInvoiceTemplate", $num)."\n";
+		    else $this->output.=$langs->trans("NoQualifiedRecurringInvoiceTemplateFound");
+		    
+		    while ($i < $num)
 			{
+			    $line = $db->fetch_object($resql);
+
+			    $db->begin();
+			    
 				$facturerec = new FactureRec($db);
 				$facturerec->fetch($line->rowid);
 			
-				$facture = new Facture($db);
-				$result = $facture->createFromRec($user, $facturerec);
-				
-				// >0 create and validate if auto_validate
-				// =0 create but not validate if auto_validate
-				// <0 broken
-				if ($result >= 0)
+				dol_syslog("createRecurringInvoices Process invoice template id=".$facturerec->id.", ref=".$facturerec->ref);
+
+			    $error=0;
+
+			    $facture = new Facture($db);
+				$facture->fac_rec = $facturerec->id;    // We will create $facture from this recurring invoice
+			    $facture->type = self::TYPE_STANDARD;
+			    $facture->brouillon = 1;
+			    $facture->date = $now;
+			    $facture->socid = $facturerec->socid;
+			    
+			    $invoiceidgenerated = $facture->create($user);       // This will also update fields of recurring invoice
+			    if ($invoiceidgenerated <= 0)
+			    {
+			        $this->errors = $facture->errors;
+			        $this->error = $facture->error;
+			        $error++;
+			    }
+			    if (! $error && $facturerec->auto_validate)
+			    {
+			        $result = $facture->validate($user);
+			        if ($result <= 0)
+			        {
+    			        $this->errors = $facture->errors;
+    			        $this->error = $facture->error;
+			            $error++;
+			        }
+			    }
+
+				if (! $error && $invoiceidgenerated >= 0)
 				{
-					$next_date = $facturerec->getNextDate();
-					$facturerec->setNextDate($next_date,1);
-					
+					$db->commit();
+					dol_syslog("createRecurringInvoices Process invoice template ".$facturerec->ref." is finished with a success generation");
 					$nb_create++;
+					$this->output.=$langs->trans("InvoiceGeneratedFromTemplate", $facture->ref, $facturerec->ref)."\n";
+				}
+				else
+				{
+				    $db->rollback();
 				}
 
+				$i++;
 			}
 		}
+		else dol_print_error($db);
+		
+		$this->output=trim($this->output);
 		
-		return $nb_create;
+		return $error?$error:0;
 	}
 	
 	/**

+ 22 - 57
htdocs/compta/facture/class/facture.class.php

@@ -217,6 +217,7 @@ class Facture extends CommonInvoice
 	/**
 	 *	Create invoice in database
 	 *  Note: this->ref can be set or empty. If empty, we will use "(PROV)"
+	 *  Note: this->fac_rec must be set to create from recurring invoice
 	 *
 	 *	@param	User	$user      		Object user that create
 	 *	@param  int		$notrigger		1=Does not execute triggers, 0 otherwise
@@ -270,16 +271,17 @@ class Facture extends CommonInvoice
 
 		$this->db->begin();
 
-		// Create invoice from a predefined invoice
+		// Create invoice from a template invoice
 		if ($this->fac_rec > 0)
 		{
 			require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php';
 			$_facrec = new FactureRec($this->db);
 			$result=$_facrec->fetch($this->fac_rec);
 
+			$this->socid 		     = $_facrec->socid;
+			
 			$this->fk_project        = $_facrec->fk_project;
 			$this->fk_account        = $_facrec->fk_account;
-			$this->note_private      = $_facrec->note_private;
 			$this->cond_reglement_id = $_facrec->cond_reglement_id;
 			$this->mode_reglement_id = $_facrec->mode_reglement_id;
 			$this->remise_absolue    = $_facrec->remise_absolue;
@@ -287,14 +289,31 @@ class Facture extends CommonInvoice
 			$this->fk_incoterms		 = $_facrec->fk_incoterms;
 			$this->location_incoterms= $_facrec->location_incoterms;
 
+			$this->note_public       = $_facrec->note_public;
+			$this->note_private      = $_facrec->note_private;
+			
 			// Clean parameters
 			if (! $this->type) $this->type = self::TYPE_STANDARD;
 			$this->ref_client=trim($this->ref_client);
-			$this->note_private=trim($this->note_private);
 			$this->note_public=trim($this->note_public);
+			$this->note_private=trim($this->note_private);
+		    $this->note_private=dol_concatdesc($facture->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->ref));
+		    
 			//if (! $this->remise) $this->remise = 0;
 			if (! $this->mode_reglement_id) $this->mode_reglement_id = 0;
 			$this->brouillon = 1;
+		
+			$forceduedate = $this->calculate_date_lim_reglement();
+			
+			// Update date and number of last generation of recurring template invoice, before inserting new invoice
+			if ($_facrec->frequency > 0)
+			{
+                $_facrec->nb_gen_done++;
+                $next_date = $_facrec->getNextDate();   // Calculate next date
+                $_facrec->setValueFrom('date_last_gen', $now, '', null, 'date');
+                $_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1);
+                $_facrec->setNextDate($next_date,1);
+			}
 		}
 
 		// Define due date if not already defined
@@ -919,60 +938,6 @@ class Facture extends CommonInvoice
 		else return -1;
 	}
 
-	/**
-	 *	Create a new invoice in database from facturerec
-	 *
-	 *	@param      User		$user    		Object user that ask creation
-	 *	@param      FactureRec	$facturerec    	Object facturerec source
-	 *	@return		int							<0 if KO, 0 if not validate, >0 if OK
-	 */
-	function createFromRec($user, $facturerec)
-	{
-
-		// Clean parameters
-		$this->type = self::TYPE_STANDARD;
-		$this->brouillon = 1;
-		
-		// Charge facture source
-		$facture=new Facture($facturerec->db);
-
-		$facture->socid 		    = $facturerec->socid;
-		$facture->date              = dol_mktime(12, 0, 0, date('m'), date('d'), date('Y'));
-		$facture->note_public       = $facturerec->note_public;
-		$facture->note_private      = $facturerec->note_private;
-		$facture->fk_project        = $facturerec->fk_project;
-		$facture->fk_account        = $facturerec->fk_account;
-		$facture->cond_reglement_id = $facturerec->cond_reglement_id;
-		$facture->mode_reglement_id = $facturerec->mode_reglement_id;
-		
-		$new_date_lim_reglement = $facture->calculate_date_lim_reglement();
-		$facture->date_lim_reglement = $new_date_lim_reglement;
-		
-		$facture->remise_absolue    = $facturerec->remise_absolue;
-		$facture->remise_percent    = $facturerec->remise_percent;
-
-		$facture->lines		    	= $facturerec->lines;	// Tableau des lignes de factures
-
-		// Loop on each line of new invoice
-		foreach($facture->lines as $i => $line)
-		{
-			$facture->lines[$i]->fk_prev_id = $facturerec->lines[$i]->rowid;
-		}
-
-		dol_syslog(get_class($this)."::createFromRec socid=".$this->socid." nboflines=".count($facture->lines));
-
-		$facid = $facture->create($user);
-		if ($facid <= 0) return -1;
-
-		if ($facturerec->auto_validate)
-		{
-			$result = $facture->validate($user);
-			if ($result<=0) return 0;
-		}
-			
-		return $facid;
-	}
-
 	/**
 	 *      Return clicable link of object (with eventually picto)
 	 *

+ 139 - 76
htdocs/compta/facture/fiche-rec.php

@@ -38,6 +38,7 @@ $langs->load('compta');
 
 // Security check
 $id=(GETPOST('facid','int')?GETPOST('facid','int'):GETPOST('id','int'));
+$ref=GETPOST('ref','alpha');
 $action=GETPOST('action', 'alpha');
 if ($user->societe_id) $socid=$user->societe_id;
 $objecttype = 'facture_rec';
@@ -58,9 +59,9 @@ if ($sortfield == "")
 $sortfield="f.datef";
 
 $object = new FactureRec($db);
-if ($id > 0 && $action != 'create' && $action != 'add')
+if (($id > 0 || $ref) && $action != 'create' && $action != 'add')
 {
-	$ret = $object->fetch($id);
+	$ret = $object->fetch($id, $ref);
 	if (!$ret)
 	{
 		setEventMessages($langs->trans("ErrorRecordNotFound"), null, 'errors');
@@ -192,6 +193,7 @@ $permissionnote=$user->rights->facture->creer;	// Used by the include of actions
 include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php';	// Must be include, not includ_once
 
 
+
 /*
  *	View
  */
@@ -201,6 +203,11 @@ llxHeader('',$langs->trans("RepeatableInvoices"),'ch-facture.html#s-fac-facture-
 $form = new Form($db);
 $companystatic = new Societe($db);
 
+$now = dol_now();
+$tmparray=dol_getdate($now);
+$today = dol_mktime(23,59,59,$tmparray['mon'],$tmparray['mday'],$tmparray['year']);   // Today is last second of current day
+	  
+
 /*
  * Create mode
  */
@@ -211,7 +218,7 @@ if ($action == 'create')
 	$object = new Facture($db);   // Source invoice
 	$product_static = new Product($db);
 
-	if ($object->fetch($id) > 0)
+	if ($object->fetch($id, $ref) > 0)
 	{
 		print '<form action="fiche-rec.php" method="post">';
 		print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
@@ -542,7 +549,7 @@ else
 		print '<table class="border" width="100%">';
 
 		$linkback = '<a href="' . DOL_URL_ROOT . '/compta/facture/fiche-rec.php' . (! empty($socid) ? '?socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
-		
+
 		// Ref
 		print '<tr><td width="20%">' . $langs->trans('Ref') . '</td>';
 		print '<td colspan="5">';
@@ -557,7 +564,7 @@ else
 		if ($result < 0) {
 		    dol_print_error('', $discount->error);
 		}*/
-		print $form->showrefnav($object, 'ref', $linkback, 1, 'facnumber', 'ref', $morehtmlref);
+		print $form->showrefnav($object, 'ref', $linkback, 1, 'titre', 'titre', $morehtmlref);
 		print '</td></tr>';
 		
 		
@@ -669,7 +676,6 @@ else
 		print "</td>";
 		print '</tr>';
 
-
 		print "</table>";
 
 		print '<br>';
@@ -716,77 +722,90 @@ else
 		}
 		print '</td></tr>';
 		
-		//if (! empty($object->frequency))    // If no frequency defined, it is not a recurring template invoice
-		//{
-    		// Date when
-    		print '<tr><td>';
-    		if ($action == 'date_when' || $object->frequency > 0)
-    		{
-    		    print $form->editfieldkey($langs->trans("NextDateToExecution"), 'date_when', $object->date_when, $object, $user->rights->facture->creer, 'dayhour');
-    		}
-    		else
-    		{
-    		    print $langs->trans("NextDateToExecution");
-    		}
-    		print '</td><td colspan="5">';
-    		if ($action == 'date_when' || $object->frequency > 0)
-    		{
-    		    print $form->editfieldval($langs->trans("NextDateToExecution"), 'date_when', $object->date_when, $object, $user->rights->facture->creer, 'dayhour');
-    		}
-    		print '</td>';
-    		print '</tr>';
-    		
+		// Date when
+		print '<tr><td>';
+		if ($action == 'date_when' || $object->frequency > 0)
+		{
+		    print $form->editfieldkey($langs->trans("NextDateToExecution"), 'date_when', $object->date_when, $object, $user->rights->facture->creer, 'day');
+		}
+		else
+		{
+		    print $langs->trans("NextDateToExecution");
+		}
+		print '</td><td colspan="5">';
+		if ($action == 'date_when' || $object->frequency > 0)
+		{
+		    print $form->editfieldval($langs->trans("NextDateToExecution"), 'date_when', $object->date_when, $object, $user->rights->facture->creer, 'day');
+		}
+		print '</td>';
+		print '</tr>';
+				
+		// Max period / Rest period
+		print '<tr><td>';
+		if ($action == 'nb_gen_max' || $object->frequency > 0)
+		{
+		    print $form->editfieldkey($langs->trans("MaxPeriodNumber"), 'nb_gen_max', $object->nb_gen_max, $object, $user->rights->facture->creer);
+		}
+		else
+		{
+		    print $langs->trans("MaxPeriodNumber");
+		}
+		print '</td><td colspan="5">';
+		if ($action == 'nb_gen_max' || $object->frequency > 0)
+		{
+		      print $form->editfieldval($langs->trans("MaxPeriodNumber"), 'nb_gen_max', $object->nb_gen_max?$object->nb_gen_max:'', $object, $user->rights->facture->creer);
+		}
+		else
+		{
+		    print '';
+		}
+		print '</td>';
+		print '</tr>';
+		
+		// Status of generated invoices
+		print '<tr><td>';
+		if ($action == 'auto_validate' || $object->frequency > 0)
+		    print $form->editfieldkey($langs->trans("StatusOfGeneratedInvoices"), 'auto_validate', $object->auto_validate, $object, $user->rights->facture->creer);
+		else
+		    print $langs->trans("StatusOfGeneratedInvoices");
+		print '</td><td colspan="5">';
+    	$select = 'select;0:'.$langs->trans('BillStatusDraft').',1:'.$langs->trans('BillStatusValidated');
+		if ($action == 'auto_validate' || $object->frequency > 0)
+		{
+    		print $form->editfieldval($langs->trans("StatusOfGeneratedInvoices"), 'auto_validate', $object->auto_validate, $object, $user->rights->facture->creer, $select);
+		}
+		print '</td>';
+		print '</tr>';
+		
+		print '</table>';
+		
+    	print '<br>';
+		
+		if ($object->frequency > 0)
+		{
     		
-    		// Max period / Rest period
-    		print '<tr><td>';
-    		if ($action == 'nb_gen_max' || $object->frequency > 0)
-    		{
-    		    print $form->editfieldkey($langs->trans("MaxPeriodNumber"), 'nb_gen_max', $object->nb_gen_max, $object, $user->rights->facture->creer);
-    		}
-    		else
-    		{
-    		    print $langs->trans("MaxPeriodNumber");
-    		}
-    		print '</td><td colspan="5">';
-    		if ($action == 'nb_gen_max' || $object->frequency > 0)
-    		{
-    		      print $form->editfieldval($langs->trans("MaxPeriodNumber"), 'nb_gen_max', $object->nb_gen_max, $object, $user->rights->facture->creer);
-    		}
-    		else
-    		{
-    		    print '';
-    		}
-    		print '</td>';
-    		print '</tr>';
+    		print '<table class="border" width="100%">';
     		
-    		print '<tr><td>'.$langs->trans("RestPeriodNumber").'</td>';
+    		// Nb of generation already done
+    		print '<tr><td width="20%">'.$langs->trans("NbOfGenerationDone").'</td>';
     		print '<td>';
-    		if ($object->frequency > 0)
-    		{
-    		  print ($object->nb_gen_max-$object->nb_gen_done);
-    		}
+    		print $object->nb_gen_done?$object->nb_gen_done:'';
     		print '</td>';
+    		print '</tr>';
     		
-    		// Status of generated invoices
+    		// Date last
     		print '<tr><td>';
-    		if ($action == 'auto_validate' || $object->frequency > 0)
-    		    print $form->editfieldkey($langs->trans("StatusOfGeneratedInvoices"), 'auto_validate', $object->auto_validate, $object, $user->rights->facture->creer);
-    		else
-    		    print $langs->trans("StatusOfGeneratedInvoices");
+    		print $langs->trans("DateLastGeneration");
     		print '</td><td colspan="5">';
-        	$select = 'select;0:'.$langs->trans('BillStatusDraft').',1:'.$langs->trans('BillStatusValidated');
-    		if ($action == 'auto_validate' || $object->frequency > 0)
-    		{
-        		print $form->editfieldval($langs->trans("StatusOfGeneratedInvoices"), 'auto_validate', $object->auto_validate, $object, $user->rights->facture->creer, $select);
-    		}
+    		print dol_print_date($object->date_last_gen, 'dayhour');
     		print '</td>';
     		print '</tr>';
-		//}
-		
-		print '</table>';
+    		
+    		print '</table>';
+    		
+    		print '<br>';
+		}		
 		
-		print '<br>';
-
 		/*
 		 * Lines
 		 */
@@ -886,9 +905,23 @@ else
 		 */
 		print '<div class="tabsAction">';
 
-		if ($object->statut == Facture::STATUS_DRAFT && $user->rights->facture->creer)
+		if ($object->statut == Facture::STATUS_DRAFT)
 		{
-		    	echo '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture.php?action=create&amp;socid='.$object->thirdparty->id.'&amp;fac_rec='.$object->id.'">'.$langs->trans("CreateBill").'</a></div>';
+		    if ($user->rights->facture->creer)
+		    {
+    		    if (empty($object->frequency) || $object->date_when <= $today)
+    		    {
+                    print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture.php?action=create&amp;socid='.$object->thirdparty->id.'&amp;fac_rec='.$object->id.'">'.$langs->trans("CreateBill").'</a></div>';
+    		    }
+    		    else
+    		    {
+    		        print '<div class="inline-block divButAction"><a class="butActionRefused" href="#" title="'.dol_escape_htmltag($langs->trans("DateIsNotEnough")).'">'.$langs->trans("CreateBill").'</a></div>';
+    		    }
+		    }
+		    else
+    	    {
+    		    print '<div class="inline-block divButAction"><a class="butActionRefused" href="#">'.$langs->trans("CreateBill").'</a></div>';
+    		}
 		}
 
 		if ($object->statut == Facture::STATUS_DRAFT && $user->rights->facture->supprimer)
@@ -904,20 +937,39 @@ else
 		/*
 		 *  List mode
 		 */
-		$sql = "SELECT s.nom as name, s.rowid as socid, f.rowid as facid, f.titre, f.total, f.tva as total_vat, f.total_ttc, f.frequency";
+		$sql = "SELECT s.nom as name, s.rowid as socid, f.rowid as facid, f.titre, f.total, f.tva as total_vat, f.total_ttc, f.frequency,";
+		$sql.= " f.date_last_gen, f.date_when";
 		$sql.= " FROM ".MAIN_DB_PREFIX."societe as s,".MAIN_DB_PREFIX."facture_rec as f";
 		$sql.= " WHERE f.fk_soc = s.rowid";
 		$sql.= " AND f.entity = ".$conf->entity;
 		if ($socid)	$sql .= " AND s.rowid = ".$socid;
 
-		//$sql .= " ORDER BY $sortfield $sortorder, rowid DESC ";
-		//	$sql .= $db->plimit($limit + 1,$offset);
-
+        $nbtotalofrecords = 0;
+        if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
+        {
+        	$result = $db->query($sql);
+        	$nbtotalofrecords = $db->num_rows($result);
+        }
+        
+        $sql.= $db->plimit($limit+1,$offset);
+		
 		$resql = $db->query($sql);
 		if ($resql)
 		{
 			$num = $db->num_rows($resql);
-			print_barre_liste($langs->trans("RepeatableInvoices"),$page,$_SERVER['PHP_SELF'],"&socid=$socid",$sortfield,$sortorder,'',$num,'','title_accountancy.png');
+			
+			$param='&socid='.$socid;
+			if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit;
+				
+            print '<form method="POST" name="searchFormList" action="'.$_SERVER["PHP_SELF"].'">'."\n";
+            if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
+        	print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+        	print '<input type="hidden" name="action" value="list">';
+        	print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
+        	print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
+        	print '<input type="hidden" name="viewstatut" value="'.$viewstatut.'">';
+            
+	       print_barre_liste($langs->trans("RepeatableInvoices"),$page,$_SERVER['PHP_SELF'],$param,$sortfield,$sortorder,'',$num,$nbtotalofrecord,'title_accountancy.png',0,'','',$limit);
 
 			print $langs->trans("ToCreateAPredefinedInvoice").'<br><br>';
 
@@ -930,6 +982,8 @@ else
 			print_liste_field_titre($langs->trans("AmountVAT"),'','','','','align="right"');
 			print_liste_field_titre($langs->trans("AmountTTC"),'','','','','align="right"');
 			print_liste_field_titre($langs->trans("RecurringInvoiceTemplate"),'','','','','align="center"');
+			print_liste_field_titre($langs->trans("DateLastGeneration"),'','','','','align="center"');
+			print_liste_field_titre($langs->trans("NextDateToExecution"),'','','','','align="center"');
 			print_liste_field_titre('');		// Field may contains ling text
 			print "</tr>\n";
 
@@ -954,12 +1008,21 @@ else
 					print '<td align="right">'.price($objp->total_vat).'</td>'."\n";
 					print '<td align="right">'.price($objp->total_ttc).'</td>'."\n";
 					print '<td align="center">'.yn($objp->frequency?1:0).'</td>';
+					print '<td align="center">'.($objp->frequency ? dol_print_date($objp->date_last_gen,'day') : '').'</td>';
+					print '<td align="center">'.($objp->frequency ? dol_print_date($objp->date_when,'day') : '').'</td>';
 						
 					print '<td align="center">';
 					if ($user->rights->facture->creer)
 					{
-                        print '<a href="'.DOL_URL_ROOT.'/compta/facture.php?action=create&amp;socid='.$objp->socid.'&amp;fac_rec='.$objp->facid.'">';
-                        print $langs->trans("CreateBill").'</a>';
+				        if (empty($objp->frequency) || $objp->date_when <= $today)
+				        {
+                            print '<a href="'.DOL_URL_ROOT.'/compta/facture.php?action=create&amp;socid='.$objp->socid.'&amp;fac_rec='.$objp->facid.'">';
+                            print $langs->trans("CreateBill").'</a>';
+				        }
+				        else
+				        {
+				            print $langs->trans("DateIsNotEnough");
+				        }
 					}
 					else
 					{

+ 8 - 10
htdocs/compta/facture/list.php

@@ -75,9 +75,9 @@ $search_paymentmode=GETPOST('search_paymentmode','int');
 $option = GETPOST('option');
 if ($option == 'late') $filter = 'paye:0';
 
+$limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit;
 $sortfield = GETPOST("sortfield",'alpha');
 $sortorder = GETPOST("sortorder",'alpha');
-$limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit;
 $page = GETPOST("page",'int');
 if ($page == -1) {
     $page = 0;
@@ -85,7 +85,6 @@ if ($page == -1) {
 $offset = $limit * $page;
 if (! $sortorder) $sortorder='DESC';
 if (! $sortfield) $sortfield='f.datef';
-
 $pageprev = $page - 1;
 $pagenext = $page + 1;
 
@@ -713,6 +712,13 @@ if ($resql)
     
     $i = 0;
     print '<form method="POST" name="searchFormList" action="'.$_SERVER["PHP_SELF"].'">'."\n";
+    if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
+    print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+    print '<input type="hidden" name="action" value="list">';
+    print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
+    print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
+    print '<input type="hidden" name="viewstatut" value="'.$viewstatut.'">';
+    
 	print_barre_liste($langs->trans('BillsCustomers').' '.($socid?' '.$soc->name:''),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,$massactionbutton,$num,$nbtotalofrecords,'title_accountancy.png',0,'','',$limit);
 
 	if ($massaction == 'presend')
@@ -811,14 +817,6 @@ if ($resql)
         dol_fiche_end();
 	}
 	
-	
-    if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
-	print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
-	print '<input type="hidden" name="action" value="list">';
-	print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
-	print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
-	print '<input type="hidden" name="viewstatut" value="'.$viewstatut.'">';
-
     if ($sall)
     {
         foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val);

+ 2 - 2
htdocs/core/class/commonobject.class.php

@@ -1185,7 +1185,7 @@ abstract class CommonObject
     }
 
     /**
-     *	Load value from specific field
+     *	Getter generic. Load value from a specific field
      *
      *	@param	string	$table		Table of element or element line
      *	@param	int		$id			Element id
@@ -1211,7 +1211,7 @@ abstract class CommonObject
     }
 
     /**
-     *	Update a specific field into database
+     *	Setter generic. Update a specific field into database
      *
      *	@param	string		$field		Field to update
      *	@param	mixed		$value		New value

+ 1 - 1
htdocs/core/lib/date.lib.php

@@ -112,7 +112,7 @@ function getServerTimeZoneInt($refgmtdate='now')
  *  @param      int			$duration_unit      Unit of added delay (d, m, y, w, h)
  *  @return     int      			        	New timestamp
  */
-function dol_time_plus_duree($time,$duration_value,$duration_unit)
+function dol_time_plus_duree($time, $duration_value, $duration_unit)
 {
 	if ($duration_value == 0)  return $time;
 	if ($duration_unit == 'h') return $time + (3600*$duration_value);

+ 1 - 1
htdocs/core/modules/modFacture.class.php

@@ -107,7 +107,7 @@ class modFacture extends DolibarrModules
 
         // Cronjobs 
         $this->cronjobs = array(
-            0=>array('label'=>'RecurringInvoices', 'jobtype'=>'method', 'class'=>'compta/facture/class/facture-rec.class.php', 'objectname'=>'FactureRec', 'method'=>'generateRecurringInvoices', 'parameters'=>'', 'comment'=>'Generate recurring invoices', 'frequency'=>1, 'unitfrequency'=>3600*24), 
+            0=>array('label'=>'RecurringInvoices', 'jobtype'=>'method', 'class'=>'compta/facture/class/facture-rec.class.php', 'objectname'=>'FactureRec', 'method'=>'createRecurringInvoices', 'parameters'=>'', 'comment'=>'Generate recurring invoices', 'frequency'=>1, 'unitfrequency'=>3600*24), 
             // 1=>array('label'=>'My label', 'jobtype'=>'command', 'command'=>'', 'parameters'=>'', 'comment'=>'Comment', 'frequency'=>3600, 'unitfrequency'=>3600)
         ); 
         // List of cron jobs entries to add 

+ 27 - 18
htdocs/cron/card.php

@@ -543,7 +543,8 @@ else
 	/*
 	 * view card
 	 */
-
+    $now = dol_now();
+    
 	dol_fiche_head($head, 'card', $langs->trans("CronTask"), 0, 'cron');
 
 	$linkback = '<a href="' . DOL_URL_ROOT . '/cron/list.php?status=-2">' . $langs->trans("BackToList") . '</a>';
@@ -624,12 +625,12 @@ else
 	
 	print '<tr><td width="30%">';
 	print $langs->trans('CronDtStart')."</td><td>";
-	if(!empty($object->datestart)) {print dol_print_date($object->datestart,'dayhourtext');}
+	if(!empty($object->datestart)) {print dol_print_date($object->datestart,'dayhoursec');}
 	print "</td></tr>";
 	
 	print "<tr><td>";
 	print $langs->trans('CronDtEnd')."</td><td>";
-	if(!empty($object->dateend)) {print dol_print_date($object->dateend,'dayhourtext');}
+	if(!empty($object->dateend)) {print dol_print_date($object->dateend,'dayhoursec');}
 	print "</td></tr>";
 	
 	print "<tr><td>";
@@ -647,11 +648,16 @@ else
 	print "<td>".$object->nbrun;
 	print "</td></tr>";
 	
+	// Date next run (from)
 	print '<tr><td>';
 	print $langs->trans('CronDtNextLaunch');
 	print ' ('.$langs->trans('CronFrom').')';
 	print "</td><td>";
-	if(!empty($object->datenextrun)) {print dol_print_date($object->datenextrun,'dayhoursec');} else {print $langs->trans('CronNone');}
+	print '<strong>';
+	if (!empty($object->datenextrun)) {print dol_print_date($object->datenextrun,'dayhoursec');} else {print $langs->trans('CronNone');}
+	if ($object->maxnbrun && $object->nbrun >= $object->maxrun) print img_warning($langs->trans("Finished"));
+	if ($object->datenextrun && $object->datenextrun < $now) print img_warning($langs->trans("Late"));
+	print '</strong>';
 	print "</td></tr>";
 	
 	print '</table>';
@@ -691,20 +697,7 @@ else
 	} else {
 		print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=edit&id='.$object->id.'">'.$langs->trans("Edit").'</a>';
 	}
-	if (! $user->rights->cron->delete) {
-		print '<a class="butActionRefused" href="#" title="'.dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions")).'">'.$langs->trans("Delete").'</a>';
-	} else {
-		print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=delete&id='.$object->id.'">'.$langs->trans("Delete").'</a>';
-	}
-	if (! $user->rights->cron->create) {
-		print '<a class="butActionRefused" href="#" title="'.dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions")).'">'.$langs->trans("CronStatusActiveBtn").'/'.$langs->trans("CronStatusInactiveBtn").'</a>';
-	} else {
-		if (empty($object->status)) {
-			print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=activate&id='.$object->id.'">'.$langs->trans("CronStatusActiveBtn").'</a>';
-		} else {
-			print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=inactive&id='.$object->id.'">'.$langs->trans("CronStatusInactiveBtn").'</a>';
-		}
-	}
+
 	if ((empty($user->rights->cron->execute)))
 	{
 		print '<a class="butActionRefused" href="#" title="'.dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions")).'">'.$langs->trans("CronExecute").'</a>';
@@ -716,6 +709,22 @@ else
 	else {
 		print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=execute&id='.$object->id.'">'.$langs->trans("CronExecute").'</a>';
 	}
+	
+	if (! $user->rights->cron->create) {
+	    print '<a class="butActionRefused" href="#" title="'.dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions")).'">'.$langs->trans("CronStatusActiveBtn").'/'.$langs->trans("CronStatusInactiveBtn").'</a>';
+	} else {
+	    if (empty($object->status)) {
+	        print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=activate&id='.$object->id.'">'.$langs->trans("CronStatusActiveBtn").'</a>';
+	    } else {
+	        print '<a class="butActionDelete" href="'.$_SERVER['PHP_SELF'].'?action=inactive&id='.$object->id.'">'.$langs->trans("CronStatusInactiveBtn").'</a>';
+	    }
+	}
+	
+	if (! $user->rights->cron->delete) {
+		print '<a class="butActionDeleteRefused" href="#" title="'.dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions")).'">'.$langs->trans("Delete").'</a>';
+	} else {
+		print '<a class="butActionDelete" href="'.$_SERVER['PHP_SELF'].'?action=delete&id='.$object->id.'">'.$langs->trans("Delete").'</a>';
+	}
 	print '</div>';
 
 	print '<br>';

+ 9 - 10
htdocs/cron/class/cronjob.class.php

@@ -876,7 +876,6 @@ class Cronjob extends CommonObject
 
 		dol_syslog(get_class($this)."::run_jobs jobtype=".$this->jobtype." userlogin=".$userlogin, LOG_DEBUG);
 
-
 		// Increase limit of time. Works only if we are not in safe mode
 		$ExecTimeLimit=600;
 		if (!empty($ExecTimeLimit))
@@ -892,13 +891,12 @@ class Cronjob extends CommonObject
 			@ini_set('memory_limit', $MemoryLimit);
 		}
 
-
-		// Update last run date (to track running jobs)
+		// Update last run date start (to track running jobs)
 		$this->datelastrun=$now;
 		$this->lastoutput='';
 		$this->lastresult='';
 		$this->nbrun=$this->nbrun + 1;
-		$result = $this->update($user);
+		$result = $this->update($user);       // This include begin/commit
 		if ($result<0) {
 			dol_syslog(get_class($this)."::run_jobs ".$this->error, LOG_ERR);
 			return -1;
@@ -953,7 +951,7 @@ class Cronjob extends CommonObject
 			
 			if (! $error)
 			{
-				dol_syslog(get_class($this)."::run_jobs ".$this->objectname."->".$this->methodename."(".$this->params.");", LOG_DEBUG);
+				dol_syslog(get_class($this)."::run_jobs START ".$this->objectname."->".$this->methodename."(".$this->params.");", LOG_DEBUG);
 	
 				// Create Object for the call module
 				$object = new $this->objectname($this->db);
@@ -971,16 +969,17 @@ class Cronjob extends CommonObject
 				if ($result===false || $result != 0)
 				{
 				    $langs->load("errors");
-					dol_syslog(get_class($this)."::run_jobs result=".$result." error=".$object->error, LOG_ERR);
-					$this->error = $object->error?$object->error:$langs->trans('ErrorUnknown');
-					$this->lastoutput = $this->error;
+					dol_syslog(get_class($this)."::run_jobs END result=".$result." error=".$object->error, LOG_ERR);
+				    $this->error = $object->error?$object->error:$langs->trans('ErrorUnknown');
+					$this->lastoutput = ($object->output?$object->output."\n":"").$this->error;
 					$this->lastresult = is_numeric($result)?$result:-1;
 		            $retval = $this->lastresult;
 		            $error++;
 				}
 				else
 				{
-					$this->lastoutput=$object->output;
+					dol_syslog(get_class($this)."::run_jobs END");
+				    $this->lastoutput=$object->output;
 					$this->lastresult=var_export($result,true);
 					$retval = $this->lastresult;
 				}
@@ -1093,7 +1092,7 @@ class Cronjob extends CommonObject
 		}
 		$this->lastresult=$retval;
 		$this->datelastresult=dol_now();
-		$result = $this->update($user);
+		$result = $this->update($user);       // This include begin/commit
 		if ($result < 0)
 		{
 			dol_syslog(get_class($this)."::run_jobs ".$this->error, LOG_ERR);

+ 28 - 14
htdocs/cron/list.php

@@ -39,9 +39,19 @@ $action=GETPOST('action','alpha');
 $confirm=GETPOST('confirm','alpha');
 $id=GETPOST('id','int');
 
-$sortorder=GETPOST('sortorder','alpha');
-$sortfield=GETPOST('sortfield','alpha');
-$page=GETPOST('page','int');
+$limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit;
+$sortfield = GETPOST("sortfield",'alpha');
+$sortorder = GETPOST("sortorder",'alpha');
+$page = GETPOST("page",'int');
+if ($page == -1) {
+    $page = 0;
+}
+$offset = $limit * $page;
+if (! $sortorder) $sortorder='ASC';
+if (! $sortfield) $sortfield='t.label';
+$pageprev = $page - 1;
+$pagenext = $page + 1;
+
 $status=GETPOST('status','int');
 if ($status == '') $status=-2;
 
@@ -55,11 +65,6 @@ if ($page == -1) {
     $page = 0 ;
 }
 
-$limit = $conf->global->MAIN_SIZE_LISTE_LIMIT;
-$offset = $limit * $page ;
-$pageprev = $page - 1;
-$pagenext = $page + 1;
-
 
 /*
  * Actions
@@ -137,6 +142,7 @@ $pagetitle=$langs->trans("CronList");
 llxHeader('',$pagetitle);
 
 // list of jobs created
+// TODO Replace this with an embedded select.
 $object = new Cronjob($db);
 $result=$object->fetch_all($sortorder, $sortfield, $limit, $offset, $status, $filter);
 if ($result < 0)
@@ -148,12 +154,8 @@ $num=count($object->lines);
 
 $param='&page='.$page.'&status='.$status.'&search_label='.$search_label;
 
+$stringcurrentdate = $langs->trans("CurrentHour").': '.dol_print_date(dol_now(), 'dayhour');
 
-print_barre_liste($pagetitle, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_setup');
-
-
-print $langs->trans('CronInfo');
-print "<br><br>";
 
 
 if ($action == 'delete')
@@ -171,6 +173,18 @@ if ($action == 'execute')
 
 print '<form method="GET" action="'.$url_form.'" name="search_form">'."\n";
 print '<input type="hidden" name="status" value="'.$status.'" >';
+if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
+print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+print '<input type="hidden" name="action" value="list">';
+print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
+print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
+print '<input type="hidden" name="viewstatut" value="'.$viewstatut.'">';
+
+print_barre_liste($pagetitle, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $stringcurrentdate, $num, 0, 'title_setup', 0, '', '', $limit);
+
+print $langs->trans('CronInfo');
+print "<br><br>";
+
 
 print '<table width="100%" class="noborder">';
 print '<tr class="liste_titre">';
@@ -303,7 +317,7 @@ if ($num > 0)
 		print '</td>';
 
 		print '<td>';
-		if(!empty($line->lastoutput)) {print dol_trunc(nl2br($line->lastoutput),100);}
+		if(!empty($line->lastoutput)) {print dol_trunc(nl2br($line->lastoutput),50);}
 		print '</td>';
 
 		// Status

+ 9 - 2
htdocs/langs/en_US/bills.lang

@@ -141,6 +141,8 @@ BillFrom=From
 BillTo=To
 ActionsOnBill=Actions on invoice
 RecurringInvoiceTemplate=Recurring invoice
+NoQualifiedRecurringInvoiceTemplateFound=No recurring template invoice qualified for generation.
+FoundXQualifiedRecurringInvoiceTemplate=Found %s recurring template invoice(s) qualified for generation.
 NotARecurringInvoiceTemplate=Not a recurring template invoice
 NewBill=New invoice
 LastBills=Last %s invoices
@@ -313,10 +315,15 @@ FrequencyPer_d=Every %s days
 FrequencyPer_m=Every %s months
 FrequencyPer_y=Every %s years
 toolTipFrequency=Examples:<br /><b>Set 7 / day</b>: give a new invoice every 7 days<br /><b>Set 3 / month</b>: give a new invoice every 3 month
-NextDateToExecution=Next date to execution
-MaxPeriodNumber=Max period number
+NextDateToExecution=Date for next invoice generation
+DateLastGeneration=Date of last generation
+MaxPeriodNumber=Max nb of invoice generation
 RestPeriodNumber=Rest period number
+NbOfGenerationDone=Nb of invoice generation already done
 InvoiceAutoValidate=Automatically validate invoice
+GeneratedFromRecurringInvoice=Generated from template recurring invoice %s
+DateIsNotEnough=Date not yet reached
+InvoiceGeneratedFromTemplate=Invoice %s generated from recurring template invoice %s
 # PaymentConditions
 PaymentConditionShortRECEP=Immediate
 PaymentConditionRECEP=Immediate

+ 2 - 2
htdocs/langs/en_US/cron.lang

@@ -40,7 +40,8 @@ CronNone=None
 CronDtStart=Not before
 CronDtEnd=Not after
 CronDtNextLaunch=Next execution
-CronDtLastLaunch=Last execution
+CronDtLastLaunch=Start date of last execution
+CronDtLastResult=End date of last execution
 CronFrequency=Frequency
 CronClass=Class
 CronMethod=Method
@@ -69,7 +70,6 @@ CronErrEndDateStartDt=End date cannot be before start date
 CronStatusActiveBtn=Enable
 CronStatusInactiveBtn=Disable
 CronTaskInactive=This job is disabled
-CronDtLastResult=Last result date
 CronId=Id
 CronClassFile=Classes (filename.class.php)
 CronModuleHelp=Name of Dolibarr module directory (also work with external Dolibarr module). <BR> For exemple to fetch method of Dolibarr Product object /htdocs/<u>product</u>/class/product.class.php, the value of module is <i>product</i>