diff --git a/htdocs/core/modules/product/modules_product.php b/htdocs/core/modules/product/modules_product.php
new file mode 100644
index 0000000000000000000000000000000000000000..afe07e3950374299c0033ea5162147d5bfafe39a
--- /dev/null
+++ b/htdocs/core/modules/product/modules_product.php
@@ -0,0 +1,52 @@
+<?php
+/* Copyright (C) 2003-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
+ * Copyright (C) 2004-2010 Laurent Destailleur  <eldy@users.sourceforge.net>
+ * Copyright (C) 2004      Eric Seigne          <eric.seigne@ryxeo.com>
+ * Copyright (C) 2005-2012 Regis Houssin        <regis.houssin@capnetworks.com>
+ * Copyright (C) 2016		Charlie Benke		<charlie@patas-monkey.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * or see http://www.gnu.org/
+ */
+
+require_once DOL_DOCUMENT_ROOT.'/core/class/commondocgenerator.class.php';
+
+/**
+ *	\class      ModeleProduct
+ *	\brief      Parent class for product models of doc generators
+ */
+abstract class ModeleProduct extends CommonDocGenerator
+{
+    var $error='';
+
+    /**
+     *  Return list of active generation modules
+     *
+	 * 	@param	DoliDB		$db					Database handler
+     *  @param	integer		$maxfilenamelength  Max length of value to show
+     * 	@return	array							List of templates
+     */
+    static function liste_modeles($db,$maxfilenamelength=0)
+    {
+        global $conf;
+
+        $type='product';
+        $liste=array();
+
+        include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
+        $liste=getListOfModels($db,$type,$maxfilenamelength);
+
+        return $liste;
+    }
+}
diff --git a/htdocs/product/admin/product.php b/htdocs/product/admin/product.php
index 57f6577f7897250d28c79a371db22b21caaa71f8..15b113ffffbf24c68a65b4cac7d77b5739a3630d 100644
--- a/htdocs/product/admin/product.php
+++ b/htdocs/product/admin/product.php
@@ -7,6 +7,7 @@
  * Copyright (C) 2011-2012 Juanjo Menent        <jmenent@2byte.es>
  * Copyright (C) 2012      Christophe Battarel   <christophe.battarel@altairis.fr>
  * Copyright (C) 2012      Cedric Salvador      <csalvador@gpcsolutions.fr>
