瀏覽代碼

Merge branch '15.0-mmi' into 16.0-mmi

Mathieu Moulin 2 年之前
父節點
當前提交
e05c290915
共有 88 個文件被更改,包括 1959 次插入216 次删除
  1. 3 0
      .gitignore
  2. 99 0
      .gitmodules
  3. 13 5
      htdocs/accountancy/class/accountancyexport.class.php
  4. 6 0
      htdocs/admin/workflow.php
  5. 3 1
      htdocs/api/index.php
  6. 41 4
      htdocs/comm/propal/card.php
  7. 15 0
      htdocs/comm/propal/list.php
  8. 25 2
      htdocs/commande/card.php
  9. 32 1
      htdocs/compta/facture/card.php
  10. 8 0
      htdocs/compta/paiement/card.php
  11. 6 0
      htdocs/compta/paiement/class/paiement.class.php
  12. 8 0
      htdocs/core/actions_massactions.inc.php
  13. 4 0
      htdocs/core/actions_setmoduleoptions.inc.php
  14. 101 0
      htdocs/core/class/commonobject.class.php
  15. 15 4
      htdocs/core/class/html.form.class.php
  16. 3 0
      htdocs/core/class/html.formmail.class.php
  17. 17 0
      htdocs/core/db/mysqli.class.php
  18. 9 0
      htdocs/core/lib/ajax.lib.php
  19. 10 0
      htdocs/core/lib/company.lib.php
  20. 8 0
      htdocs/core/lib/functions.lib.php
  21. 35 3
      htdocs/core/lib/payments.lib.php
  22. 27 3
      htdocs/core/lib/pdf.lib.php
  23. 28 1
      htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php
  24. 252 39
      htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php
  25. 48 1
      htdocs/core/modules/expedition/doc/doc_generic_shipment_odt.modules.php
  26. 10 4
      htdocs/core/modules/expedition/doc/pdf_rouget.modules.php
  27. 29 1
      htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php
  28. 275 40
      htdocs/core/modules/facture/doc/pdf_sponge.modules.php
  29. 8 0
      htdocs/core/modules/modFournisseur.class.php
  30. 30 2
      htdocs/core/modules/propale/doc/doc_generic_proposal_odt.modules.php
  31. 270 40
      htdocs/core/modules/propale/doc/pdf_cyan.modules.php
  32. 16 0
      htdocs/core/tpl/objectline_create.tpl.php
  33. 23 0
      htdocs/core/tpl/onlinepaymentlinks.tpl.php
  34. 1 0
      htdocs/custom/abricot
  35. 1 0
      htdocs/custom/aproximite
  36. 1 0
      htdocs/custom/bompdf
  37. 1 0
      htdocs/custom/concatpdf
  38. 1 0
      htdocs/custom/contacttracking
  39. 1 0
      htdocs/custom/dropdownmenu
  40. 1 0
      htdocs/custom/ecotaxdeee
  41. 1 0
      htdocs/custom/externalaccess
  42. 1 0
      htdocs/custom/inventory
  43. 1 0
      htdocs/custom/margincontrol
  44. 1 0
      htdocs/custom/mbietransactions
  45. 1 0
      htdocs/custom/milestone
  46. 1 0
      htdocs/custom/mmiatelier
  47. 1 0
      htdocs/custom/mmiavisverifies
  48. 1 0
      htdocs/custom/mmicommon
  49. 1 0
      htdocs/custom/mmicompta
  50. 1 0
      htdocs/custom/mmicrm
  51. 1 0
      htdocs/custom/mmidocuments
  52. 1 0
      htdocs/custom/mmientrepots
  53. 1 0
      htdocs/custom/mmifilters
  54. 1 0
      htdocs/custom/mmifournisseurprice
  55. 1 0
      htdocs/custom/mmipayments
  56. 1 0
      htdocs/custom/mmiprestasync
  57. 1 0
      htdocs/custom/mmiproduct
  58. 1 0
      htdocs/custom/mmiproductdluo
  59. 1 0
      htdocs/custom/mmiproject
  60. 1 0
      htdocs/custom/mmistats
  61. 1 0
      htdocs/custom/mmivateec
  62. 1 0
      htdocs/custom/mmiworkflow
  63. 1 0
      htdocs/custom/multismtp
  64. 1 0
      htdocs/custom/ovh
  65. 1 0
      htdocs/custom/sfycustom
  66. 1 0
      htdocs/custom/stocktransfers
  67. 1 0
      htdocs/custom/supplierorderfromorder
  68. 1 0
      htdocs/custom/zenfusionmaps
  69. 1 0
      htdocs/custom/zipautofillfr
  70. 1 0
      htdocs/custom_dolicloud
  71. 11 1
      htdocs/expedition/card.php
  72. 4 1
      htdocs/expedition/class/expedition.class.php
  73. 6 1
      htdocs/fourn/commande/dispatch.php
  74. 18 5
      htdocs/includes/odtphp/Segment.php
  75. 13 1
      htdocs/includes/odtphp/odf.php
  76. 1 0
      htdocs/langs/en_US/workflow.lang
  77. 2 0
      htdocs/langs/fr_FR/main.lang
  78. 4 4
      htdocs/langs/fr_FR/productbatch.lang
  79. 2 0
      htdocs/langs/fr_FR/propal.lang
  80. 1 0
      htdocs/langs/fr_FR/workflow.lang
  81. 17 2
      htdocs/product/class/product.class.php
  82. 14 4
      htdocs/product/stock/class/mouvementstock.class.php
  83. 7 3
      htdocs/product/stock/product.php
  84. 19 0
      htdocs/product/stock/replenish.php
  85. 97 13
      htdocs/projet/tasks/time.php
  86. 196 26
      htdocs/public/payment/newpayment.php
  87. 9 1
      htdocs/reception/card.php
  88. 24 3
      htdocs/societe/checkvat/checkVatPopup.php

+ 3 - 0
.gitignore

@@ -45,6 +45,9 @@ htdocs/.well-known/apple-developer-merchantid-domain-association
 /factory/
 /output/
 
+# MMI Mathieu Moulin iProspective
+htdocs/.user.ini
+
 # Node Modules
 build/yarn-error.log
 build/node_modules/

+ 99 - 0
.gitmodules

@@ -0,0 +1,99 @@
+[submodule "htdocs/custom/mmicommon"]
+	path = htdocs/custom/mmicommon
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmicommon.git
+[submodule "htdocs/custom/mmicompta"]
+	path = htdocs/custom/mmicompta
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmicompta.git
+[submodule "htdocs/custom/sfycustom"]
+	path = htdocs/custom/sfycustom
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-sfycustom.git
+[submodule "htdocs/custom/mbietransactions"]
+	path = htdocs/custom/mbietransactions
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mbietransactions.git
+[submodule "htdocs/custom/mmiatelier"]
+	path = htdocs/custom/mmiatelier
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmiatelier.git
+[submodule "htdocs/custom/mmiavisverifies"]
+	path = htdocs/custom/mmiavisverifies
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmiavisverifies.git
+[submodule "htdocs/custom/mmicrm"]
+	path = htdocs/custom/mmicrm
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmicrm.git
+[submodule "htdocs/custom/mmidocuments"]
+	path = htdocs/custom/mmidocuments
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmidocuments.git
+[submodule "htdocs/custom/mmientrepots"]
+	path = htdocs/custom/mmientrepots
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmientrepots.git
+[submodule "htdocs/custom/mmifilters"]
+	path = htdocs/custom/mmifilters
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmifilters.git
+[submodule "htdocs/custom/mmifournisseurprice"]
+	path = htdocs/custom/mmifournisseurprice
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmifournisseurprice.git
+[submodule "htdocs/custom/mmipayments"]
+	path = htdocs/custom/mmipayments
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmipayments.git
+[submodule "htdocs/custom/mmiprestasync"]
+	path = htdocs/custom/mmiprestasync
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmiprestasync.git
+[submodule "htdocs/custom/mmiproduct"]
+	path = htdocs/custom/mmiproduct
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmiproduct.git
+[submodule "htdocs/custom/mmiproductdluo"]
+	path = htdocs/custom/mmiproductdluo
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmiproductdluo.git
+[submodule "htdocs/custom/mmiproject"]
+	path = htdocs/custom/mmiproject
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmiproject.git
+[submodule "htdocs/custom/mmistats"]
+	path = htdocs/custom/mmistats
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmistats.git
+[submodule "htdocs/custom/mmivateec"]
+	path = htdocs/custom/mmivateec
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmivateec.git
+[submodule "htdocs/custom/mmiworkflow"]
+	path = htdocs/custom/mmiworkflow
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-mmworkflow.git
+[submodule "htdocs/custom/abricot"]
+	path = htdocs/custom/abricot
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-abricot.git
+[submodule "htdocs/custom/aproximite"]
+	path = htdocs/custom/aproximite
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-aproximite.git
+[submodule "htdocs/custom/bompdf"]
+	path = htdocs/custom/bompdf
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-bompdf.git
+[submodule "htdocs/custom/contacttracking"]
+	path = htdocs/custom/contacttracking
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-contacttracking.git
+[submodule "htdocs/custom/dropdownmenu"]
+	path = htdocs/custom/dropdownmenu
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-dropdownmenu.git
+[submodule "htdocs/custom/externalaccess"]
+	path = htdocs/custom/externalaccess
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-externalaccess.git
+[submodule "htdocs/custom/inventory"]
+	path = htdocs/custom/inventory
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-inventory.git
+[submodule "htdocs/custom/margincontrol"]
+	path = htdocs/custom/margincontrol
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-margincontrol.git
+[submodule "htdocs/custom/milestone"]
+	path = htdocs/custom/milestone
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-milestone.git
+[submodule "htdocs/custom/stocktransfers"]
+	path = htdocs/custom/stocktransfers
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-stocktransfers.git
+[submodule "htdocs/custom/multismtp"]
+	path = htdocs/custom/multismtp
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-multismtp.git
+[submodule "htdocs/custom/supplierorderfromorder"]
+	path = htdocs/custom/supplierorderfromorder
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-supplierorderfromorder.git
+[submodule "htdocs/custom/zenfusionmaps"]
+	path = htdocs/custom/zenfusionmaps
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-module-zenfusionmaps.git
+[submodule "htdocs/custom_dolicloud"]
+	path = htdocs/custom_dolicloud
+	url = git@gogs.iprospective.fr:iProspective/dolibarr-modules-dolicloud.git

+ 13 - 5
htdocs/accountancy/class/accountancyexport.class.php

