From 33ceb22b8b814aca7a43616bb801f29692bf0f98 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur <eldy@destailleur.fr>
Date: Wed, 12 Jul 2017 13:25:18 +0200
Subject: [PATCH] Fix error management into modulebuilder

---
 htdocs/core/class/commonobject.class.php      | 337 +++++++++++-----
 htdocs/core/lib/modulebuilder.lib.php         |  96 ++---
 htdocs/langs/en_US/main.lang                  |   1 +
 .../template/class/myobject.class.php         | 364 +-----------------
 .../modulebuilder/template/myobject_card.php  |  84 ++--
 .../modulebuilder/template/myobject_list.php  |  46 +--
 6 files changed, 353 insertions(+), 575 deletions(-)

diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php
index 96de26be956..7917234158e 100644
--- a/htdocs/core/class/commonobject.class.php
+++ b/htdocs/core/class/commonobject.class.php
@@ -599,7 +599,7 @@ abstract class CommonObject
         }
 
         $datecreate = dol_now();
-		
+
         // Socpeople must have already been added by some a trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
         $TListeContacts=$this->liste_contact(-1, $source);
         $already_added=false;
@@ -611,11 +611,11 @@ abstract class CommonObject
 	        	}
 	        }
         }
-        
+
         if(!$already_added) {
-        	
+
         	$this->db->begin();
-        	
+
 	        // Insertion dans la base
 	        $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
 	        $sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
@@ -623,7 +623,7 @@ abstract class CommonObject
 	        $sql.= "'".$this->db->idate($datecreate)."'";
 	        $sql.= ", 4, ". $id_type_contact;
 	        $sql.= ")";
-	        
+
 	        $resql=$this->db->query($sql);
 	        if ($resql)
 	        {
@@ -636,7 +636,7 @@ abstract class CommonObject
 		                return -1;
 		            }
 	            }
-	
+
 	            $this->db->commit();
 	            return 1;
 	        }
@@ -4726,6 +4726,9 @@ abstract class CommonObject
 		return $buyPrice;
 	}
 
+
+
+
 	/**
 	 * Function test if type is date
 	 *
@@ -4734,7 +4737,7 @@ abstract class CommonObject
 	 */
 	protected function isDate($info)
 	{
-		if(isset($info['type']) && $info['type']=='date') return true;
+		if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
 		else return false;
 	}
 
@@ -4880,104 +4883,183 @@ abstract class CommonObject
 		return $query;
 	}
 