+ * Copyright (C) 2016		Charlie Benke		 <charlie@patas-monkey.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -42,6 +43,9 @@ if (! $user->admin || (empty($conf->product->enabled) && empty($conf->service->e
 
 $action = GETPOST('action','alpha');
 $value = GETPOST('value','alpha');
+$type = GETPOST('type','alpha');
+$label = GETPOST('label','alpha');
+$scandir = GETPOST('scandir','alpha');
 
 // Pricing Rules
 $select_pricing_rules=array(
@@ -98,12 +102,13 @@ if ($action == 'setModuleOptions')
 	if (! $error)
     {
         $db->commit();
-	    setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
+	//setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
     }
     else
     {
         $db->rollback();
-	    setEventMessages($langs->trans("Error"), null, 'errors');
+	// message yet present at the bottom if($action)
+	//setEventMessages($langs->trans("Error"), null, 'errors');
 	}
 }
 
@@ -171,6 +176,86 @@ if ($action == 'other')
 	$value = GETPOST('activate_usesearchtoselectproduct','alpha');
 	$res = dolibarr_set_const($db, "PRODUIT_USE_SEARCH_TO_SELECT", $value,'chaine',0,'',$conf->entity);
 }
+
+if ($action == 'specimen') // For products
+{
+	$modele= GETPOST('module','alpha');
+
+	$inter = new Fichinter($db);
+	$inter->initAsSpecimen();
+
+	// Search template files
+	$file=''; $classname=''; $filefound=0;
+	$dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']);
+	foreach($dirmodels as $reldir)
+	{
+	    $file=dol_buildpath($reldir."core/modules/product/doc/pdf_".$modele.".modules.php",0);
+		if (file_exists($file))
+		{
+			$filefound=1;
+			$classname = "pdf_".$modele;
+			break;
+		}
+	}
+
+	if ($filefound)
+	{
+		require_once $file;
+
+		$module = new $classname($db);
+
+		if ($module->write_file($inter,$langs) > 0)
+		{
+			header("Location: ".DOL_URL_ROOT."/document.php?modulepart=products&file=SPECIMEN.pdf");
+			return;
+		}
+		else
+		{
+			setEventMessage($obj->error,'errors');
+			dol_syslog($obj->error, LOG_ERR);
+		}
+	}
+	else
+	{
+		setEventMessage($langs->trans("ErrorModuleNotFound"),'errors');
+		dol_syslog($langs->trans("ErrorModuleNotFound"), LOG_ERR);
+	}
+}
+
+// Activate a model
+if ($action == 'set')
+{
+	$ret = addDocumentModel($value, $type, $label, $scandir);
+}
+
+if ($action == 'del')
+{
+	$ret = delDocumentModel($value, $type);
+	if ($ret > 0)
+	{
+		if ($conf->global->PRODUCT_ADDON_PDF == "$value") dolibarr_del_const($db, 'PRODUCT_ADDON_PDF',$conf->entity);
+	}
+}
+
+// Set default model
+if ($action == 'setdoc')
+{
+	if (dolibarr_set_const($db, "PRODUCT_ADDON_PDF",$value,'chaine',0,'',$conf->entity))
+	{
+		// La constante qui a ete lue en avant du nouveau set
+		// on passe donc par une variable pour avoir un affichage coherent
+		$conf->global->PRODUCT_ADDON_PDF = $value;
+	}
+
+	// On active le modele
+	$ret = delDocumentModel($value, $type);
+	if ($ret > 0)
+	{
+		$ret = addDocumentModel($value, $type, $label, $scandir);
+	}
+}
+
+
 if ($action == 'set')
 {
 	$const = "PRODUCT_SPECIAL_".strtoupper(GETPOST('spe','alpha'));
@@ -314,6 +399,143 @@ foreach ($dirproduct as $dirroot)
 }
 print '</table>';
 
+
+print '<br>';
+print_titre($langs->trans("ModelModulesProduct"));
+
+// Load array def with activated templates
+$def = array();
+$sql = "SELECT nom";
+$sql.= " FROM ".MAIN_DB_PREFIX."document_model";
+$sql.= " WHERE type = 'product'";
+$sql.= " AND entity = ".$conf->entity;
+$resql=$db->query($sql);
+if ($resql)
+{
+	$i = 0;
+	$num_rows=$db->num_rows($resql);
+	while ($i < $num_rows)
+	{
+		$array = $db->fetch_array($resql);
+		array_push($def, $array[0]);
+		$i++;
+	}
+}
+else
+{
+	dol_print_error($db);
+}
+
+print '<table class="noborder" width="100%">';
+print '<tr class="liste_titre">';
+print '<td width="140">'.$langs->trans("Name").'</td>';
+print '<td>'.$langs->trans("Description").'</td>';
+print '<td align="center" width="80">'.$langs->trans("Status").'</td>';
+print '<td align="center" width="60">'.$langs->trans("ShortInfo").'</td>';
+print '<td align="center" width="60">'.$langs->trans("Preview").'</td>';
+print "</tr>\n";
+
+$var=true;
+foreach ($dirproduct as $dirroot)
+{
+	$dir = dol_buildpath($dirroot.'core/modules/product/doc/',0);
+	$handle=@opendir($dir);
+	if (is_resource($handle))
+	{
+		while (($file = readdir($handle))!==false)
+		{
+			if (preg_match('/\.modules\.php$/i',$file))
+			{
+				$name = substr($file, 4, dol_strlen($file) -16);
+				$classname = substr($file, 0, dol_strlen($file) -12);
+
+			    try {
+        			dol_include_once($dirroot.'core/modules/product/doc/'.$file);
+    			}
+    			catch(Exception $e)
+    			{
+    			    dol_syslog($e->getMessage(), LOG_ERR);
+    			}
+
+    			$module = new $classname($db);
+
+				$modulequalified=1;
+				if (! empty($module->version)) {
+					if ($module->version == 'development'  && $conf->global->MAIN_FEATURES_LEVEL < 2) $modulequalified=0;
+					else if ($module->version == 'experimental' && $conf->global->MAIN_FEATURES_LEVEL < 1) $modulequalified=0;
+				}
+
+				if ($modulequalified)
+				{
+					$var = !$var;
+					print '<tr '.$bc[$var].'><td width="100">';
+					print $module->name;
+					print "</td><td>\n";
+					if (method_exists($module,'info')) print $module->info($langs);
+					else print $module->description;
+					print '</td>';
+
+					// Activate / Disable
+					if (in_array($name, $def))
+					{
+						print "<td align=\"center\">\n";
+						print '<a href="'.$_SERVER["PHP_SELF"].'?action=del&value='.$name.'&type=product&scandir='.$module->scandir.'&label='.urlencode($module->name).'">';
+						print img_picto($langs->trans("Enabled"),'switch_on');
+						print '</a>';
+						print "</td>";
+					}
+					else
+					{
+						if (versioncompare($module->phpmin,versionphparray()) > 0)
+						{
+							print "<td align=\"center\">\n";
+							print img_picto(dol_escape_htmltag($langs->trans("ErrorModuleRequirePHPVersion",join('.',$module->phpmin))),'switch_off');
+							print "</td>";
+						}
+						else
+						{
+							print "<td align=\"center\">\n";
+							print '<a href="'.$_SERVER["PHP_SELF"].'?action=set&value='.$name.'&type=product&scandir='.$module->scandir.'&label='.urlencode($module->name).'">'.img_picto($langs->trans("Disabled"),'switch_off').'</a>';
+							print "</td>";
+						}
+					}
+
+					// Info
+					$htmltooltip =    ''.$langs->trans("Name").': '.$module->name;
+					$htmltooltip.='<br>'.$langs->trans("Type").': '.($module->type?$module->type:$langs->trans("Unknown"));
+					if ($module->type == 'pdf')
+					{
+						$htmltooltip.='<br>'.$langs->trans("Height").'/'.$langs->trans("Width").': '.$module->page_hauteur.'/'.$module->page_largeur;
+					}
+					$htmltooltip.='<br><br><u>'.$langs->trans("FeaturesSupported").':</u>';
+					$htmltooltip.='<br>'.$langs->trans("WatermarkOnDraft").': '.yn((! empty($module->option_draft_watermark)?$module->option_draft_watermark:''), 1, 1);
+
+					print '<td align="center" class="nowrap">';
+					print $form->textwithpicto('',$htmltooltip,1,0);
+					print '</td>';
+
+					// Preview
+					print '<td align="center" class="nowrap">';
+					if ($module->type == 'pdf')
+					{
+						$linkspec='<a href="'.$_SERVER["PHP_SELF"].'?action=specimen&module='.$name.'">'.img_object($langs->trans("Preview"),'bill').'</a>';
+					}
+					else
+					{
+						$linkspec=img_object($langs->trans("PreviewNotAvailable"),'generic');
+					}
+					print $linkspec;
+					print '</td>';
+
+					print "</tr>\n";
+				}
+			}
+		}
+		closedir($handle);
+	}
+}
+print '</table>';
+
 /*
  * Other conf
  */
diff --git a/htdocs/product/card.php b/htdocs/product/card.php
index d65c7fe1f350ce4b5794ae11d57c83d31fcc1cbd..bdfdf986d173a7bc35d9bb36cbe2b525a1224931 100644
--- a/htdocs/product/card.php
+++ b/htdocs/product/card.php
@@ -13,6 +13,7 @@
  * Copyright (C) 2014-2015	Ferran Marcet			<fmarcet@2byte.es>
  * Copyright (C) 2015       Jean-François Ferry     <jfefe@aternatik.fr>
  * Copyright (C) 2015       Raphaël Doursenaud      <rdoursenaud@gpcsolutions.fr>
+ * Copyright (C) 2016		Charlie Benke		 <charlie@patas-monkey.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -44,6 +45,8 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
 require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/modules/product/modules_product.php';
+
 if (! empty($conf->propal->enabled))     require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
 if (! empty($conf->facture->enabled))    require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
 if (! empty($conf->commande->enabled))   require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
@@ -125,7 +128,33 @@ if (empty($reshook))
     	header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
     	exit;
     }
+	/*
+	 * Build doc
+	 */
+	else if ($action == 'builddoc' && $user->rights->produit->creer)
+	{
 
+		// Save last template used to generate document
+		if (GETPOST('model')) $object->setDocModel($user, GETPOST('model','alpha'));
+
+		// Define output language
+		$outputlangs = $langs;
+		$newlang='';
+		if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id','alpha')) $newlang=GETPOST('lang_id','alpha');
+		if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang=$object->client->default_lang;
+		if (! empty($newlang))
+		{
+			$outputlangs = new Translate("",$conf);
+			$outputlangs->setDefaultLang($newlang);
+		}
+		$result=product_create($db, $object, GETPOST('model','alpha'), $outputlangs);
+		if ($result <= 0)
+		{
+			dol_print_error($db,$result);
+			exit;
+		}
+	}
+	
     // Barcode type
     if ($action ==	'setfk_barcode_type' && $createbarcode)
     {
@@ -1649,7 +1678,28 @@ if (($action == 'clone' && (empty($conf->use_javascript_ajax) || ! empty($conf->
 {
     print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id,$langs->trans('CloneProduct'),$langs->trans('ConfirmCloneProduct',$object->ref),'confirm_clone',$formquestionclone,'yes','action-clone',250,600);
 }
+print '<div class="fichecenter"><div class="fichehalfleft">';
+print '<a name="builddoc"></a>'; // ancre
+
+/*
+ * Documents generes
+ */
+$filedir=$conf->product->dir_output.'/product/'.$object->id;
+
+$urlsource=$_SERVER["PHP_SELF"]."?id=".$object->id;
+$genallowed=$user->rights->produit->creer;
+$delallowed=$user->rights->produit->supprimer;
+
+$var=true;
+
+$somethingshown=$formfile->show_documents('product',$object->id,$filedir,$urlsource,$genallowed,$delallowed,'',0,0,0,28,0,'',0,'',$object->default_lang);
+
+print '</div><div class="fichehalfright"><div class="ficheaddleft">';
+
+
+print '</div></div></div>';
 
+print '<br><br>';
 
 
 /* ************************************************************************** */