Browse Source

Merge branch '14.0.5-mmi' into 14.0-mmi

Mathieu Moulin 2 years ago
parent
commit
f715161af8
50 changed files with 1828 additions and 224 deletions
  1. 1 0
      .gitignore
  2. 13 5
      htdocs/accountancy/class/accountancyexport.class.php
  3. 6 0
      htdocs/admin/workflow.php
  4. 3 1
      htdocs/api/index.php
  5. 44 5
      htdocs/comm/propal/card.php
  6. 15 0
      htdocs/comm/propal/list.php
  7. 25 2
      htdocs/commande/card.php
  8. 32 1
      htdocs/compta/facture/card.php
  9. 8 0
      htdocs/compta/paiement/card.php
  10. 7 0
      htdocs/compta/paiement/class/paiement.class.php
  11. 7 0
      htdocs/core/actions_massactions.inc.php
  12. 4 0
      htdocs/core/actions_setmoduleoptions.inc.php
  13. 101 0
      htdocs/core/class/commonobject.class.php
  14. 15 4
      htdocs/core/class/html.form.class.php
  15. 3 0
      htdocs/core/class/html.formmail.class.php
  16. 17 0
      htdocs/core/db/mysqli.class.php
  17. 10 0
      htdocs/core/lib/ajax.lib.php
  18. 10 0
      htdocs/core/lib/company.lib.php
  19. 8 0
      htdocs/core/lib/functions.lib.php
  20. 35 3
      htdocs/core/lib/payments.lib.php
  21. 27 3
      htdocs/core/lib/pdf.lib.php
  22. 28 1
      htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php
  23. 254 41
      htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php
  24. 48 1
      htdocs/core/modules/expedition/doc/doc_generic_shipment_odt.modules.php
  25. 10 5
      htdocs/core/modules/expedition/doc/pdf_rouget.modules.php
  26. 31 2
      htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php
  27. 276 42
      htdocs/core/modules/facture/doc/pdf_sponge.modules.php
  28. 6 3
      htdocs/core/modules/modFournisseur.class.php
  29. 30 2
      htdocs/core/modules/propale/doc/doc_generic_proposal_odt.modules.php
  30. 272 42
      htdocs/core/modules/propale/doc/pdf_cyan.modules.php
  31. 15 0
      htdocs/core/tpl/objectline_create.tpl.php
  32. 23 0
      htdocs/core/tpl/onlinepaymentlinks.tpl.php
  33. 8 0
      htdocs/core/triggers/interface_20_modWorkflow_WorkflowManager.class.php
  34. 11 1
      htdocs/expedition/card.php
  35. 4 1
      htdocs/expedition/class/expedition.class.php
  36. 7 2
      htdocs/fourn/commande/dispatch.php
  37. 15 2
      htdocs/includes/odtphp/Segment.php
  38. 14 1
      htdocs/includes/odtphp/odf.php
  39. 1 0
      htdocs/langs/en_US/workflow.lang
  40. 2 0
      htdocs/langs/fr_FR/main.lang
  41. 2 0
      htdocs/langs/fr_FR/propal.lang
  42. 1 0
      htdocs/langs/fr_FR/workflow.lang
  43. 16 2
      htdocs/product/class/product.class.php
  44. 17 5
      htdocs/product/stock/class/mouvementstock.class.php
  45. 7 3
      htdocs/product/stock/product.php
  46. 19 0
      htdocs/product/stock/replenish.php
  47. 90 13
      htdocs/projet/tasks/time.php
  48. 197 27
      htdocs/public/payment/newpayment.php
  49. 9 1
      htdocs/reception/card.php
  50. 24 3
      htdocs/societe/checkvat/checkVatPopup.php

+ 1 - 0
.gitignore

@@ -42,6 +42,7 @@ htdocs/includes/sebastian/
 htdocs/includes/squizlabs/
 htdocs/includes/webmozart/
 htdocs/.well-known/apple-developer-merchantid-domain-association
+htdocs/.user.ini
 
 # Node Modules
 build/yarn-error.log

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