+	/**
+	 * Function to load data into current object this
+	 *
+	 * @param   stdClass    $obj    Contain data of object from database
+	 */
+	private function set_vars_by_db(&$obj)
+	{
+	    foreach ($this->fields as $field => $info)
+	    {
+	        if($this->isDate($info))
+	        {
+	            if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
+	            else $this->{$field} = strtotime($obj->{$field});
+	        }
+	        elseif($this->isArray($info))
+	        {
+	            $this->{$field} = @unserialize($obj->{$field});
+	            // Hack for data not in UTF8
+	            if($this->{$field } === FALSE) @unserialize(utf8_decode($obj->{$field}));
+	        }
+	        elseif($this->isInt($info))
+	        {
+	            $this->{$field} = (int) $obj->{$field};
+	        }
+	        elseif($this->isFloat($info))
+	        {
+	            $this->{$field} = (double) $obj->{$field};
+	        }
+	        elseif($this->isNull($info))
+	        {
+	            $val = $obj->{$field};
+	            // zero is not null
+	            $this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
+	        }
+	        else
+	        {
+	            $this->{$field} = $obj->{$field};
+	        }
+
+	    }
+	}
+
+	/**
+	 * Function to concat keys of fields
+	 *
+	 * @return string
+	 */
+	private function get_field_list()
+	{
+	    $keys = array_keys($this->fields);
+	    return implode(',', $keys);
+	}
+
+	/**
+	 * Add quote to field value if necessary
+	 *
+	 * @param string|int	$value	value to protect
+	 * @return string|int
+	 */
+	protected function quote($value) {
+
+	    if(is_null($value)) return 'NULL';
+	    else if(is_numeric($value)) return $value;
+	    else return "'".$this->db->escape( $value )."'";
+
+	}
+
+
 	/**
 	 * Create object into database
 	 *
 	 * @param  User $user      User that creates
 	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
-	 *
-	 * @return int <0 if KO, Id of created object if OK
+	 * @return int             <0 if KO, Id of created object if OK
 	 */
 	public function createCommon(User $user, $notrigger = false)
 	{
+        $error = 0;
 
 	    $fields = array_merge(array('datec'=>$this->db->idate(dol_now())), $this->set_save_query());
 
 	    foreach ($fields as $k => $v) {
-
 	    	$keys[] = $k;
 	    	$values[] = $this->quote($v);
-
 	    }
-	    $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element.'
-					( '.implode( ",", $keys ).' )
-					VALUES ( '.implode( ",", $values ).' ) ';
-	    $res = $this->db->query( $sql );
-	    if($res===false) {
 
-	    	return false;
+	    $this->db->begin();
+
+	    if (! $error)
+	    {
+    	    $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element.'
+    					( '.implode( ",", $keys ).' )
+    					VALUES ( '.implode( ",", $values ).' ) ';
+    	    $res = $this->db->query( $sql );
+    	    if ($res===false) {
+    	        $error++;
+    	        $this->errors[] = $this->db->lasterror();
+    	    }
 	    }
 
-	    // TODO Add triggers
+        if (! $error && ! $notrigger) {
+            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
 
-	    return true;
+            if (!$notrigger) {
+                // Call triggers
+                $result=$this->call_trigger(strtoupper(get_class(self)).'_CREATE',$user);
+                if ($result < 0) { $error++; }
+                // End call triggers
+            }
+        }
 
+		// Commit or rollback
+		if ($error) {
+			$this->db->rollback();
+			return -1;
+		} else {
+			$this->db->commit();
+			return $this->id;
+		}
 	}
 
 	/**
-	 * Function to load data into current object this
+	 * Load an object from its id and create a new one in database
 	 *
-	 * @param   stdClass    $obj    Contain data of object from database
+	 * @param  User $user      User that creates
+	 * @param  int $fromid     Id of object to clone
+	 * @return int             New id of clone
 	 */
-	private function set_vars_by_db(&$obj)
+	public function createFromCloneCommon(User $user, $fromid)
 	{
-		foreach ($this->fields as $field => $info)
-		{
-			if($this->isDate($info))
-			{
-				if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
-				else $this->{$field} = strtotime($obj->{$field});
-			}
-			elseif($this->isArray($info))
-			{
-				$this->{$field} = @unserialize($obj->{$field});
-				// Hack for data not in UTF8
-				if($this->{$field } === FALSE) @unserialize(utf8_decode($obj->{$field}));
-			}
-			elseif($this->isInt($info))
-			{
-				$this->{$field} = (int) $obj->{$field};
-			}
-			elseif($this->isFloat($info))
-			{
-				$this->{$field} = (double) $obj->{$field};
-			}
-			elseif($this->isNull($info))
-			{
-				$val = $obj->{$field};
-				// zero is not null
-				$this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
-			}
-			else
-			{
-				$this->{$field} = $obj->{$field};
-			}
+	    global $user;
 
-		}
-	}
+	    $error = 0;
 
-	/**
-	 * Function to concat keys of fields
-	 *
-	 * @return string
-	 */
-	private function get_field_list()
-	{
-		$keys = array_keys($this->fields);
-		return implode(',', $keys);
+	    dol_syslog(__METHOD__, LOG_DEBUG);
+
+	    $object = new self($this->db);
+
+	    $this->db->begin();
+
+	    // Load source object
+	    $object->fetchCommon($fromid);
+	    // Reset object
+	    $object->id = 0;
+
+	    // Clear fields
+	    // ...
+
+	    // Create clone
+	    $result = $object->createCommon($user);
+
+	    // Other options
+	    if ($result < 0) {
+	        $error ++;
+	        $this->errors = $object->errors;
+	        dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+	    }
+
+	    // End
+	    if (!$error) {
+	        $this->db->commit();
+	        return $object->id;
+	    } else {
+	        $this->db->rollback();
+	        return -1;
+	    }
 	}
 
 	/**
 	 * Load object in memory from the database
 	 *
-	 * @param int    $id  Id object
-	 * @param string $ref Ref
-	 *
-	 * @return int <0 if KO, 0 if not found, >0 if OK
+	 * @param int    $id   Id object
+	 * @param string $ref  Ref
+	 * @return int         <0 if KO, 0 if not found, >0 if OK
 	 */
 	public function fetchCommon($id, $ref = null)
 	{
-
 		if (empty($id) && empty($ref)) return false;
 
 		$sql = 'SELECT '.$this->get_field_list().', datec, tms';
@@ -4991,13 +5073,20 @@ abstract class CommonObject
 		{
     		if ($obj = $this->db->fetch_object($res))
     		{
-    			$this->id = $id;
-    			$this->set_vars_by_db($obj);
-
-    			$this->datec = $this->db->idate($obj->datec);
-    			$this->tms = $this->db->idate($obj->tms);
-
-    			return $this->id;
+    		    if ($obj)
+    		    {
+        			$this->id = $id;
+        			$this->set_vars_by_db($obj);
+
+        			$this->datec = $this->db->idate($obj->datec);
+        			$this->tms = $this->db->idate($obj->tms);
+
+        			return $this->id;
+    		    }
+    		    else
+    		    {
+    		        return 0;
+    		    }
     		}
     		else
     		{
@@ -5019,15 +5108,15 @@ abstract class CommonObject
 	 *
 	 * @param  User $user      User that modifies
 	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
-	 *
-	 * @return int <0 if KO, >0 if OK
+	 * @return int             <0 if KO, >0 if OK
 	 */
 	public function updateCommon(User $user, $notrigger = false)
 	{
+	    $error = 0;
+
 		$fields = $this->set_save_query();
 
 		foreach ($fields as $k => $v) {
-
 			if (is_array($key)){
 				$i=array_search($k, $key);
 				if ( $i !== false) {
@@ -5040,57 +5129,93 @@ abstract class CommonObject
 					continue;
 				}
 			}
-
 			$tmp[] = $k.'='.$this->quote($v);
 		}
 		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode( ',', $tmp ).' WHERE rowid='.$this->id ;
-		$res = $this->db->query( $sql );
 
-		if($res===false) {
-			//error
-			return false;
+		$this->db->begin();
+
+		if (! $error)
+		{
+    		$res = $this->db->query($sql);
+    		if ($res===false)
+    		{
+    		    $error++;
+    	        $this->errors[] = $this->db->lasterror();
+    		}
 		}
 
-		// TODO Add triggers
+		if (! $error && ! $notrigger) {
+		    // Call triggers
+		    $result=$this->call_trigger(strtoupper(get_class(self)).'_MODIFY',$user);
+		    if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
+		    // End call triggers
+		}
 
-		return true;
+		// Commit or rollback
+		if ($error) {
+		    $this->db->rollback();
+		    return -1;
+		} else {
+		    $this->db->commit();
+		    return $this->id;
+		}
 	}
 
 	/**
 	 * Delete object in database
 	 *
-	 * @param User $user      User that deletes
-	 * @param bool $notrigger false=launch triggers after, true=disable triggers
-	 *
-	 * @return int <0 if KO, >0 if OK
+	 * @param User $user       User that deletes
+	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, >0 if OK
 	 */
 	public function deleteCommon(User $user, $notrigger = false)
 	{
-		$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
+	    $error=0;
 
-		$res = $this->db->query( $sql );
-		if($res===false) {
-			return false;
-		}
+	    $this->db->begin();
+
+	    if (! $error) {
+	        if (! $notrigger) {
+	            // Call triggers
+	            $result=$this->call_trigger(strtoupper(get_class(self)).'_DELETE', $user);
+	            if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
+	            // End call triggers
+	        }
+	    }
 
-		// TODO Add triggers
+	    if (! $error)
+	    {
+    		$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
 
-		return true;
+    		$res = $this->db->query($sql);
+    		if($res===false) {
+    		    $error++;
+    		    $this->errors[] = $this->db->lasterror();
+    		}
+	    }
+
+    	// Commit or rollback
+		if ($error) {
+		    $this->db->rollback();
+		    return -1;
+		} else {
+		    $this->db->commit();
+		    return 1;
+		}
 	}
 
 	/**
-	 * Add quote to field value if necessary
+	 * Initialise object with example values
+	 * Id must be 0 if object instance is a specimen
 	 *
-	 * @param string|int	$value	value to protect
-	 * @return string|int
+	 * @return void
 	 */
-	protected function quote($value) {
-
-	    if(is_null($value)) return 'NULL';
-	    else if(is_numeric($value)) return $value;
-	    else return "'".$this->db->escape( $value )."'";
+	public function initAsSpecimenCommon()
+	{
+	    $this->id = 0;
 
+	    // TODO...
 	}
 
 }
-
diff --git a/htdocs/core/lib/modulebuilder.lib.php b/htdocs/core/lib/modulebuilder.lib.php
index 7c14f139c59..cb5d143cff9 100644
--- a/htdocs/core/lib/modulebuilder.lib.php
+++ b/htdocs/core/lib/modulebuilder.lib.php
@@ -55,67 +55,69 @@ function rebuildObjectClass($destdir, $module, $objectname, $newmask)
     {
         include_once $pathoffiletoeditsrc;
         if (class_exists($objectname)) $object=new $objectname($db);
-    }
-    catch(Exception $e)
-    {
-        print $e->getMessage();
-    }
 
-    // Edit class files
+        // Backup old file
+        dol_copy($pathoffiletoeditsrc, $pathoffiletoeditsrc.'.back', $newmask, 1);
 
-    $contentclass = file_get_contents(dol_osencode($pathoffiletoeditsrc), 'r');
+        // Edit class files
+        $contentclass = file_get_contents(dol_osencode($pathoffiletoeditsrc), 'r');
 
-    $i=0;
-    $texttoinsert = '// BEGIN MODULEBUILDER PROPERTIES'."\n";
-    $texttoinsert.= "\t".'/**'."\n";
-    $texttoinsert.= "\t".' * @var array  Array with all fields and their property'."\n";
-    $texttoinsert.= "\t".' */'."\n";
-    $texttoinsert.= "\t".'public $fields=array('."\n";
+        $i=0;
+        $texttoinsert = '// BEGIN MODULEBUILDER PROPERTIES'."\n";
+        $texttoinsert.= "\t".'/**'."\n";
+        $texttoinsert.= "\t".' * @var array  Array with all fields and their property'."\n";
+        $texttoinsert.= "\t".' */'."\n";
+        $texttoinsert.= "\t".'public $fields=array('."\n";
 
-    if (count($object->fields))
-    {
-        foreach($object->fields as $key => $val)
+        if (count($object->fields))
         {
-            $i++;
-            $typephp='';
-            $texttoinsert.= "\t\t'".$key."' => array('type'=>'".$val['type']."', 'label'=>'".$val['label']."',";
-            if ($val['position']) $texttoinsert.= " 'position'=>".$val['position'].",";
-            if ($val['notnull']) $texttoinsert.= " 'notnull'=>".$val['notnull'].",";
-            if ($val['index']) $texttoinsert.= " 'index'=>".$val['index'].",";
-            if ($val['searchall']) $texttoinsert.= " 'searchall'=>".$val['searchall'].",";
-            if ($val['comment']) $texttoinsert.= " 'comment'=>'".$val['comment']."',";
-            $texttoinsert.= "),\n";
+            foreach($object->fields as $key => $val)
+            {
+                $i++;
+                $typephp='';
+                $texttoinsert.= "\t\t'".$key."' => array('type'=>'".$val['type']."', 'label'=>'".$val['label']."',";
+                if ($val['position']) $texttoinsert.= " 'position'=>".$val['position'].",";
+                if ($val['notnull']) $texttoinsert.= " 'notnull'=>".$val['notnull'].",";
+                if ($val['index']) $texttoinsert.= " 'index'=>".$val['index'].",";
+                if ($val['searchall']) $texttoinsert.= " 'searchall'=>".$val['searchall'].",";
+                if ($val['comment']) $texttoinsert.= " 'comment'=>'".$val['comment']."',";
+                $texttoinsert.= "),\n";
+            }
         }
-    }
-    $texttoinsert.= "\t".');'."\n";
+        $texttoinsert.= "\t".');'."\n";
 
-    $texttoinsert.= "\n";
+        $texttoinsert.= "\n";
 
-    if (count($object->fields))
-    {
-        foreach($object->fields as $key => $val)
+        if (count($object->fields))
         {
-            $i++;
-            $typephp='';
-            $texttoinsert.= "\t".'public $'.$key.$typephp.";";
-            //if ($key == 'rowid')  $texttoinsert.= ' AUTO_INCREMENT PRIMARY KEY';
-            //if ($key == 'entity') $texttoinsert.= ' DEFAULT 1';
-            //$texttoinsert.= ($val['notnull']?' NOT NULL':'');
-            //if ($i < count($object->fields)) $texttoinsert.=";";
-            $texttoinsert.= "\n";
+            foreach($object->fields as $key => $val)
+            {
+                $i++;
+                $typephp='';
+                $texttoinsert.= "\t".'public $'.$key.$typephp.";";
+                //if ($key == 'rowid')  $texttoinsert.= ' AUTO_INCREMENT PRIMARY KEY';
+                //if ($key == 'entity') $texttoinsert.= ' DEFAULT 1';
+                //$texttoinsert.= ($val['notnull']?' NOT NULL':'');
+                //if ($i < count($object->fields)) $texttoinsert.=";";
+                $texttoinsert.= "\n";
+            }
         }
-    }
 
-    $texttoinsert.= "\t".'// END MODULEBUILDER PROPERTIES';
+        $texttoinsert.= "\t".'// END MODULEBUILDER PROPERTIES';
 
-    $contentclass = preg_replace('/\/\/ BEGIN MODULEBUILDER PROPERTIES.*END MODULEBUILDER PROPERTIES/ims', $texttoinsert, $contentclass);
-
-    //file_put_contents($pathoffiletoedittmp, $contentclass);
-    file_put_contents(dol_osencode($pathoffiletoedittarget), $contentclass);
-    @chmod($pathoffiletoedit, octdec($newmask));
+        $contentclass = preg_replace('/\/\/ BEGIN MODULEBUILDER PROPERTIES.*END MODULEBUILDER PROPERTIES/ims', $texttoinsert, $contentclass);
 
+        //file_put_contents($pathoffiletoedittmp, $contentclass);
+        file_put_contents(dol_osencode($pathoffiletoedittarget), $contentclass);
+        @chmod($pathoffiletoedit, octdec($newmask));
 
-    return 1;
+        return 1;
+    }
+    catch(Exception $e)
+    {
+        print $e->getMessage();
+        return -1;
+    }
 }
 
 /**
diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang
index 9251db5311f..88b9d2a3fb0 100644
--- a/htdocs/langs/en_US/main.lang
+++ b/htdocs/langs/en_US/main.lang
@@ -197,6 +197,7 @@ Parameter=Parameter
 Parameters=Parameters
 Value=Value
 PersonalValue=Personal value
+NewObject=New %s
 NewValue=New value
 CurrentValue=Current value
 Code=Code
diff --git a/htdocs/modulebuilder/template/class/myobject.class.php b/htdocs/modulebuilder/template/class/myobject.class.php
index 6f55f2c9ef6..f8882b68c56 100644
--- a/htdocs/modulebuilder/template/class/myobject.class.php
+++ b/htdocs/modulebuilder/template/class/myobject.class.php
@@ -109,366 +109,6 @@ class MyObject extends CommonObject
 		$this->db = $db;
 	}
 
-	/**
-	 * Create object into database
-	 *
-	 * @param  User $user      User that creates
-	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
-	 *
-	 * @return int <0 if KO, Id of created object if OK
-	 */
-	public function create(User $user, $notrigger = false)
-	{
-		dol_syslog(__METHOD__, LOG_DEBUG);
-
-		$error = 0;
-
-		// Clean parameters
-		if (isset($this->prop1)) {
-			$this->prop1 = trim($this->prop1);
-		}
-		if (isset($this->prop2)) {
-			$this->prop2 = trim($this->prop2);
-		}
-		//...
-
-		// Check parameters
-		// Put here code to add control on parameters values
-
-		// Insert request
-		$sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . '(';
-		$sql .= ' field1,';
-		$sql .= ' field2';
-		//...
-		$sql .= ') VALUES (';
-		$sql .= ' \'' . $this->prop1 . '\',';
-		$sql .= ' \'' . $this->prop2 . '\'';
-		//...
-		$sql .= ')';
-
-		$this->db->begin();
-
-		$resql = $this->db->query($sql);
-		if (!$resql) {
-			$error ++;
-			$this->errors[] = 'Error ' . $this->db->lasterror();
-			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
-		}
-
-		if (!$error) {
-			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
-
-			if (!$notrigger) {
-				// Uncomment this and change MYOBJECT to your own tag if you
-				// want this action to call a trigger.
-
-				//// Call triggers
-				//$result=$this->call_trigger('MYOBJECT_CREATE',$user);
-				//if ($result < 0) $error++;
-				//// End call triggers
-			}
-		}
-
-		// Commit or rollback
-		if ($error) {
-			$this->db->rollback();
-
-			return - 1 * $error;
-		} else {
-			$this->db->commit();
-
-			return $this->id;
-		}
-	}
-
-	/**
-	 * Load object in memory from the database
-	 *
-	 * @param  int     $id      Id object
-	 * @param  string  $ref     Ref
-	 * @return int              <0 if KO, 0 if not found, >0 if OK
-	 */
-	public function fetch($id, $ref = null)
-	{
-		dol_syslog(__METHOD__, LOG_DEBUG);
-
-		$sql = 'SELECT';
-		$sql .= ' t.rowid,';
-		$sql .= ' t.field1,';
-		$sql .= ' t.field2';
-		//...
-		$sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
-		$sql.= ' WHERE 1 = 1';
-		if (! empty($conf->multicompany->enabled)) {
-		    $sql .= " AND entity IN (" . getEntity('mymoduleobject') . ")";
-		}
-		if (null !== $ref) {
-			$sql .= ' AND t.ref = ' . '\'' . $ref . '\'';
-		} else {
-			$sql .= ' AND t.rowid = ' . $id;
-		}
-
-		$resql = $this->db->query($sql);
-		if ($resql) {
-			$numrows = $this->db->num_rows($resql);
-			if ($numrows) {
-				$obj = $this->db->fetch_object($resql);
-
-				$this->id = $obj->rowid;
-				$this->prop1 = $obj->field1;
-				$this->prop2 = $obj->field2;
-				//...
-			}
-
-			$this->db->free($resql);
-
-			$this->fetch_optionals();
-
-			// $this->fetch_lines();
-
-			if ($numrows) {
-				return 1;
-			} else {
-				return 0;
-			}
-		} else {
-			$this->errors[] = 'Error ' . $this->db->lasterror();
-			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
-			return - 1;
-		}
-	}
-
-	/**
-	 * Load object in memory from the database
-	 *
-	 * @param string $sortorder Sort Order
-	 * @param string $sortfield Sort field
-	 * @param int    $limit     offset limit
-	 * @param int    $offset    offset limit
-	 * @param array  $filter    filter array
-	 * @param string $filtermode filter mode (AND or OR)
-	 *
-	 * @return int <0 if KO, >0 if OK
-	 */
-	public function fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter = array(), $filtermode='AND')
-	{
-		dol_syslog(__METHOD__, LOG_DEBUG);
-
-		$sql = 'SELECT';
-		$sql .= ' t.rowid,';
-		$sql .= ' t.field1,';
-		$sql .= ' t.field2';
-		//...
-		$sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element. ' as t';
-
-		// Manage filter
-		$sqlwhere = array();
-		if (count($filter) > 0) {
-			foreach ($filter as $key => $value) {
-				$sqlwhere [] = $key . ' LIKE \'%' . $this->db->escape($value) . '%\'';
-			}
-		}
-		$sql.= ' WHERE 1 = 1';
-		if (! empty($conf->multicompany->enabled)) {
-		    $sql .= " AND entity IN (" . getEntity('mymoduleobject') . ")";
-		}
-		if (count($sqlwhere) > 0) {
-			$sql .= ' AND ' . implode(' '.$filtermode.' ', $sqlwhere);
-		}
-		if (!empty($sortfield)) {
-			$sql .= $this->db->order($sortfield,$sortorder);
-		}
-		if (!empty($limit)) {
-		 $sql .=  ' ' . $this->db->plimit($limit, $offset);
-		}
-
-		$resql = $this->db->query($sql);
-		if ($resql) {
-			$num = $this->db->num_rows($resql);
-
-			while ($obj = $this->db->fetch_object($resql)) {
-				$line = new self($this->db);
-
-				$line->id = $obj->rowid;
-				$line->prop1 = $obj->field1;
-				$line->prop2 = $obj->field2;
-				//...
-			}
-			$this->db->free($resql);
-
-			return $num;
-		} else {
-			$this->errors[] = 'Error ' . $this->db->lasterror();
-			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
-
-			return - 1;
-		}
-	}
-
-	/**
-	 * Update object into database
-	 *
-	 * @param  User $user      User that modifies
-	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
-	 *
-	 * @return int <0 if KO, >0 if OK
-	 */
-	public function update(User $user, $notrigger = false)
-	{
-		dol_syslog(__METHOD__, LOG_DEBUG);
-
-		$error = 0;
-
-		// Clean parameters
-		if (isset($this->prop1)) {
-			$this->prop1 = trim($this->prop1);
-		}
-		if (isset($this->prop2)) {
-			$this->prop2 = trim($this->prop2);
-		}
-		//...
-
-		// Check parameters
-		// Put here code to add a control on parameters values
-
-		// Update request
-		$sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET';
-		$sql .= " field1=".(isset($this->field1)?"'".$this->db->escape($this->field1)."'":"null").",";
-        $sql .= " field2=".(isset($this->field2)?"'".$this->db->escape($this->field2)."'":"null")."";
-		//...
-		$sql .= ' WHERE rowid=' . $this->id;
-
-		$this->db->begin();
-
-		$resql = $this->db->query($sql);
-		if (!$resql) {
-			$error ++;
-			$this->errors[] = 'Error ' . $this->db->lasterror();
-			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
-		}
-
-		if (!$error && !$notrigger) {
-			// Uncomment this and change MYOBJECT to your own tag if you
-			// want this action calls a trigger.
-
-			//// Call triggers
-			//$result=$this->call_trigger('MYOBJECT_MODIFY',$user);
-			//if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
-			//// End call triggers
-		}
-
-		// Commit or rollback
-		if ($error) {
-			$this->db->rollback();
-
-			return - 1 * $error;
-		} else {
-			$this->db->commit();
-
-			return 1;
-		}
-	}
-
-	/**
-	 * Delete object in database
-	 *
-	 * @param User $user      User that deletes
-	 * @param bool $notrigger false=launch triggers after, true=disable triggers
-	 *
-	 * @return int <0 if KO, >0 if OK
-	 */
-	public function delete(User $user, $notrigger = false)
-	{
-		dol_syslog(__METHOD__, LOG_DEBUG);
-
-		$error = 0;
-
-		$this->db->begin();
-
-		if (!$error) {
-			if (!$notrigger) {
-				// Uncomment this and change MYOBJECT to your own tag if you
-				// want this action calls a trigger.
-
-				//// Call triggers
-				//$result=$this->call_trigger('MYOBJECT_DELETE',$user);
-				//if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
-				//// End call triggers
-			}
-		}
-
-		// If you need to delete child tables to, you can insert them here
-
-		if (!$error) {
-			$sql = 'DELETE FROM ' . MAIN_DB_PREFIX . $this->table_element;
-			$sql .= ' WHERE rowid=' . $this->id;
-
-			$resql = $this->db->query($sql);
-			if (!$resql) {
-				$error ++;
-				$this->errors[] = 'Error ' . $this->db->lasterror();
-				dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
-			}
-		}
-
-		// Commit or rollback
-		if ($error) {
-			$this->db->rollback();
-
-			return - 1 * $error;
-		} else {
-			$this->db->commit();
-
-			return 1;
-		}
-	}
-
-	/**
-	 * Load an object from its id and create a new one in database
-	 *
-	 * @param int $fromid Id of object to clone
-	 *
-	 * @return int New id of clone
-	 */
-	public function createFromClone($fromid)
-	{
-		dol_syslog(__METHOD__, LOG_DEBUG);
-
-		global $user;
-		$error = 0;
-		$object = new self($this->db);
-
-		$this->db->begin();
-
-		// Load source object
-		$object->fetch($fromid);
-		// Reset object
-		$object->id = 0;
-
-		// Clear fields
-		// ...
-
-		// Create clone
-		$result = $object->create($user);
-
-		// Other options
-		if ($result < 0) {
-			$error ++;
-			$this->errors = $object->errors;
-			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
-		}
-
-		// End
-		if (!$error) {
-			$this->db->commit();
-
-			return $object->id;
-		} else {
-			$this->db->rollback();
-
-			return - 1;
-		}
-	}
 
 	/**
 	 *  Return a link to the object card (with optionaly the picto)
@@ -592,9 +232,7 @@ class MyObject extends CommonObject
 	 */
 	public function initAsSpecimen()
 	{
-		$this->id = 0;
-		$this->prop1 = 'prop1';
-		$this->prop2 = 'prop2';
+		$this->initAsSpecimenCommon();
 	}
 
 }
diff --git a/htdocs/modulebuilder/template/myobject_card.php b/htdocs/modulebuilder/template/myobject_card.php
index 73f00abc89f..14b88767128 100644
--- a/htdocs/modulebuilder/template/myobject_card.php
+++ b/htdocs/modulebuilder/template/myobject_card.php
@@ -58,14 +58,27 @@ dol_include_once('/mymodule/class/myobject.class.php');
 $langs->loadLangs(array("mymodule","other"));
 
 // Get parameters
-$id			= GETPOST('id','int');
-$action		= GETPOST('action','alpha');
-$cancel     = GETPOST('cancel');
-$backtopage = GETPOST('backtopage');
-$myparam	= GETPOST('myparam','alpha');
+$id			= GETPOST('id', 'int');
+$action		= GETPOST('action', 'alpha');
+$cancel     = GETPOST('cancel', 'aZ09');
+$backtopage = GETPOST('backtopage', 'alpha');
 
-$search_field1=GETPOST("search_field1");
-$search_field2=GETPOST("search_field2");
+// Initialize technical objects
+$object=new MyObject($db);
+$extrafields = new ExtraFields($db);
+$diroutputmassaction=$conf->mymodule->dir_output . '/temp/massgeneration/'.$user->id;
+$hookmanager->initHooks(array('myobjectcard'));     // Note that conf->hooks_modules contains array
+// Fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label('myobject');
+$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+
+// Initialize array of search criterias
+$search_all=trim(GETPOST("search_all",'alpha'));
+$search=array();
+foreach($object->fields as $key => $val)
+{
+    if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha');
+}
 
 if (empty($action) && empty($id) && empty($ref)) $action='view';
 
@@ -76,19 +89,12 @@ if ($user->societe_id > 0)
 }
 //$result = restrictedArea($user, 'mymodule', $id);
 