@@ -593,14 +593,20 @@ class AccountancyExport
 			if (!empty($data->subledger_account)) {
 				$Tab['type_ligne'] = 'C';
 				$Tab['num_compte'] = str_pad(self::trunc($data->subledger_account, 8), 8);
-				$Tab['lib_compte'] = str_pad(self::trunc($data->subledger_label, 30), 30);
+				// Added by MMI Mathieu Moulin iProspective
+				// Feature : Option to use Account account as label (in case of multiples customers with same accounts)
+				// Fix : remove accents using dol_string_unaccent()
+				if (!empty($conf->global->MMI_COMPTA_EXPORT_LABEL_AS_ACCOUNT))
+					$Tab['lib_compte'] = str_pad(self::trunc(dol_string_unaccent($data->subledger_account), 8), 30);
+				else
+					$Tab['lib_compte'] = str_pad(self::trunc(dol_string_unaccent($data->subledger_label), 30), 30);
 
 				if ($data->doc_type == 'customer_invoice') {
-					$Tab['lib_alpha'] = strtoupper(str_pad('C'.self::trunc($data->subledger_label, 6), 6));
+					$Tab['lib_alpha'] = strtoupper(str_pad('C'.self::trunc(dol_string_unaccent($data->subledger_label), 6), 6));
 					$Tab['filler'] = str_repeat(' ', 52);
 					$Tab['coll_compte'] = str_pad(self::trunc($conf->global->ACCOUNTING_ACCOUNT_CUSTOMER, 8), 8);
 				} elseif ($data->doc_type == 'supplier_invoice') {
-					$Tab['lib_alpha'] = strtoupper(str_pad('F'.self::trunc($data->subledger_label, 6), 6));
+					$Tab['lib_alpha'] = strtoupper(str_pad('F'.self::trunc(dol_string_unaccent($data->subledger_label), 6), 6));
 					$Tab['filler'] = str_repeat(' ', 52);
 					$Tab['coll_compte'] = str_pad(self::trunc($conf->global->ACCOUNTING_ACCOUNT_SUPPLIER, 8), 8);
 				} else {
@@ -637,7 +643,7 @@ class AccountancyExport
 			//$Tab['date_ecriture'] = $date_ecriture;
 			$Tab['date_ecriture'] = dol_print_date($data->doc_date, '%d%m%y');
 			$Tab['filler'] = ' ';
-			$Tab['libelle_ecriture'] = str_pad(self::trunc($data->doc_ref.' '.$data->label_operation, 20), 20);
+			$Tab['libelle_ecriture'] = str_pad(self::trunc(dol_string_unaccent($data->doc_ref).' '.dol_string_unaccent($data->label_operation), 20), 20);
 
 			// Credit invoice - invert sens
 			/*
@@ -698,7 +704,9 @@ class AccountancyExport
 
 			$Tab['end_line'] = $end_line;
 
-			print implode($Tab);
+			// Added by MMI Mathieu Moulin iProspective
+			// Fix : Quadra does not like …
+			print str_replace('…', '.', implode($Tab));
 		}
 	}
 

+ 6 - 0
htdocs/admin/workflow.php

@@ -65,6 +65,12 @@ $workflowcodes = array(
 		'enabled'=>(!empty($conf->propal->enabled) && !empty($conf->commande->enabled)),
 		'picto'=>'order'
 	),
+	'WORKFLOW_PROPAL_AUTOCREATE_ORDER_IFNOTEXISTS'=>array(
+		'family'=>'create',
+		'position'=>11,
+		'enabled'=>(!empty($conf->propal->enabled) && !empty($conf->commande->enabled)),
+		'picto'=>'order'
+	),
 	'WORKFLOW_ORDER_AUTOCREATE_INVOICE'=>array(
 		'family'=>'create',
 		'position'=>20,

+ 3 - 1
htdocs/api/index.php

@@ -415,7 +415,9 @@ if (Luracast\Restler\Defaults::$returnResponse) {
 	} elseif (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'bz') !== false && is_callable('bzcompress')) {
 		header('Content-Encoding: bz');
 		$result = bzcompress($result, 9);
-	} elseif (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false && is_callable('gzencode')) {
+	// Added by MMI Moulin Mathieu iProspective
+	// Hack for nginx
+	} elseif (empty($conf->global->MMI_API_DISABLE_GZIP) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false && is_callable('gzencode')) {
 		header('Content-Encoding: gzip');
 		$result = gzencode($result, 9);
 	} else {

+ 41 - 4
htdocs/comm/propal/card.php

@@ -2603,10 +2603,19 @@ if ($action == 'create') {
 		}
 		print '</td></tr>';
 	}
-
-	// Other attributes
+	
+	// Added by MMI Mathieu Moulin iProspective
+	// Other attributes / extrafields show/hide
+	$extrafields_showhide = $conf->global->DOCUMENT_EXTRAFIELDS_SHOWHIDE && (empty($action) || $action != 'edit_extras');
+	if ($extrafields_showhide) {
+		echo '<tr> <td colspan="2"><a href="javascript:;" onclick="$(\'#extrafields_form\').toggle();">'.$langs->trans('ToggleExtrafields').'</a></td> </tr>';
+		echo '<tbody id="extrafields_form" class="extrafields" style="display: none;">';
+	}
 	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
-
+	if ($extrafields_showhide) {
+		echo '</tbody>';
+	}
+	
 	print '</table>';
 
 	print '</div>';
@@ -2668,6 +2677,13 @@ if ($action == 'create') {
 	if (!empty($conf->margin->enabled)) {
 		$formmargin->displayMarginInfos($object);
 	}
+	
+	// Added by MMI Mathieu Moulin iProspective
+	$parameters = array();
+	$reshook = $hookmanager->executeHooks('doDisplayMoreInfos', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+	if ($reshook < 0) {
+		setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+	}
 
 	print '</div>';
 	print '</div>';
@@ -2693,7 +2709,8 @@ if ($action == 'create') {
 	// Show object lines
 	$result = $object->getLinesArray();
 
-	print '	<form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.(($action != 'editline') ? '' : '#line_'.GETPOST('lineid', 'int')).'" method="POST">
+	// Updated by MMI Mathieu Moulin iProspective
+	print '	<form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.(($action != 'editline') ? ($conf->global->MAIN_DOCUMENT_FORM_ADD_PRODUCT_STAY ?'#addproduct' :'') : '#line_'.GETPOST('lineid', 'int')).'" method="POST">
 	<input type="hidden" name="token" value="' . newToken().'">
 	<input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline').'">
 	<input type="hidden" name="mode" value="">
@@ -2901,6 +2918,26 @@ if ($action == 'create') {
 			print showOnlineSignatureUrl('proposal', $object->ref).'<br>';
 		}
 
+		// Added by MMI Mathieu Moulin iProspective
+		// Show online payment link
+		$useonlinepayment = (!empty($conf->paypal->enabled) || !empty($conf->stripe->enabled) || !empty($conf->paybox->enabled));
+		if (!$useonlinepayment) {
+			$parameters = array();
+			$reshook = $hookmanager->executeHooks('addOnlinePaymentMeans', $parameters, $object, $action);
+			if ($reshook==0 && !empty($hookmanager->results['useonlinepayment']))
+				$useonlinepayment = true;
+		}
+
+		if (!empty($conf->global->ORDER_HIDE_ONLINE_PAYMENT_ON_ORDER)) {
+			$useonlinepayment = 0;
+		}
+
+		if ($object->statut != Propal::STATUS_DRAFT && $useonlinepayment) {
+			print '<br><!-- Link to pay -->';
+			require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
+			print showOnlinePaymentUrl('propal', $object->ref).'<br>';
+		}
+
 		print '</div><div class="fichehalfright">';
 
 		// List of actions on element

+ 15 - 0
htdocs/comm/propal/list.php

@@ -564,6 +564,13 @@ $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_departements as state on (state.rowid =
 if (!empty($search_categ_cus) && $search_categ_cus != '-1') {
 	$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX."categorie_societe as cc ON s.rowid = cc.fk_soc"; // We'll need this table joined to the select in order to filter by categ
 }
+
+// Added by MMI Mathieu Moulin iProspective
+// Add fields from hooks
+$parameters = array();
+$reshook = $hookmanager->executeHooks('printFieldListJoinSoc', $parameters); // Note that $action and $object may have been modified by hook
+$sql .= $hookmanager->resPrint;
+
 $sql .= ', '.MAIN_DB_PREFIX.'propal as p';
 if (!empty($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) {
 	$sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (p.rowid = ef.fk_object)";
@@ -577,6 +584,14 @@ if ($search_product_category > 0) {
 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'user as u ON p.fk_user_author = u.rowid';
 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet as pr ON pr.rowid = p.fk_projet";
 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_availability as ava on (ava.rowid = p.fk_availability)";
+
+// Added by MMI Mathieu Moulin iProspective
+// Add fields from hooks
+$parameters = array();
+$reshook = $hookmanager->executeHooks('printFieldListJoinPropal', $parameters); // Note that $action and $object may have been modified by hook
+$sql .= $hookmanager->resPrint;
+//echo ($sql); die();
+
 // We'll need this table joined to the select in order to filter by sale
 if ($search_sale > 0 || (empty($user->rights->societe->client->voir) && !$socid)) {
 	$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";

+ 25 - 2
htdocs/commande/card.php

@@ -2560,8 +2560,17 @@ if ($action == 'create' && $usercancreate) {
 			print '</tr>';
 		}
 
-		// Other attributes
+		// Added by MMI Mathieu Moulin iProspective
+		// Other attributes/extrafields show/hide
+		$extrafields_showhide = $conf->global->DOCUMENT_EXTRAFIELDS_SHOWHIDE && (empty($action) || $action != 'edit_extras');
+		if ($extrafields_showhide) {
+			echo '<tr> <td colspan="2"><a href="javascript:;" onclick="$(\'#extrafields_form\').toggle();">'.$langs->trans('ToggleExtrafields').'</a></td> </tr>';
+			echo '<tbody id="extrafields_form" class="extrafields" style="display: none;">';
+		}
 		include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
+		if ($extrafields_showhide) {
+			echo '</tbody>';
+		}
 
 		print '</table>';
 
@@ -2621,7 +2630,13 @@ if ($action == 'create' && $usercancreate) {
 		if (!empty($conf->margin->enabled)) {
 			$formmargin->displayMarginInfos($object);
 		}
-
+	
+		// Added by MMI Mathieu Moulin iProspective
+		$parameters = array();
+		$reshook = $hookmanager->executeHooks('doDisplayMoreInfos', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+		if ($reshook < 0) {
+			setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+		}
 
 		print '</div>';
 		print '</div>'; // Close fichecenter
@@ -2852,6 +2867,14 @@ if ($action == 'create' && $usercancreate) {
 
 			// Show online payment link
 			$useonlinepayment = (!empty($conf->paypal->enabled) || !empty($conf->stripe->enabled) || !empty($conf->paybox->enabled));
+			// Added by MMI Mathieu Moulin iProspective
+			if (!$useonlinepayment) {
+				$parameters = array();
+				$reshook = $hookmanager->executeHooks('addOnlinePaymentMeans', $parameters, $object, $action);
+				if ($reshook==0 && !empty($hookmanager->results['useonlinepayment']))
+					$useonlinepayment = true;
+			}
+			
 			if (!empty($conf->global->ORDER_HIDE_ONLINE_PAYMENT_ON_ORDER)) {
 				$useonlinepayment = 0;
 			}

+ 32 - 1
htdocs/compta/facture/card.php

@@ -1501,7 +1501,15 @@ if (empty($reshook)) {
 								}
 							} else {
 								if ($typeamount == 'amount') {
-									$amountdeposit[0] = $valuedeposit;
+									// Added by MMI Mathieu Moulin iProspective
+									if ($conf->global->MMI_INVOICE_DEPOSIT_USE_TVA_TX > 0) {
+										$tva_tx = $conf->global->MMI_INVOICE_DEPOSIT_USE_TVA_TX;
+										$amountdeposit[0] = 0;
+										$amountdeposit[$tva_tx] = $valuedeposit/(1+$tva_tx/100);
+									}
+									else {
+										$amountdeposit[0] = $valuedeposit;
+									}
 								} elseif ($typeamount == 'variable') {
 									if ($result > 0) {
 										$totalamount = 0;
@@ -1951,6 +1959,12 @@ if (empty($reshook)) {
 		// End of object creation, we show it
 		if ($id > 0 && !$error) {
 			$db->commit();
+			
+			// Added by MMI Mathieu Moulin iProspective
+			// For payment assignment
+			$parameters = array();
+			$reshook = $hookmanager->executeHooks('afterCreateAction', $parameters, $object, $action);
+			
 
 			// Define output language
 			if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE) && count($object->lines)) {
@@ -4704,7 +4718,17 @@ if ($action == 'create') {
 
 	// Other attributes
 	$cols = 2;
+	// Added by MMI Mathieu Moulin iProspective
+	// Hack extrafields show/hide
+	$extrafields_showhide = $conf->global->DOCUMENT_EXTRAFIELDS_SHOWHIDE && (empty($action) || $action != 'edit_extras');
+	if ($extrafields_showhide) {
+		echo '<tr> <td colspan="2"><a href="javascript:;" onclick="$(\'#extrafields_form\').toggle();">'.$langs->trans('ToggleExtrafields').'</a></td> </tr>';
+		echo '<tbody id="extrafields_form" class="extrafields" style="display: none;">';
+	}
 	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
+	if ($extrafields_showhide) {
+		echo '</tbody>';
+	}
 
 	print '</table>';
 
@@ -5731,6 +5755,13 @@ if ($action == 'create') {
 
 		// Show online payment link
 		$useonlinepayment = (!empty($conf->paypal->enabled) || !empty($conf->stripe->enabled) || !empty($conf->paybox->enabled));
+		// Added by MMI Mathieu Moulin iProspective
+		if (!$useonlinepayment) {
+			$parameters = array();
+			$reshook = $hookmanager->executeHooks('addOnlinePaymentMeans', $parameters, $object, $action);
+			if ($reshook==0 && !empty($hookmanager->results['useonlinepayment']))
+				$useonlinepayment = true;
+		}
 
 		if ($object->statut != Facture::STATUS_DRAFT && $useonlinepayment) {
 			print '<br><!-- Link to pay -->'."\n";

+ 8 - 0
htdocs/compta/paiement/card.php

@@ -391,6 +391,10 @@ print '<tr><td class="tdtop">'.$form->editfieldkey("Comments", 'note', $object->
 print $form->editfieldval("Note", 'note', $object->note, $object, $user->rights->facture->paiement, 'textarea:'.ROWS_3.':90%');
 print '</td></tr>';
 
+// Added by MMI Mathieu Moulin iProspective
+$parameters = array();
+$reshook = $hookmanager->executeHooks('addMoreInformations', $parameters, $object, $action);
+
 print '</table>';
 
 print '</div>';
@@ -510,6 +514,10 @@ if ($resql) {
 
 print '<div class="tabsAction">';
 
+// Added by MMI Mathieu Moulin iProspective
+$parameters = array();
+$reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action);
+
 if (!empty($conf->global->BILL_ADD_PAYMENT_VALIDATION)) {
 	if ($user->socid == 0 && $object->statut == 0 && $_GET['action'] == '') {
 		if ($user->rights->facture->paiement) {

+ 6 - 0
htdocs/compta/paiement/class/paiement.class.php

@@ -306,6 +306,12 @@ class Paiement extends CommonObject
 			}
 		}
 
+		// Added by MMI Mathieu Moulin iProspective
+		// MMIPayment for order & propal : Permit to specify only an amount
+		if (empty($amounts) && !empty($this->amount)) {
+			$totalamount = $this->amount;
+			$totalamount_converted = $this->amount;
+		}
 
 		$totalamount = price2num($totalamount);
 		$totalamount_converted = price2num($totalamount_converted);

+ 8 - 0
htdocs/core/actions_massactions.inc.php

@@ -328,7 +328,15 @@ if (!$error && $massaction == 'confirm_presend') {
 					// TODO Set subdir to be compatible with multi levels dir trees
 					// $subdir = get_exdir($objectobj->id, 2, 0, 0, $objectobj, $objectobj->element)
 					$filedir = $uploaddir.'/'.$subdir.dol_sanitizeFileName($objectobj->ref);
+
 					$filepath = $filedir.'/'.$filename;
+					// Added By MMI Mathieu Moulin iProspective
+					// Fix search by filename
+					if (!file_exists($filepath)) {
+						$files = glob($filedir.'/'.dol_sanitizeFileName($objectobj->ref).'*.pdf');
+						if (!empty($files))
+							$filepath = $files[0];
+					}
 
 					// For supplier invoices, we use the file provided by supplier, not the one we generate
 					if ($objectobj->element == 'invoice_supplier') {

+ 4 - 0
htdocs/core/actions_setmoduleoptions.inc.php

@@ -47,6 +47,10 @@ if ($action == 'update' && is_array($arrayofparameters) && !empty($user->admin))
 				} else {
 					$val_const = GETPOST($key, 'int');
 				}
+			// Added by MMI Mathieu Moulin iProspective
+			// Action:setmoduleoption:update Permit html data in module parameter
+			} elseif ($val['type']=='html') {
+				$val_const = GETPOST($key, 'html');
 			} else {
 				$val_const = GETPOST($key, 'alpha');
 			}

+ 101 - 0
htdocs/core/class/commonobject.class.php

@@ -10000,4 +10000,105 @@ abstract class CommonObject
 		$this->db->commit();
 		return true;
 	}
+
+	/**
+	 * Get linked objects ids
+	 * @author MMI Mathieu Moulin iProspective
+	 *
+	 * @return Array
+	 */
+	public function fetchObjectLinkedIDs($object_class=true)
+	{
+		$classname = strtolower(get_class($this));
+
+		$sql = 'SELECT e.sourcetype object_type, e.fk_source fk_object
+			FROM '.MAIN_DB_PREFIX.'element_element e
+			WHERE e.targettype="'.$classname.'" AND e.fk_target="'.$this->id.'"
+			UNION
+			SELECT e.targettype object_type, e.fk_target fk_object
+			FROM '.MAIN_DB_PREFIX.'element_element e
+			WHERE e.sourcetype="'.$classname.'" AND e.fk_source="'.$this->id.'"';
+		//echo $sql;
+		$resql = $this->db->query($sql);
+		if (!$resql)
+			return;
+		$l = [];
+		while ($obj = $this->db->fetch_object($resql)) {
+			//var_dump($obj);
+			$l[] = [$obj->object_type, $obj->fk_object];
+		}
+
+		return $l;
+
+	}
+
+	/**
+	 * Get total amount already paid
+	 * @author MMI Mathieu Moulin iProspective
+	 *
+	 * @return []
+	 */
+	public function getPaiements()
+	{
+		$classname = strtolower(get_class($this));
+
+		$ol = $this->fetchObjectLinkedIDs();
+		//var_dump($ol);
+		if (!is_array($ol))
+			$ol = [];
+		$ol[] = [$classname, $this->id];
+
+		$sql_w = [];
+		foreach($ol as $o) {
+			if ($o[0]=='facture')
+				$sql_w[] = "(pf.`fk_facture`='".$o[1]."')";
+			$sql_w[] = "(po.`objecttype`='".$o[0]."' AND po.`fk_object`='".$o[1]."')";
+		}
+
+		$sql = "SELECT DISTINCT p2.*, p.*
+			FROM ".MAIN_DB_PREFIX."paiement p
+			LEFT JOIN ".MAIN_DB_PREFIX."paiement_extrafields p2 ON p2.fk_object=p.rowid
+			LEFT JOIN ".MAIN_DB_PREFIX."paiement_object po ON po.fk_paiement=p.rowid
+			LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture pf ON pf.fk_paiement=p.rowid
+			WHERE ".implode(' OR ', $sql_w);
+		//echo $sql;
+		$resql = $this->db->query($sql);
+		if (!$resql)
+			return;
+		$l = [];
+		while ($obj = $this->db->fetch_object($resql)) {
+			//var_dump($obj);
+			$l[$obj->rowid] = $obj;
+		}
+
+		return $l;
+	}
+	
+	/**
+	 * Get total amount already paid
+	 * @author MMI Mathieu Moulin iProspective
+	 *
+	 *	@return float
+	 */
+	public function getSommePaiement()
+	{
+		$mt = 0;
+		$l = $this->getPaiements();
+		foreach($l as $p) {
+			$mt += $p->amount;
+		}
+		//echo $mt;
+		return $mt;
+	}
+
+	/**
+	 * Get total amount already paid
+	 * @author MMI Mathieu Moulin iProspective
+	 *
+	 * @return float
+	 */
+	public function paye()
+	{
+		return $this->getSommePaiement() >= $this->total_ttc;
+	}
 }

+ 15 - 4
htdocs/core/class/html.form.class.php

@@ -2595,9 +2595,14 @@ class Form
 		}
 
 		// include search in supplier ref
-		if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
+		// Added by MMI Mathieu Moulin iProspective
+		// Hack : include search in supplier name
+		if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF) || !empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_LABEL)) {
 			$sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
 		}
+		if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_LABEL)) {
+			$sql .= " LEFT JOIN ".$this->db->prefix()."societe as pf ON pfp.fk_soc = pf.rowid";
+		}
 
 		//Price by customer
 		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
@@ -2689,6 +2694,11 @@ class Form
 				if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
 					$sql .= " OR pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
 				}
+				// Added by MMI Mathieu Moulin iProspective
+				if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_LABEL)) {
+					$sql .= " OR pf.nom LIKE '".$this->db->escape($prefix.$crit)."%'";
+					$sql .= " OR pf.name_alias LIKE '".$this->db->escape($prefix.$crit)."%'";
+				}
 				$sql .= ")";
 				$i++;
 			}