@@ -554,14 +554,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 {
@@ -598,7 +604,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
 			/*
@@ -659,7 +665,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

@@ -372,7 +372,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);
 	}

+ 44 - 5
htdocs/comm/propal/card.php

@@ -2364,10 +2364,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>';
@@ -2430,6 +2439,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>';
@@ -2456,7 +2472,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="">
@@ -2585,7 +2602,9 @@ if ($action == 'create') {
 				}
 
 				// Create an invoice and classify billed
-				if ($object->statut == Propal::STATUS_SIGNED) {
+				// Added by MMI Mathieu Moulin iProspective
+				// Parameter to block creation of invoices for Proposals (must do it from orders)
+				if (empty($conf->global->WORKFLOW_CANNOT_CREATE_INVOICE_FROM_PROPOSAL) && $object->statut == Propal::STATUS_SIGNED) {
 					if (!empty($conf->facture->enabled) && $usercancreateinvoice) {
 						print '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/card.php?action=create&amp;origin='.$object->element.'&amp;originid='.$object->id.'&amp;socid='.$object->socid.'">'.$langs->trans("CreateBill").'</a>';
 					}
@@ -2664,6 +2683,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"><div class="ficheaddleft">';
 
 		// List of actions on element

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

@@ -533,6 +533,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 (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)";
@@ -546,6 +553,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 || (!$user->rights->societe->client->voir && !$socid)) {
 	$sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";

+ 25 - 2
htdocs/commande/card.php

@@ -2343,8 +2343,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>';
 
@@ -2405,7 +2414,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>';
@@ -2637,6 +2652,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

@@ -1467,7 +1467,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;
@@ -1903,6 +1911,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)) {
@@ -4608,7 +4622,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>';
 
@@ -5569,6 +5593,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

@@ -326,6 +326,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>';
@@ -445,6 +449,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) {

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

@@ -260,6 +260,13 @@ 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);
 

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

@@ -307,6 +307,13 @@ if (!$error && $massaction == 'confirm_presend') {
 					// $subdir = get_exdir($objectobj->id, 2, 0, 0, $objectobj, $objectobj->element)
 					$filedir = $uploaddir.'/'.$subdir.dol_sanitizeFileName($objectobj->ref);
 					$file = $filedir.'/'.$filename;
+					// Added By MMI Mathieu Moulin iProspective
+					// Fix search by filename
+					if (!file_exists($file)) {
+						$files = glob($filedir.'/'.dol_sanitizeFileName($objectobj->ref).'*.pdf');
+						if (!empty($files))
+							$file = $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

@@ -39,6 +39,10 @@ if ($action == 'update' && is_array($arrayofparameters)) {
 				} 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

@@ -9339,4 +9339,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

@@ -2465,9 +2465,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 ".MAIN_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 ".MAIN_DB_PREFIX."societe as pf ON pfp.fk_soc = pf.rowid";
+		}
 
 		//Price by customer
 		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
@@ -2552,6 +2557,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++;
 			}
@@ -5832,10 +5842,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
@@ -5843,7 +5854,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

@@ -852,6 +852,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

@@ -322,6 +322,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
 	/**

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

@@ -46,6 +46,7 @@
  */
 function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLength = 2, $autoselect = 0, $ajaxoptions = array(), $moreparams = '')
 {
+	global $conf;
 	if (empty($minLength)) {
 		$minLength = 1;
 	}
@@ -179,6 +180,15 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLen
 							$("#'.$htmlname.'").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 .= '
 							$("#'.$htmlname.'").val(ui.item.id).trigger("change");	// Select new value
 

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

@@ -2121,3 +2121,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

@@ -2165,6 +2165,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')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
 		// US: title firstname name \n address lines \n town, state, zip \n country
@@ -7181,6 +7186,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

@@ -133,7 +133,7 @@ function payment_supplier_prepare_head(Paiement $object)
  */
 function getValidOnlinePaymentMethods($paymentmethod = '')
 {
-	global $conf, $langs;
+	global $conf, $langs, $db;// @todo, $user;
 
 	$validpaymentmethod = array();
 
@@ -149,8 +149,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;
 }
@@ -233,7 +241,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=propal&ref='.($mode ? '<font 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=order&ref='.($mode ? '<font color="#666666">' : '');
 		if ($mode == 1) {
 			$out .= 'order_ref';

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

@@ -1302,7 +1302,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 : '');
@@ -1535,12 +1537,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

@@ -402,6 +402,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');
@@ -431,7 +437,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) {

+ 254 - 41
htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php

@@ -384,6 +384,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);
@@ -862,6 +870,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')) {
@@ -1592,6 +1605,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
 			$pdf->SetTextColor(0, 0, 0);
@@ -1614,56 +1646,173 @@ 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
+				$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
+				$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 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);
+				// 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;
+				}
 