-
-$object = new MyObject_Class($db);
-$extrafields = new ExtraFields($db);
-
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label($object->table_element);
 
 // Load object
 include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php';  // Must be include, not include_once  // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals
 
-// Initialize technical object to manage hooks of modules. Note that conf->hooks_modules contains array array
-$hookmanager->initHooks(array('mymodule'));
-
 
 
 /*
@@ -107,7 +113,7 @@ if (empty($reshook))
 	{
 		if ($action != 'addlink')
 		{
-			$urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/list.php',1);
+			$urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/myobject_list.php',1);
 			header("Location: ".$urltogo);
 			exit;
 		}
@@ -120,33 +126,35 @@ if (empty($reshook))
 	{
 		if ($cancel)
 		{
-			$urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/list.php',1);
+			$urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/myobject_list.php',1);
 			header("Location: ".$urltogo);
 			exit;
 		}
 
 		$error=0;
 
-		/* object_prop_getpost_prop */
-		$object->prop1=GETPOST("field1");
-		$object->prop2=GETPOST("field2");
-
-		if (empty($object->ref))
-		{
-			$error++;
-			setEventMessages($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Ref")), null, 'errors');
-		}
+        foreach ($object->fields as $key => $val)
+        {
+            $object->$key=GETPOST($key,'alpha');
+            if (in_array($key, array('entity', 'datec', 'tms'))) continue;
+            if ($val['notnull'] && $object->$key == '')
+            {
+                $error++;
+                setEventMessages($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv($val['label'])), null, 'errors');
+            }
+        }
 
 		if (! $error)
 		{
-			$result=$object->create($user);
+			$result=$object->createCommon($user);
 			if ($result > 0)
 			{
 				// Creation OK
-				$urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/list.php',1);
+				$urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/myobject_list.php',1);
 				header("Location: ".$urltogo);
 				exit;
 			}
+			else
 			{
 				// Creation KO
 				if (! empty($object->errors)) setEventMessages(null, $object->errors, 'errors');
@@ -203,7 +211,7 @@ if (empty($reshook))
 		{
 			// Delete OK
 			setEventMessages("RecordDeleted", null, 'mesgs');
-			header("Location: ".dol_buildpath('/mymodule/list.php',1));
+			header("Location: ".dol_buildpath('/mymodule/myobject_list.php',1));
 			exit;
 		}
 		else
@@ -225,10 +233,7 @@ if (empty($reshook))
 
 $form=new Form($db);
 
-llxHeader('','MyPageName','');
-
-
-// Put here content of your page
+llxHeader('','MyObject','');
 
 // Example : Adding jquery code
 print '<script type="text/javascript" language="javascript">
@@ -249,7 +254,7 @@ jQuery(document).ready(function() {
 // Part to create
 if ($action == 'create')
 {
-	print load_fiche_titre($langs->trans("NewMyModule"));
+	print load_fiche_titre($langs->trans("NewObject", $langs->transnoentitiesnoconv("MyObject")));
 
 	print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
 	print '<input type="hidden" name="action" value="add">';
@@ -258,8 +263,15 @@ if ($action == 'create')
 	dol_fiche_head();
 
 	print '<table class="border centpercent">'."\n";
-	// print '<tr><td class="fieldrequired">'.$langs->trans("Label").'</td><td><input class="flat" type="text" size="36" name="label" value="'.$label.'"></td></tr>';
-	// LIST_OF_TD_LABEL_FIELDS_CREATE
+	foreach($object->fields as $key => $val)
+	{
+	    if (in_array($key, array('rowid', 'entity', 'datec', 'tms'))) continue;
+    	print '<tr><td';
+    	print ' class="titlefieldcreate';
+    	if ($val['notnull']) print ' fieldrequired';
+    	print '"';
+    	print '>'.$langs->trans($val['label']).'</td><td><input class="flat" type="text" name="'.$key.'" value="'.(GETPOST($key,'alpha')?GETPOST($key,'alpha'):'').'"></td></tr>';
+	}
 	print '</table>'."\n";
 
 	dol_fiche_end();
@@ -343,7 +355,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
 	// Object card
 	// ------------------------------------------------------------
 
-	$linkback = '<a href="' . DOL_URL_ROOT . '/mymodule/list.php' . (! empty($socid) ? '?socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
+	$linkback = '<a href="' . DOL_URL_ROOT . '/mymodule/myobject_list.php' . (! empty($socid) ? '?socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
 
 
 	$morehtmlref='<div class="refidno">';
diff --git a/htdocs/modulebuilder/template/myobject_list.php b/htdocs/modulebuilder/template/myobject_list.php
index 569a07209f8..d024a5d9277 100644
--- a/htdocs/modulebuilder/template/myobject_list.php
+++ b/htdocs/modulebuilder/template/myobject_list.php
@@ -81,8 +81,14 @@ $offset = $limit * $page;
 $pageprev = $page - 1;
 $pagenext = $page + 1;
 
+// Initialize technical objects
 $object=new MyObject($db);
+$extrafields = new ExtraFields($db);
 $diroutputmassaction=$conf->mymodule->dir_output . '/temp/massgeneration/'.$user->id;
+$hookmanager->initHooks(array('myobjectlist'));     // Note that conf->hooks_modules contains array
+// Fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label('myobject');
+$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
 
 // Default sort order (if not yet defined by previous GETPOST)
 if (! $sortfield) $sortfield="t.".key($object->fields);   // Set here default search field. By default 1st field in definition.
@@ -104,13 +110,6 @@ foreach($object->fields as $key => $val)
     if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha');
 }
 
-// Initialize technical object to manage hooks. Note that conf->hooks_modules contains array
-$hookmanager->initHooks(array('myobjectlist'));
-$extrafields = new ExtraFields($db);
-// Fetch optionals attributes and labels
-$extralabels = $extrafields->fetch_name_optionals_label('myobject');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
-
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array();
 foreach($object->fields as $key => $val)
@@ -579,28 +578,29 @@ print '</div>'."\n";
 
 print '</form>'."\n";
 
-
-if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files)
+if ($nbtotalofrecords === '' || $nbtotalofrecords)
 {
-    require_once(DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php');
-    $formfile = new FormFile($db);
+    if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files)
+    {
+        require_once(DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php');
+        $formfile = new FormFile($db);
 
-    // Show list of available documents
-    $urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder;
-    $urlsource.=str_replace('&amp;','&',$param);
+        // Show list of available documents
+        $urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder;
+        $urlsource.=str_replace('&amp;','&',$param);
 
-    $filedir=$diroutputmassaction;
-    $genallowed=$user->rights->mymodule->read;
-    $delallowed=$user->rights->mymodule->read;
+        $filedir=$diroutputmassaction;
+        $genallowed=$user->rights->mymodule->read;
+        $delallowed=$user->rights->mymodule->read;
 
-    print $formfile->showdocuments('massfilesarea_mymodule','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'');
-}
-else
-{
-    print '<br><a name="show_files"></a><a href="'.$_SERVER["PHP_SELF"].'?show_files=1'.$param.'#show_files">'.$langs->trans("ShowTempMassFilesArea").'</a>';
+        print $formfile->showdocuments('massfilesarea_mymodule','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'');
+    }
+    else
+    {
+        print '<br><a name="show_files"></a><a href="'.$_SERVER["PHP_SELF"].'?show_files=1'.$param.'#show_files">'.$langs->trans("ShowTempMassFilesArea").'</a>';
+    }
 }
 
-
 // End of page
 llxFooter();
 $db->close();
-- 
GitLab