@@ -6148,10 +6158,11 @@ class Form
 		}
 		if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {    // If option to have vat for end customer for services is on
 			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
-			if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
+			if (isInEEC($societe_vendeuse) && (is_object($societe_acheteuse) && (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
+				//var_dump($type); die();
 				// We also add the buyer
 				if (is_numeric($type)) {
-					if ($type == 1) { // We know product is a service
+					if ($type == 1 || $type == 0) { // We know product is a service
 						$code_country .= ",'".$societe_acheteuse->country_code."'";
 					}
 				} elseif (!$idprod) {  // We don't know type of product
@@ -6159,7 +6170,7 @@ class Form
 				} else {
 					$prodstatic = new Product($this->db);
 					$prodstatic->fetch($idprod);
-					if ($prodstatic->type == Product::TYPE_SERVICE) {   // We know product is a service
+					if ($prodstatic->type == Product::TYPE_SERVICE || $prodstatic->type == Product::TYPE_PRODUCT) {   // We know product is a service
 						$code_country .= ",'".$societe_acheteuse->country_code."'";
 					}
 				}

+ 3 - 0
htdocs/core/class/html.formmail.class.php

@@ -871,6 +871,9 @@ class FormMail extends Form
 					require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
 					$langs->loadLangs(array('paypal', 'other'));
 					$typeforonlinepayment = 'free';
+					if ($this->param["models"] == 'propal' || $this->param["models"] == 'propal_send') {
+						$typeforonlinepayment = 'propal'; // TODO use detection on something else than template
+					}
 					if ($this->param["models"] == 'order' || $this->param["models"] == 'order_send') {
 						$typeforonlinepayment = 'order'; // TODO use detection on something else than template
 					}

+ 17 - 0
htdocs/core/db/mysqli.class.php

@@ -366,6 +366,23 @@ class DoliDBMysqli extends DoliDB
 		return $resultset->fetch_object();
 	}
 
+	// Added by MMI Mathieu Moulin iProspective
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	/**
+	 *	Return datas as an array
+	 *
+	 *	@param	mysqli_result	$resultset	Resultset of request
+	 *	@return	array|null					Array or null if KO or end of cursor
+	 */
+	public function fetch_assoc($resultset)
+	{
+		// phpcs:enable
+		// If resultset not provided, we take the last used by connexion
+		if (!is_object($resultset)) {
+			$resultset = $this->_results;
+		}
+		return $resultset->fetch_assoc();
+	}
 
 	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
 	/**

+ 9 - 0
htdocs/core/lib/ajax.lib.php

@@ -197,6 +197,15 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLen
 							$("#'.$htmlnamejquery.'").attr("data-pbqpercent", ui.item.discount);
 		';
 	}
+	// Added by MMI Mathieu Moulin iProspective
+	// Hack : to keep show product label
+	if (!empty($conf->global->MAIN_SHOW_ADDED_PRODUCT_LABEL)) {
+		$script .= '
+							// Add label
+							$("#'.$htmlname.'").attr("data-label", ui.item.label);
+
+		';
+	}
 	$script .= '
 							// A new value has been selected, we trigger the handlers on #htmlnamejquery
 							console.log("Trigger changes on #'.$htmlnamejquery.'");

+ 10 - 0
htdocs/core/lib/company.lib.php

@@ -2148,3 +2148,13 @@ function addMailingEventTypeSQL($actioncode, $objcon, $filterobj)
 		return $sql2;
 	}
 }
+
+// Added by MMI Mathieu Moulin iProspective
+function tel_international($value) {
+	$value = str_replace([' ', '.', '-'], ['', '', ''], $value);
+	if (substr($value, 0, 2)=='00')
+		$value = '+'.substr($value, 2);
+	if (substr($value, 0, 1)=='0')
+		$value = '+33'.substr($value, 1);
+	return $value;
+}

+ 8 - 0
htdocs/core/lib/functions.lib.php

@@ -2388,6 +2388,11 @@ function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs
 	if (empty($mode)) {
 		$ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : $object->address));
 	}
+	// Added by MMI Mathieu Moulin iProspective
+	// MMIPrestaSync : Complément d'adresse
+	if (!empty($object->array_options['options_p_address2'])) {
+		$ret .= (empty($object->address) ? '' : ($ret ? $sep : '').$object->array_options['options_p_address2']);
+	}
 	// Zip/Town/State
 	if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
 		// US: title firstname name \n address lines \n town, state, zip \n country
@@ -7629,6 +7634,9 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
 				require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
 				$outputlangs->loadLangs(array('paypal', 'other'));
 				$typeforonlinepayment = 'free';
+				if (is_object($object) && $object->element == 'propal') {
+					$typeforonlinepayment = 'propal';
+				}
 				if (is_object($object) && $object->element == 'commande') {
 					$typeforonlinepayment = 'order';
 				}

+ 35 - 3
htdocs/core/lib/payments.lib.php

@@ -146,7 +146,7 @@ function payment_supplier_prepare_head(Paiement $object)
  */
 function getValidOnlinePaymentMethods($paymentmethod = '')
 {
-	global $conf, $langs;
+	global $conf, $langs, $db;// @todo, $user;
 
 	$validpaymentmethod = array();
 
@@ -162,8 +162,16 @@ function getValidOnlinePaymentMethods($paymentmethod = '')
 		$langs->load("stripe");
 		$validpaymentmethod['stripe'] = 'valid';
 	}
+	// Added by MMI Mathieu Moulin iProspective
+	if ((empty($paymentmethod) || $paymentmethod == 'mbietransactions') && !empty($conf->mbietransactions->enabled)) {
+		$langs->load("mbietransactions@mbietransactions");
+		$validpaymentmethod['mbietransactions'] = 'valid';
+	}
 	// TODO Add trigger
-
+	//include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
+	//$interface = new Interfaces($db);
+	//$result = $interface->run_triggers('', NULL, $user, $langs, $conf);
+	//var_dump($validpaymentmethod);
 
 	return $validpaymentmethod;
 }
@@ -247,7 +255,31 @@ function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag
 			}
 		}
 		//if ($mode) $out.='&noidempotency=1';
-	} elseif ($type == 'order') {
+	// Added by MMI Mathieu Moulin iProspective
+	} elseif ($type == 'propal') {
+		$out = $urltouse.'/public/payment/newpayment.php?source='.$type.'&ref='.($mode ? '<span style="color: #666666">' : '');
+		if ($mode == 1) {
+			$out .= 'propal_ref';
+		}
+		if ($mode == 0) {
+			$out .= urlencode($ref);
+		}
+		$out .= ($mode ? '</font>' : '');
+		if (!empty($conf->global->PAYMENT_SECURITY_TOKEN)) {
+			if (empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) {
+				$out .= '&securekey='.urlencode($conf->global->PAYMENT_SECURITY_TOKEN);
+			} else {
+				$out .= '&securekey='.($mode ? '<font color="#666666">' : '');
+				if ($mode == 1) {
+					$out .= "hash('".$conf->global->PAYMENT_SECURITY_TOKEN."' + '".$type."' + propal_ref)";
+				}
+				if ($mode == 0) {
+					$out .= dol_hash($conf->global->PAYMENT_SECURITY_TOKEN.$type.$ref, 2);
+				}
+				$out .= ($mode ? '</font>' : '');
+			}
+		}
+	}  elseif ($type == 'order') {
 		$out = $urltouse.'/public/payment/newpayment.php?source='.$type.'&ref='.($mode ? '<span style="color: #666666">' : '');
 		if ($mode == 1) {
 			$out .= 'order_ref';

+ 27 - 3
htdocs/core/lib/pdf.lib.php

@@ -1397,7 +1397,9 @@ function pdf_getlinedesc($object, $i, $outputlangs, $hideref = 0, $hidedesc = 0,
 	global $db, $conf, $langs;
 
 	$idprod = (!empty($object->lines[$i]->fk_product) ? $object->lines[$i]->fk_product : false);
-	$label = (!empty($object->lines[$i]->label) ? $object->lines[$i]->label : (!empty($object->lines[$i]->product_label) ? $object->lines[$i]->product_label : ''));
+	// Added by MMI Mathieu Moulin iProspective
+	// Trim & Strio tags to be sure
+	$label = (!empty(trim(strip_tags($object->lines[$i]->label))) ? $object->lines[$i]->label : (!empty($object->lines[$i]->product_label) ? $object->lines[$i]->product_label : ''));
 	$desc = (!empty($object->lines[$i]->desc) ? $object->lines[$i]->desc : (!empty($object->lines[$i]->description) ? $object->lines[$i]->description : ''));
 	$ref_supplier = (!empty($object->lines[$i]->ref_supplier) ? $object->lines[$i]->ref_supplier : (!empty($object->lines[$i]->ref_fourn) ? $object->lines[$i]->ref_fourn : '')); // TODO Not yet saved for supplier invoices, only supplier orders
 	$note = (!empty($object->lines[$i]->note) ? $object->lines[$i]->note : '');
@@ -1647,12 +1649,34 @@ function pdf_getlinedesc($object, $i, $outputlangs, $hideref = 0, $hidedesc = 0,
 		foreach ($dbatch as $detail) {
 			$dte = array();
 			if ($detail->eatby) {
-				$dte[] = $outputlangs->transnoentitiesnoconv('printEatby', dol_print_date($detail->eatby, $format, false, $outputlangs));
+				// Added by MMI Mathieu Moulin iProspective
+				// Hack important DDM information
+				if (!empty($conf->global->SHIPPING_PDF_ANTIGASPI) && !empty($object->commande)) {
+					$eatdiff = ($detail->eatby - $object->commande->date_commande);
+					$eatdiff_days = round($eatdiff/86400);
+					$days_antigaspi = $conf->global->PRODUCT_ANTIGASPI_DAYS;
+					$days_daecourte = $conf->global->PRODUCT_DATECOURTE_DAYS;
+					//var_dump($eatdiff_days);
+					if ($eatdiff_days<$days_antigaspi)
+						$antigaspi = ' - '.$outputlangs->transnoentitiesnoconv('printAntiGaspi');
+					elseif ($eatdiff_days<$days_daecourte)
+							$antigaspi = ' - '.$outputlangs->transnoentitiesnoconv('printDateCourte');
+					else
+						$antigaspi = '';
+					//die();
+					//$antigaspi .= ' - '.$eatdiff_days;
+					$dte[] = ($antigaspi ?'<span style="color: red;">' :'').$outputlangs->transnoentitiesnoconv('printEatby', dol_print_date($detail->eatby, $format, false, $outputlangs)).($antigaspi ?$antigaspi.'</span>' :'');
+				}
+				else {
+					$dte[] = $outputlangs->transnoentitiesnoconv('printEatby', dol_print_date($detail->eatby, $format, false, $outputlangs));
+				}
 			}
 			if ($detail->sellby) {
 				$dte[] = $outputlangs->transnoentitiesnoconv('printSellby', dol_print_date($detail->sellby, $format, false, $outputlangs));
 			}
-			if ($detail->batch) {
+			// Added by MMI Mathieu Moulin iProspective
+			// Hack Hide Batch number (not very useful)
+			if ($detail->batch && empty($conf->global->SHIPPING_PDF_HIDE_BATCH)) {
 				$dte[] = $outputlangs->transnoentitiesnoconv('printBatch', $detail->batch);
 			}
 			$dte[] = $outputlangs->transnoentitiesnoconv('printQty', $detail->qty);

+ 28 - 1
htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php

@@ -412,6 +412,12 @@ class doc_generic_order_odt extends ModelePDFCommandes
 							} else {
 								$odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
 							}
+						// Added by MMI Mathieu Moulin iProspective
+						// Display hypertext links
+						} elseif (is_string($value) && (preg_match('%^((https?://)|(www\.))([a-z0-9-].?)+(:[0-9]+)?(/.*)?$%i', $value) || preg_match('/^[a-z0-9\._-]+@[a-z0-9\._-]+$/i', $value))) // link
+						{
+							//var_dump($key); var_dump($value); die();
+							$odfHandler->setVars($key, '<a href="'.$value.'">'.$value.'</a>', false, 'UTF-8');
 						} else // Text
 						{
 							$odfHandler->setVars($key, $value, true, 'UTF-8');
@@ -441,7 +447,28 @@ class doc_generic_order_odt extends ModelePDFCommandes
 							$reshook = $hookmanager->executeHooks('ODTSubstitutionLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
 							foreach ($tmparray as $key => $val) {
 								try {
-									$listlines->setVars($key, $val, true, 'UTF-8');
+									// Added by MMI Mathieu Moulin iProspective
+									// Display logos
+									if (preg_match('/logo$/', $key) || preg_match('/logo2$/', $key)) { // Image
+										//var_dump($key); var_dump($val); die();
+										if (empty($val)) {
+											$listlines->setVars($key, $val, true, 'UTF-8');
+										}
+										elseif (file_exists($val)) {
+											//var_dump($key); var_dump($val); die();
+											$listlines->setImage($key, $val);
+										} else {
+											$listlines->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
+										}
+									} elseif (preg_match('/_$/', $key)) // Text
+									{
+										//var_dump($key); var_dump($val); die();
+										$listlines->setVars($key, $val, false, 'UTF-8');
+									} else // Text
+									{
+										//var_dump($key); var_dump($val); die();
+										$listlines->setVars($key, $val, true, 'UTF-8');
+									}
 								} catch (OdfException $e) {
 									dol_syslog($e->getMessage(), LOG_INFO);
 								} catch (SegmentException $e) {

+ 252 - 39
htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php

@@ -390,6 +390,14 @@ class pdf_eratosthene extends ModelePDFCommandes
 				$pdf->MultiCell(0, 3, ''); // Set interline to 3
 				$pdf->SetTextColor(0, 0, 0);
 
+				// Added by MMI Mathieu Moulin iProspective
+				// New text complement zone
+				if (!empty($conf->global->DOCUMENT_SHOW_COMPLEMENT)) {
+					$textComplement = $this->textComplement($object, $outputlangs);
+					$heightfocomplement = $this->heightComplementArea($pdf, $textComplement, $default_font_size);
+					//var_dump($heightfocomplement); die();
+					//$heightforsignature += $heightfocomplement;
+				}
 
 				$tab_top = 90 + $top_shift;
 				$tab_top_newpage = (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD) ? 42 + $top_shift : 10);
@@ -880,6 +888,11 @@ class pdf_eratosthene extends ModelePDFCommandes
 				}
 				*/
 
+				// MMI Hack
+				if (!empty($textComplement)) {
+					$posy = $this->drawComplementArea($pdf, $textComplement, $posy, $outputlangs);
+				}
+
 				// Pied de page
 				$this->_pagefoot($pdf, $object, $outputlangs);
 				if (method_exists($pdf, 'AliasNbPages')) {
@@ -1622,6 +1635,25 @@ class pdf_eratosthene extends ModelePDFCommandes
 			$hautcadre = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 38 : 40;
 			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
 
+			// Added by MMI Mathieu Moulin iProspective
+			// If CUSTOMER/SHIPPING contact defined, we use it
+			$useshippingcontact = false;
+			$arrayidcontact = $object->getIdContact('external', 'SHIPPING');
+			if (count($arrayidcontact) > 0) {
+				$usecontact = true;
+				$useshippingcontact = true;
+				$result = $object->fetch_contact($arrayidcontact[0]);
+			}
+			// If CUSTOMER/BILLING contact defined, we use it
+			$usebillingcontact = false;
+			$arrayidcontact = $object->getIdContact('external', 'BILLING');
+			if (count($arrayidcontact) > 0) {
+				$usebillingcontact = true;
+				$result = $object->fetch_contact($arrayidcontact[0]);
+			}
+			// MMI Hack size
+			if ($twocontacts = !empty($conf->global->MMI_DOCUMENT_PDF_SEPARATE_CONTACTS) && $useshippingcontact)
+				$widthrecbox = 60;
 
 			// Show sender frame
 			if (empty($conf->global->MAIN_PDF_NO_SENDER_FRAME)) {
@@ -1648,58 +1680,175 @@ class pdf_eratosthene extends ModelePDFCommandes
 			$pdf->SetFont('', '', $default_font_size - 1);
 			$pdf->MultiCell($widthrecbox - 2, 4, $carac_emetteur, 0, $ltrdirection);
 
-			// If CUSTOMER contact defined, we use it
-			$usecontact = false;
-			$arrayidcontact = $object->getIdContact('external', 'CUSTOMER');
-			if (count($arrayidcontact) > 0) {
-				$usecontact = true;
-				$result = $object->fetch_contact($arrayidcontact[0]);
-			}
+			// Added by MMI Mathieu Moulin iProspective
+			// 2 Adresses livraison & facturation
+			if ($twocontacts) {
+				// ---- RECIPIENT SHIPPING
+
+				// If CUSTOMER/SHIPPING contact defined, we use it
+				$usecontact = false;
+				$arrayidcontact = $object->getIdContact('external', 'SHIPPING');
+				if (count($arrayidcontact) > 0) {
+					$usecontact = true;
+					$result = $object->fetch_contact($arrayidcontact[0]);
+				}
 
-			//Recipient name
-			if ($usecontact && ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
-				$thirdparty = $object->contact;
-			} else {
-				$thirdparty = $object->thirdparty;
-			}
+				// Recipient name
+				if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+					$thirdparty = $object->contact;
+				} else {
+					$thirdparty = $object->thirdparty;
+				}
 
-			$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
 
-			$mode =  'target';
-			$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+				$mode =  'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
 
-			// Show recipient
-			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 100;
-			if ($this->page_largeur < 210) {
-				$widthrecbox = 84; // To work with US executive format
-			}
-			$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
-			$posy += $top_shift;
-			$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
-			if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
-				$posx = $this->marge_gauche;
-			}
+				// Show recipient
+				$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
+				
+				if ($this->page_largeur < 210) {
+					$widthrecbox = 84; // To work with US executive format
+				}
+				$widthrecbox = 60;
+				$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+				$posy += $top_shift;
+				$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+				if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+					$posx = $this->marge_gauche;
+				}
 
-			// Show recipient frame
-			if (empty($conf->global->MAIN_PDF_NO_RECIPENT_FRAME)) {
+				// Show recipient frame
 				$pdf->SetTextColor(0, 0, 0);
 				$pdf->SetFont('', '', $default_font_size - 2);
 				$pdf->SetXY($posx + 2, $posy - 5);
-				$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
+				$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("DeliveryAddress"), 0, $ltrdirection);
 				$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+
+				// Show recipient name
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+
+				$posy = $pdf->getY();
+
+				// Show recipient information
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->SetXY($posx + 2, $posy);
+				$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
+
+				// ---- RECIPIENT INVOICE
+
+				// If CUSTOMER contact defined, we use it
+				$usecontact = false;
+				$arrayidcontact = $object->getIdContact('external', 'BILLING');
+				if (count($arrayidcontact) > 0) {
+					$usecontact = true;
+					$result = $object->fetch_contact($arrayidcontact[0]);
+				}
+
+				// Recipient name
+				if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+					$thirdparty = $object->contact;
+				} else {
+					$thirdparty = $object->thirdparty;
+				}
+
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
+
+				$mode =  'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+
+				// Show recipient
+				$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
+				
+				if ($this->page_largeur < 210) {
+					$widthrecbox = 84; // To work with US executive format
+				}
+				$widthrecbox = 60;
+				$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+				$posy += $top_shift;
+				$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+				if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+					$posx = $this->marge_gauche;
+				}
+				$posx -= $widthrecbox +5;
+
+				// Show recipient frame
+				$pdf->SetTextColor(0, 0, 0);
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->SetXY($posx + 2, $posy - 5);
+				$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillAddress"), 0, $ltrdirection);
+				$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+
+				// Show recipient name
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+
+				$posy = $pdf->getY();
+
+				// Show recipient information
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->SetXY($posx + 2, $posy);
+				$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
 			}
+			// 1 seule adresse
+			else {
+				// If CUSTOMER contact defined, we use it
+				$usecontact = false;
+				$arrayidcontact = $object->getIdContact('external', 'CUSTOMER');
+				if (count($arrayidcontact) > 0) {
+					$usecontact = true;
+					$result = $object->fetch_contact($arrayidcontact[0]);
+				}
 
-			// Show recipient name
-			$pdf->SetXY($posx + 2, $posy + 3);
-			$pdf->SetFont('', 'B', $default_font_size);
-			$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+				// Recipient name
+				if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+					$thirdparty = $object->contact;
+				} else {
+					$thirdparty = $object->thirdparty;
+				}
 
-			$posy = $pdf->getY();
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
 
-			// Show recipient information
-			$pdf->SetFont('', '', $default_font_size - 1);
-			$pdf->SetXY($posx + 2, $posy);
-			$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
+				$mode = 'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+
+				// Show recipient
+				$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 100;
+				if ($this->page_largeur < 210) {
+					$widthrecbox = 84; // To work with US executive format
+				}
+				$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+				$posy += $top_shift;
+				$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+				if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+					$posx = $this->marge_gauche;
+				}
+
+				// Show recipient frame
+				if (empty($conf->global->MAIN_PDF_NO_RECIPENT_FRAME)) {
+					$pdf->SetTextColor(0, 0, 0);
+					$pdf->SetFont('', '', $default_font_size - 2);
+					$pdf->SetXY($posx + 2, $posy - 5);
+					$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
+					$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+				}
+
+				// Show recipient name
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+
+				$posy = $pdf->getY();
+
+				// Show recipient information
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->SetXY($posx + 2, $posy);
+				$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
+			}
 		}
 
 		$pdf->SetTextColor(0, 0, 0);
@@ -1725,6 +1874,70 @@ class pdf_eratosthene extends ModelePDFCommandes
 		return pdf_pagefoot($pdf, $outputlangs, 'ORDER_FREE_TEXT', $this->emetteur, $this->marge_basse, $this->marge_gauche, $this->page_hauteur, $object, $showdetails, $hidefreetext, $this->page_largeur, $this->watermark);
 	}
 