-			// Show recipient name
-			$pdf->SetXY($posx + 2, $posy + 3);
-			$pdf->SetFont('', 'B', $default_font_size);
-			$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
 
-			$posy = $pdf->getY();
+				$mode =  'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
 
-			// Show recipient information
-			$pdf->SetFont('', '', $default_font_size - 1);
-			$pdf->SetXY($posx + 2, $posy);
-			$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
+				// 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("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);
@@ -1689,6 +1838,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);
 	}
 
+	/**
+	 * 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

@@ -384,6 +384,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');
@@ -404,6 +410,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');
@@ -426,6 +438,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');
@@ -445,6 +463,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');
@@ -470,6 +494,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');
@@ -497,7 +527,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 - 5
htdocs/core/modules/expedition/doc/pdf_rouget.modules.php

@@ -213,7 +213,12 @@ class pdf_rouget extends ModelePdfExpedition
 
 		// Load traductions files required by page
 		$outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies", "propal", "deliveries", "sendings", "productbatch"));
-
+		// 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
@@ -232,7 +237,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;
 				}
 
@@ -475,7 +480,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'];
 					}
@@ -964,7 +969,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);
@@ -1067,7 +1072,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;

+ 31 - 2
htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php

@@ -411,7 +411,14 @@ class doc_generic_invoice_odt extends ModelePDFFactures
 							} 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');
 						}
@@ -440,7 +447,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) {

+ 276 - 42
htdocs/core/modules/facture/doc/pdf_sponge.modules.php

@@ -416,6 +416,15 @@ class pdf_sponge extends ModelePDFFactures
 				$pdf->MultiCell(0, 3, ''); // Set interline to 3
 				$pdf->SetTextColor(0, 0, 0);
 
+				// 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 = 90 + $top_shift;
 				$tab_top_newpage = (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD) ? 42 + $top_shift : 10);
 				$tab_height = 130 - $top_shift;
@@ -916,6 +925,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')) {
@@ -1141,6 +1156,23 @@ class pdf_sponge extends ModelePDFFactures
 			$posy = $pdf->GetY() + 3;
 		}
 
+		// 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)
@@ -2086,7 +2118,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
 			$pdf->SetTextColor(0, 0, 0);
@@ -2109,56 +2160,173 @@ 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
+				$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
+				$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 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);
+				// 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;
+				}
 
-			// 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);
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
 
-			$posy = $pdf->getY();
+				$mode =  'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
 
-			// Show recipient information
-			$pdf->SetFont('', '', $default_font_size - 1);
-			$pdf->SetXY($posx + 2, $posy);
-			$pdf->MultiCell($widthrecbox - 2, 4, $carac_client, 0, $ltrdirection);
+				// 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("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);
@@ -2182,6 +2350,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);
 	}
 
+	/**
+	 * 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
 	 *

+ 6 - 3
htdocs/core/modules/modFournisseur.class.php

@@ -434,7 +434,8 @@ class modFournisseur extends DolibarrModules
 			'f.note_public'=>"NotePublic", 'f.note_private'=>"NotePrivate", 'ua1.login'=>'ApprovedBy', 'ua2.login'=>'ApprovedBy2', 'fd.rowid'=>'LineId', 'fd.description'=>"LineDescription",
 			'fd.tva_tx'=>"LineVATRate", 'fd.qty'=>"LineQty", 'fd.remise_percent'=>"Discount", 'fd.total_ht'=>"LineTotalHT", 'fd.total_ttc'=>"LineTotalTTC",
 			'fd.total_tva'=>"LineTotalVAT", 'fd.product_type'=>'TypeOfLineServiceOrProduct', 'fd.ref'=>'RefSupplier', 'fd.fk_product'=>'ProductId',
-			'p.ref'=>'ProductRef', 'p.label'=>'ProductLabel', 'project.rowid'=>'ProjectId', 'project.ref'=>'ProjectRef', 'project.title'=>'ProjectLabel'
+			// Added by MMI Mathieu Moulin iProspective : add BarCodeValue
+			'p.ref'=>'ProductRef', 'p.label'=>'ProductLabel', 'p.barcode'=>'BarcodeValue', 'project.rowid'=>'ProjectId', 'project.ref'=>'ProjectRef', 'project.title'=>'ProjectLabel'
 		);
 		if (!empty($conf->multicurrency->enabled)) {
 			$this->export_fields_array[$r]['f.multicurrency_code'] = 'Currency';
@@ -453,14 +454,16 @@ class modFournisseur extends DolibarrModules
 			'f.date_creation'=>"Date", 'f.date_commande'=>"Date", 'f.date_livraison'=>"Date", 'f.total_ht'=>"Numeric", 'f.total_ttc'=>"Numeric", 'f.total_tva'=>"Numeric",
 			'f.fk_statut'=>'Status', '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.product_type'=>'Numeric', 'fd.ref'=>'Text', 'fd.fk_product'=>'List:product:label', 'p.ref'=>'Text', 'p.label'=>'Text', 'project.ref'=>'Text', 'project.title'=>'Text'
+			// Added by MMI Mathieu Moulin iProspective : add BarCodeValue
+			'fd.product_type'=>'Numeric', 'fd.ref'=>'Text', 'fd.fk_product'=>'List:product:label', 'p.ref'=>'Text', 'p.label'=>'Text', 'p.barcode'=>'Text', 'project.ref'=>'Text', 'project.title'=>'Text'
 		);
 		$this->export_entities_array[$r] = array(
 			's.rowid'=>"company", 's.nom'=>'company', 'ps.nom'=>'company', 's.address'=>'company', 's.zip'=>'company', 's.town'=>'company', 'c.code'=>'company', 's.phone'=>'company', 's.siren'=>'company',
 			's.siret'=>'company', 's.ape'=>'company', 's.idprof4'=>'company', 's.idprof5'=>'company', 's.idprof6'=>'company', 's.tva_intra'=>'company', 'ua1.login'=>'user',
 			'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.product_type'=>'order_line', 'fd.ref'=>'order_line', 'fd.fk_product'=>'product',
-			'p.ref'=>'product', 'p.label'=>'product', 'project.rowid'=>'project', 'project.ref'=>'project', 'project.title'=>'project'
+			// Added by MMI Mathieu Moulin iProspective : add BarCodeValue
+			'p.ref'=>'product', 'p.label'=>'product', 'p.barcode'=>'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

@@ -429,7 +429,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');
 						}
@@ -458,7 +465,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) {

+ 272 - 42
htdocs/core/modules/propale/doc/pdf_cyan.modules.php

@@ -377,6 +377,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;
@@ -885,6 +893,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')) {
@@ -1439,6 +1452,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));
 	}
@@ -1688,6 +1720,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
 			$pdf->SetTextColor(0, 0, 0);
 			$pdf->SetFont('', '', $default_font_size - 2);
@@ -1709,57 +1759,173 @@ 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
+				$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 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);
+				// 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;
+				}
 
-			// Show recipient name
-			$pdf->SetXY($posx + 2, $posy + 3);
-			$pdf->SetFont('', 'B', $default_font_size);
-			$pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, $ltrdirection);
+				$carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs);
 
-			$posy = $pdf->getY();
+				$mode =  'target';
+				$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
 
-			// Show recipient information
-			$pdf->SetFont('', '', $default_font_size - 1);
-			$pdf->SetXY($posx + 2, $posy);
-			$pdf->MultiCell($widthrecbox, 4, $carac_client, 0, $ltrdirection);
+				// 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("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);
@@ -1815,7 +1981,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;
 	}
 
 

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

@@ -255,6 +255,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>
@@ -675,6 +678,18 @@ if (!empty($usemargins) && $user->rights->margins->creer) {
 	}
 	?>
 
+	<?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>';

+ 8 - 0
htdocs/core/triggers/interface_20_modWorkflow_WorkflowManager.class.php

@@ -72,6 +72,14 @@ class InterfaceWorkflowManager extends DolibarrTriggers
 		if ($action == 'PROPAL_CLOSE_SIGNED') {
 			dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
 			if (!empty($conf->commande->enabled) && !empty($conf->global->WORKFLOW_PROPAL_AUTOCREATE_ORDER)) {
+				// Added by MMI Mathieu Moulin iProspective
+				// Check if there is already one
+				if (!empty($conf->global->WORKFLOW_PROPAL_AUTOCREATE_ORDER_IFNOTEXISTS)) {
+					$object->fetchObjectLinked();
+					if (!empty($object->linkedObjectsIds['commande']))
+						return $ret;
+				}
+				
 				include_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
 				$newobject = new Commande($this->db);
 

+ 11 - 1
htdocs/expedition/card.php

@@ -1893,7 +1893,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>';
 
@@ -2483,7 +2492,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 '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/card.php?action=create&amp;origin='.$object->element.'&amp;originid='.$object->id.'&amp;socid='.$object->socid.'">'.$langs->trans("CreateBill").'</a>';
+					if (empty($conf->global->BILL_ON_SHIPMENT_DISABLE))
+						print '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/card.php?action=create&amp;origin='.$object->element.'&amp;originid='.$object->id.'&amp;socid='.$object->socid.'">'.$langs->trans("CreateBill").'</a>';
 				}
 			}
 

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

@@ -1583,7 +1583,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";
+		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";
 
 		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
 		$resql = $this->db->query($sql);

+ 7 - 2
htdocs/fourn/commande/dispatch.php

@@ -709,8 +709,13 @@ 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 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 p.ref, p.label";
+		
 		$resql = $db->query($sql);
 		if ($resql) {
 			$num = $db->num_rows($resql);

+ 15 - 2
htdocs/includes/odtphp/Segment.php

@@ -255,8 +255,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);

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

@@ -228,6 +228,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>';
@@ -245,6 +248,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) {
@@ -273,13 +277,22 @@ 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 . '" />';
+                                            //var_dump($odtStyles); die();
+                                        }
+                                        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

@@ -370,6 +370,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
@@ -1159,3 +1160,4 @@ hasBeenValidated=%s a été validé
 ClientTZ=Fuseau horaire client (utilisateur)
 NotClosedYet=Pas encore fermé
 ClearSignature=Signature supprimée
+ToggleExtrafields=Montrer/Maqquer les champs supplémentaires

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

@@ -97,3 +97,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)

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

@@ -3998,6 +3998,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 '.MAIN_DB_PREFIX.'product_association';
@@ -4061,9 +4065,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
@@ -4094,6 +4103,11 @@ class Product extends CommonObject
 			return -1;
 		}
 
+		// Added by MIM Mathieu Moulin iProspective
+		// Call trigger
+		global $user;
+		$result = $this->call_trigger('PRODUCT_SOUSPRODUIT', $user);
+
 		return 1;
 	}
 

+ 17 - 5
htdocs/product/stock/class/mouvementstock.class.php

@@ -162,6 +162,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)) {
@@ -259,7 +261,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;
@@ -565,8 +567,12 @@ class MouvementStock extends CommonObject
 
 			// 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.
-			$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_stock WHERE reel = 0 AND rowid NOT IN (SELECT fk_product_stock FROM ".MAIN_DB_PREFIX."product_batch as pb)";
-			$resql = $this->db->query($sql);
+			// Added by MMI Mathieu Moulin iProspective
+			// DO NOT DELETE STOCK !!
+			if (! $conf->global->STOCK_KEEP_EMPTY_IN_DB) {
+				$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_stock WHERE reel = 0 AND rowid NOT IN (SELECT fk_product_stock FROM ".MAIN_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
 		}
 
@@ -842,7 +848,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');
 
@@ -880,7 +887,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

@@ -968,7 +968,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);
@@ -1099,7 +1103,7 @@ if (!$variants) {
 						print '<td colspan="4"></td>';
 						print '<td>';
 						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>';
@@ -1111,7 +1115,7 @@ if (!$variants) {
 						print '</td>';
 						print '<td>';
 						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);

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

@@ -73,6 +73,9 @@ $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.
@@ -165,10 +168,20 @@ if ($action == 'addtimespent' && $user->rights->projet->lire) {
 		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) {
@@ -204,13 +217,28 @@ if ($action == 'addtimespent' && $user->rights->projet->lire) {
 				} 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++;
+					}
 				}
 			}
 		}
@@ -1195,9 +1223,54 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) {
 			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>';
 
 			// Task
@@ -1222,7 +1295,11 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) {
 				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');

+ 197 - 27
htdocs/public/payment/newpayment.php

@@ -993,6 +993,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;
@@ -1011,7 +1156,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');
 		}
@@ -1057,31 +1205,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').'">';
-	} else {
-		print '<b>'.price($amount).'</b>';
-		print '<input type="hidden" name="amount" value="'.$amount.'">';
-		print '<input type="hidden" name="newamount" value="'.$amount.'">';
+	// 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>';
+		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";
 	}
-	// Currency
-	print ' <b>'.$langs->trans("Currency".$currency).'</b>';
-	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').'">';
+		} 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";
+		// 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;
@@ -1566,7 +1732,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.'">';
@@ -2037,7 +2203,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>';
@@ -2055,7 +2224,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

@@ -1558,8 +1558,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 '<font class="error">'.$langs->trans("No").'</font>';
 				print '<br>'."\n";