+	/**
+	 * Added by MMI Mathieu Moulin iProspective
+	 */
+	protected function textComplement(&$object, $outputlangs)
+	{
+		global $conf;
+		$outputlangs->load('mmidocuments@mmidocuments');
+
+		//var_dump($object); die();
+		//var_dump($object->array_options['options_cgv_cpv']); die();
+		$complement = [];
+		if (!empty($object->array_options['options_cgv_cpv']))
+			$complement[] = '<h3>'.$outputlangs->transnoentities("DocumentMoreInfoCGP")."</h3>\r\n".$object->array_options['options_cgv_cpv'];
+		if (!empty($object->array_options['options_propal_decennale']))
+			$complement[] = '<h3>'.$outputlangs->transnoentities("DocumentMoreInfoDecennale")."</h3>\r\n".$conf->global->MMIPROJECT_DECENNALE_TEXT;
+		//var_dump($complement); die();
+		return !empty($complement) ?implode("\r\n", $complement) :'';
+	}
+	protected function heightComplement(&$pdf, $text, $default_font_size)
+	{
+		$pdf->SetFont('', '', $default_font_size - 2);
+		$useborder = 0;
+		$cellpadding = 0;
+		$reseth = false;
+		$autopadding = true;
+		$largcol = ($this->page_largeur - $this->marge_droite - $this->marge_gauche);
+		return $pdf->getStringHeight($largcol, strip_tags($text), $reseth, $autopadding, $cellpadding, $useborder);
+	}
+	protected function heightComplementArea(&$pdf, $text, $default_font_size)
+	{
+		$marg_top = 8;
+		$tab_titre = 4;
+		$tab_text = $this->heightComplement($pdf, $text, $default_font_size);
+		//var_dump($tab_text);
+		return $marg_top + $tab_titre + $tab_text;
+	}
+	protected function drawComplementArea(&$pdf, $text, $posy, $outputlangs)
+	{
+		global $conf;
+		$outputlangs->load('mmidocuments@mmidocuments');
+		
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+		$marg_top = 8;
+		$tab_top = $posy + $marg_top;
+
+		$posx = $this->marge_gauche;
+		$largcol = ($this->page_largeur - $this->marge_droite - $posx);
+
+		$pdf->SetFillColor(255, 255, 255);
+		$pdf->SetFont('', '', $default_font_size - 2);
+
+		// Titre
+		$pdf->SetXY($posx, $tab_top);
+		$tab_titre = 4;
+		$pdf->WriteHTMLCell($largcol, $tab_titre, $posx, $posy+$marg_top, '<b>'.$outputlangs->transnoentities("DocumentMoreInfo").'</b>', 0);
+		// Texte
+		$pdf->SetXY($posx, $tab_top + $tab_titre);
+		$tab_text = $this->heightComplement($pdf, $text, $default_font_size);
+		//var_dump($tab_text); die();
+		$pdf->WriteHTMLCell($largcol, $tab_text, $posx,$tab_top+$tab_titre, $text, 1, 'L');
+		//var_dump($tab_top + $tab_titre + $tab_text); die();
+
+		return $tab_top + $tab_titre + $tab_text;
+	}
 
 
 	/**

+ 48 - 1
htdocs/core/modules/expedition/doc/doc_generic_shipment_odt.modules.php

@@ -392,6 +392,12 @@ class doc_generic_shipment_odt extends ModelePdfExpedition
 							} else {
 								$odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
 							}
+						// Added by MMI Mathieu Moulin iProspective
+						// Hypertext links
+						} elseif (is_string($value) && (preg_match('/^http/', $value) || preg_match('/^[a-z0-9\._-]+@[a-z0-9\._-]+/i', $value))) // link
+						{
+							//var_dump($key); var_dump($value); die();
+							$odfHandler->setVars($key, '<a href="'.$value.'">'.$value.'</a>', false, 'UTF-8');
 						} else // Text
 						{
 							$odfHandler->setVars($key, $value, true, 'UTF-8');
@@ -412,6 +418,12 @@ class doc_generic_shipment_odt extends ModelePdfExpedition
 							} else {
 								$odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
 							}
+						// Added by MMI Mathieu Moulin iProspective
+						// Hypertext links
+						} elseif (is_string($value) && (preg_match('/^http/', $value) || preg_match('/^[a-z0-9\._-]+@[a-z0-9\._-]+/i', $value))) // link
+						{
+							//var_dump($key); var_dump($value); die();
+							$odfHandler->setVars($key, '<a href="'.$value.'">'.$value.'</a>', false, 'UTF-8');
 						} else // Text
 						{
 							$odfHandler->setVars($key, $value, true, 'UTF-8');
@@ -434,6 +446,12 @@ class doc_generic_shipment_odt extends ModelePdfExpedition
 							} else {
 								$odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
 							}
+						// Added by MMI Mathieu Moulin iProspective
+						// Hypertext links
+						} elseif (is_string($value) && (preg_match('/^http/', $value) || preg_match('/^[a-z0-9\._-]+@[a-z0-9\._-]+/i', $value))) // link
+						{
+							//var_dump($key); var_dump($value); die();
+							$odfHandler->setVars($key, '<a href="'.$value.'">'.$value.'</a>', false, 'UTF-8');
 						} else // Text
 						{
 							$odfHandler->setVars($key, $value, true, 'UTF-8');
@@ -453,6 +471,12 @@ class doc_generic_shipment_odt extends ModelePdfExpedition
 								} else {
 									$odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
 								}
+							// Added by MMI Mathieu Moulin iProspective
+							// Hypertext links
+							} elseif (is_string($value) && (preg_match('/^http/', $value) || preg_match('/^[a-z0-9\._-]+@[a-z0-9\._-]+/i', $value))) // link
+							{
+								//var_dump($key); var_dump($value); die();
+								$odfHandler->setVars($key, '<a href="'.$value.'">'.$value.'</a>', false, 'UTF-8');
 							} else // Text
 							{
 								$odfHandler->setVars($key, $value, true, 'UTF-8');
@@ -478,6 +502,12 @@ class doc_generic_shipment_odt extends ModelePdfExpedition
 							} else {
 								$odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
 							}
+						// Added by MMI Mathieu Moulin iProspective
+						// Hypertext links
+						} elseif (is_string($value) && (preg_match('/^http/', $value) || preg_match('/^[a-z0-9\._-]+@[a-z0-9\._-]+/i', $value))) // link
+						{
+							//var_dump($key); var_dump($value); die();
+							$odfHandler->setVars($key, '<a href="'.$value.'">'.$value.'</a>', false, 'UTF-8');
 						} else // Text
 						{
 							$odfHandler->setVars($key, $value, true, 'UTF-8');
@@ -505,7 +535,24 @@ class doc_generic_shipment_odt extends ModelePdfExpedition
 							$reshook = $hookmanager->executeHooks('ODTSubstitutionLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
 							foreach ($tmparray as $key => $val) {
 								try {
-									$listlines->setVars($key, $val, true, 'UTF-8');
+									// Added by MMI Mathieu Moulin iProspective
+									// Logo
+									if (preg_match('/logo$/', $key) || preg_match('/logo2$/', $key)) { // Image
+										//var_dump($key); var_dump($val); die();
+										if (empty($val)) {
+											$listlines->setVars($key, $val, true, 'UTF-8');
+										}
+										elseif (file_exists($val)) {
+											//var_dump($key); var_dump($val); die();
+											$listlines->setImage($key, $val);
+										} else {
+											$listlines->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
+										}
+									} else // Text
+									{
+										//var_dump($key); var_dump($val); die();
+										$listlines->setVars($key, $val, true, 'UTF-8');
+									}
 								} catch (OdfException $e) {
 									dol_syslog($e->getMessage(), LOG_INFO);
 								} catch (SegmentException $e) {

+ 10 - 4
htdocs/core/modules/expedition/doc/pdf_rouget.modules.php

@@ -221,6 +221,12 @@ class pdf_rouget extends ModelePdfExpedition
 			$this->watermark = $conf->global->SHIPPING_DRAFT_WATERMARK;
 		}
 
+		// Added by MMI Mathieu Moulin iProspective
+		// Sort by Product Ref
+		if (!empty($conf->global->MMI_EXPE_ORDER_LINES_BY_REF))
+			usort($object->lines, function($i, $j){
+				return ($i->ref > $j->ref) ?1 :(($i->ref < $j->ref) ?-1 :0);
+			});
 		$nblines = count($object->lines);
 
 		// Loop on each lines to detect if there is at least one image to show
@@ -239,7 +245,7 @@ class pdf_rouget extends ModelePdfExpedition
 					$pdir = get_exdir($object->lines[$i]->fk_product, 2, 0, 0, $objphoto, 'product').$object->lines[$i]->fk_product."/photos/";
 					$dir = $conf->product->dir_output.'/'.$pdir;
 				} else {
-					$pdir = get_exdir(0, 2, 0, 0, $objphoto, 'product').dol_sanitizeFileName($objphoto->ref).'/';
+					$pdir = get_exdir(0, 2, 0, 0, $objphoto, 'product');
 					$dir = $conf->product->dir_output.'/'.$pdir;
 				}
 
@@ -482,7 +488,7 @@ class pdf_rouget extends ModelePdfExpedition
 
 					if (isset($imglinesize['width']) && isset($imglinesize['height'])) {
 						$curX = $this->posxpicture - 1;
-						$pdf->Image($realpatharray[$i], $curX + (($this->posxweightvol - $this->posxpicture - $imglinesize['width']) / 2), $curY, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
+						$pdf->Image($realpatharray[$i], $curX + (($this->posxweightvol - $this->posxpicture - $imglinesize['width']) / 2), $curY, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, !empty($conf->global->MAIN_SHIPPING_PDF_IMAGE_DPI) ?$conf->global->MAIN_SHIPPING_PDF_IMAGE_DPI :300); // Use 300 dpi
 						// $pdf->Image does not increase value return by getY, so we save it manually
 						$posYAfterImage = $curY + $imglinesize['height'];
 					}
@@ -977,7 +983,7 @@ class pdf_rouget extends ModelePdfExpedition
 		$pdf->MultiCell($w, 4, $outputlangs->transnoentities("RefSending")." : ".$object->ref, '', 'R');
 
 		// Date planned delivery
-		if (!empty($object->date_delivery)) {
+		if (empty($conf->global->SHIPPING_PDF_HIDE_DELIVERY_DATE) && !empty($object->date_delivery)) {
 				$posy += 4;
 				$pdf->SetXY($posx, $posy);
 				$pdf->SetTextColor(0, 0, 60);
@@ -1084,7 +1090,7 @@ class pdf_rouget extends ModelePdfExpedition
 			}
 
 			// Recipient name
-			if ($usecontact && ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+			if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
 				$thirdparty = $object->contact;
 			} else {
 				$thirdparty = $object->thirdparty;

+ 29 - 1
htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php

@@ -437,6 +437,12 @@ class doc_generic_invoice_odt extends ModelePDFFactures
 							} else {
 								$odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
 							}
+						// Added by MMI Mathieu Moulin iProspective
+						// Hypertext links
+						} elseif (is_string($value) && (preg_match('%^((https?://)|(www\.))([a-z0-9-].?)+(:[0-9]+)?(/.*)?$%i', $value) || preg_match('/^[a-z0-9\._-]+@[a-z0-9\._-]+$/i', $value))) {
+							// link
+							//var_dump($key); var_dump($value); die();
+							$odfHandler->setVars($key, '<a href="'.$value.'">'.$value.'</a>', false, 'UTF-8');
 						} else {
 							// Text
 							$odfHandler->setVars($key, $value, true, 'UTF-8');
@@ -466,7 +472,29 @@ class doc_generic_invoice_odt extends ModelePDFFactures
 							$reshook = $hookmanager->executeHooks('ODTSubstitutionLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
 							foreach ($tmparray as $key => $val) {
 								try {
-									$listlines->setVars($key, $val, true, 'UTF-8');
+									// Added by MMI Mathieu Moulin iProspective
+									// Logo
+									if (preg_match('/logo$/', $key) || preg_match('/logo2$/', $key)) { // Image
+										//var_dump($key); var_dump($val); die();
+										if (empty($val)) {
+											$listlines->setVars($key, $val, true, 'UTF-8');
+										}
+										elseif (file_exists($val)) {
+											//var_dump($key); var_dump($val); die();
+											$listlines->setImage($key, $val);
+										} else {
+											$listlines->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
+										}
+									} elseif (preg_match('/_$/', $key)) // Text
+									{
+										//var_dump($key); var_dump($val); die();
+										$listlines->setVars($key, $val, false, 'UTF-8');
+										//var_dump($key, $val);
+									} else // Text
+									{
+										//var_dump($key); var_dump($val); die();
+										$listlines->setVars($key, $val, true, 'UTF-8');
+									}
 								} catch (OdfException $e) {
 									dol_syslog($e->getMessage(), LOG_INFO);
 								} catch (SegmentException $e) {

+ 275 - 40
htdocs/core/modules/facture/doc/pdf_sponge.modules.php

@@ -431,6 +431,16 @@ class pdf_sponge extends ModelePDFFactures
 
 				// $tab_top is y where we must continue content (90 = 42 + 48: 42 is height of logo and ref, 48 is address blocks)
 				$tab_top = 90 + $top_shift;		// top_shift is an addition for linked objects or addons (0 in most cases)
+
+				// Added by MMI Mathieu Moulin iProspective
+				// Text complement
+				if (!empty($conf->global->DOCUMENT_SHOW_COMPLEMENT)) {
+					$textComplement = $this->textComplement($object, $outputlangs);
+					$heightfocomplement = $this->heightComplementArea($pdf, $textComplement, $default_font_size);
+					//var_dump($heightfocomplement); die();
+					//$heightforsignature += $heightfocomplement;
+				}
+
 				$tab_top_newpage = (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD) ? 42 + $top_shift : 10);
 
 				// You can add more thing under header here, if you increase $extra_under_address_shift too.
@@ -978,6 +988,12 @@ class pdf_sponge extends ModelePDFFactures
 					$posy = $this->drawPaymentsTable($pdf, $object, $posy, $outputlangs);
 				}
 
+				// Added by MMI Mathieu Moulin iProspective
+				// Text complement
+				if (!empty($textComplement)) {
+					$posy = $this->drawComplementArea($pdf, $textComplement, $posy, $outputlangs);
+				}
+
 				// Pagefoot
 				$this->_pagefoot($pdf, $object, $outputlangs);
 				if (method_exists($pdf, 'AliasNbPages')) {
@@ -1212,6 +1228,23 @@ class pdf_sponge extends ModelePDFFactures
 			$posy = $pdf->GetY() + 3; // We need spaces for 2 lines payment conditions
 		}
 
+		// Added by MMI Mathieu Moulin iProspective
+		// Show shipping date
+		if (!empty($object->array_options['options_date_livraison_aff']) && !empty($object->array_options['options_date_livraison'])) {
+			$outputlangs->load("sendings");
+			$pdf->SetFont('', 'B', $default_font_size - 2);
+			$pdf->SetXY($this->marge_gauche, $posy);
+			$liv_type = $object->array_options['options_date_livraison_aff'];
+			$titre = $outputlangs->transnoentities(($liv_type==2 ?'DeliveryDate' :"DateDeliveryPlanned")).':';
+			$pdf->MultiCell(80, 4, $titre, 0, 'L');
+			$pdf->SetFont('', '', $default_font_size - 2);
+			$pdf->SetXY($posxval, $posy);
+			$dlp = dol_print_date($object->array_options['options_date_livraison'], "daytext", false, $outputlangs, true);
+			$pdf->MultiCell(80, 4, $dlp, 0, 'L');
+
+			$posy = $pdf->GetY() + 1;
+		}
+
 		if ($object->type != 2) {
 			// Check a payment mode is defined
 			if (empty($object->mode_reglement_code)
@@ -2183,7 +2216,26 @@ class pdf_sponge extends ModelePDFFactures
 
 			$hautcadre = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 38 : 40;
 			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
-
+			
+			// Added by MMI Mathieu Moulin iProspective
+			// Multiple contacts shipping & invoice
+			// If CUSTOMER/SHIPPING contact defined, we use it
+			$useshippingcontact = false;
+			$arrayidcontact = $object->getIdContact('external', 'SHIPPING');
+			if (count($arrayidcontact) > 0) {
+				$usecontact = true;
+				$useshippingcontact = true;
+				$result = $object->fetch_contact($arrayidcontact[0]);
+			}
+			// If CUSTOMER/BILLING contact defined, we use it
+			$usebillingcontact = false;
+			$arrayidcontact = $object->getIdContact('external', 'BILLING');
+			if (count($arrayidcontact) > 0) {
+				$usebillingcontact = true;
+				$result = $object->fetch_contact($arrayidcontact[0]);
+			}
+			if ($twocontacts = !empty($conf->global->MMI_DOCUMENT_PDF_SEPARATE_CONTACTS) && $useshippingcontact)
+				$widthrecbox = 60;
 
 			// Show sender frame
 			if (empty($conf->global->MAIN_PDF_NO_SENDER_FRAME)) {
@@ -2210,58 +2262,175 @@ class pdf_sponge extends ModelePDFFactures
 			$pdf->SetFont('', '', $default_font_size - 1);
 			$pdf->MultiCell($widthrecbox - 2, 4, $carac_emetteur, 0, $ltrdirection);
 
-			// If BILLING contact defined on invoice, we use it
-			$usecontact = false;
-			$arrayidcontact = $object->getIdContact('external', 'BILLING');
-			if (count($arrayidcontact) > 0) {
-				$usecontact = true;
-				$result = $object->fetch_contact($arrayidcontact[0]);
-			}
+			// Added by MMI Mathieu Moulin iProspective
+			// Multiple contacts shipping & invoice
+			if ($twocontacts) {
+				// ---- RECIPIENT SHIPPING
+
+				// If CUSTOMER/SHIPPING contact defined, we use it
+				$usecontact = false;
+				$arrayidcontact = $object->getIdContact('external', 'SHIPPING');
+				if (count($arrayidcontact) > 0) {
+					$usecontact = true;
+					$result = $object->fetch_contact($arrayidcontact[0]);
+				}
 
-			// Recipient name
-			if ($usecontact && ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
-				$thirdparty = $object->contact;
-			} else {
-				$thirdparty = $object->thirdparty;
-			}
+				// Recipient name
+				if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+					$thirdparty = $object->contact;
+				} else {
+					$thirdparty = $object->thirdparty;
+				}
 
-			$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
 
-			$mode = 'target';
-			$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+				$mode =  'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
 
-			// Show recipient
-			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 100;
-			if ($this->page_largeur < 210) {
-				$widthrecbox = 84; // To work with US executive format
-			}
-			$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
-			$posy += $top_shift;
-			$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
-			if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
-				$posx = $this->marge_gauche;
-			}
+				// Show recipient
+				$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
+				
+				if ($this->page_largeur < 210) {
+					$widthrecbox = 84; // To work with US executive format
+				}
+				$widthrecbox = 60;
+				$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+				$posy += $top_shift;
+				$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+				if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+					$posx = $this->marge_gauche;
+				}
 
-			// Show recipient frame
-			if (empty($conf->global->MAIN_PDF_NO_RECIPENT_FRAME)) {
+				// Show recipient frame
 				$pdf->SetTextColor(0, 0, 0);
 				$pdf->SetFont('', '', $default_font_size - 2);
 				$pdf->SetXY($posx + 2, $posy - 5);
-				$pdf->MultiCell($widthrecbox - 2, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
+				$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("DeliveryAddress"), 0, $ltrdirection);
 				$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+
+				// Show recipient name
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+
+				$posy = $pdf->getY();
+
+				// Show recipient information
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->SetXY($posx + 2, $posy);
+				$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
+
+				// ---- RECIPIENT INVOICE
+
+				// If CUSTOMER contact defined, we use it
+				$usecontact = false;
+				$arrayidcontact = $object->getIdContact('external', 'BILLING');
+				if (count($arrayidcontact) > 0) {
+					$usecontact = true;
+					$result = $object->fetch_contact($arrayidcontact[0]);
+				}
+
+				// Recipient name
+				if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+					$thirdparty = $object->contact;
+				} else {
+					$thirdparty = $object->thirdparty;
+				}
+
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
+
+				$mode =  'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+
+				// Show recipient
+				$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
+				
+				if ($this->page_largeur < 210) {
+					$widthrecbox = 84; // To work with US executive format
+				}
+				$widthrecbox = 60;
+				$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+				$posy += $top_shift;
+				$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+				if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+					$posx = $this->marge_gauche;
+				}
+				$posx -= $widthrecbox +5;
+
+				// Show recipient frame
+				$pdf->SetTextColor(0, 0, 0);
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->SetXY($posx + 2, $posy - 5);
+				$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillAddress"), 0, $ltrdirection);
+				$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+
+				// Show recipient name
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+
+				$posy = $pdf->getY();
+
+				// Show recipient information
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->SetXY($posx + 2, $posy);
+				$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
 			}
+			// 1 seule adresse
+			else {
+				// If CUSTOMER contact defined, we use it
+				$usecontact = false;
+				$arrayidcontact = $object->getIdContact('external', 'CUSTOMER');
+				if (count($arrayidcontact) > 0) {
+					$usecontact = true;
+					$result = $object->fetch_contact($arrayidcontact[0]);
+				}
 
-			// Show recipient name
-			$pdf->SetXY($posx + 2, $posy + 3);
-			$pdf->SetFont('', 'B', $default_font_size);
-			$pdf->MultiCell($widthrecbox - 2, 2, $carac_client_name, 0, $ltrdirection);
+				// Recipient name
+				if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+					$thirdparty = $object->contact;
+				} else {
+					$thirdparty = $object->thirdparty;
+				}
 
-			$posy = $pdf->getY();
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
 
-			// Show recipient information
-			$pdf->SetFont('', '', $default_font_size - 1);
-			$pdf->SetXY($posx + 2, $posy);
-			$pdf->MultiCell($widthrecbox - 2, 4, $carac_client, 0, $ltrdirection);
+				$mode = 'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+
+				// Show recipient
+				$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 100;
+				if ($this->page_largeur < 210) {
+					$widthrecbox = 84; // To work with US executive format
+				}
+				$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+				$posy += $top_shift;
+				$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+				if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+					$posx = $this->marge_gauche;
+				}
+
+				// Show recipient frame
+				if (empty($conf->global->MAIN_PDF_NO_RECIPENT_FRAME)) {
+					$pdf->SetTextColor(0, 0, 0);
+					$pdf->SetFont('', '', $default_font_size - 2);
+					$pdf->SetXY($posx + 2, $posy - 5);
+					$pdf->MultiCell($widthrecbox - 2, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
+					$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+				}
+
+				// Show recipient name
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox - 2, 2, $carac_client_name, 0, $ltrdirection);
+
+				$posy = $pdf->getY();
+
+				// Show recipient information
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->SetXY($posx + 2, $posy);
+				$pdf->MultiCell($widthrecbox - 2, 4, $carac_client, 0, $ltrdirection);
+			}
 		}
 
 		$pdf->SetTextColor(0, 0, 0);
@@ -2285,6 +2454,72 @@ class pdf_sponge extends ModelePDFFactures
 		return pdf_pagefoot($pdf, $outputlangs, 'INVOICE_FREE_TEXT', $this->emetteur, $this->marge_basse, $this->marge_gauche, $this->page_hauteur, $object, $showdetails, $hidefreetext, $this->page_largeur, $this->watermark);
 	}
 
+	/**
+	 * Added by MMI Mathieu Moulin iProspective
+	 * Text complement
+	 */
+	protected function textComplement(&$object, $outputlangs)
+	{
+		global $conf;
+		$outputlangs->load('mmidocuments@mmidocuments');
+
+		//var_dump($object); die();
+		//var_dump($object->array_options['options_cgv_cpv']); die();
+		$complement = [];
+		if (!empty($object->array_options['options_cgv_cpv']))
+			$complement[] = '<p><b>'.$outputlangs->transnoentities("DocumentMoreInfoCGP")."</b></p>\r\n".$object->array_options['options_cgv_cpv'];
+		if (!empty($object->array_options['options_propal_decennale']))
+			$complement[] = '<p><b>'.$outputlangs->transnoentities("DocumentMoreInfoDecennale")."</b></p>\r\n".$conf->global->MMIPROJECT_DECENNALE_TEXT;
+		//var_dump($complement); die();
+		return !empty($complement) ?implode("\r\n", $complement) :'';
+	}
+	protected function heightComplement(&$pdf, $text, $default_font_size)
+	{
+		$pdf->SetFont('', '', $default_font_size - 2);
+		$useborder = 0;
+		$cellpadding = 0;
+		$reseth = false;
+		$autopadding = true;
+		$largcol = ($this->page_largeur - $this->marge_droite - $this->marge_gauche);
+		return $pdf->getStringHeight($largcol, strip_tags($text), $reseth, $autopadding, $cellpadding, $useborder);
+	}
+	protected function heightComplementArea(&$pdf, $text, $default_font_size)
+	{
+		$marg_top = 8;
+		$tab_titre = 4;
+		$tab_text = $this->heightComplement($pdf, $text, $default_font_size);
+		//var_dump($tab_text);
+		return $marg_top + $tab_titre + $tab_text;
+	}
+	protected function drawComplementArea(&$pdf, $text, $posy, $outputlangs)
+	{
+		global $conf;
+		$outputlangs->load('mmidocuments@mmidocuments');
+		
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+		$marg_top = 8;
+		$tab_top = $posy + $marg_top;
+
+		$posx = $this->marge_gauche;
+		$largcol = ($this->page_largeur - $this->marge_droite - $posx);
+
+		$pdf->SetFillColor(255, 255, 255);
+		$pdf->SetFont('', '', $default_font_size - 2);
+
+		// Titre
+		$pdf->SetXY($posx, $tab_top);
+		$tab_titre = 4;
+		$pdf->WriteHTMLCell($largcol, $tab_titre, $posx, $posy+$marg_top, '<b>'.$outputlangs->transnoentities("DocumentMoreInfo").'</b>', 0);
+		// Texte
+		$pdf->SetXY($posx, $tab_top + $tab_titre);
+		$tab_text = $this->heightComplement($pdf, $text, $default_font_size);
+		//var_dump($tab_text); die();
+		$pdf->WriteHTMLCell($largcol, $tab_text, $posx,$tab_top+$tab_titre, $text, 1, 'L');
+		//var_dump($tab_top + $tab_titre + $tab_text); die();
+
+		return $tab_top + $tab_titre + $tab_text;
+	}
+
 	/**
 	 *  Define Array Column Field
 	 *

+ 8 - 0
htdocs/core/modules/modFournisseur.class.php

@@ -456,7 +456,10 @@ class modFournisseur extends DolibarrModules
 			'fd.tva_tx'=>"LineVATRate", 'fd.qty'=>"LineQty", 'fd.remise_percent'=>"Discount", 'fd.total_ht'=>"LineTotalHT", 'fd.total_ttc'=>"LineTotalTTC",
 			'fd.total_tva'=>"LineTotalVAT", 'fd.date_start'=>"DateStart", 'fd.date_end'=>"DateEnd", 'fd.special_code'=>'SpecialCode',
 			'fd.product_type'=>'TypeOfLineServiceOrProduct', 'fd.ref'=>'RefSupplier', 'fd.fk_product'=>'ProductId',
+			// Added by MMI Mathieu Moulin iProspective : add BarCodeValue
+			'p.barcode'=>'BarcodeValue',
 			'p.ref'=>'ProductRef', 'p.label'=>'ProductLabel', 'project.rowid'=>'ProjectId', 'project.ref'=>'ProjectRef', 'project.title'=>'ProjectLabel'
+
 		);
 		if (!empty($conf->multicurrency->enabled)) {
 			$this->export_fields_array[$r]['f.multicurrency_code'] = 'Currency';
@@ -476,6 +479,8 @@ class modFournisseur extends DolibarrModules
 			'f.fk_statut'=>'Status', 'f.date_valid'=>'Date', 'f.date_approve'=>'Date', 'f.date_approve2'=>'Date', 'f.note_public'=>"Text", 'f.note_private'=>"Text", 'fd.description'=>"Text",
 			'fd.tva_tx'=>"Numeric", 'fd.qty'=>"Numeric", 'fd.remise_percent'=>"Numeric", 'fd.total_ht'=>"Numeric", 'fd.total_ttc'=>"Numeric", 'fd.total_tva'=>"Numeric",
 			'fd.date_start'=>"Date", 'fd.date_end'=>"Date", 'fd.special_code'=>"Numeric",
+			// Added by MMI Mathieu Moulin iProspective : add BarCodeValue
+			'p.barcode'=>'Text',
 			'fd.product_type'=>'Numeric', 'fd.ref'=>'Text', 'fd.fk_product'=>'List:product:label', 'p.ref'=>'Text', 'p.label'=>'Text', 'project.ref'=>'Text', 'project.title'=>'Text'
 		);
 		$this->export_entities_array[$r] = array(
@@ -484,7 +489,10 @@ class modFournisseur extends DolibarrModules
 			'ua2.login'=>'user', 'fd.rowid'=>'order_line', 'fd.description'=>"order_line", 'fd.tva_tx'=>"order_line", 'fd.qty'=>"order_line", 'fd.remise_percent'=>"order_line",
 			'fd.total_ht'=>"order_line", 'fd.total_ttc'=>"order_line", 'fd.total_tva'=>"order_line", 'fd.date_start'=>"order_line", 'fd.date_end'=>"order_line", 'fd.special_code'=>"order_line",
 			'fd.product_type'=>'order_line', 'fd.ref'=>'order_line', 'fd.fk_product'=>'product',
+			// Added by MMI Mathieu Moulin iProspective : add BarCodeValue
+			'p.barcode'=>'product',
 			'p.ref'=>'product', 'p.label'=>'product', 'project.rowid'=>'project', 'project.ref'=>'project', 'project.title'=>'project'
+
 		);
 		$this->export_dependencies_array[$r] = array('order_line'=>'fd.rowid', 'product'=>'fd.rowid'); // To add unique key if we ask a field of a child to avoid the DISTINCT to discard them
 		// Add extra fields object

+ 30 - 2
htdocs/core/modules/propale/doc/doc_generic_proposal_odt.modules.php

@@ -442,7 +442,14 @@ class doc_generic_proposal_odt extends ModelePDFPropales
 							} else {
 								$odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
 							}
-						} else // Text
+						// Added by MMI Mathieu Moulin iProspective
+						// Hypertext links
+						} elseif (is_string($value) && (preg_match('%^((https?://)|(www\.))([a-z0-9-].?)+(:[0-9]+)?(/.*)?$%i', $value) || preg_match('/^[a-z0-9\._-]+@[a-z0-9\._-]+$/i', $value))) // link
+						{
+							//var_dump($key); var_dump($value); die();
+							$odfHandler->setVars($key, '<a href="'.$value.'">'.$value.'</a>', false, 'UTF-8');
+						}
+						 else // Text
 						{
 							$odfHandler->setVars($key, $value, true, 'UTF-8');
 						}
@@ -471,7 +478,28 @@ class doc_generic_proposal_odt extends ModelePDFPropales
 							$reshook = $hookmanager->executeHooks('ODTSubstitutionLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
 							foreach ($tmparray as $key => $val) {
 								try {
-									$listlines->setVars($key, $val, true, 'UTF-8');
+									// Added by MMI Mathieu Moulin iProspective
+									// Logo
+									if (preg_match('/logo$/', $key) || preg_match('/logo2$/', $key)) { // Image
+										//var_dump($key); var_dump($val); die();
+										if (empty($val)) {
+											$listlines->setVars($key, $val, true, 'UTF-8');
+										}
+										elseif (file_exists($val)) {
+											//var_dump($key); var_dump($val); die();
+											$listlines->setImage($key, $val);
+										} else {
+											$listlines->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
+										}
+									} elseif (preg_match('/_$/', $key)) // Text
+									{
+										//var_dump($key); var_dump($val); die();
+										$listlines->setVars($key, $val, false, 'UTF-8');
+									} else // Text
+									{
+										//var_dump($key); var_dump($val); die();
+										$listlines->setVars($key, $val, true, 'UTF-8');
+									}
 								} catch (OdfException $e) {
 									dol_syslog($e->getMessage(), LOG_INFO);
 								} catch (SegmentException $e) {

+ 270 - 40
htdocs/core/modules/propale/doc/pdf_cyan.modules.php

@@ -383,6 +383,14 @@ class pdf_cyan extends ModelePDFPropales
 
 				$heightforinfotot = 40; // Height reserved to output the info and total part
 				$heightforsignature = empty($conf->global->PROPAL_DISABLE_SIGNATURE) ? (pdfGetHeightForHtmlContent($pdf, $outputlangs->transnoentities("ProposalCustomerSignature")) + 10) : 0;
+				// Added by MMI Mathieu Moulin iProspective
+				$heightfocomplement = 0;
+				if (!empty($conf->global->DOCUMENT_SHOW_COMPLEMENT)) {
+					$textComplement = $this->textComplement($object, $outputlangs);
+					$heightfocomplement = $this->heightComplementArea($pdf, $textComplement, $default_font_size);
+					//var_dump($heightfocomplement); die();
+					$heightforsignature += $heightfocomplement;
+				}
 				$heightforfreetext = (isset($conf->global->MAIN_PDF_FREETEXT_HEIGHT) ? $conf->global->MAIN_PDF_FREETEXT_HEIGHT : 5); // Height reserved to output the free text on last page
 				$heightforfooter = $this->marge_basse + (empty($conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS) ? 12 : 22); // Height reserved to output the footer (value include bottom margin)
 				//print $heightforinfotot + $heightforsignature + $heightforfreetext + $heightforfooter;exit;
@@ -902,6 +910,11 @@ class pdf_cyan extends ModelePDFPropales
 					$posy = $this->drawSignatureArea($pdf, $object, $posy, $outputlangs);
 				}
 
+				// Added by MMI Mathieu Moulin iProspective
+				if (!empty($textComplement)) {
+					$posy = $this->drawComplementArea($pdf, $textComplement, $posy, $outputlangs);
+				}
+
 				// Pagefoot
 				$this->_pagefoot($pdf, $object, $outputlangs);
 				if (method_exists($pdf, 'AliasNbPages')) {
@@ -1465,6 +1478,25 @@ class pdf_cyan extends ModelePDFPropales
 			$pdf->SetTextColor(0, 0, 0);
 		}
 
+		// Added by MMI Mathieu Moulin iProspective
+		// @todo Hook
+		$acompte_p = (!empty($object->array_options['options_acompte']) && $object->array_options['options_acompte']>0) ?$object->array_options['options_acompte'] :0;
+		if (!$acompte_p)
+			$acompte_val = (!empty($object->array_options['options_acompte_val']) && $object->array_options['options_acompte_val']>0) ?round($object->array_options['options_acompte_val'], 2) :0;
+		if (!empty($object->array_options['options_acompte_aff']) && ($acompte_p || $acompte_val)) {
+			$index++;
+			$outputlangs->load('mmidocuments@mmidocuments');
+			if ($acompte_p)
+				$acompte_val = round($object->total_ttc*$acompte_p/100, 2);
+			$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+			$pdf->SetFont('', 'B', $default_font_size - 1);
+			if ($acompte_p)
+				$pdf->MultiCell($col2x, $tab2_hl, $outputlangs->transnoentities('PaymentAcomptePourcent', str_replace('.', ',', $acompte_p).' %', price($acompte_val)), 0, 'L', 0);
+			else
+				$pdf->MultiCell($col2x, $tab2_hl, $outputlangs->transnoentities('PaymentAcompte', price($acompte_val)), 0, 'L', 0);
+			$posy = $pdf->GetY() + 1;
+		}
+
 		$index++;
 		return ($tab2_top + ($tab2_hl * $index));
 	}
@@ -1723,6 +1755,24 @@ class pdf_cyan extends ModelePDFPropales
 			$hautcadre = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 38 : 40;
 			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
 
+			// Added by MMI Mathieu Moulin iProspective
+			// If CUSTOMER/SHIPPING contact defined, we use it
+			$useshippingcontact = false;
+			$arrayidcontact = $object->getIdContact('external', 'SHIPPING');
+			if (count($arrayidcontact) > 0) {
+				$useshippingcontact = true;
+				$result = $object->fetch_contact($arrayidcontact[0]);
+			}
+			// If CUSTOMER/BILLING contact defined, we use it
+			$usebillingcontact = false;
+			$arrayidcontact = $object->getIdContact('external', 'BILLING');
+			if (count($arrayidcontact) > 0) {
+				$usebillingcontact = true;
+				$result = $object->fetch_contact($arrayidcontact[0]);
+			}
+			if ($twocontacts = !empty($conf->global->MMI_DOCUMENT_PDF_SEPARATE_CONTACTS) && $useshippingcontact)
+				$widthrecbox = 60;
+
 			// Show sender frame
 			if (empty($conf->global->MAIN_PDF_NO_SENDER_FRAME)) {
 				$pdf->SetTextColor(0, 0, 0);
@@ -1748,59 +1798,175 @@ class pdf_cyan extends ModelePDFPropales
 			$pdf->SetFont('', '', $default_font_size - 1);
 			$pdf->MultiCell($widthrecbox - 2, 4, $carac_emetteur, 0, $ltrdirection);
 
+			// Added by MMI Mathieu Moulin iProspective
+			// Adresses livraison & facturation
+			if ($twocontacts) {
+				// ---- RECIPIENT SHIPPING
+
+				// If CUSTOMER/SHIPPING contact defined, we use it
+				$usecontact = false;
+				$arrayidcontact = $object->getIdContact('external', 'SHIPPING');
+				if (count($arrayidcontact) > 0) {
+					$usecontact = true;
+					$result = $object->fetch_contact($arrayidcontact[0]);
+				}
 
-			// If CUSTOMER contact defined, we use it
-			$usecontact = false;
-			$arrayidcontact = $object->getIdContact('external', 'CUSTOMER');
-			if (count($arrayidcontact) > 0) {
-				$usecontact = true;
-				$result = $object->fetch_contact($arrayidcontact[0]);
-			}
+				// Recipient name
+				if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+					$thirdparty = $object->contact;
+				} else {
+					$thirdparty = $object->thirdparty;
+				}
 
-			// Recipient name
-			if ($usecontact && ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
-				$thirdparty = $object->contact;
-			} else {
-				$thirdparty = $object->thirdparty;
-			}
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
 
-			$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
+				$mode =  'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
 
-			$mode = 'target';
-			$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+				// Show recipient
+				$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
+				
+				if ($this->page_largeur < 210) {
+					$widthrecbox = 84; // To work with US executive format
+				}
+				$widthrecbox = 60;
+				$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+				$posy += $top_shift;
+				$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+				if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+					$posx = $this->marge_gauche;
+				}
 
-			// Show recipient
-			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 100;
-			if ($this->page_largeur < 210) {
-				$widthrecbox = 84; // To work with US executive format
-			}
-			$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
-			$posy += $top_shift;
-			$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
-			if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
-				$posx = $this->marge_gauche;
-			}
+				// Show recipient frame
+				$pdf->SetTextColor(0, 0, 0);
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->SetXY($posx + 2, $posy - 5);
+				$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("DeliveryAddress"), 0, $ltrdirection);
+				$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+
+				// Show recipient name
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+
+				$posy = $pdf->getY();
+
+				// Show recipient information
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->SetXY($posx + 2, $posy);
+				$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
+
+				// ---- RECIPIENT INVOICE
+
+				// If CUSTOMER contact defined, we use it
+				$usecontact = false;
+				$arrayidcontact = $object->getIdContact('external', 'BILLING');
+				if (count($arrayidcontact) > 0) {
+					$usecontact = true;
+					$result = $object->fetch_contact($arrayidcontact[0]);
+				}
+
+				// Recipient name
+				if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+					$thirdparty = $object->contact;
+				} else {
+					$thirdparty = $object->thirdparty;
+				}
+
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
+
+				$mode =  'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+
+				// Show recipient
+				$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
+				
+				if ($this->page_largeur < 210) {
+					$widthrecbox = 84; // To work with US executive format
+				}
+				$widthrecbox = 60;
+				$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+				$posy += $top_shift;
+				$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+				if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+					$posx = $this->marge_gauche;
+				}
+				$posx -= $widthrecbox +5;
 
-			// Show recipient frame
-			if (empty($conf->global->MAIN_PDF_NO_RECIPENT_FRAME)) {
+				// Show recipient frame
 				$pdf->SetTextColor(0, 0, 0);
 				$pdf->SetFont('', '', $default_font_size - 2);
 				$pdf->SetXY($posx + 2, $posy - 5);
-				$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
+				$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillAddress"), 0, $ltrdirection);
 				$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+
+				// Show recipient name
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+
+				$posy = $pdf->getY();
+
+				// Show recipient information
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->SetXY($posx + 2, $posy);
+				$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
 			}
+			// 1 seule adresse
+			else {
+				// If CUSTOMER contact defined, we use it
+				$usecontact = false;
+				$arrayidcontact = $object->getIdContact('external', 'CUSTOMER');
+				if (count($arrayidcontact) > 0) {
+					$usecontact = true;
+					$result = $object->fetch_contact($arrayidcontact[0]);
+				}
 
-			// Show recipient name
-			$pdf->SetXY($posx + 2, $posy + 3);
-			$pdf->SetFont('', 'B', $default_font_size);
-			$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+				// Recipient name
+				if ($usecontact && ($object->contact->socid == $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)))) {
+					$thirdparty = $object->contact;
+				} else {
+					$thirdparty = $object->thirdparty;
+				}
 
-			$posy = $pdf->getY();
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
 
-			// Show recipient information
-			$pdf->SetFont('', '', $default_font_size - 1);
-			$pdf->SetXY($posx + 2, $posy);
-			$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
+				$mode = 'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+
+				// Show recipient
+				$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 100;
+				if ($this->page_largeur < 210) {
+					$widthrecbox = 84; // To work with US executive format
+				}
+				$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+				$posy += $top_shift;
+				$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+				if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+					$posx = $this->marge_gauche;
+				}
+
+				// Show recipient frame
+				if (empty($conf->global->MAIN_PDF_NO_RECIPENT_FRAME)) {
+					$pdf->SetTextColor(0, 0, 0);
+					$pdf->SetFont('', '', $default_font_size - 2);
+					$pdf->SetXY($posx + 2, $posy - 5);
+					$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
+					$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+				}
+
+				// Show recipient name
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+
+				$posy = $pdf->getY();
+
+				// Show recipient information
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->SetXY($posx + 2, $posy);
+				$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
+			}
 		}
 
 		$pdf->SetTextColor(0, 0, 0);
@@ -1856,7 +2022,71 @@ class pdf_cyan extends ModelePDFPropales
 			$pdf->addEmptySignatureAppearance($posx, $tab_top + $tab_hl, $largcol, $tab_hl * 3);
 		}
 
-		return ($tab_hl * 7);
+		return $posy+($tab_hl * 7);
+	}
+
+	/**
+	 *  Added by MMI Mathieu Moulin iProspective
+	 */
+	protected function textComplement(&$object, $outputlangs)
+	{
+		global $conf;
+		$outputlangs->load('mmidocuments@mmidocuments');
+
+		//var_dump($object); die();
+		//var_dump($object->array_options['options_cgv_cpv']); die();
+		$complement = [];
+		if (!empty($object->array_options['options_cgv_cpv']))
+			$complement[] = '<h3>'.$outputlangs->transnoentities("DocumentMoreInfoCGP")."</h3>\r\n".$object->array_options['options_cgv_cpv'];
+		if (!empty($object->array_options['options_propal_decennale']))
+			$complement[] = '<h3>'.$outputlangs->transnoentities("DocumentMoreInfoDecennale")."</h3>\r\n".$conf->global->MMIPROJECT_DECENNALE_TEXT;
+
+		return !empty($complement) ?implode("\r\n", $complement) :'';
+	}
+	protected function heightComplement(&$pdf, $text, $default_font_size)
+	{
+		$pdf->SetFont('', '', $default_font_size - 2);
+		$useborder = 0;
+		$cellpadding = 0;
+		$reseth = false;
+		$autopadding = true;
+		$largcol = ($this->page_largeur - $this->marge_droite - $this->marge_gauche);
+		return $pdf->getStringHeight($largcol, strip_tags($text), $reseth, $autopadding, $cellpadding, $useborder);
+	}
+	protected function heightComplementArea(&$pdf, $text, $default_font_size)
+	{
+		$marg_top = 8;
+		$tab_titre = 4;
+		$tab_text = $this->heightComplement($pdf, $text, $default_font_size);
+		//var_dump($tab_text);
+		return $marg_top + $tab_titre + $tab_text;
+	}
+	protected function drawComplementArea(&$pdf, $text, $posy, $outputlangs)
+	{
+		global $conf;
+		
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+		$marg_top = 8;
+		$tab_top = $posy + $marg_top;
+
+		$posx = $this->marge_gauche;
+		$largcol = ($this->page_largeur - $this->marge_droite - $posx);
+
+		$pdf->SetFillColor(255, 255, 255);
+		$pdf->SetFont('', '', $default_font_size - 2);
+
+		// Titre
+		$pdf->SetXY($posx, $tab_top);
+		$tab_titre = 4;
+		$pdf->WriteHTMLCell($largcol, $tab_titre, $posx, $posy+$marg_top, '<b>'.$outputlangs->transnoentities("DocumentMoreInfo").'</b>', 0);
+		// Texte
+		$pdf->SetXY($posx, $tab_top + $tab_titre);
+		$tab_text = $this->heightComplement($pdf, $text, $default_font_size);
+		//var_dump($tab_text); die();
+		$pdf->WriteHTMLCell($largcol, $tab_text, $posx,$tab_top+$tab_titre, $text, 1, 'L');
+		//var_dump($tab_top + $tab_titre + $tab_text); die();
+
+		return $tab_top + $tab_titre + $tab_text;
 	}
 
 

+ 16 - 0
htdocs/core/tpl/objectline_create.tpl.php

@@ -263,6 +263,9 @@ if ($nolinesbefore) {
 				} else {
 					$form->select_produits(GETPOST('idprod'), 'idprod', $filtertype, $conf->product->limit_size, $buyer->price_level, $statustoshow, 2, '', 1, array(), $buyer->id, '1', 0, 'maxwidth500', 0, '', GETPOST('combinations', 'array'));
 				}
+				// Added by MMI Mathieu Moulin iProspective
+				if (!empty($conf->global->MAIN_SHOW_ADDED_PRODUCT_LABEL))
+					echo '<span id="added_labelprod"></span>';
 				if (!empty($conf->global->MAIN_AUTO_OPEN_SELECT2_ON_FOCUS_FOR_CUSTOMER_PRODUCTS)) {
 					?>
 				<script>
@@ -710,6 +713,19 @@ if (!empty($usemargins) && $user->rights->margins->creer) {
 	});
 		<?php
 	} ?>
+
+	<?php
+	// Added by MMI Mathieu Moulin iProspective
+	if (!empty($conf->global->MAIN_SHOW_ADDED_PRODUCT_LABEL)) { ?>
+	$("#idprod").change(function()
+	{
+		var label = $(this).attr('data-label');
+		$("#added_labelprod").html(label);
+	});
+		<?php
+	}
+	?>
+
 	/* When changing predefined product, we reload list of supplier prices required for margin combo */
 	$("#idprod, #idprodfournprice").change(function()
 	{

+ 23 - 0
htdocs/core/tpl/onlinepaymentlinks.tpl.php

@@ -30,6 +30,29 @@ print '<u>'.$langs->trans("FollowingUrlAreAvailableToMakePayments").':</u><br><b
 print img_picto('', 'globe').' <span class="opacitymedium">'.$langs->trans("ToOfferALinkForOnlinePaymentOnFreeAmount", $servicename).':</span><br>';
 print '<strong class="wordbreak">'.getOnlinePaymentUrl(1, 'free')."</strong><br><br>\n";
 
+// Added by MMI Mathieu Moulin iProspective
+if (!empty($conf->propal->enabled)) {
+	print '<div id="propal"></div>';
+	print img_picto('', 'globe').' <span class="opacitymedium">'.$langs->trans("ToOfferALinkForOnlinePaymentOnPropal", $servicename).':</span><br>';
+	print '<strong class="wordbreak">'.getOnlinePaymentUrl(1, 'propal')."</strong><br>\n";
+	if (!empty($conf->global->PAYMENT_SECURITY_TOKEN) && !empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) {
+		$langs->load("propals");
+		print '<form action="'.$_SERVER["PHP_SELF"].'#order" method="POST">';
+		print '<input type="hidden" name="token" value="'.newToken().'">';
+
+		print $langs->trans("EnterRefToBuildUrl", $langs->transnoentitiesnoconv("Propal")).': ';
+		print '<input type="text class="flat" id="generate_propal_ref" name="generate_propal_ref" value="'.GETPOST('generate_propal_ref', 'alpha').'" size="10">';
+		print '<input type="submit" class="none reposition button smallpaddingimp" value="'.$langs->trans("GetSecuredUrl").'">';
+		if (GETPOST('generate_propal_ref', 'alpha')) {
+			print '<br> -> <strong class="wordbreak">';
+			$url = getOnlinePaymentUrl(0, 'propal', GETPOST('generate_propal_ref', 'alpha'));
+			print $url;
+			print "</strong><br>\n";
+		}
+		print '</form>';
+	}
+	print '<br>';
+}
 if (!empty($conf->commande->enabled)) {
 	print '<div id="order"></div>';
 	print img_picto('', 'globe').' <span class="opacitymedium">'.$langs->trans("ToOfferALinkForOnlinePaymentOnOrder", $servicename).':</span><br>';

+ 1 - 0
htdocs/custom/abricot

@@ -0,0 +1 @@
+Subproject commit 612485dd5b4410214c14b7cbd3fe797f9c0750e4

+ 1 - 0
htdocs/custom/aproximite

@@ -0,0 +1 @@
+Subproject commit 2d4bd30b36a5dc9fafde63ff9b7389a83fb395fd

+ 1 - 0
htdocs/custom/bompdf

@@ -0,0 +1 @@
+Subproject commit 1d839202c69a3d75c8eda7d14d22e5b52c96e7ff

+ 1 - 0
htdocs/custom/concatpdf

@@ -0,0 +1 @@
+../custom_dolicloud/htdocs/concatpdf

+ 1 - 0
htdocs/custom/contacttracking

@@ -0,0 +1 @@
+Subproject commit f473d5b5e1b2688fdb80a763e962af186b5b005e

+ 1 - 0
htdocs/custom/dropdownmenu

@@ -0,0 +1 @@
+Subproject commit 0247a7c1d16ba02c59f0f7e8fdd836f5229e97f7

+ 1 - 0
htdocs/custom/ecotaxdeee

@@ -0,0 +1 @@
+../custom_dolicloud/htdocs/ecotaxdeee

+ 1 - 0
htdocs/custom/externalaccess

@@ -0,0 +1 @@
+Subproject commit a79146e33c480b247a2785bcbc3b1146c01f1a87

+ 1 - 0
htdocs/custom/inventory

@@ -0,0 +1 @@
+Subproject commit 1ee645c7b3d6d37090244707ff574675bb3f5ed9

+ 1 - 0
htdocs/custom/margincontrol

@@ -0,0 +1 @@
+Subproject commit 6be114e16aece9c3b80685e47dab86c2a989e99a

+ 1 - 0
htdocs/custom/mbietransactions

@@ -0,0 +1 @@
+Subproject commit c4a722860122fc85ee1b9f23a3111994ff292dde

+ 1 - 0
htdocs/custom/milestone

@@ -0,0 +1 @@
+Subproject commit 465fb3f14db4c035fcf65bf68c0b79c83b6448d7

+ 1 - 0
htdocs/custom/mmiatelier

@@ -0,0 +1 @@
+Subproject commit 0369c5b6f64162cbed8ee0a7dfa7b25eae4fb8a7

+ 1 - 0
htdocs/custom/mmiavisverifies

@@ -0,0 +1 @@
+Subproject commit 14d4faca5d5ef59f8404ed868c2ee2298b987fda

+ 1 - 0
htdocs/custom/mmicommon

@@ -0,0 +1 @@
+Subproject commit 42f7b6e8cf211267fa87a7f1b9e87f31cc0269eb

+ 1 - 0
htdocs/custom/mmicompta

@@ -0,0 +1 @@
+Subproject commit 66eb2458b315d7a004a23cb41b763e18ee85f078

+ 1 - 0
htdocs/custom/mmicrm

@@ -0,0 +1 @@
+Subproject commit fb33fd23f96d431b00c90f30ce3cede31b55d90e

+ 1 - 0
htdocs/custom/mmidocuments

@@ -0,0 +1 @@
+Subproject commit c3c8499d45fd7109d7bd84c146e2d76bf3eae3ea

+ 1 - 0
htdocs/custom/mmientrepots

@@ -0,0 +1 @@
+Subproject commit 17af96ba25c82a29dea26d35f526fb2d4b7351bf

+ 1 - 0
htdocs/custom/mmifilters

@@ -0,0 +1 @@
+Subproject commit 5de0ef85df5128cf901137141c518595e1a19ab6

+ 1 - 0
htdocs/custom/mmifournisseurprice

@@ -0,0 +1 @@
+Subproject commit 325976e00778e14988d6deca11c40043f2e9abe6

+ 1 - 0
htdocs/custom/mmipayments

@@ -0,0 +1 @@
+Subproject commit ec0eebd166ff626973ab9daea1165082b94de99d

+ 1 - 0
htdocs/custom/mmiprestasync

@@ -0,0 +1 @@
+Subproject commit 4430593e815a1c670b5ad2a71b63e2de96a47dfc

+ 1 - 0
htdocs/custom/mmiproduct

@@ -0,0 +1 @@
+Subproject commit 3792be112520730b143bdf79d87221bb96d72cef

+ 1 - 0
htdocs/custom/mmiproductdluo

@@ -0,0 +1 @@
+Subproject commit 085ad72380a48646a82350e9edd40e09b841dd67

+ 1 - 0
htdocs/custom/mmiproject

@@ -0,0 +1 @@
+Subproject commit 58194165223da00f555b30e8a41cf13fd6bec5f2

+ 1 - 0
htdocs/custom/mmistats

@@ -0,0 +1 @@
+Subproject commit b84817605d5e32f05b2e5fd7d91b3aa8881c7182

+ 1 - 0
htdocs/custom/mmivateec

@@ -0,0 +1 @@
+Subproject commit 6ad1bf6f54db417247894bf03ae19c49763bb145

+ 1 - 0
htdocs/custom/mmiworkflow

@@ -0,0 +1 @@
+Subproject commit cc5836cd8739350fdfa363722c85819a1d2a261d

+ 1 - 0
htdocs/custom/multismtp

@@ -0,0 +1 @@
+Subproject commit 8390f88ff44da27fa7a9941c920f71c7e03d737c

+ 1 - 0
htdocs/custom/ovh

@@ -0,0 +1 @@
+../custom_dolicloud/htdocs/ovh

+ 1 - 0
htdocs/custom/sfycustom

@@ -0,0 +1 @@
+Subproject commit 49d3990aadc1a01144036d6c04a636a2eef0e208

+ 1 - 0
htdocs/custom/stocktransfers

@@ -0,0 +1 @@
+Subproject commit 80ea48edf7d25e05db3ebb2b7694ff3c64eabe3c

+ 1 - 0
htdocs/custom/supplierorderfromorder

@@ -0,0 +1 @@
+Subproject commit bc6d921393e156bb63bb7301f2a8f5dd8c2c71f0

+ 1 - 0
htdocs/custom/zenfusionmaps

@@ -0,0 +1 @@
+Subproject commit 521c9a1b1f67ea61432ff5f74eead68ce5ddff11

+ 1 - 0
htdocs/custom/zipautofillfr

@@ -0,0 +1 @@
+../custom_dolicloud/htdocs/zipautofillfr

+ 1 - 0
htdocs/custom_dolicloud

@@ -0,0 +1 @@
+Subproject commit fb3b1b0de65c95a5cf5ce85b4a47303ed729f176

+ 11 - 1
htdocs/expedition/card.php

@@ -1920,7 +1920,16 @@ if ($action == 'create') {
 
 		// Other attributes
 		$cols = 2;
+		// Added by MMI Mathieu Moulin iProspective
+		// Extrafields show/hide
+		if ($conf->global->DOCUMENT_EXTRAFIELDS_SHOWHIDE) {
+			echo '<tr> <td colspan="2"><a href="javascript:;" onclick="$(\'#extrafields_form\').toggle();">'.$langs->trans('ToggleExtrafields').'</a></td> </tr>';
+			echo '<tbody id="extrafields_form" class="extrafields" style="display: none;">';
+		}
 		include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
+		if ($conf->global->DOCUMENT_EXTRAFIELDS_SHOWHIDE) {
+			echo '</tbody>';
+		}
 
 		print '</table>';
 
@@ -2515,7 +2524,8 @@ if ($action == 'create') {
 				if ($user->rights->facture->creer) {
 					// TODO show button only   if (! empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))
 					// If we do that, we must also make this option official.
-					print dolGetButtonAction('', $langs->trans('CreateBill'), 'default', DOL_URL_ROOT.'/compta/facture/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid, '');
+					if (empty($conf->global->BILL_ON_SHIPMENT_DISABLE))
+						print dolGetButtonAction('', $langs->trans('CreateBill'), 'default', DOL_URL_ROOT.'/compta/facture/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid, '');
 				}
 			}
 

+ 4 - 1
htdocs/expedition/class/expedition.class.php

@@ -1600,7 +1600,10 @@ class Expedition extends CommonObject
 		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product";
 		$sql .= " WHERE ed.fk_expedition = ".((int) $this->id);
 		$sql .= " AND ed.fk_origin_line = cd.rowid";
-		$sql .= " ORDER BY cd.rang, ed.fk_origin_line";		// We need after a break on fk_origin_line but when there is no break on fk_origin_line, cd.rang is same so we can add it as first order criteria.
+		if (!empty($conf->global->MMI_EXPE_ORDER_LINES_BY_REF))
+			$sql .= " ORDER BY p.ref, cd.rang, ed.fk_origin_line";
+		else
+			$sql .= " ORDER BY cd.rang, ed.fk_origin_line";		// We need after a break on fk_origin_line but when there is no break on fk_origin_line, cd.rang is same so we can add it as first order criteria.
 
 		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
 		$resql = $this->db->query($sql);

+ 6 - 1
htdocs/fourn/commande/dispatch.php

@@ -723,7 +723,12 @@ if ($id > 0 || !empty($ref)) {
 		$sql .= $hookmanager->resPrint;
 
 		//$sql .= " GROUP BY p.ref, p.label, p.tobatch, p.fk_default_warehouse, l.rowid, l.fk_product, l.subprice, l.remise_percent, l.ref"; // Calculation of amount dispatched is done per fk_product so we must group by fk_product
-		$sql .= " ORDER BY l.rang, p.ref, p.label";
+		// Added by MMI Mathieu Moulin iProspective
+		// Sort commandedet lines by id when receive
+		if (!empty($conf->global->ORDER_DISPATCH_BY_COMMANDEDET_ID))
+			$sql .= " ORDER BY l.rowid";
+		else
+			$sql .= " ORDER BY l.rang, p.ref, p.label";
 
 		$resql = $db->query($sql);
 		if ($resql) {

+ 18 - 5
htdocs/includes/odtphp/Segment.php

@@ -230,9 +230,9 @@ class Segment implements IteratorAggregate, Countable
 		}
 
 		$this->vars[$tag] = $this->odf->convertVarToOdf($value, $encode, $charset);
-
-		return $this;
-	}
+        
+        return $this;
+    }
 
 	/**
 	 * Assign a template variable as a picture
@@ -252,8 +252,21 @@ class Segment implements IteratorAggregate, Countable
 		}
 		// Set the width and height of the page
 		list ($width, $height) = $size;
-		$width *= Odf::PIXEL_TO_CM;
-		$height *= Odf::PIXEL_TO_CM;
+        // Added by MMI Mathieu Moulin iProspective
+        if (preg_match('/produit/', $value)) {
+            if (preg_match('/logo2$/', $key)) {
+                $width = 75*Odf::PIXEL_TO_CM;
+                $height = 75*Odf::PIXEL_TO_CM;
+            }
+            else {
+                $width = 150*Odf::PIXEL_TO_CM;
+                $height = 150*Odf::PIXEL_TO_CM;
+            }
+        }
+		else {
+			$width *= Odf::PIXEL_TO_CM;
+			$height *= Odf::PIXEL_TO_CM;
+		}
 		// Fix local-aware issues (eg: 12,10 -> 12.10)
 		$width = sprintf("%F", $width);
 		$height = sprintf("%F", $height);

+ 13 - 1
htdocs/includes/odtphp/odf.php

@@ -235,6 +235,9 @@ class Odf
 					case 'b':
 						$odtResult .= '<text:span text:style-name="boldText">' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . '</text:span>';
 						break;
+					case 'a':
+						$odtResult = '<text:a xlink:type="simple" xlink:href="'.(is_numeric(strpos($tag['innerText'], '@')) ?'mailto:'.$tag['innerText'] :$tag['innerText']).'" office:name="">'.$tag['innerText'].'</text:a>';
+						break;
 					case 'i':
 					case 'em':
 						$odtResult .= '<text:span text:style-name="italicText">' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . '</text:span>';
@@ -252,6 +255,7 @@ class Odf
 						$odtResult .= '<text:span text:style-name="supText">' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . '</text:span>';
 						break;
 					case 'span':
+					case 'p':
 						if (isset($tag['attributes']['style'])) {
 							$odtStyles = '';
 							foreach ($tag['attributes']['style'] as $styleName => $styleValue) {
@@ -280,13 +284,21 @@ class Odf
 											$odtStyles .= '<style:text-properties fo:color="' . $styleValue . '" />';
 										}
 										break;
+									case 'background-color':
+										if (preg_match('/#[0-9A-Fa-f]{3}(?:[0-9A-Fa-f]{3})?/', $styleValue)) {
+											$odtStyles .= '<style:text-properties fo:background-color="' . $styleValue . '" />';
+										}
+										break;
 								}
 							}
 							if (strlen($odtStyles) > 0) {
 								// Generate a unique id for the style (using microtime and random because some CPUs are really fast...)
 								$key = floatval(str_replace('.', '', microtime(true)))+rand(0, 10);
 								$customStyles[$key] = $odtStyles;
-								$odtResult .= '<text:span text:style-name="customStyle' . $key . '">' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . '</text:span>';
+								$odtResult .= '<text:'.($tag['name']).' text:style-name="customStyle' . $key . '">' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . '</text:'.($tag['name']).'>';
+							}
+							else {
+								$odtResult .= ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']);
 							}
 						}
 						break;

+ 1 - 0
htdocs/langs/en_US/workflow.lang

@@ -4,6 +4,7 @@ WorkflowDesc=This module provides some automatic actions. By default, the workfl
 ThereIsNoWorkflowToModify=There is no workflow modifications available with the activated modules.
 # Autocreate
 descWORKFLOW_PROPAL_AUTOCREATE_ORDER=Automatically create a sales order after a commercial proposal is signed (the new order will have same amount as the proposal)
+descWORKFLOW_PROPAL_AUTOCREATE_ORDER_IFNOTEXISTS=Automatically create a sales order after a commercial proposal is signed only if the is no order already linked to the proposal
 descWORKFLOW_PROPAL_AUTOCREATE_INVOICE=Automatically create a customer invoice after a commercial proposal is signed (the new invoice will have same amount as the proposal)
 descWORKFLOW_CONTRACT_AUTOCREATE_INVOICE=Automatically create a customer invoice after a contract is validated
 descWORKFLOW_ORDER_AUTOCREATE_INVOICE=Automatically create a customer invoice after a sales order is closed (the new invoice will have same amount as the order)

+ 2 - 0
htdocs/langs/fr_FR/main.lang

@@ -380,6 +380,7 @@ AmountInvoiced=Montant facturé
 AmountInvoicedHT=Montant facturé (HT)
 AmountInvoicedTTC=Montant facturé (TTC)
 AmountPayment=Montant paiement
+AmountAlreadyPaid=Montant déjà réglé
 AmountHTShort=Montant HT
 AmountTTCShort=Montant TTC
 AmountHT=Montant HT
@@ -1187,3 +1188,4 @@ UrlToCheck=URL à vérifier
 Automation=Automatisation
 CreatedByEmailCollector=Créé par Collecteur d'e-mails
 CreatedByPublicPortal=Créé à partir du portail public
+ToggleExtrafields=Montrer/Maqquer les champs supplémentaires

+ 4 - 4
htdocs/langs/fr_FR/productbatch.lang

@@ -10,12 +10,12 @@ Batch=Lot/Série
 atleast1batchfield=Date limite utilisation optimale, de consommation ou numéro de lot/série
 batch_number=Numéro de Lot/Série
 BatchNumberShort=Lot/Série
-EatByDate=DMD/DLUO
-SellByDate=DLC
+EatByDate=DLC
+SellByDate=DMD/DLUO
 DetailBatchNumber=Détails Lot/Série
 printBatch=Lot/Série: %s
-printEatby=DMD/DLUO: %s
-printSellby=DLC: %s
+printEatby=DLC: %s
+printSellby=DMD/DLUO: %s
 printQty=Qté: %d
 AddDispatchBatchLine=Ajouter une ligne pour la répartition par durée de conservation
 WhenProductBatchModuleOnOptionAreForced=Quand le module Lot/Série est activé, le mode de décrémentation automatique des stocks est forcé sur 'Décrémenter les stocks réel sur validation d'expédition' et le mode d'incrémentation automatique de stocks est forcé sur 'Incrémenter les stocks réels sur ventilation manuels dans les entrepôts' et ne peut pas être édité. Les autres options peuvent être définies comme vous le voulez.

+ 2 - 0
htdocs/langs/fr_FR/propal.lang

@@ -111,3 +111,5 @@ PropalAlreadyRefused=Proposition déjà refusée
 PropalSigned=Proposition acceptée
 PropalRefused=Proposition refusée
 ConfirmRefusePropal=Êtes-vous sûr de vouloir refuser cette proposition ?
+PaymentPropalRef=Paiement Proposition %s
+PropalPaid=Proposition commerciale déjà réglée

+ 1 - 0
htdocs/langs/fr_FR/workflow.lang

@@ -4,6 +4,7 @@ WorkflowDesc=Ce module est conçu pour modifier le comportement des actions auto
 ThereIsNoWorkflowToModify=Il n'y a pas de modifications de workflow disponibles avec les modules activés.
 # Autocreate
 descWORKFLOW_PROPAL_AUTOCREATE_ORDER=Créer automatiquement une commande client à la signature d'un proposition commerciale (la commande sera du même montant que la proposition commerciale)
+descWORKFLOW_PROPAL_AUTOCREATE_ORDER_IFNOTEXISTS=Créer automatiquement une commande client à la signature d'un proposition commerciale seulement si il n'y a pas déjà une commande liée à la proposition
 descWORKFLOW_PROPAL_AUTOCREATE_INVOICE=Créer automatiquement une facture client à la signature d'une proposition commerciale (la facture sera du même montant que la proposition commerciale source)
 descWORKFLOW_CONTRACT_AUTOCREATE_INVOICE=Créer une facture client automatiquement à la validation d'un contrat
 descWORKFLOW_ORDER_AUTOCREATE_INVOICE=Créer automatiquement une facture client à la cloture d'un commande (la facture sera du même montant que la commande)

+ 17 - 2
htdocs/product/class/product.class.php

@@ -4129,6 +4129,10 @@ class Product extends CommonObject
 		if ($result < 0) {
 			return $result;
 		}
+		// Added by MIM Mathieu Moulin iProspective
+		// Call trigger
+		global $user;
+		$result = $this->call_trigger('PRODUCT_SOUSPRODUIT', $user);
 
 		// Check not already father of id_pere (to avoid father -> child -> father links)
 		$sql = "SELECT fk_product_pere from ".$this->db->prefix()."product_association";
@@ -4195,9 +4199,14 @@ class Product extends CommonObject
 		if (!$this->db->query($sql)) {
 			dol_print_error($this->db);
 			return -1;
-		} else {
-			return 1;
 		}
+
+		// Added by MIM Mathieu Moulin iProspective
+		// Call trigger
+		global $user;
+		$result = $this->call_trigger('PRODUCT_SOUSPRODUIT', $user);
+		
+		return 1;
 	}
 
 	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
@@ -4246,6 +4255,12 @@ class Product extends CommonObject
 				}
 			}
 		}
+
+		// Added by MIM Mathieu Moulin iProspective
+		// Call trigger
+		global $user;
+		$result = $this->call_trigger('PRODUCT_SOUSPRODUIT', $user);
+
 		return 1;
 	}
 

+ 14 - 4
htdocs/product/stock/class/mouvementstock.class.php

@@ -179,6 +179,8 @@ class MouvementStock extends CommonObject
 
 		// Call hook at beginning
 		global $action, $hookmanager;
+		if (empty($hookmanager))
+			$hookmanager = new HookManager($this->db);
 		$hookmanager->initHooks(array('mouvementstock'));
 
 		if (is_object($hookmanager)) {
@@ -276,7 +278,7 @@ class MouvementStock extends CommonObject
 			if (empty($batch)) {
 				$langs->load("errors");
 				$this->errors[] = $langs->transnoentitiesnoconv("ErrorTryToMakeMoveOnProductRequiringBatchData", $product->ref);
-				dol_syslog("Try to make a movement of a product with status_batch on without any batch data");
+				dol_syslog("Try to make a movement of a product with status_batch on without any batch data", LOG_ERR);
 
 				$this->db->rollback();
 				return -2;
@@ -575,9 +577,11 @@ class MouvementStock extends CommonObject
 				}
 			}
 
-			if (empty($donotcleanemptylines)) {
+			if (empty($donotcleanemptylines) && empty($conf->global->STOCK_KEEP_EMPTY_IN_DB)) {
 				// If stock is now 0, we can remove entry into llx_product_stock, but only if there is no child lines into llx_product_batch (detail of batch, because we can imagine
 				// having a lot1/qty=X and lot2/qty=-X, so 0 but we must not loose repartition of different lot.
+				// Added by MMI Mathieu Moulin iProspective
+				// DO NOT DELETE STOCK !!
 				$sql = "DELETE FROM ".$this->db->prefix()."product_stock WHERE reel = 0 AND rowid NOT IN (SELECT fk_product_stock FROM ".$this->db->prefix()."product_batch as pb)";
 				$resql = $this->db->query($sql);
 				// We do not test error, it can fails if there is child in batch details
@@ -856,7 +860,8 @@ class MouvementStock extends CommonObject
 	 */
 	private function createBatch($dluo, $qty)
 	{
-		global $user, $langs;
+		// Added by MMI Mathieu Moulin iProspective
+		global $user, $langs, $conf;
 
 		$langs->load('productbatch');
 
@@ -894,7 +899,12 @@ class MouvementStock extends CommonObject
 				//print "Avant ".$pdluo->qty." Apres ".($pdluo->qty + $qty)."<br>";
 				$pdluo->qty += $qty;
 				if ($pdluo->qty == 0) {
-					$result = $pdluo->delete($user, 1);
+					// Added by MMI Mathieu Moulin iProspective
+					// DO NOT DELETE !
+					if (!empty($conf->global->STOCK_KEEP_EMPTY_IN_DB))
+						$result = $pdluo->update($user, 1);
+					else
+						$result = $pdluo->delete($user, 1);
 				} else {
 					$result = $pdluo->update($user, 1);
 				}

+ 7 - 3
htdocs/product/stock/product.php

@@ -974,7 +974,11 @@ if (!$variants) {
 	$sql .= " FROM ".MAIN_DB_PREFIX."entrepot as e,";
 	$sql .= " ".MAIN_DB_PREFIX."product_stock as ps";
 	$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = ps.fk_product";
-	$sql .= " WHERE ps.reel != 0";
+	// Added by MMI Mathieu Moulin iProspective
+	if (!empty($conf->global->MMI_STOCK_SHOW_EVEN_IF_EMPTY))
+		$sql .= " WHERE 1";
+	else
+		$sql .= " WHERE ps.reel != 0";
 	$sql .= " AND ps.fk_entrepot = e.rowid";
 	$sql .= " AND e.entity IN (".getEntity('stock').")";
 	$sql .= " AND ps.fk_product = ".((int) $object->id);
@@ -1134,7 +1138,7 @@ if (!$variants) {
 						print '<td colspan="4"></td>';
 						print '<td class="center">';
 						if ($entrepotstatic->status != $entrepotstatic::STATUS_CLOSED) {
-							print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&amp;id_entrepot='.$entrepotstatic->id.'&amp;action=transfert&amp;pdluoid='.$pdluo->id.'">';
+							print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&amp;id_entrepot='.$entrepotstatic->id.'&amp;action=transfert&amp;pdluoid='.$pdluo->id.'&amp;batch_number='.$pdluo->batch.'">';
 							print img_picto($langs->trans("TransferStock"), 'add', 'class="hideonsmartphone paddingright" style="color: #a69944"');
 							print $langs->trans("TransferStock");
 							print '</a>';
@@ -1146,7 +1150,7 @@ if (!$variants) {
 						print '</td>';
 						print '<td class="center">';
 						if ($entrepotstatic->status != $entrepotstatic::STATUS_CLOSED) {
-							print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&amp;id_entrepot='.$entrepotstatic->id.'&amp;action=correction&amp;pdluoid='.$pdluo->id.'">';
+							print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&amp;id_entrepot='.$entrepotstatic->id.'&amp;action=correction&amp;pdluoid='.$pdluo->id.'&amp;batch_number='.$pdluo->batch.'">';
 							print img_picto($langs->trans("CorrectStock"), 'add', 'class="hideonsmartphone paddingright" style="color: #a69944"');
 							print $langs->trans("CorrectStock");
 							print '</a>';

+ 19 - 0
htdocs/product/stock/replenish.php

@@ -525,6 +525,18 @@ if ($usevirtualstock) {
 	}
 }
 
+// Added by MMI Mathieu Moulin iProspective
+// Add Having from hooks
+$parameters = array(
+	'includeproductswithoutdesiredqty' => $includeproductswithoutdesiredqty,
+	//'sqlCommandesCli' => $sqlCommandesCli,
+	//'sqlExpeditionsCli' => $sqlExpeditionsCli,
+	//'sqlCommandesFourn' => $sqlCommandesFourn,
+	//'sqlReceptionFourn' => $sqlReceptionFourn,
+);
+$reshook = $hookmanager->executeHooks('printFieldListHaving', $parameters); // Note that $action and $object may have been modified by hook
+$sql .= $hookmanager->resPrint;
+
 $includeproductswithoutdesiredqtychecked = '';
 if ($includeproductswithoutdesiredqty == 'on') {
 	$includeproductswithoutdesiredqtychecked = 'checked';
@@ -672,6 +684,13 @@ if ($limit > 0 && $limit != $conf->liste_limit) {
 if (!empty($includeproductswithoutdesiredqty)) $filters .= '&includeproductswithoutdesiredqty='.urlencode($includeproductswithoutdesiredqty);
 if (!empty($salert)) $filters .= '&salert='.urlencode($salert);
 
+// Added by MMI Mathieu Moulin iProspective
+$parameters = array();
+$reshook = $hookmanager->executeHooks('printFieldListFilters', $parameters);
+if (empty($reshook)) {
+	$filters .= $hookmanager->resPrint;
+}
+
 $param = (isset($type) ? '&type='.urlencode($type) : '');
 $param .= '&fourn_id='.urlencode($fourn_id).'&search_label='.urlencode($search_label).'&includeproductswithoutdesiredqty='.urlencode($includeproductswithoutdesiredqty).'&salert='.urlencode($salert).'&draftorder='.urlencode($draftorder);
 $param .= '&search_ref='.urlencode($search_ref);

+ 97 - 13
htdocs/projet/tasks/time.php

@@ -74,6 +74,16 @@ $search_task_label = GETPOST('search_task_label', 'alpha');
 $search_user = GETPOST('search_user', 'int');
 $search_valuebilled = GETPOST('search_valuebilled', 'int');
 
+// Added by MMI Mathieu Moulin iProspective
+$addtimespent_multiple_userid = $conf->global->PROJECT_ADDTIMESPENT_MULTIPLE_USERID;
+
+// Security check
+$socid = 0;
+//if ($user->socid > 0) $socid = $user->socid;	  // For external user, no check is done on company because readability is managed by public status of project and assignement.
+if (!$user->rights->projet->lire) {
+	accessforbidden();
+}
+
 $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit;
 $sortfield = GETPOST('sortfield', 'aZ09comma');
 $sortorder = GETPOST('sortorder', 'aZ09comma');
@@ -180,10 +190,20 @@ if ($action == 'addtimespent' && $user->rights->projet->time) {
 		setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Duration")), null, 'errors');
 		$error++;
 	}
-	if (!GETPOST("userid", 'int')) {
-		$langs->load("errors");
-		setEventMessages($langs->trans('ErrorUserNotAssignedToTask'), null, 'errors');
-		$error++;
+	// Added by MMI Mathieu Moulin iProspective
+	if ($addtimespent_multiple_userid) {
+		if (empty(GETPOST("userid", 'array:int'))) {
+			$langs->load("errors");
+			setEventMessages($langs->trans('ErrorUserNotAssignedToTask'), null, 'errors');
+			$error++;
+		}
+	}
+	else {
+		if (!GETPOST("userid", 'int')) {
+			$langs->load("errors");
+			setEventMessages($langs->trans('ErrorUserNotAssignedToTask'), null, 'errors');
+			$error++;
+		}
 	}
 
 	if (!$error) {
@@ -219,13 +239,28 @@ if ($action == 'addtimespent' && $user->rights->projet->time) {
 				} else {
 					$object->timespent_date = dol_mktime(12, 0, 0, GETPOST("timemonth", 'int'), GETPOST("timeday", 'int'), GETPOST("timeyear", 'int'));
 				}
-				$object->timespent_fk_user = GETPOST("userid", 'int');
-				$result = $object->addTimeSpent($user);
-				if ($result >= 0) {
-					setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
-				} else {
-					setEventMessages($langs->trans($object->error), null, 'errors');
-					$error++;
+				// Added by MMI Mathieu Moulin iProspective
+				if ($addtimespent_multiple_userid) {
+					foreach(GETPOST("userid", 'array:int') as $userid) {
+						$object->timespent_fk_user = $userid;
+						$result = $object->addTimeSpent($user);
+						if ($result >= 0) {
+							setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
+						} else {
+							setEventMessages($langs->trans($object->error), null, 'errors');
+							$error++;
+						}
+					}
+				}
+				else {
+					$object->timespent_fk_user = GETPOST("userid", 'int');
+					$result = $object->addTimeSpent($user);
+					if ($result >= 0) {
+						setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
+					} else {
+						setEventMessages($langs->trans($object->error), null, 'errors');
+						$error++;
+					}
 				}
 			}
 		}
@@ -1470,9 +1505,54 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser
 			print '<tr class="oddeven nohover">';
 
 			// Date
-			print '<td class="maxwidthonsmartphone">';
+			print '<td class="maxwidthonsmartphone" align="right">';
 			$newdate = '';
 			print $form->selectDate($newdate, 'time', ($conf->browser->layout == 'phone' ? 2 : 1), 1, 2, "timespent_date", 1, 0);
+			// Added by MMI Mathieu Moulin iProspective
+			if (true) {
+				print '<br />'.$form->selectDate($newdate, 'time2', ($conf->browser->layout == 'phone' ? 2 : 1), 1, 2, "timespent_date", 1, 0);
+				echo '<script type="text/javascript">
+					$(document).ready(function(){
+						$("#time2").parent().find("img.ui-datepicker-trigger").remove();
+						$("#time2").remove();
+						$("#timehour, #timemin, #time2hour, #time2min").change(function(){
+							var timehour = $("#timehour").val();
+							var timemin = $("#timemin").val();
+							var timehour2 = $("#time2hour").val();
+							var timemin2 = $("#time2min").val();
+							
+							//alert(timemin2);
+							//alert(timemin2-timemin);
+							
+							if (timehour==timehour2) {
+								if (timemin<timemin2) {
+									$("input[name=timespent_durationhour]").val(0);
+									$("input[name=timespent_durationmin]").val(timemin2-timemin);
+								}
+								else {
+									$("input[name=timespent_durationhour]").val("");
+									$("input[name=timespent_durationmin]").val("");
+								}
+							}
+							else if (timehour<timehour2) {
+								if (timemin<=timemin2) {
+									$("input[name=timespent_durationhour]").val(timehour2-timehour);
+									$("input[name=timespent_durationmin]").val(timemin2-timemin);
+								}
+								else {
+									$("input[name=timespent_durationhour]").val(timehour2-timehour-1);
+									$("input[name=timespent_durationmin]").val(60-(timemin-timemin2));
+								}
+							}
+							else {
+								$("input[name=timespent_durationhour]").val("");
+								$("input[name=timespent_durationmin]").val("");
+							}
+							
+						});
+					});
+				</script>';
+			}
 			print '</td>';
 
 			if (!empty($allprojectforuser)) {
@@ -1503,7 +1583,11 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser
 				if ($projectstatic->public) {
 					$contactsofproject = array();
 				}
-				print $form->select_dolusers((GETPOST('userid', 'int') ? GETPOST('userid', 'int') : $userid), 'userid', 0, '', 0, '', $contactsofproject, 0, 0, 0, '', 0, $langs->trans("ResourceNotAssignedToProject"), 'maxwidth250');
+				// Added by MMI Mathieu Moulin iProspective
+				if ($addtimespent_multiple_userid)
+					print $form->select_dolusers((GETPOST('userid', 'array:int') ? GETPOST('userid', 'array:int') : [$userid]), 'userid', 0, '', 0, '', $contactsofproject, 0, 0, 0, '', 0, $langs->trans("ResourceNotAssignedToProject"), 'maxwidth250', 0, 0, 1);
+				else
+					print $form->select_dolusers((GETPOST('userid', 'int') ? GETPOST('userid', 'int') : $userid), 'userid', 0, '', 0, '', $contactsofproject, 0, 0, 0, '', 0, $langs->trans("ResourceNotAssignedToProject"), 'maxwidth250');
 			} else {
 				if ($nboftasks) {
 					print img_error($langs->trans('FirstAddRessourceToAllocateTime')).' '.$langs->trans('FirstAddRessourceToAllocateTime');

+ 196 - 26
htdocs/public/payment/newpayment.php

@@ -1009,6 +1009,151 @@ if (!$source) {
 }
 
 
+// Added by MMI Mathieu Moulin iProspective
+// Payment on customer propal
+if ($source == 'propal') {
+	$found = true;
+	$langs->load("propal");
+
+	require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
+
+	$propal = new Propal($db);
+	$result = $propal->fetch('', $ref);
+	if ($result <= 0) {
+		$mesg = $propal->error;
+		$error++;
+	} else {
+		$result = $propal->fetch_thirdparty($propal->socid);
+	}
+	$object = $propal;
+
+	if ($action != 'dopayment') { // Do not change amount if we just click on first dopayment
+		$dejaregle = $propal->getSommePaiement();
+		$amount = max(0, $propal->total_ttc - $dejaregle);
+		$paye = $propal->paye();
+		if (GETPOST("amount", 'alpha')) {
+			$amount = GETPOST("amount", 'alpha');
+		}
+		$amount = price2num($amount);
+	}
+
+	if (GETPOST('fulltag', 'alpha')) {
+		$fulltag = GETPOST('fulltag', 'alpha');
+	} else {
+		$fulltag = 'PRO='.$propal->id.'.CUS='.$propal->thirdparty->id;
+		if (!empty($TAG)) {
+			$tag = $TAG; $fulltag .= '.TAG='.$TAG;
+		}
+	}
+	$fulltag = dol_string_unaccent($fulltag);
+
+	// Creditor
+	print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("Creditor");
+	print '</td><td class="CTableRow2"><b>'.$creditor.'</b>';
+	print '<input type="hidden" name="creditor" value="'.$creditor.'">';
+	print '</td></tr>'."\n";
+
+	// Debitor
+	print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("ThirdParty");
+	print '</td><td class="CTableRow2"><b>'.$propal->thirdparty->name.'</b>';
+	print '</td></tr>'."\n";
+
+	// Object
+	$text = '<b>'.$langs->trans("PaymentPropalRef", $propal->ref).'</b>';
+	if (GETPOST('desc', 'alpha')) {
+		$text = '<b>'.$langs->trans(GETPOST('desc', 'alpha')).'</b>';
+	}
+	print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("Designation");
+	print '</td><td class="CTableRow2">'.$text;
+	print '<input type="hidden" name="s" value="'.dol_escape_htmltag($source).'">';
+	print '<input type="hidden" name="ref" value="'.dol_escape_htmltag($propal->ref).'">';
+	print '<input type="hidden" name="dol_id" value="'.dol_escape_htmltag($propal->id).'">';
+	$directdownloadlink = $propal->getLastMainDocLink('propal');
+	if ($directdownloadlink) {
+		print '<br><a href="'.$directdownloadlink.'" rel="nofollow noopener">';
+		print img_mime($propal->last_main_doc, '');
+		print $langs->trans("DownloadDocument").'</a>';
+	}
+	print '</td></tr>'."\n";
+
+	// MMI
+	if ($dejaregle) {
+		// Total
+		print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("AmountTotal");
+		print '</td><td class="CTableRow2">';
+		print '<b>'.price2num($propal->total_ttc).'</b>';
+		print ' <b>'.$langs->trans("Currency".$currency).'</b>';
+		print '</td></tr>'."\n";
+		// Déjà réglé
+		print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("AmountAlreadyPaid");
+		print '</td><td class="CTableRow2">';
+		print '<b>'.price2num($dejaregle).'</b>';
+		print ' <b>'.$langs->trans("Currency".$currency).'</b>';
+		print '</td></tr>'."\n";
+	}
+	
+	if (! $paye) {
+		// Amount
+		print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("AmountPayment");
+		if (empty($amount)) {
+			print ' ('.$langs->trans("ToComplete").')';
+		}
+		print '</td><td class="CTableRow2">';
+		if (empty($amount) || !is_numeric($amount)) {
+			print '<input type="hidden" name="amount" value="'.price2num(GETPOST("amount", 'alpha'), 'MT').'">';
+			print '<input class="flat maxwidth75" type="text" name="newamount" value="'.price2num(GETPOST("newamount", "alpha"), 'MT').'">';
+		} else {
+			print '<b>'.price($amount).'</b>';
+			print '<input type="hidden" name="amount" value="'.$amount.'">';
+			print '<input type="hidden" name="newamount" value="'.$amount.'">';
+		}
+		// Currency
+		print ' <b>'.$langs->trans("Currency".$currency).'</b>';
+		print '<input type="hidden" name="currency" value="'.$currency.'">';
+		print '</td></tr>'."\n";
+
+		// Tag
+		print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("PaymentCode");
+		print '</td><td class="CTableRow2"><b style="word-break: break-all;">'.$fulltag.'</b>';
+		print '<input type="hidden" name="tag" value="'.dol_escape_htmltag($tag).'">';
+		print '<input type="hidden" name="fulltag" value="'.dol_escape_htmltag($fulltag).'">';
+		print '</td></tr>'."\n";
+	}
+
+	// Shipping address
+	$shipToName = $propal->thirdparty->name;
+	$shipToStreet = $propal->thirdparty->address;
+	$shipToCity = $propal->thirdparty->town;
+	$shipToState = $propal->thirdparty->state_code;
+	$shipToCountryCode = $propal->thirdparty->country_code;
+	$shipToZip = $propal->thirdparty->zip;
+	$shipToStreet2 = '';
+	$phoneNum = $propal->thirdparty->phone;
+	if ($shipToName && $shipToStreet && $shipToCity && $shipToCountryCode && $shipToZip) {
+		print '<input type="hidden" name="shipToName" value="'.dol_escape_htmltag($shipToName).'">'."\n";
+		print '<input type="hidden" name="shipToStreet" value="'.dol_escape_htmltag($shipToStreet).'">'."\n";
+		print '<input type="hidden" name="shipToCity" value="'.dol_escape_htmltag($shipToCity).'">'."\n";
+		print '<input type="hidden" name="shipToState" value="'.dol_escape_htmltag($shipToState).'">'."\n";
+		print '<input type="hidden" name="shipToCountryCode" value="'.dol_escape_htmltag($shipToCountryCode).'">'."\n";
+		print '<input type="hidden" name="shipToZip" value="'.dol_escape_htmltag($shipToZip).'">'."\n";
+		print '<input type="hidden" name="shipToStreet2" value="'.dol_escape_htmltag($shipToStreet2).'">'."\n";
+		print '<input type="hidden" name="phoneNum" value="'.dol_escape_htmltag($phoneNum).'">'."\n";
+	} else {
+		print '<!-- Shipping address not complete, so we don t use it -->'."\n";
+	}
+	if (is_object($propal->thirdparty)) {
+		print '<input type="hidden" name="thirdparty_id" value="'.$propal->thirdparty->id.'">'."\n";
+	}
+	print '<input type="hidden" name="email" value="'.$propal->thirdparty->email.'">'."\n";
+	print '<input type="hidden" name="vatnumber" value="'.dol_escape_htmltag($propal->thirdparty->tva_intra).'">'."\n";
+	$labeldesc = $langs->trans("Propal").' '.$propal->ref;
+	if (GETPOST('desc', 'alpha')) {
+		$labeldesc = GETPOST('desc', 'alpha');
+	}
+	print '<input type="hidden" name="desc" value="'.dol_escape_htmltag($labeldesc).'">'."\n";
+}
+
+
 // Payment on customer order
 if ($source == 'order') {
 	$found = true;
@@ -1027,7 +1172,10 @@ if ($source == 'order') {
 	$object = $order;
 
 	if ($action != 'dopayment') { // Do not change amount if we just click on first dopayment
-		$amount = $order->total_ttc;
+		// Added by MMI Mathieu Moulin iProspective
+		$dejaregle = $order->getSommePaiement();
+		$amount = max(0, $order->total_ttc - $dejaregle);
+		$paye = $order->paye();
 		if (GETPOST("amount", 'alpha')) {
 			$amount = GETPOST("amount", 'alpha');
 		}
@@ -1077,31 +1225,49 @@ if ($source == 'order') {
 	}
 	print '</td></tr>'."\n";
 
-	// Amount
-	print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("Amount");
-	if (empty($amount)) {
-		print ' ('.$langs->trans("ToComplete").')';
-	}
-	print '</td><td class="CTableRow2">';
-	if (empty($amount) || !is_numeric($amount)) {
-		print '<input type="hidden" name="amount" value="'.price2num(GETPOST("amount", 'alpha'), 'MT').'">';
-		print '<input class="flat maxwidth75" type="text" name="newamount" value="'.price2num(GETPOST("newamount", "alpha"), 'MT').'">';
-		// Currency
+	// Added by MMI Mathieu Moulin iProspective
+	if ($dejaregle) {
+		// Total
+		print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("AmountTotal");
+		print '</td><td class="CTableRow2">';
+		print '<b>'.price2num($order->total_ttc).'</b>';
 		print ' <b>'.$langs->trans("Currency".$currency).'</b>';
-	} else {
-		print '<b class="amount">'.price($amount, 1, $langs, 1, -1, -1, $currency).'</b>';	// Price with currency
-		print '<input type="hidden" name="amount" value="'.$amount.'">';
-		print '<input type="hidden" name="newamount" value="'.$amount.'">';
+		print '</td></tr>'."\n";
+		// Déjà réglé
+		print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("AmountAlreadyPaid");
+		print '</td><td class="CTableRow2">';
+		print '<b>'.price2num($dejaregle).'</b>';
+		print ' <b>'.$langs->trans("Currency".$currency).'</b>';
+		print '</td></tr>'."\n";
 	}
-	print '<input type="hidden" name="currency" value="'.$currency.'">';
-	print '</td></tr>'."\n";
+	
+	if (! $paye) {
+		// Amount
+		print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("AmountPayment");
+		if (empty($amount)) {
+			print ' ('.$langs->trans("ToComplete").')';
+		}
+		print '</td><td class="CTableRow2">';
+		if (empty($amount) || !is_numeric($amount)) {
+			print '<input type="hidden" name="amount" value="'.price2num(GETPOST("amount", 'alpha'), 'MT').'">';
+			print '<input class="flat maxwidth75" type="text" name="newamount" value="'.price2num(GETPOST("newamount", "alpha"), 'MT').'">';
+			// Currency
+			print ' <b>'.$langs->trans("Currency".$currency).'</b>';
+		} else {
+			print '<b class="amount">'.price($amount, 1, $langs, 1, -1, -1, $currency).'</b>';	// Price with currency
+			print '<input type="hidden" name="amount" value="'.$amount.'">';
+			print '<input type="hidden" name="newamount" value="'.$amount.'">';
+		}
+		print '<input type="hidden" name="currency" value="'.$currency.'">';
+		print '</td></tr>'."\n";
 
-	// Tag
-	print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("PaymentCode");
-	print '</td><td class="CTableRow2"><b style="word-break: break-all;">'.$fulltag.'</b>';
-	print '<input type="hidden" name="tag" value="'.dol_escape_htmltag($tag).'">';
-	print '<input type="hidden" name="fulltag" value="'.dol_escape_htmltag($fulltag).'">';
-	print '</td></tr>'."\n";
+		// Tag
+		print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("PaymentCode");
+		print '</td><td class="CTableRow2"><b style="word-break: break-all;">'.$fulltag.'</b>';
+		print '<input type="hidden" name="tag" value="'.dol_escape_htmltag($tag).'">';
+		print '<input type="hidden" name="fulltag" value="'.dol_escape_htmltag($fulltag).'">';
+		print '</td></tr>'."\n";
+	}
 
 	// Shipping address
 	$shipToName = $order->thirdparty->name;
@@ -1601,7 +1767,7 @@ if ($source == 'member' || $source == 'membersubscription') {
 				print '</td><td class="CTableRow2">';
 				print $form->selectarray("typeid", $adht->liste_array(1), $member->typeid, 0, 0, 0, 'onchange="window.location.replace(\''.$urlwithroot.'/public/payment/newpayment.php?source='.urlencode($source).'&ref='.urlencode($ref).'&amount='.urlencode($amount).'&typeid=\' + this.value + \'&securekey='.urlencode($SECUREKEY).'\');"', 0, 0, 0, '', '', 1);
 				print "</td></tr>\n";
-			} elseif ($action == dopayment) {
+			} elseif ($action == 'dopayment') {
 				print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("NewMemberType");
 				print '</td><td class="CTableRow2">'.dol_escape_htmltag($member->type);
 				print '<input type="hidden" name="membertypeid" value="'.$member->typeid.'">';
@@ -2065,7 +2231,10 @@ if ($action != 'dopayment') {
 			print $hookmanager->resPrint;
 		}
 
-		if ($source == 'order' && $object->billed) {
+		// Added by MMI Mathieu Moulin iProspective
+		if ($source == 'propal' && $object->paye()) {
+			print '<br><br><span class="amountpaymentcomplete size15x">'.$langs->trans("PropalPaid").'</span>';
+		} elseif ($source == 'order' && $object->billed) {
 			print '<br><br><span class="amountpaymentcomplete size15x">'.$langs->trans("OrderBilled").'</span>';
 		} elseif ($source == 'invoice' && $object->paye) {
 			print '<br><br><span class="amountpaymentcomplete size15x">'.$langs->trans("InvoicePaid").'</span>';
@@ -2083,7 +2252,8 @@ if ($action != 'dopayment') {
 
 			// This hook is used to add Button to newpayment.php for external payment modules (ie Payzen, ...)
 			$parameters = [
-				'paymentmethod' => $paymentmethod
+				'paymentmethod' => $paymentmethod,
+				'amount' => price2num(GETPOST("amount", 'alpha')),
 			];
 			$reshook = $hookmanager->executeHooks('doAddButton', $parameters, $object, $action);
 			if ($reshook < 0) {

+ 9 - 1
htdocs/reception/card.php

@@ -1579,8 +1579,16 @@ if ($action == 'create') {
 
 		// Other attributes
 		$cols = 2;
-
+		// Addes by MMI Mathieu Moulin iProspective
+		// Hack extrafields show/hide
+		if ($conf->global->DOCUMENT_EXTRAFIELDS_SHOWHIDE) {
+			echo '<tr> <td colspan="2"><a href="javascript:;" onclick="$(\'#extrafields_form\').toggle();">'.$langs->trans('ToggleExtrafields').'</a></td> </tr>';
+			echo '<tbody id="extrafields_form" class="extrafields" style="display: none;">';
+		}
 		include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
+		if ($conf->global->DOCUMENT_EXTRAFIELDS_SHOWHIDE) {
+			echo '</tbody>';
+		}
 
 		print '</table>';
 

+ 24 - 3
htdocs/societe/checkvat/checkVatPopup.php

@@ -27,14 +27,17 @@ require "../../main.inc.php";
 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
 require_once NUSOAP_PATH.'/nusoap.php';
 
+// Added by MMI Mathieu Moulin iProspective
+$hookmanager->initHooks(array('checkvatpopup'));
+
 $langs->load("companies");
 
 //http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
 $WS_DOL_URL = 'https://ec.europa.eu/taxation_customs/vies/services/checkVatService';
 //$WS_DOL_URL_WSDL=$WS_DOL_URL.'?wsdl';
 $WS_DOL_URL_WSDL = 'https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl';
-$WS_METHOD = 'checkVat';
-
+$WS_METHOD = !empty($conf->global->VAT_INTRA_CHECK_VIES_WS_METHOD) ?$conf->global->VAT_INTRA_CHECK_VIES_WS_METHOD :'checkVat';
+//$WS_METHOD = 'checkVatApprox';
 
 $conf->dol_hide_topmenu = 1;
 $conf->dol_hide_leftmenu = 1;
@@ -63,7 +66,10 @@ if (!$vatNumber) {
 	// Set the parameters to send to the WebService
 	$parameters = array("countryCode" => $countryCode,
 						"vatNumber" => $vatNumber);
-
+	if ($WS_METHOD == 'checkVatApprox') {
+		$parameters['requesterCountryCode'] = substr($mysoc->tva_intra, 0, 2);
+		$parameters['requesterVatNumber'] = substr($mysoc->tva_intra, 2);
+	}
 	// Set the WebService URL
 	dol_syslog("Create nusoap_client for URL=".$WS_DOL_URL." WSDL=".$WS_DOL_URL_WSDL);
 	require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
@@ -92,6 +98,17 @@ if (!$vatNumber) {
 	//print $soapclient->request.'<br>';
 	//print $soapclient->response.'<br>';
 
+	$parameters = [
+		'WS_METHOD' => $WS_METHOD,
+		'parameters' => $parameters,
+		'result' => &$result,
+		'soapclient' => $soapclient,
+	];
+	$reshook = $hookmanager->executeHooks('checkVatResult', $parameters);
+	if ($reshook < 0) {
+		setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+	}
+
 	$messagetoshow = '';
 	print '<b>'.$langs->trans("Response").'</b>:<br>';
 
@@ -132,6 +149,10 @@ if (!$vatNumber) {
 				print '<br>';
 				print $langs->trans("Name").': '.$result['name'].'<br>';
 				print $langs->trans("Address").': '.$result['address'].'<br>';
+				$reshook = $hookmanager->executeHooks('checkVatDisplay', $parameters);
+				if ($reshook < 0) {
+					setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+				}
 			} else {
 				print '<span	 class="error">'.$langs->trans("No").'</span>';
 				print '<br>'."\n";