Pārlūkot izejas kodu

all mods number_1

szollosil 1 gadu atpakaļ
vecāks
revīzija
b17dd0a700
59 mainītis faili ar 9459 papildinājumiem un 2410 dzēšanām
  1. 21 0
      admin/company.php
  2. 43 0
      admin/facture.php
  3. 1599 642
      compta/facture/card.php
  4. 5 0
      compta/facture/class/api_invoices.class.php
  5. 307 284
      compta/facture/class/facture.class.php
  6. 7 0
      compta/facture/contact.php
  7. 7 0
      compta/facture/document.php
  8. 7 0
      compta/facture/info.php
  9. 7 0
      compta/facture/note.php
  10. 20 9
      compta/paiement.php
  11. 3 1
      compta/paiement/class/paiement.class.php
  12. 1 1
      core/ajax/constantonoff.php
  13. 17 1
      core/class/commoninvoice.class.php
  14. 212 210
      core/class/commonobject.class.php
  15. 8 2
      core/class/html.form.class.php
  16. 3 0
      core/lib/files.lib.php
  17. 2 0
      core/lib/functions.lib.php
  18. 2 1
      core/lib/multicurrency.lib.php
  19. 35 1
      core/lib/pdf.lib.php
  20. 5 5
      core/lib/price.lib.php
  21. 357 130
      core/modules/facture/doc/pdf_crabe.modules.php
  22. 2180 0
      core/modules/facture/doc/pdf_crabe_new_printinginvoice.modules.php
  23. 2354 0
      core/modules/facture/doc/pdf_crabe_receipt.modules.php
  24. 32 14
      core/modules/facture/mod_facture_mercure.php
  25. 2 2
      core/modules/modTicket.class.php
  26. 21 2
      core/tpl/extrafields_list_search_input.tpl.php
  27. 26 11
      core/tpl/extrafields_list_search_sql.tpl.php
  28. 40 28
      core/tpl/extrafields_view.tpl.php
  29. 623 556
      core/tpl/objectline_create.tpl.php
  30. 3 0
      emailcollector/class/emailcollector.class.php
  31. 7 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
  32. 2 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
  33. 1 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
  34. 3 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php
  35. 1 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory.php
  36. 18 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
  37. 2 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
  38. 9 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
  39. 1 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
  40. 2 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
  41. 8 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php
  42. 1 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php
  43. 18 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
  44. 3 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php
  45. 3 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php
  46. 18 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php
  47. 2 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
  48. 2 0
      includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
  49. 2 1
      includes/restler/framework/Luracast/Restler/RestException.php
  50. 6 0
      langs/en_GB/stocks.lang
  51. 6 0
      langs/hu_HU/stocks.lang
  52. 5 1
      main.inc.php
  53. 18 3
      product/card.php
  54. 19 36
      product/inventory/class/inventory.class.php
  55. 866 0
      product/inventory/class/inventory.class.php.bak
  56. 1 1
      product/list.php
  57. 260 254
      user/class/user.class.php
  58. 1 1
      user/perms.php
  59. 225 213
      variants/combinations.php

+ 21 - 0
admin/company.php

@@ -197,6 +197,13 @@ if (($action == 'update' && !GETPOST("cancel", 'alpha'))
 	dolibarr_set_const($db, "MAIN_INFO_TVAINTRA", GETPOST("tva", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
 	dolibarr_set_const($db, "MAIN_INFO_SOCIETE_OBJECT", GETPOST("object", 'alphanohtml'), 'chaine', 0, '', $conf->entity);
 
+	if($user->admin){
+		$arr = ['NAV_USER','NAV_PASS','NAV_VAT_NUMBER','NAV_VAT_CODE','NAV_VAT_COUNTYCODE','NAV_SIGN_KEY','NAV_EXCHANGE_KEY','NAV_URL'];
+		foreach($arr as $k=>$v){
+			dolibarr_set_const($db, $v, GETPOST($v, 'alphanohtml'), 'chaine', 0, '', $conf->entity);
+		}
+	}
+
 	dolibarr_set_const($db, "SOCIETE_FISCAL_MONTH_START", GETPOST("SOCIETE_FISCAL_MONTH_START", 'int'), 'chaine', 0, '', $conf->entity);
 
 	// Sale tax options
@@ -680,6 +687,20 @@ print '<tr class="oddeven"><td><label for="object">'.$langs->trans("CompanyObjec
 print '<textarea class="flat quatrevingtpercent" name="object" id="object" rows="'.ROWS_5.'">'.(!empty($conf->global->MAIN_INFO_SOCIETE_OBJECT) ? $conf->global->MAIN_INFO_SOCIETE_OBJECT : '').'</textarea></td></tr>';
 print '</td></tr>';
 
+// NAV Online
+if($user->admin){
+	$arr = ['NAV_USER','NAV_PASS','NAV_VAT_NUMBER','NAV_VAT_CODE','NAV_VAT_COUNTYCODE','NAV_SIGN_KEY','NAV_EXCHANGE_KEY','NAV_URL'];
+	$defVal=[
+		'NAV_URL' => "https://api-test.onlineszamla.nav.gov.hu/invoiceService/v3", // teszt
+		// 'NAV_URL' => "https://api.onlineszamla.nav.gov.hu/invoiceService/v3", // éles
+	];
+	foreach($arr as $k=>$v){
+		print '<tr class="oddeven"><td><label for="'.$v.'">'.$langs->trans($v).'</label></td><td>';
+		print '<input name="'.$v.'" id="'.$v.'" class="minwidth500" value="'.dol_escape_htmltag(!empty($conf->global->$v) ? $conf->global->$v : ((isset($defVal[$v]))?$defVal[$v]:'')).'">';
+		print '</td></tr>';
+	}
+}
+
 print '</table>';
 print '</div>';
 

+ 43 - 0
admin/facture.php

@@ -59,22 +59,32 @@ include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php';
 
 if ($action == 'updateMask') {
 	$maskconstinvoice = GETPOST('maskconstinvoice', 'aZ09');
+	$maskconstreceipt = GETPOST('maskconstreceipt', 'alpha');
 	$maskconstreplacement = GETPOST('maskconstreplacement', 'aZ09');
 	$maskconstcredit = GETPOST('maskconstcredit', 'aZ09');
+	$maskconstreceiptcredit = GETPOST('maskconstreceiptcredit', 'alpha');
 	$maskconstdeposit = GETPOST('maskconstdeposit', 'aZ09');
 	$maskinvoice = GETPOST('maskinvoice', 'alpha');
+	$maskreceipt = GETPOST('maskreceipt', 'alpha');
 	$maskreplacement = GETPOST('maskreplacement', 'alpha');
 	$maskcredit = GETPOST('maskcredit', 'alpha');
+	$maskreceiptcredit = GETPOST('maskreceiptcredit', 'alpha');
 	$maskdeposit = GETPOST('maskdeposit', 'alpha');
 	if ($maskconstinvoice && preg_match('/_MASK_/', $maskconstinvoice)) {
 		$res = dolibarr_set_const($db, $maskconstinvoice, $maskinvoice, 'chaine', 0, '', $conf->entity);
 	}
+	if ($maskconstreceipt) {
+		$res = dolibarr_set_const($db, $maskconstreceipt, $maskreceipt, 'chaine', 0, '', $conf->entity);
+	}
 	if ($maskconstreplacement && preg_match('/_MASK_/', $maskconstreplacement)) {
 		$res = dolibarr_set_const($db, $maskconstreplacement, $maskreplacement, 'chaine', 0, '', $conf->entity);
 	}
 	if ($maskconstcredit && preg_match('/_MASK_/', $maskconstcredit)) {
 		$res = dolibarr_set_const($db, $maskconstcredit, $maskcredit, 'chaine', 0, '', $conf->entity);
 	}
+	if ($maskconstreceiptcredit) {
+		$res = dolibarr_set_const($db, $maskconstreceiptcredit, $maskreceiptcredit, 'chaine', 0, '', $conf->entity);
+	}
 	if ($maskconstdeposit && preg_match('/_MASK_/', $maskconstdeposit)) {
 		$res = dolibarr_set_const($db, $maskconstdeposit, $maskdeposit, 'chaine', 0, '', $conf->entity);
 	}
@@ -416,6 +426,37 @@ foreach ($dirmodels as $reldir) {
 									$htmltooltip .= $langs->trans($module->error).'<br>';
 								}
 							}
+
+							// Example for credit receipt
+							$facture->type = 8;
+							$nextval = $module->getNextValue($mysoc, $facture);
+							if ("$nextval" != $langs->trans("NotAvailable")) {  // Keep " on nextval
+								$htmltooltip .= $langs->trans("NextValueForCreditNotes").': ';
+								if ($nextval) {
+									if (preg_match('/^Error/', $nextval) || $nextval == 'NotConfigured') {
+										$nextval = $langs->trans($nextval);
+									}
+									$htmltooltip .= $nextval.'<br>';
+								} else {
+									$htmltooltip .= $langs->trans($module->error).'<br>';
+								}
+							}
+
+							// Example for Receipt
+							$facture->type = 7;
+							$nextval = $module->getNextValue($mysoc, $facture);
+							if ("$nextval" != $langs->trans("NotAvailable")) {  // Keep " on nextval
+								$htmltooltip .= $langs->trans("NextValueForCreditNotes").': ';
+								if ($nextval) {
+									if (preg_match('/^Error/', $nextval) || $nextval == 'NotConfigured') {
+										$nextval = $langs->trans($nextval);
+									}
+									$htmltooltip .= $nextval.'<br>';
+								} else {
+									$htmltooltip .= $langs->trans($module->error).'<br>';
+								}
+							}
+							
 							// Example for deposit invoice
 							$facture->type = 3;
 							$nextval = $module->getNextValue($mysoc, $facture);
@@ -625,8 +666,10 @@ if (!empty($conf->global->INVOICE_USE_DEFAULT_DOCUMENT)) { // Hidden conf
 
 	$listtype = array(
 		Facture::TYPE_STANDARD=>$langs->trans("InvoiceStandard"),
+		//Facture::TYPE_RECEIPT=>$langs->trans("ReceiptStandard"),
 		Facture::TYPE_REPLACEMENT=>$langs->trans("InvoiceReplacement"),
 		Facture::TYPE_CREDIT_NOTE=>$langs->trans("InvoiceAvoir"),
+		Facture::TYPE_RECEIPT_CREDIT_NOTE=>$langs->trans("ReceiptInvoiceAvoir"),
 		Facture::TYPE_DEPOSIT=>$langs->trans("InvoiceDeposit"),
 	);
 	if (getDolGlobalInt('INVOICE_USE_SITUATION')) {

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1599 - 642
compta/facture/card.php


+ 5 - 0
compta/facture/class/api_invoices.class.php

@@ -301,6 +301,11 @@ class Invoices extends DolibarrApi
 		$result = $this->_validate($request_data);
 
 		foreach ($request_data as $field => $value) {
+			if (preg_match('/^array_options/', $field)) {
+				$f = str_replace('array_options_', '', $field);
+				$this->invoice->array_options[$f] = $value;
+				continue;	
+			}
 			$this->invoice->$field = $value;
 		}
 		if (!array_key_exists('date', $request_data)) {

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 307 - 284
compta/facture/class/facture.class.php


+ 7 - 0
compta/facture/contact.php

@@ -35,6 +35,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
 if (isModEnabled('project')) {
 	require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
 }
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketcounter.class.php';
 
 // Load translation files required by the page
 $langs->loadLangs(array('bills', 'companies'));
@@ -45,6 +46,8 @@ $lineid = GETPOST('lineid', 'int');
 $socid  = GETPOST('socid', 'int');
 $action = GETPOST('action', 'aZ09');
 
+$Bbticketcounter = new Bbticketcounter();
+
 // Security check
 if ($user->socid) {
 	$socid = $user->socid;
@@ -128,6 +131,7 @@ if ($id > 0 || !empty($ref)) {
 		$totalpaid = $object->getSommePaiement();
 
 		print dol_get_fiche_head($head, 'contact', $langs->trans('InvoiceCustomer'), -1, 'bill');
+        print '<input type="hidden" id="printedTicketsNumber" name="printedticketsnumber" value="' . $Bbticketcounter->numberOfPrintedTickets . '">';
 
 		// Invoice content
 
@@ -187,3 +191,6 @@ if ($id > 0 || !empty($ref)) {
 // End of page
 llxFooter();
 $db->close();
+?>
+<link rel="stylesheet" type="text/css" href="/custom/bbus/views/bbus.css">
+<script src="/custom/bbus/js/bbus.js"></script>;

+ 7 - 0
compta/facture/document.php

@@ -38,6 +38,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
 if (isModEnabled('project')) {
 	include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
 }
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketcounter.class.php';
 
 // Load translation files required by the page
 $langs->loadLangs(array('propal', 'compta', 'other', 'bills', 'companies'));
@@ -49,6 +50,8 @@ $socid = GETPOST('socid', 'int');
 $action = GETPOST('action', 'aZ09');
 $confirm = GETPOST('confirm', 'alpha');
 
+$Bbticketcounter = new Bbticketcounter();
+
 // Get parameters
 $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit;
 $sortfield = GETPOST('sortfield', 'aZ09comma');
@@ -122,6 +125,7 @@ if ($id > 0 || !empty($ref)) {
 
 		$head = facture_prepare_head($object);
 		print dol_get_fiche_head($head, 'documents', $langs->trans('InvoiceCustomer'), -1, 'bill');
+        print '<input type="hidden" id="printedTicketsNumber" name="printedticketsnumber" value="' . $Bbticketcounter->numberOfPrintedTickets . '">';
 
 		$totalpaid = $object->getSommePaiement();
 
@@ -198,3 +202,6 @@ if ($id > 0 || !empty($ref)) {
 // End of page
 llxFooter();
 $db->close();
+?>
+<link rel="stylesheet" type="text/css" href="/custom/bbus/views/bbus.css">
+<script src="/custom/bbus/js/bbus.js"></script>;

+ 7 - 0
compta/facture/info.php

@@ -33,11 +33,14 @@ if (isModEnabled('project')) {
 	include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
 }
 
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketcounter.class.php';
+
 // Load translation files required by the page
 $langs->loadLangs(array('companies', 'bills'));
 
 $id = GETPOST("facid", "int");
 $ref = GETPOST("ref", 'alpha');
+$Bbticketcounter = new Bbticketcounter();
 
 $object = new Facture($db);
 
@@ -86,6 +89,7 @@ $object->info($object->id);
 
 $head = facture_prepare_head($object);
 print dol_get_fiche_head($head, 'info', $langs->trans("InvoiceCustomer"), -1, 'bill');
+print '<input type="hidden" id="printedTicketsNumber" name="printedticketsnumber" value="' . $Bbticketcounter->numberOfPrintedTickets . '">';
 
 $totalpaid = $object->getSommePaiement();
 
@@ -142,3 +146,6 @@ print dol_get_fiche_end();
 // End of page
 llxFooter();
 $db->close();
+?>
+<link rel="stylesheet" type="text/css" href="/custom/bbus/views/bbus.css">
+<script src="/custom/bbus/js/bbus.js"></script>;

+ 7 - 0
compta/facture/note.php

@@ -33,6 +33,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/invoice.lib.php';
 if (isModEnabled('project')) {
 	require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
 }
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketcounter.class.php';
 
 // Load translation files required by the page
 $langs->loadLangs(array('companies', 'bills'));
@@ -42,6 +43,8 @@ $ref = GETPOST('ref', 'alpha');
 $socid = GETPOST('socid', 'int');
 $action = GETPOST('action', 'aZ09');
 
+$Bbticketcounter = new Bbticketcounter();
+
 $object = new Facture($db);
 // Load object
 if ($id > 0 || !empty($ref)) {
@@ -110,6 +113,7 @@ if ($id > 0 || !empty($ref)) {
 	$totalpaid = $object->getSommePaiement();
 
 	print dol_get_fiche_head($head, 'note', $langs->trans("InvoiceCustomer"), -1, 'bill');
+	print '<input type="hidden" id="printedTicketsNumber" name="printedticketsnumber" value="' . $Bbticketcounter->numberOfPrintedTickets . '">';
 
 	// Invoice content
 
@@ -161,3 +165,6 @@ if ($id > 0 || !empty($ref)) {
 // End of page
 llxFooter();
 $db->close();
+?>
+<link rel="stylesheet" type="text/css" href="/custom/bbus/views/bbus.css">
+<script src="/custom/bbus/js/bbus.js"></script>;

+ 20 - 9
compta/paiement.php

@@ -36,6 +36,8 @@ require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
 require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
+require_once DOL_DOCUMENT_ROOT.'/custom/bbus/class/bbticketinvoiceprinting.class.php';
+require_once DOL_DOCUMENT_ROOT.'/custom/bbus/class/bbticket.class.php';
 
 // Load translation files required by the page
 $langs->loadLangs(array('companies', 'bills', 'banks', 'multicurrency'));
@@ -228,7 +230,7 @@ if (empty($reshook)) {
 		foreach ($amounts as $key => $value) {	// How payment is dispatched
 			$tmpinvoice = new Facture($db);
 			$tmpinvoice->fetch($key);
-			if ($tmpinvoice->type == Facture::TYPE_CREDIT_NOTE) {
+			if ($tmpinvoice->type == Facture::TYPE_CREDIT_NOTE || $tmpinvoice->type == Facture::TYPE_RECEIPT_CREDIT_NOTE) {
 				$newvalue = price2num($value, 'MT');
 				$amounts[$key] = - abs($newvalue);
 			}
@@ -238,7 +240,7 @@ if (empty($reshook)) {
 		foreach ($multicurrency_amounts as $key => $value) {	// How payment is dispatched
 			$tmpinvoice = new Facture($db);
 			$tmpinvoice->fetch($key);
-			if ($tmpinvoice->type == Facture::TYPE_CREDIT_NOTE) {
+			if ($tmpinvoice->type == Facture::TYPE_CREDIT_NOTE || $tmpinvoice->type == Facture::TYPE_RECEIPT_CREDIT_NOTE) {
 				$newvalue = price2num($value, 'MT');
 				$multicurrency_amounts[$key] = - abs($newvalue);
 			}
@@ -280,6 +282,9 @@ if (empty($reshook)) {
 			if (GETPOST('type') == Facture::TYPE_CREDIT_NOTE) {
 				$label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
 			}
+			if (GETPOST('type') == Facture::TYPE_RECEIPT_CREDIT_NOTE) {
+				$label = '(CustomerReceiptPaymentBack)'; // Refund of a credit note
+			}
 			$result = $paiement->addPaymentToBank($user, 'payment', $label, GETPOST('accountid', 'int'), GETPOST('chqemetteur'), GETPOST('chqbank'));
 			if ($result < 0) {
 				setEventMessages($paiement->error, $paiement->errors, 'errors');
@@ -335,10 +340,10 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie
 		$facture->fetch_thirdparty();
 
 		$title = '';
-		if ($facture->type != Facture::TYPE_CREDIT_NOTE) {
+		if ($facture->type != Facture::TYPE_CREDIT_NOTE && $facture->type != Facture::TYPE_RECEIPT_CREDIT_NOTE) {
 			$title .= $langs->trans("EnterPaymentReceivedFromCustomer");
 		}
-		if ($facture->type == Facture::TYPE_CREDIT_NOTE) {
+		if ($facture->type == Facture::TYPE_CREDIT_NOTE || $facture->type == Facture::TYPE_RECEIPT_CREDIT_NOTE) {
 			$title .= $langs->trans("EnterPaymentDueToCustomer");
 		}
 		print load_fiche_titre($title);
@@ -558,10 +563,10 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie
 		}
 		$sql .= ') AND f.paye = 0';
 		$sql .= ' AND f.fk_statut = 1'; // Statut=0 => not validated, Statut=2 => canceled
-		if ($facture->type != Facture::TYPE_CREDIT_NOTE) {
-			$sql .= ' AND type IN (0,1,3,5)'; // Standard invoice, replacement, deposit, situation
+		if ($facture->type != Facture::TYPE_CREDIT_NOTE  && $facture->type != Facture::TYPE_RECEIPT_CREDIT_NOTE) {
+			$sql .= ' AND type IN (0,1,3,5,7)'; // Standard invoice, replacement, deposit, situation
 		} else {
-			$sql .= ' AND type = 2'; // If paying back a credit note, we show all credit notes
+			$sql .= ' AND type IN (2,8)'; // If paying back a credit note, we show all credit notes
 		}
 		// Sort invoices by date and serial number: the older one comes first
 		$sql .= ' ORDER BY f.datef ASC, f.ref ASC';
@@ -628,7 +633,10 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie
 					if ($facture->type == Facture::TYPE_CREDIT_NOTE) {
 						$sign = -1;
 					}
-
+					if ($facture->type == Facture::TYPE_RECEIPT_CREDIT_NOTE) {
+						$sign = -1;
+					}
+					
 					$soc = new Societe($db);
 					$soc->fetch($objp->socid);
 
@@ -852,8 +860,11 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie
 			if ($facture->type == Facture::TYPE_CREDIT_NOTE) {
 				$checkboxlabel = $langs->trans("ClosePaidCreditNotesAutomatically");
 			}
+			if ($facture->type == Facture::TYPE_RECEIPT_CREDIT_NOTE) {
+				$checkboxlabel = $langs->trans("ClosePaidReceiptCreditNotesAutomatically");
+			}
 			$buttontitle = $langs->trans('ToMakePayment');
-			if ($facture->type == Facture::TYPE_CREDIT_NOTE) {
+			if ($facture->type == Facture::TYPE_CREDIT_NOTE || $facture->type == Facture::TYPE_RECEIPT_CREDIT_NOTE) {
 				$buttontitle = $langs->trans('ToMakePaymentBack');
 			}
 

+ 3 - 1
compta/paiement/class/paiement.class.php

@@ -377,8 +377,10 @@ class Paiement extends CommonObject
 								Facture::TYPE_STANDARD,
 								Facture::TYPE_REPLACEMENT,
 								Facture::TYPE_CREDIT_NOTE,
+								Facture::TYPE_RECEIPT_CREDIT_NOTE,
 								Facture::TYPE_DEPOSIT,
-								Facture::TYPE_SITUATION
+								Facture::TYPE_SITUATION,
+								Facture::TYPE_RECEIPT
 							);
 
 							if (!in_array($invoice->type, $affected_types)) {

+ 1 - 1
core/ajax/constantonoff.php

@@ -40,7 +40,7 @@ if (!defined('NOREQUIRETRAN')) {
 	define('NOREQUIRETRAN', '1');
 }
 if (!defined('CSRFCHECK_WITH_TOKEN')) {
-	define('CSRFCHECK_WITH_TOKEN', '1'); // Token is required even in GET mode
+	define('CSRFCHECK_WITH_TOKEN', '0'); // Token is required even in GET mode
 }
 
 // Load Dolibarr environment

+ 17 - 1
core/class/commoninvoice.class.php

@@ -64,6 +64,16 @@ abstract class CommonInvoice extends CommonObject
 	 */
 	const TYPE_SITUATION = 5;
 
+	/**
+	 * Receipt
+	 */
+	const TYPE_RECEIPT = 7;
+	
+	/**
+	 * Receipt credit note
+	 */
+	const TYPE_RECEIPT_CREDIT_NOTE = 8;
+
 	/**
 	 * Draft status
 	 */
@@ -534,12 +544,18 @@ abstract class CommonInvoice extends CommonObject
 		if ($this->type == CommonInvoice::TYPE_STANDARD) {
 			$labellong = "InvoiceStandard";
 			$labelshort = "InvoiceStandardShort";
+		} elseif ($this->type == CommonInvoice::TYPE_RECEIPT) {
+			$labellong = "InvoiceReceipt";
+			$labelshort = "InvoiceReceiptShort";
 		} elseif ($this->type == CommonInvoice::TYPE_REPLACEMENT) {
 			$labellong = "InvoiceReplacement";
 			$labelshort = "InvoiceReplacementShort";
 		} elseif ($this->type == CommonInvoice::TYPE_CREDIT_NOTE) {
 			$labellong = "InvoiceAvoir";
 			$labelshort = "CreditNote";
+		} elseif ($this->type == CommonInvoice::TYPE_RECEIPT_CREDIT_NOTE) {
+			$labellong = "ReceiptInvoiceAvoir";
+			$labelshort = "ReceiptInvoice";
 		} elseif ($this->type == CommonInvoice::TYPE_DEPOSIT) {
 			$labellong = "InvoiceDeposit";
 			$labelshort = "Deposit";
@@ -626,7 +642,7 @@ abstract class CommonInvoice extends CommonObject
 		} else {
 			$statusType = 'status6';
 
-			if ($type == self::TYPE_CREDIT_NOTE) {
+			if ($type == self::TYPE_CREDIT_NOTE || $type == self::TYPE_RECEIPT_CREDIT_NOTE) {
 				$labelStatus = $langs->transnoentitiesnoconv('BillStatusPaidBackOrConverted'); // credit note
 				$labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusPaidBackOrConverted'); // credit note
 			} elseif ($type == self::TYPE_DEPOSIT) {

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 212 - 210
core/class/commonobject.class.php


+ 8 - 2
core/class/html.form.class.php

@@ -7853,7 +7853,9 @@ class Form
 				}
 			}
 		}
-
+		if($conf->global->PACKAGEHISTORY_FOR_ALL_USERS && $user->array_options['options_user_category'] == 6 && $htmlname == 'search_user_id'){
+			$sql .= " INNER JOIN ".$this->db->prefix().$objecttmp->table_element."_extrafields as te ON te.fk_object = t.rowid";
+		}
 		// Add where from hooks
 		$parameters = array(
 			'object' => $objecttmp,
@@ -7869,7 +7871,11 @@ class Form
 			$sql .= " WHERE 1=1";
 			if (isset($objecttmp->ismultientitymanaged)) {
 				if ($objecttmp->ismultientitymanaged == 1) {
-					$sql .= " AND t.entity IN (".getEntity($objecttmp->table_element).")";
+					if($conf->global->PACKAGEHISTORY_FOR_ALL_USERS && $user->array_options['options_user_category'] == 6 && $htmlname == 'search_user_id'){
+						$sql .= " AND t.entity IS NOT NULL AND te.user_category != '6'";
+					} else {
+						$sql .= " AND t.entity IN (".getEntity($objecttmp->table_element).")";
+					}
 				}
 				if (!is_numeric($objecttmp->ismultientitymanaged)) {
 					$sql .= " AND parenttable.entity = t.".$tmparray[0];

+ 3 - 0
core/lib/files.lib.php

@@ -3119,6 +3119,9 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity,
 				$accessallowed = 1;
 			}
 			$original_file = $conf->{$reg[1]}->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
+		} elseif ($modulepart == 'settlements') {
+			$path = '/var/www/html/documents/settlements';
+			$original_file = $path.'/'.$original_file;
 		} else {
 			if (empty($conf->$modulepart->dir_output)) {	// modulepart not supported
 				dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.'). The module for this modulepart value may not be activated.');

+ 2 - 0
core/lib/functions.lib.php

@@ -2308,6 +2308,8 @@ function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldi
 		}
 		$tmptxt = $object->getLibStatut(5);
 		$morehtmlstatus .= $tmptxt; // No status on task
+	} elseif ($object->element == 'checkoutclosure') {
+		
 	} elseif (method_exists($object, 'getLibStatut')) { // Generic case
 		$tmptxt = $object->getLibStatut(6);
 		if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {

+ 2 - 1
core/lib/multicurrency.lib.php

@@ -40,7 +40,8 @@ function multicurrencyAdminPrepareHead()
 	$head[$h][2] = 'settings';
 	$h++;
 
-	$head[$h][0] = dol_buildpath("/multicurrency/multicurrency_rate.php", 1);
+	$head[$h][0] = dol_buildpath("/custom/bbus/multicurrency_rate.php", 1);
+	//$head[$h][0] = dol_buildpath("/multicurrency/multicurrency_rate.php", 1);
 	$head[$h][1] = $langs->trans("TabTitleMulticurrencyRate");
 	$head[$h][2] = 'ratelist';
 	$h++;

+ 35 - 1
core/lib/pdf.lib.php

@@ -1139,7 +1139,7 @@ function pdf_pagefoot(&$pdf, $outputlangs, $paramfreetext, $fromcompany, $marge_
 	}
 	// IntraCommunautary VAT
 	if (!empty($fromcompany->tva_intra)  && $fromcompany->tva_intra != '') {
-		$line4 .= ($line4 ? " - " : "").$outputlangs->transnoentities("VATIntraShort").": ".$outputlangs->convToOutputCharset($fromcompany->tva_intra);
+		//$line4 .= ($line4 ? " - " : "").$outputlangs->transnoentities("VATIntraShort").": ".$outputlangs->convToOutputCharset($fromcompany->tva_intra);
 	}
 
 	$pdf->SetFont('', '', 7);
@@ -1865,6 +1865,40 @@ function pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails = 0)
 	return $result;
 }
 
+function pdf_getlinevatprice($object, $i, $outputlangs, $hidedetails = 0)
+{
+	global $conf, $hookmanager;
+
+	$sign = 1;
+	if (isset($object->type) && $object->type == 2 && !empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) {
+		$sign = -1;
+	}
+
+	$result = '';
+	$reshook = 0;
+	//if (is_object($hookmanager) && ( (isset($object->lines[$i]->product_type) && $object->lines[$i]->product_type == 9 && ! empty($object->lines[$i]->special_code)) || ! empty($object->lines[$i]->fk_parent_line) ) )
+	if (is_object($hookmanager)) {   // Old code is commented on preceding line. Reproduct this test in the pdf_xxx function if you don't want your hook to run
+		$special_code = $object->lines[$i]->special_code;
+		if (!empty($object->lines[$i]->fk_parent_line)) {
+			$special_code = $object->getSpecialCode($object->lines[$i]->fk_parent_line);
+		}
+		$parameters = array('i'=>$i, 'outputlangs'=>'ÁFA', 'hidedetails'=>$hidedetails, 'special_code'=>$special_code);
+		$action = '';
+		$reshook = $hookmanager->executeHooks('pdf_getlineupexcltax', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+
+		if (!empty($hookmanager->resPrint)) {
+			$result .= $hookmanager->resPrint;
+		}
+	}
+	if (empty($reshook)) {
+		if (empty($hidedetails) || $hidedetails > 1) {
+			$subprice = $object->lines[$i]->multicurrency_total_tva;
+			$result .= price($subprice, 0, $outputlangs);
+		}
+	}
+	return $result;
+}
+
 /**
  *	Return line unit price excluding tax
  *

+ 5 - 5
core/lib/price.lib.php

@@ -413,11 +413,11 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt
 			$conf->global->MAIN_MAX_DECIMALS_TOT = $savMAIN_MAX_DECIMALS_TOT;
 			$conf->global->MAIN_ROUNDING_RULE_TOT = $savMAIN_ROUNDING_RULE_TOT;
 		}
-
-		$result[16] = $newresult[0];
-		$result[17] = $newresult[1];
-		$result[18] = $newresult[2];
-		$result[19] = $newresult[3];
+		
+		$result[16] = $result[0];
+		$result[17] = $result[1];
+		$result[18] = $result[2];
+		$result[19] = $result[3];
 		$result[20] = $newresult[4];
 		$result[21] = $newresult[5];
 		$result[22] = $newresult[6];

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 357 - 130
core/modules/facture/doc/pdf_crabe.modules.php


+ 2180 - 0
core/modules/facture/doc/pdf_crabe_new_printinginvoice.modules.php

@@ -0,0 +1,2180 @@
+<?php
+/* Copyright (C) 2004-2014	Laurent Destailleur	<eldy@users.sourceforge.net>
+ * Copyright (C) 2005-2012	Regis Houssin		<regis.houssin@inodbox.com>
+ * Copyright (C) 2008		Raphael Bertrand		<raphael.bertrand@resultic.fr>
+ * Copyright (C) 2010-2014	Juanjo Menent		<jmenent@2byte.es>
+ * Copyright (C) 2012		Christophe Battarel	<christophe.battarel@altairis.fr>
+ * Copyright (C) 2012		Cédric Salvador		<csalvador@gpcsolutions.fr>
+ * Copyright (C) 2012-2014	Raphaël Doursenaud	<rdoursenaud@gpcsolutions.fr>
+ * Copyright (C) 2015		Marcos García		<marcosgdf@gmail.com>
+ * Copyright (C) 2017-2018	Ferran Marcet		<fmarcet@2byte.es>
+ * Copyright (C) 2018-2020  Frédéric France     <frederic.france@netlogic.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ * or see https://www.gnu.org/
+ */
+
+/**
+ *	\file       htdocs/core/modules/facture/doc/pdf_crabe.modules.php
+ *	\ingroup    facture
+ *	\brief      File of class to generate customers invoices from crabe model
+ */
+
+require_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php';
+
+
+/**
+ *	Class to generate the customer invoice PDF with template Crabe
+ */
+class pdf_crabe_new_printinginvoice extends ModelePDFFactures
+{
+	 /**
+	  * @var DoliDb Database handler
+	  */
+	public $db;
+
+	/**
+	 * @var string model name
+	 */
+	public $name;
+
+	/**
+	 * @var string model description (short text)
+	 */
+	public $description;
+
+	/**
+	 * @var int 	Save the name of generated file as the main doc when generating a doc with this template
+	 */
+	public $update_main_doc_field;
+
+	/**
+	 * @var string document type
+	 */
+	public $type;
+
+	/**
+	 * @var array Minimum version of PHP required by module.
+	 * e.g.: PHP ≥ 5.6 = array(5, 6)
+	 */
+	public $phpmin = array(5, 6);
+
+	/**
+	 * Dolibarr version of the loaded document
+	 * @var string
+	 */
+	public $version = 'dolibarr';
+
+	/**
+	 * @var int page_largeur
+	 */
+	public $page_largeur;
+
+	/**
+	 * @var int page_hauteur
+	 */
+	public $page_hauteur;
+
+	/**
+	 * @var array format
+	 */
+	public $format;
+
+	/**
+	 * @var int marge_gauche
+	 */
+	public $marge_gauche;
+
+	/**
+	 * @var int marge_droite
+	 */
+	public $marge_droite;
+
+	/**
+	 * @var int marge_haute
+	 */
+	public $marge_haute;
+
+	/**
+	 * @var int marge_basse
+	 */
+	public $marge_basse;
+
+	/**
+	 * Issuer
+	 * @var Societe Object that emits
+	 */
+	public $emetteur;
+
+	/**
+	 * @var bool Situation invoice type
+	 */
+	public $situationinvoice;
+
+	/**
+	 * @var float X position for the situation progress column
+	 */
+	public $posxprogress;
+
+
+	/**
+	 *	Constructor
+	 *
+	 *  @param		DoliDB		$db      Database handler
+	 */
+	public function __construct($db)
+	{
+		global $conf, $langs, $mysoc;
+
+		// Translations
+		$langs->loadLangs(array("main", "bills"));
+
+		$this->db = $db;
+		$this->name = "crabe";
+		$this->description = $langs->trans('PDFCrabeDescription');
+		$this->update_main_doc_field = 1; // Save the name of generated file as the main doc when generating a doc with this template
+
+		// Dimension page
+		$this->type = 'pdf';
+		$formatarray = pdf_getFormat();
+		$this->page_largeur = $formatarray['width'];
+		$this->page_hauteur = $formatarray['height'];
+		$this->format = array($this->page_largeur, $this->page_hauteur);
+		$this->marge_gauche = isset($conf->global->MAIN_PDF_MARGIN_LEFT) ? $conf->global->MAIN_PDF_MARGIN_LEFT : 10;
+		$this->marge_droite = isset($conf->global->MAIN_PDF_MARGIN_RIGHT) ? $conf->global->MAIN_PDF_MARGIN_RIGHT : 10;
+		$this->marge_haute = isset($conf->global->MAIN_PDF_MARGIN_TOP) ? $conf->global->MAIN_PDF_MARGIN_TOP : 10;
+		$this->marge_basse = isset($conf->global->MAIN_PDF_MARGIN_BOTTOM) ? $conf->global->MAIN_PDF_MARGIN_BOTTOM : 10;
+
+		$this->option_logo = 1; // Display logo
+		$this->option_tva = 1; // Manage the vat option FACTURE_TVAOPTION
+		$this->option_modereg = 1; // Display payment mode
+		$this->option_condreg = 1; // Display payment terms
+		$this->option_multilang = 1; // Available in several languages
+		$this->option_escompte = 1; // Displays if there has been a discount
+		$this->option_credit_note = 1; // Support credit notes
+		$this->option_freetext = 1; // Support add of a personalised text
+		$this->option_draft_watermark = 1; // Support add of a watermark on drafts
+		$this->watermark = '';
+
+		// Get source company
+		$this->emetteur = $mysoc;
+		if (empty($this->emetteur->country_code)) {
+			$this->emetteur->country_code = substr($langs->defaultlang, -2); // By default, if was not defined
+		}
+
+		// Define position of columns
+		$this->posxdesc = $this->marge_gauche + 1;
+		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
+			$this->posxtva = 101;
+			$this->posxup = 118;
+			$this->posxqty = 135;
+			$this->posxunit = 151;
+		} else {
+			$this->posxtva = 110;
+			$this->posxup = 126;
+			$this->posxqty = 145;
+			$this->posxunit = 162;
+		}
+		$this->posxprogress = 151; // Only displayed for situation invoices
+		$this->posxdiscount = 162;
+		$this->posxprogress = 174;
+		$this->postotalht = 174;
+		if (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) || !empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN)) {
+			$this->posxtva = $this->posxup;
+		}
+		$this->posxpicture = $this->posxtva - (empty($conf->global->MAIN_DOCUMENTS_WITH_PICTURE_WIDTH) ? 20 : $conf->global->MAIN_DOCUMENTS_WITH_PICTURE_WIDTH); // width of images
+		if ($this->page_largeur < 210) { // To work with US executive format
+			$this->posxpicture -= 20;
+			$this->posxtva -= 20;
+			$this->posxup -= 20;
+			$this->posxqty -= 20;
+			$this->posxunit -= 20;
+			$this->posxdiscount -= 20;
+			$this->posxprogress -= 20;
+			$this->postotalht -= 20;
+		}
+
+		$this->tva = array();
+		$this->tva_array = array();
+		$this->localtax1 = array();
+		$this->localtax2 = array();
+		$this->atleastoneratenotnull = 0;
+		$this->atleastonediscount = 0;
+		$this->situationinvoice = false;
+	}
+
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	/**
+	 *  Function to build pdf onto disk
+	 *
+	 *  @param		Facture		$object				Object to generate
+	 *  @param		Translate	$outputlangs		Lang output object
+	 *  @param		string		$srctemplatepath	Full path of source filename for generator using a template file
+	 *  @param		int			$hidedetails		Do not show line details
+	 *  @param		int			$hidedesc			Do not show desc
+	 *  @param		int			$hideref			Do not show ref
+	 *  @return     int         	    			1=OK, 0=KO
+	 */
+	public function write_file($object, $timestamp,  $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0)
+	{
+		// phpcs:enable
+		global $user, $langs, $conf, $mysoc, $hookmanager, $nblines;
+
+		dol_syslog("write_file outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
+
+		if (!is_object($outputlangs)) {
+			$outputlangs = $langs;
+		}
+		// For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO
+		if (!empty($conf->global->MAIN_USE_FPDF)) {
+			$outputlangs->charset_output = 'ISO-8859-1';
+		}
+
+		// Load translation files required by the page
+		$outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies"));
+
+		// Show Draft Watermark
+		if ($object->statut == $object::STATUS_DRAFT && (!empty($conf->global->FACTURE_DRAFT_WATERMARK))) {
+			$this->watermark = $conf->global->FACTURE_DRAFT_WATERMARK;
+		}
+
+		global $outputlangsbis;
+		$outputlangsbis = null;
+		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && $outputlangs->defaultlang != $conf->global->PDF_USE_ALSO_LANGUAGE_CODE) {
+			$outputlangsbis = new Translate('', $conf);
+			$outputlangsbis->setDefaultLang($conf->global->PDF_USE_ALSO_LANGUAGE_CODE);
+			$outputlangsbis->loadLangs(array("main", "bills", "products", "dict", "companies"));
+		}
+
+		$nblines = count($object->lines);
+
+		// Loop on each lines to detect if there is at least one image to show
+		$realpatharray = array();
+		if (!empty($conf->global->MAIN_GENERATE_INVOICES_WITH_PICTURE)) {
+			for ($i = 0; $i < $nblines; $i++) {
+				if (empty($object->lines[$i]->fk_product)) {
+					continue;
+				}
+
+				$objphoto = new Product($this->db);
+				$objphoto->fetch($object->lines[$i]->fk_product);
+
+				$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;
+
+				$realpath = '';
+				foreach ($objphoto->liste_photos($dir, 1) as $key => $obj) {
+					$filename = $obj['photo'];
+					//if ($obj['photo_vignette']) $filename='thumbs/'.$obj['photo_vignette'];
+					$realpath = $dir.$filename;
+					break;
+				}
+
+				if ($realpath) {
+					$realpatharray[$i] = $realpath;
+				}
+			}
+		}
+		if (count($realpatharray) == 0) {
+			$this->posxpicture = $this->posxtva;
+		}
+
+		if ($conf->facture->dir_output) {
+			$object->fetch_thirdparty();
+
+			$deja_regle = $object->getSommePaiement((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0);
+			$amount_credit_notes_included = $object->getSumCreditNotesUsed((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0);
+			$amount_deposits_included = $object->getSumDepositsUsed((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0);
+
+			// Definition of $dir and $file
+			if ($object->specimen) {
+				$dir = empty($conf->facture->multidir_output[$conf->entity]) ? $conf->facture->dir_output : $conf->facture->multidir_output[$conf->entity];
+				$file = $dir."/SPECIMEN.pdf";
+			} else {
+				$objectref = dol_sanitizeFileName($object->ref);
+				$dir = (empty($conf->facture->multidir_output[$conf->entity]) ? $conf->facture->dir_output : $conf->facture->multidir_output[$conf->entity])."/".$objectref;
+				$file = $dir."/".$objectref."_". $timestamp .".pdf";
+			}
+			if (!file_exists($dir)) {
+				if (dol_mkdir($dir) < 0) {
+					$this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
+					return 0;
+				}
+			}
+
+			if (file_exists($dir)) {
+				// Add pdfgeneration hook
+				if (!is_object($hookmanager)) {
+					include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
+					$hookmanager = new HookManager($this->db);
+				}
+				$hookmanager->initHooks(array('pdfgeneration'));
+				$parameters = array('file'=>$file, 'object'=>$object, 'outputlangs'=>$outputlangs);
+				global $action;
+				$reshook = $hookmanager->executeHooks('beforePDFCreation', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+
+				// Set nblines with the new facture lines content after hook
+				$nblines = count($object->lines);
+				$nbpayments = count($object->getListOfPayments());
+
+				// Create pdf instance
+				$pdf = pdf_getInstance($this->format);
+				$default_font_size = pdf_getPDFFontSize($outputlangs); // Must be after pdf_getInstance
+				$pdf->SetAutoPageBreak(1, 0);
+
+				$heightforinfotot = 50 + (4 * $nbpayments); // Height reserved to output the info and total part and payment part
+				if ($heightforinfotot > 220) {
+					$heightforinfotot = 220;
+				}
+				$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 + 8; // Height reserved to output the footer (value include bottom margin)
+				if (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS)) {
+					$heightforfooter += 6;
+				}
+
+				if (class_exists('TCPDF')) {
+					$pdf->setPrintHeader(false);
+					$pdf->setPrintFooter(false);
+				}
+				$pdf->SetFont(pdf_getPDFFont($outputlangs));
+
+				// Set path to the background PDF File
+				if (!empty($conf->global->MAIN_ADD_PDF_BACKGROUND)) {
+					$logodir = $conf->mycompany->dir_output;
+					if (!empty($conf->mycompany->multidir_output[$object->entity])) {
+						$logodir = $conf->mycompany->multidir_output[$object->entity];
+					}
+					$pagecount = $pdf->setSourceFile($logodir.'/'.$conf->global->MAIN_ADD_PDF_BACKGROUND);
+					$tplidx = $pdf->importPage(1);
+				}
+
+				$pdf->Open();
+				$pagenb = 0;
+				$pdf->SetDrawColor(128, 128, 128);
+
+				$pdf->SetTitle($outputlangs->convToOutputCharset($object->ref));
+				$pdf->SetSubject($outputlangs->transnoentities("PdfInvoiceTitle"));
+				$pdf->SetCreator("Dolibarr ".DOL_VERSION);
+				$pdf->SetAuthor($mysoc->name.($user->id > 0 ? ' - '.$outputlangs->convToOutputCharset($user->getFullName($outputlangs)) : ''));
+				$pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref)." ".$outputlangs->transnoentities("PdfInvoiceTitle")." ".$outputlangs->convToOutputCharset($object->thirdparty->name));
+				if (!empty($conf->global->MAIN_DISABLE_PDF_COMPRESSION)) {
+					$pdf->SetCompression(false);
+				}
+
+				// Set certificate
+				$cert = empty($user->conf->CERTIFICATE_CRT) ? '' : $user->conf->CERTIFICATE_CRT;
+				$certprivate = empty($user->conf->CERTIFICATE_CRT_PRIVATE) ? '' : $user->conf->CERTIFICATE_CRT_PRIVATE;
+				// If user has no certificate, we try to take the company one
+				if (!$cert) {
+					$cert = empty($conf->global->CERTIFICATE_CRT) ? '' : $conf->global->CERTIFICATE_CRT;
+				}
+				if (!$certprivate) {
+					$certprivate = empty($conf->global->CERTIFICATE_CRT_PRIVATE) ? '' : $conf->global->CERTIFICATE_CRT_PRIVATE;
+				}
+				// If a certificate is found
+				if ($cert) {
+					$info = array(
+						'Name' => $this->emetteur->name,
+						'Location' => getCountry($this->emetteur->country_code, 0),
+						'Reason' => 'INVOICE',
+						'ContactInfo' => $this->emetteur->email
+					);
+					$pdf->setSignature($cert, $certprivate, $this->emetteur->name, '', 2, $info);
+				}
+
+				$pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right
+
+				// Set $this->atleastonediscount if you have at least one discount
+				for ($i = 0; $i < $nblines; $i++) {
+					if ($object->lines[$i]->remise_percent) {
+						$this->atleastonediscount++;
+					}
+				}
+				if (empty($this->atleastonediscount)) {    // retrieve space not used by discount
+					$delta = ($this->posxprogress - $this->posxdiscount);
+					$this->posxpicture += $delta;
+					$this->posxtva += $delta;
+					$this->posxup += $delta;
+					$this->posxqty += $delta;
+					$this->posxunit += $delta;
+					$this->posxdiscount += $delta;
+					// post of fields after are not modified, stay at same position
+				}
+
+				$progress_width = 0;
+				// Situation invoice handling
+				if ($object->situation_cycle_ref && empty($conf->global->MAIN_PDF_HIDE_SITUATION)) {
+					$this->situationinvoice = true;
+					$progress_width = 10;
+					$this->posxpicture -= $progress_width;
+					$this->posxtva -= $progress_width;
+					$this->posxup -= $progress_width;
+					$this->posxqty -= $progress_width;
+					$this->posxunit -= $progress_width;
+					$this->posxdiscount -= $progress_width;
+					$this->posxprogress -= $progress_width;
+				}
+
+				// New page
+				$pdf->AddPage();
+				if (!empty($tplidx)) {
+					$pdf->useTemplate($tplidx);
+				}
+				$pagenb++;
+
+				// Output header (logo, ref and address blocks). This is first call for first page.
+				$top_shift = $this->_pagehead($pdf, $object, 1, $outputlangs);
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->MultiCell(0, 3, ''); // Set interline to 3
+				$pdf->SetTextColor(0, 0, 0);
+
+				// $pdf->GetY() here can't be used. It is bottom of the second addresse box but first one may be higher
+
+				// $tab_top is y where we must continue content (90 = 42 + 48: 42 is height of logo and ref, 48 is address blocks)
+				$tab_top = 90 + $top_shift;		// top_shift is an addition for linked objects or addons (0 in most cases)
+				$tab_top_newpage = (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD) ? 42 + $top_shift : 10);
+
+				// You can add more thing under header here, if you increase $extra_under_address_shift too.
+				$extra_under_address_shift = 0;
+				$qrcodestring = '';
+				if (! empty($conf->global->INVOICE_ADD_ZATCA_QR_CODE)) {
+					$qrcodestring = $object->buildZATCAQRString();
+				} elseif (! empty($conf->global->INVOICE_ADD_SWISS_QR_CODE)) {
+					$qrcodestring = $object->buildSwitzerlandQRString();
+				}
+				
+				$tsForQR = time();
+				$sql = "SELECT * FROM " . $this->db->prefix() . "bbus_bbticket WHERE fk_facture = '".$object->id."'";
+				$res = $this->db->query($sql);
+				$row = pg_fetch_assoc($res);
+
+				$checkSQL = "SELECT * FROM " . $this->db->prefix() . "bbus_bbticketinvoiceprinting WHERE fk_facture = '".$object->id."'";
+				$resCheck = $this->db->query($checkSQL);
+				$rowCheck = pg_fetch_assoc($resCheck);
+
+				if ($rowCheck['fk_facture'] == '' && intval($object->statut) === 2 && intval($object->array_options['options_app_facture']) === 0) {
+					if ($row['bundle_id'] == $row['ticket_id']) {
+						$this->db->begin();
+						$ticketPrintSQL = "INSERT INTO " . $this->db->prefix() . "bbus_bbticketinvoiceprinting 
+						(fk_user_creat,status,fk_facture,fk_user_api_key,ticket_id,printing_date,printing_date_timestamp,product_id) VALUES 
+						('".$row['fk_user_creat']."', '0', '".$row['fk_facture']."', 'OajW1ZUiqN7P', '".$row['rowid']."', '".date("Y-m-d H:i:s",$tsForQR)."', '".$tsForQR."', '".$row['ticket_id']."');";
+						if ($this->db->query($ticketPrintSQL))	{
+							$this->db->commit();
+						} else {
+							$errorMsg = $this->db->lasterror;
+							$this->db->rollback();
+						}
+					}
+					else {
+						$all = pg_fetch_all($res);
+						foreach ($all as $row) {
+							$this->db->begin();
+							$ticketPrintSQL1 = "INSERT INTO " . $this->db->prefix() . "bbus_bbticketinvoiceprinting 
+							(fk_user_creat,status,fk_facture,fk_user_api_key,ticket_id,printing_date,printing_date_timestamp,product_id) VALUES  
+							('".$row['fk_user_creat']."', '0', '".$row['fk_facture']."', 'OajW1ZUiqN7P', '".$row['rowid']."', '".date("Y-m-d H:i:s",$tsForQR)."', '".$tsForQR."', '".$row['ticket_id']."');";
+							if ($this->db->query($ticketPrintSQL1))	{
+								$this->db->commit();
+							} else {
+								$errorMsg = $this->db->lasterror;
+								$this->db->rollback();
+							}	
+						}
+					}
+				}
+				$qrcodestring = $object->ref.'_'.$timestamp;
+				if ($qrcodestring) {
+					$qrcodecolor = array('25', '25', '25');
+					// set style for QR-code
+					$styleQr = array(
+						'border' => false,
+						'padding' => 0,
+						'fgcolor' => $qrcodecolor,
+						'bgcolor' => false, //array(255,255,255)
+						'module_width' => 1, // width of a single module in points
+						'module_height' => 1 // height of a single module in points
+					);
+					$pdf->write2DBarcode($qrcodestring, 'QRCODE,M', $this->marge_gauche, $tab_top - 5, 25, 25, $styleQr, 'N');
+					$extra_under_address_shift += 25;
+				}
+
+				// Call hook printUnderHeaderPDFline
+				$parameters = array(
+					'object' => $object,
+					'i' => $i,
+					'pdf' =>& $pdf,
+					'outputlangs' => $outputlangs,
+					'hidedetails' => $hidedetails
+				);
+				$reshook = $hookmanager->executeHooks('printUnderHeaderPDFline', $parameters, $this); // Note that $object may have been modified by hook
+				if (!empty($hookmanager->resArray['extra_under_address_shift'])) {
+					$extra_under_address_shift += $hookmanager->resArray['extra_under_header_shift'];
+				}
+
+				$tab_top += $extra_under_address_shift;
+				$tab_top_newpage += 0;
+
+				// Incoterm
+				$height_incoterms = 0;
+				if (!empty($conf->incoterm->enabled)) {
+					$desc_incoterms = $object->getIncotermsForPDF();
+					if ($desc_incoterms) {
+						$tab_top -= 2;
+
+						$pdf->SetFont('', '', $default_font_size - 1);
+						$pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top - 1, dol_htmlentitiesbr($desc_incoterms), 0, 1);
+						$nexY = $pdf->GetY();
+						$height_incoterms = $nexY - $tab_top;
+
+						// Rect takes a length in 3rd parameter
+						$pdf->SetDrawColor(192, 192, 192);
+						$pdf->Rect($this->marge_gauche, $tab_top - 1, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $height_incoterms + 1);
+
+						$tab_top = $nexY + 6;
+					}
+				}
+
+				// Display notes
+				$notetoshow = empty($object->note_public) ? '' : $object->note_public;
+				if (!empty($conf->global->MAIN_ADD_SALE_REP_SIGNATURE_IN_NOTE)) {
+					// Get first sale rep
+					if (is_object($object->thirdparty)) {
+						$salereparray = $object->thirdparty->getSalesRepresentatives($user);
+						$salerepobj = new User($this->db);
+						$salerepobj->fetch($salereparray[0]['id']);
+						if (!empty($salerepobj->signature)) {
+							$notetoshow = dol_concatdesc($notetoshow, $salerepobj->signature);
+						}
+					}
+				}
+				// Extrafields in note
+				$extranote = $this->getExtrafieldsInHtml($object, $outputlangs);
+				if (!empty($extranote)) {
+					$notetoshow = dol_concatdesc($notetoshow, $extranote);
+				}
+				if ($notetoshow) {
+					$tab_top -= 2;
+
+					$substitutionarray = pdf_getSubstitutionArray($outputlangs, null, $object);
+					complete_substitutions_array($substitutionarray, $outputlangs, $object);
+
+					$notetoshow = make_substitutions($notetoshow, $substitutionarray, $outputlangs);
+					$notetoshow = convertBackOfficeMediasLinksToPublicLinks($notetoshow);
+
+					$pdf->SetFont('', '', $default_font_size - 1);
+					$pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top - 1, dol_htmlentitiesbr($notetoshow), 0, 1);
+					$nexY = $pdf->GetY();
+					$height_note = $nexY - $tab_top;
+
+					// Rect takes a length in 3rd parameter
+					$pdf->SetDrawColor(192, 192, 192);
+					$pdf->Rect($this->marge_gauche, $tab_top - 1, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $height_note + 1);
+
+					$tab_top = $nexY + 6;
+				}
+
+				$iniY = $tab_top + 7;
+				$curY = $tab_top + 7;
+				$nexY = $tab_top + 7;
+
+				// Loop on each lines
+				for ($i = 0; $i < $nblines; $i++) {
+					$curY = $nexY;
+					$pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
+					$pdf->SetTextColor(0, 0, 0);
+
+					// Define size of image if we need it
+					$imglinesize = array();
+					if (!empty($realpatharray[$i])) {
+						$imglinesize = pdf_getSizeForImage($realpatharray[$i]);
+					}
+
+					$pdf->setTopMargin($tab_top_newpage);
+					$pdf->setPageOrientation('', 1, $heightforfooter + $heightforfreetext + $heightforinfotot); // The only function to edit the bottom margin of current page to set it.
+					$pageposbefore = $pdf->getPage();
+
+					$showpricebeforepagebreak = 1;
+					$posYAfterImage = 0;
+					$posYAfterDescription = 0;
+
+					// We start with Photo of product line
+					if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imglinesize['height']) > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforinfotot))) {	// If photo too high, we moved completely on new page
+						$pdf->AddPage('', '', true);
+						if (!empty($tplidx)) {
+							$pdf->useTemplate($tplidx);
+						}
+						if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+							$this->_pagehead($pdf, $object, 0, $outputlangs);
+						}
+						$pdf->setPage($pageposbefore + 1);
+
+						$curY = $tab_top_newpage;
+
+						// Allows data in the first page if description is long enough to break in multiples pages
+						if (!empty($conf->global->MAIN_PDF_DATA_ON_FIRST_PAGE)) {
+							$showpricebeforepagebreak = 1;
+						} else {
+							$showpricebeforepagebreak = 0;
+						}
+					}
+
+					if (isset($imglinesize['width']) && isset($imglinesize['height'])) {
+						$curX = $this->posxpicture - 1;
+						$pdf->Image($realpatharray[$i], $curX + (($this->posxtva - $this->posxpicture - $imglinesize['width']) / 2), $curY, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
+						// $pdf->Image does not increase value return by getY, so we save it manually
+						$posYAfterImage = $curY + $imglinesize['height'];
+					}
+
+					// Description of product line
+					$curX = $this->posxdesc - 1;
+
+					$pdf->startTransaction();
+					pdf_writelinedesc($pdf, $object, $i, $outputlangs, $this->posxpicture - $curX - $progress_width, 3, $curX, $curY, $hideref, $hidedesc);
+					$pageposafter = $pdf->getPage();
+					if ($pageposafter > $pageposbefore) {	// There is a pagebreak
+						$pdf->rollbackTransaction(true);
+						$pageposafter = $pageposbefore;
+						//print $pageposafter.'-'.$pageposbefore;exit;
+						$pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
+						pdf_writelinedesc($pdf, $object, $i, $outputlangs, $this->posxpicture - $curX - $progress_width, 3, $curX, $curY, $hideref, $hidedesc);
+						$pageposafter = $pdf->getPage();
+						$posyafter = $pdf->GetY();
+						//var_dump($posyafter); var_dump(($this->page_hauteur - ($heightforfooter+$heightforfreetext+$heightforinfotot))); exit;
+						if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforinfotot))) {	// There is no space left for total+free text
+							if ($i == ($nblines - 1)) {	// No more lines, and no space left to show total, so we create a new page
+								$pdf->AddPage('', '', true);
+								if (!empty($tplidx)) {
+									$pdf->useTemplate($tplidx);
+								}
+								if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+									$this->_pagehead($pdf, $object, 0, $outputlangs);
+								}
+								$pdf->setPage($pageposafter + 1);
+							}
+						} else {
+							// We found a page break
+
+							// Allows data in the first page if description is long enough to break in multiples pages
+							if (!empty($conf->global->MAIN_PDF_DATA_ON_FIRST_PAGE)) {
+								$showpricebeforepagebreak = 1;
+							} else {
+								$showpricebeforepagebreak = 0;
+							}
+						}
+					} else // No pagebreak
+					{
+						$pdf->commitTransaction();
+					}
+					$posYAfterDescription = $pdf->GetY();
+
+					$nexY = $pdf->GetY();
+					$pageposafter = $pdf->getPage();
+					$pdf->setPage($pageposbefore);
+					$pdf->setTopMargin($this->marge_haute);
+					$pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
+
+					// We suppose that a too long description or photo were moved completely on next page
+					if ($pageposafter > $pageposbefore && empty($showpricebeforepagebreak)) {
+						$pdf->setPage($pageposafter);
+						$curY = $tab_top_newpage;
+					}
+
+					$pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
+
+					// VAT Rate
+					if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) && empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN)) {
+						$vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
+						$pdf->SetXY($this->posxtva - 5, $curY);
+						$pdf->MultiCell($this->posxup - $this->posxtva + 4, 3, $vat_rate, 0, 'R');
+					}
+
+					// Unit price before discount
+					$up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
+					$pdf->SetXY($this->posxup, $curY);
+					$pdf->MultiCell($this->posxqty - $this->posxup - 0.8, 3, $up_excl_tax, 0, 'R', 0);
+
+					// Quantity
+					$qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
+					$pdf->SetXY($this->posxqty, $curY);
+					$pdf->MultiCell($this->posxunit - $this->posxqty - 0.8, 4, $qty, 0, 'R'); // Enough for 6 chars
+
+					// Unit
+					if (!empty($conf->global->PRODUCT_USE_UNITS)) {
+						$unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails, $hookmanager);
+						$pdf->SetXY($this->posxunit, $curY);
+						$pdf->MultiCell($this->posxdiscount - $this->posxunit - 0.8, 4, $unit, 0, 'L');
+					}
+
+					// Discount on line
+					if ($object->lines[$i]->remise_percent) {
+						$pdf->SetXY($this->posxdiscount - 2, $curY);
+						$remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
+						$pdf->MultiCell($this->posxprogress - $this->posxdiscount + 2, 3, $remise_percent, 0, 'R');
+					}
+
+					// Situation progress
+					if ($this->situationinvoice) {
+						$progress = pdf_getlineprogress($object, $i, $outputlangs, $hidedetails);
+						$pdf->SetXY($this->posxprogress, $curY);
+						$pdf->MultiCell($this->postotalht - $this->posxprogress + 1, 3, $progress, 0, 'R');
+					}
+
+					// Total HT line
+					$total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
+					$pdf->SetXY($this->postotalht, $curY);
+					$pdf->MultiCell($this->page_largeur - $this->marge_droite - $this->postotalht, 3, $total_excl_tax, 0, 'R', 0);
+
+
+					$sign = 1;
+					if (isset($object->type) && $object->type == 2 && !empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) {
+						$sign = -1;
+					}
+					// Collection of totals by value of VAT in $this->tva["taux"]=total_tva
+					$prev_progress = $object->lines[$i]->get_prev_progress($object->id);
+					if ($prev_progress > 0 && !empty($object->lines[$i]->situation_percent)) { // Compute progress from previous situation
+						if (!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) {
+							$tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
+						} else {
+							$tvaligne = $sign * $object->lines[$i]->total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
+						}
+					} else {
+						if (!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) {
+							$tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva;
+						} else {
+							$tvaligne = $sign * $object->lines[$i]->total_tva;
+						}
+					}
+
+					$localtax1ligne = $object->lines[$i]->total_localtax1;
+					$localtax2ligne = $object->lines[$i]->total_localtax2;
+					$localtax1_rate = $object->lines[$i]->localtax1_tx;
+					$localtax2_rate = $object->lines[$i]->localtax2_tx;
+					$localtax1_type = $object->lines[$i]->localtax1_type;
+					$localtax2_type = $object->lines[$i]->localtax2_type;
+
+					if ($object->remise_percent) {
+						$tvaligne -= ($tvaligne * $object->remise_percent) / 100;
+					}
+					if ($object->remise_percent) {
+						$localtax1ligne -= ($localtax1ligne * $object->remise_percent) / 100;
+					}
+					if ($object->remise_percent) {
+						$localtax2ligne -= ($localtax2ligne * $object->remise_percent) / 100;
+					}
+
+					$vatrate = (string) $object->lines[$i]->tva_tx;
+
+					// Retrieve type from database for backward compatibility with old records
+					if ((!isset($localtax1_type) || $localtax1_type == '' || !isset($localtax2_type) || $localtax2_type == '') // if tax type not defined
+					&& (!empty($localtax1_rate) || !empty($localtax2_rate))) { // and there is local tax
+						$localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc);
+						$localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : '';
+						$localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : '';
+					}
+
+					// retrieve global local tax
+					if ($localtax1_type && $localtax1ligne != 0) {
+						$this->localtax1[$localtax1_type][$localtax1_rate] += $localtax1ligne;
+					}
+					if ($localtax2_type && $localtax2ligne != 0) {
+						$this->localtax2[$localtax2_type][$localtax2_rate] += $localtax2ligne;
+					}
+
+					if (($object->lines[$i]->info_bits & 0x01) == 0x01) {
+						$vatrate .= '*';
+					}
+
+					// Fill $this->tva and $this->tva_array
+					if (!isset($this->tva[$vatrate])) {
+						$this->tva[$vatrate] = 0;
+					}
+					$this->tva[$vatrate] += $tvaligne;	// ->tva is abandonned, we use now ->tva_array that is more complete
+					$vatcode = $object->lines[$i]->vat_src_code;
+					if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) {
+						$this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0;
+					}
+					$this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate'=>$vatrate, 'vatcode'=>$vatcode, 'amount'=> $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne);
+
+					if ($posYAfterImage > $posYAfterDescription) {
+						$nexY = $posYAfterImage;
+					}
+
+					// Add line
+					if (!empty($conf->global->MAIN_PDF_DASH_BETWEEN_LINES) && $i < ($nblines - 1)) {
+						$pdf->setPage($pageposafter);
+						$pdf->SetLineStyle(array('dash'=>'1,1', 'color'=>array(80, 80, 80)));
+						//$pdf->SetDrawColor(190,190,200);
+						$pdf->line($this->marge_gauche, $nexY + 1, $this->page_largeur - $this->marge_droite, $nexY + 1);
+						$pdf->SetLineStyle(array('dash'=>0));
+					}
+
+					$nexY += 2; // Add space between lines
+
+					// Detect if some page were added automatically and output _tableau for past pages
+					while ($pagenb < $pageposafter) {
+						$pdf->setPage($pagenb);
+						if ($pagenb == 1) {
+							$this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, 0, 1, $object->multicurrency_code);
+						} else {
+							$this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code);
+						}
+						$this->_pagefoot($pdf, $object, $outputlangs, 1);
+						$pagenb++;
+						$pdf->setPage($pagenb);
+						$pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
+						if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+							$this->_pagehead($pdf, $object, 0, $outputlangs);
+						}
+						if (!empty($tplidx)) {
+							$pdf->useTemplate($tplidx);
+						}
+					}
+					if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) {
+						if ($pagenb == 1) {
+							$this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, 0, 1, $object->multicurrency_code);
+						} else {
+							$this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code);
+						}
+						$this->_pagefoot($pdf, $object, $outputlangs, 1);
+						// New page
+						$pdf->AddPage();
+						if (!empty($tplidx)) {
+							$pdf->useTemplate($tplidx);
+						}
+						$pagenb++;
+						if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+							$this->_pagehead($pdf, $object, 0, $outputlangs);
+						}
+					}
+				}
+
+				// Show square
+				if ($pagenb == 1) {
+					$this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforinfotot - $heightforfreetext - $heightforfooter, 0, $outputlangs, 0, 0, $object->multicurrency_code);
+					$bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforfooter + 1;
+				} else {
+					$this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforinfotot - $heightforfreetext - $heightforfooter, 0, $outputlangs, 1, 0, $object->multicurrency_code);
+					$bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforfooter + 1;
+				}
+				dol_syslog("bottomlasttab=".$bottomlasttab." this->page_hauteur=".$this->page_hauteur." heightforinfotot=".$heightforinfotot." heightforfreetext=".$heightforfreetext." heightforfooter=".$heightforfooter);
+
+				// Display info area
+				$posy = $this->_tableau_info($pdf, $object, $bottomlasttab, $outputlangs, $outputlangsbis);
+
+				// Display total area
+				$posy = $this->_tableau_tot($pdf, $object, $deja_regle, $bottomlasttab, $outputlangs, $outputlangsbis);
+
+				// Display Payments area
+				if (($deja_regle || $amount_credit_notes_included || $amount_deposits_included) && empty($conf->global->INVOICE_NO_PAYMENT_DETAILS)) {
+					$posy = $this->_tableau_versements($pdf, $object, $posy, $outputlangs, $heightforfooter);
+				}
+
+				// Pagefoot
+				$this->_pagefoot($pdf, $object, $outputlangs);
+				if (method_exists($pdf, 'AliasNbPages')) {
+					$pdf->AliasNbPages();
+				}
+
+				$pdf->Close();
+
+				$pdf->Output($file, 'F');
+
+				// Add pdfgeneration hook
+				$hookmanager->initHooks(array('pdfgeneration'));
+				$parameters = array('file'=>$file, 'object'=>$object, 'outputlangs'=>$outputlangs);
+				global $action;
+				$reshook = $hookmanager->executeHooks('afterPDFCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
+				if ($reshook < 0) {
+					$this->error = $hookmanager->error;
+					$this->errors = $hookmanager->errors;
+				}
+
+				if (!empty($conf->global->MAIN_UMASK)) {
+					@chmod($file, octdec($conf->global->MAIN_UMASK));
+				}
+
+				$this->result = array('fullpath'=>$file);
+
+				return 1; // No error
+			} else {
+				$this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
+				return 0;
+			}
+		} else {
+			$this->error = $langs->transnoentities("ErrorConstantNotDefined", "FAC_OUTPUTDIR");
+			return 0;
+		}
+	}
+
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *  Show payments table
+	 *
+	 *  @param	TCPDF		$pdf            	Object PDF
+	 *  @param  Facture		$object         	Object invoice
+	 *  @param  int			$posy           	Position y in PDF
+	 *  @param  Translate	$outputlangs    	Object langs for output
+	 *  @param  int			$heightforfooter 	Height for footer
+	 *  @return int             				<0 if KO, >0 if OK
+	 */
+	protected function _tableau_versements(&$pdf, $object, $posy, $outputlangs, $heightforfooter = 0)
+	{
+		// phpcs:enable
+		global $conf;
+
+		$sign = 1;
+		if ($object->type == 2 && !empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) {
+			$sign = -1;
+		}
+
+		$current_page = $pdf->getPage();
+		$tab3_posx = 120;
+		$tab3_top = $posy + 8;
+		$tab3_width = 80;
+		$tab3_height = 4;
+		if ($this->page_largeur < 210) { // To work with US executive format
+			$tab3_posx -= 15;
+		}
+
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		$this->_tableau_versements_header($pdf, $object, $outputlangs, $default_font_size, $tab3_posx, $tab3_top, $tab3_width, $tab3_height);
+
+		$y = 0;
+
+		$pdf->SetFont('', '', $default_font_size - 4);
+
+
+		// Loop on each discount available (deposits and credit notes and excess of payment included)
+		$sql = "SELECT re.rowid, re.amount_ht, re.multicurrency_amount_ht, re.amount_tva, re.multicurrency_amount_tva,  re.amount_ttc, re.multicurrency_amount_ttc,";
+		$sql .= " re.description, re.fk_facture_source,";
+		$sql .= " f.type, f.datef";
+		$sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re, ".MAIN_DB_PREFIX."facture as f";
+		$sql .= " WHERE re.fk_facture_source = f.rowid AND re.fk_facture = ".((int) $object->id);
+		$resql = $this->db->query($sql);
+		if ($resql) {
+			$num = $this->db->num_rows($resql);
+			$i = 0;
+			$invoice = new Facture($this->db);
+			while ($i < $num) {
+				$y += 3;
+				if ($tab3_top + $y >= ($this->page_hauteur - $heightforfooter)) {
+					$y = 0;
+					$current_page++;
+					$pdf->AddPage('', '', true);
+					if (!empty($tplidx)) {
+						$pdf->useTemplate($tplidx);
+					}
+					if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+						$this->_pagehead($pdf, $object, 0, $outputlangs);
+					}
+					$pdf->setPage($current_page);
+					$this->_tableau_versements_header($pdf, $object, $outputlangs, $default_font_size, $tab3_posx, $tab3_top + $y - 3, $tab3_width, $tab3_height);
+				}
+
+				$obj = $this->db->fetch_object($resql);
+
+				if ($obj->type == 2) {
+					$text = $outputlangs->transnoentities("CreditNote");
+				} elseif ($obj->type == 3) {
+					$text = $outputlangs->transnoentities("Deposit");
+				} elseif ($obj->type == 0) {
+					$text = $outputlangs->transnoentities("ExcessReceived");
+				} else {
+					$text = $outputlangs->transnoentities("UnknownType");
+				}
+
+				$invoice->fetch($obj->fk_facture_source);
+
+				$pdf->SetXY($tab3_posx, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, dol_print_date($this->db->jdate($obj->datef), 'day', false, $outputlangs, true), 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 21, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, price((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? $obj->multicurrency_amount_ttc : $obj->amount_ttc, 0, $outputlangs), 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 40, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, $text, 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 58, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, $invoice->ref, 0, 'L', 0);
+
+				$pdf->line($tab3_posx, $tab3_top + $y + 3, $tab3_posx + $tab3_width, $tab3_top + $y + 3);
+
+				$i++;
+			}
+		} else {
+			$this->error = $this->db->lasterror();
+			return -1;
+		}
+
+		// Loop on each payment
+		// TODO Call getListOfPaymentsgetListOfPayments instead of hard coded sql
+		$sql = "SELECT p.datep as date, p.fk_paiement, p.num_paiement as num, pf.amount as amount, pf.multicurrency_amount,";
+		$sql .= " cp.code";
+		$sql .= " FROM ".MAIN_DB_PREFIX."paiement_facture as pf, ".MAIN_DB_PREFIX."paiement as p";
+		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as cp ON p.fk_paiement = cp.id";
+		$sql .= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = ".((int) $object->id);
+		//$sql.= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = 1";
+		$sql .= " ORDER BY p.datep";
+
+		$resql = $this->db->query($sql);
+		if ($resql) {
+			$num = $this->db->num_rows($resql);
+			$i = 0;
+			while ($i < $num) {
+				$y += 3;
+				if ($tab3_top + $y >= ($this->page_hauteur - $heightforfooter)) {
+					$y = 0;
+					$current_page++;
+					$pdf->AddPage('', '', true);
+					if (!empty($tplidx)) {
+						$pdf->useTemplate($tplidx);
+					}
+					if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+						$this->_pagehead($pdf, $object, 0, $outputlangs);
+					}
+					$pdf->setPage($current_page);
+					$this->_tableau_versements_header($pdf, $object, $outputlangs, $default_font_size, $tab3_posx, $tab3_top + $y - 3, $tab3_width, $tab3_height);
+				}
+
+				$row = $this->db->fetch_object($resql);
+
+				$pdf->SetXY($tab3_posx, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, dol_print_date($this->db->jdate($row->date), 'day', false, $outputlangs, true), 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 21, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, price($sign * ((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? $row->multicurrency_amount : $row->amount), 0, $outputlangs), 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 40, $tab3_top + $y);
+				$oper = $outputlangs->transnoentitiesnoconv("PaymentTypeShort".$row->code);
+
+				$pdf->MultiCell(20, 3, $oper, 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 58, $tab3_top + $y);
+				$pdf->MultiCell(30, 3, $row->num, 0, 'L', 0);
+
+				$pdf->line($tab3_posx, $tab3_top + $y + 3, $tab3_posx + $tab3_width, $tab3_top + $y + 3);
+
+				$i++;
+			}
+
+			return $tab3_top + $y + 3;
+		} else {
+			$this->error = $this->db->lasterror();
+			return -1;
+		}
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 * Function _tableau_versements_header
+	 *
+	 * @param TCPDF 		$pdf				Object PDF
+	 * @param Facture		$object				Object invoice
+	 * @param Translate		$outputlangs		Object langs for output
+	 * @param int			$default_font_size	Font size
+	 * @param int			$tab3_posx			pos x
+	 * @param int 			$tab3_top			pos y
+	 * @param int 			$tab3_width			width
+	 * @param int 			$tab3_height		height
+	 * @return void
+	 */
+	protected function _tableau_versements_header($pdf, $object, $outputlangs, $default_font_size, $tab3_posx, $tab3_top, $tab3_width, $tab3_height)
+	{
+		// phpcs:enable
+		$title = $outputlangs->transnoentities("PaymentsAlreadyDone");
+		if ($object->type == 2) {
+			$title = $outputlangs->transnoentities("PaymentsBackAlreadyDone");
+		}
+
+		$pdf->SetFont('', '', $default_font_size - 3);
+		$pdf->SetXY($tab3_posx, $tab3_top - 4);
+		$pdf->MultiCell(60, 3, $title, 0, 'L', 0);
+
+		$pdf->line($tab3_posx, $tab3_top, $tab3_posx + $tab3_width, $tab3_top);
+
+		$pdf->SetFont('', '', $default_font_size - 4);
+		$pdf->SetXY($tab3_posx, $tab3_top);
+		$pdf->MultiCell(20, 3, $outputlangs->transnoentities("Payment"), 0, 'L', 0);
+		$pdf->SetXY($tab3_posx + 21, $tab3_top);
+		$pdf->MultiCell(20, 3, $outputlangs->transnoentities("Amount"), 0, 'L', 0);
+		$pdf->SetXY($tab3_posx + 40, $tab3_top);
+		$pdf->MultiCell(20, 3, $outputlangs->transnoentities("Type"), 0, 'L', 0);
+		$pdf->SetXY($tab3_posx + 58, $tab3_top);
+		$pdf->MultiCell(20, 3, $outputlangs->transnoentities("Num"), 0, 'L', 0);
+
+		$pdf->line($tab3_posx, $tab3_top - 1 + $tab3_height, $tab3_posx + $tab3_width, $tab3_top - 1 + $tab3_height);
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *   Show miscellaneous information (payment mode, payment term, ...)
+	 *
+	 *   @param		TCPDF		$pdf     		Object PDF
+	 *   @param		Facture		$object			Object to show
+	 *   @param		int			$posy			Y
+	 *   @param		Translate	$outputlangs	Langs object
+	 *   @param  	Translate	$outputlangsbis	Object lang for output bis
+	 *   @return	int							Pos y
+	 */
+	protected function _tableau_info(&$pdf, $object, $posy, $outputlangs, $outputlangsbis)
+	{
+		// phpcs:enable
+		global $conf, $mysoc;
+
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		$pdf->SetFont('', '', $default_font_size - 1);
+
+		// If France, show VAT mention if not applicable
+		if ($this->emetteur->country_code == 'FR' && empty($mysoc->tva_assuj)) {
+			$pdf->SetFont('', 'B', $default_font_size - 2);
+			$pdf->SetXY($this->marge_gauche, $posy);
+			if ($mysoc->forme_juridique_code == 92) {
+				$pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoiceAsso"), 0, 'L', 0);
+			} else {
+				$pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoice"), 0, 'L', 0);
+			}
+
+			$posy = $pdf->GetY() + 4;
+		}
+
+		$posxval = 52;
+
+		// Show payments conditions
+		if ($object->type != 2 && ($object->cond_reglement_code || $object->cond_reglement)) {
+			$pdf->SetFont('', 'B', $default_font_size - 2);
+			$pdf->SetXY($this->marge_gauche, $posy);
+			$titre = $outputlangs->transnoentities("PaymentConditions").':';
+			$pdf->MultiCell(43, 4, $titre, 0, 'L');
+
+			$pdf->SetFont('', '', $default_font_size - 2);
+			$pdf->SetXY($posxval, $posy);
+			$lib_condition_paiement = $outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code) != ('PaymentCondition'.$object->cond_reglement_code) ? $outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code) : $outputlangs->convToOutputCharset($object->cond_reglement_doc ? $object->cond_reglement_doc : $object->cond_reglement_label);
+			$lib_condition_paiement = str_replace('\n', "\n", $lib_condition_paiement);
+			$pdf->MultiCell(67, 4, $lib_condition_paiement, 0, 'L');
+
+			$posy = $pdf->GetY() + 3;	// We need spaces for 2 lines payment conditions
+		}
+
+		if ($object->type != 2) {
+			// Check a payment mode is defined
+			if (empty($object->mode_reglement_code)
+			&& empty($conf->global->FACTURE_CHQ_NUMBER)
+			&& empty($conf->global->FACTURE_RIB_NUMBER)) {
+				$this->error = $outputlangs->transnoentities("ErrorNoPaiementModeConfigured");
+			} elseif (($object->mode_reglement_code == 'CHQ' && empty($conf->global->FACTURE_CHQ_NUMBER) && empty($object->fk_account) && empty($object->fk_bank))
+				|| ($object->mode_reglement_code == 'VIR' && empty($conf->global->FACTURE_RIB_NUMBER) && empty($object->fk_account) && empty($object->fk_bank))) {
+				// Avoid having any valid PDF with setup that is not complete
+				$outputlangs->load("errors");
+
+				$pdf->SetXY($this->marge_gauche, $posy);
+				$pdf->SetTextColor(200, 0, 0);
+				$pdf->SetFont('', 'B', $default_font_size - 2);
+				$this->error = $outputlangs->transnoentities("ErrorPaymentModeDefinedToWithoutSetup", $object->mode_reglement_code);
+				$pdf->MultiCell(80, 3, $this->error, 0, 'L', 0);
+				$pdf->SetTextColor(0, 0, 0);
+
+				$posy = $pdf->GetY() + 1;
+			}
+
+			// Show payment mode
+			if (!empty($object->mode_reglement_code)
+			&& $object->mode_reglement_code != 'CHQ'
+			&& $object->mode_reglement_code != 'VIR') {
+				$pdf->SetFont('', 'B', $default_font_size - 2);
+				$pdf->SetXY($this->marge_gauche, $posy);
+				$titre = $outputlangs->transnoentities("PaymentMode").':';
+				$pdf->MultiCell(80, 5, $titre, 0, 'L');
+
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->SetXY($posxval, $posy);
+				$lib_mode_reg = $outputlangs->transnoentities("PaymentType".$object->mode_reglement_code) != ('PaymentType'.$object->mode_reglement_code) ? $outputlangs->transnoentities("PaymentType".$object->mode_reglement_code) : $outputlangs->convToOutputCharset($object->mode_reglement);
+				$pdf->MultiCell(80, 5, $lib_mode_reg, 0, 'L');
+
+				//szisz
+				//$currency_code = !empty($currency_code) ? $currency_code : $conf->currency;
+				$currency_code = $object->multicurrency_code;
+				$pdf->SetFont('', 'B', $default_font_size - 2);
+				$currency = $outputlangs->transnoentities("Currency").': ';
+				$pdf->MultiCell(80, 5, $currency, 0, 'L');
+				$pdf->SetXY($posxval, $posy+5);
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->MultiCell(80, 5, $currency_code, 0, 'L');
+
+				$posy = $pdf->GetY();
+			}
+
+			// Show online payment link
+			if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CB' || $object->mode_reglement_code == 'VAD') {
+				$useonlinepayment = 0;
+				if (!empty($conf->global->PDF_SHOW_LINK_TO_ONLINE_PAYMENT)) {
+					if (!empty($conf->paypal->enabled)) {
+						$useonlinepayment++;
+					}
+					if (!empty($conf->stripe->enabled)) {
+						$useonlinepayment++;
+					}
+					if (!empty($conf->paybox->enabled)) {
+						$useonlinepayment++;
+					}
+				}
+
+				if ($object->statut != Facture::STATUS_DRAFT && $useonlinepayment) {
+					require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
+					global $langs;
+
+					$langs->loadLangs(array('payment', 'paybox', 'stripe'));
+					$servicename = $langs->transnoentities('Online');
+					$paiement_url = getOnlinePaymentUrl('', 'invoice', $object->ref, '', '', '');
+					$linktopay = $langs->trans("ToOfferALinkForOnlinePayment", $servicename).' <a href="'.$paiement_url.'">'.$outputlangs->transnoentities("ClickHere").'</a>';
+
+					$pdf->SetXY($this->marge_gauche, $posy);
+					$pdf->writeHTMLCell(80, 5, '', '', dol_htmlentitiesbr($linktopay), 0, 1);
+
+					$posy = $pdf->GetY() + 1;
+				}
+			}
+
+			// Show payment mode CHQ
+			if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CHQ') {
+				// If payment mode unregulated or payment mode forced to CHQ
+				if (!empty($conf->global->FACTURE_CHQ_NUMBER)) {
+					$diffsizetitle = (empty($conf->global->PDF_DIFFSIZE_TITLE) ? 3 : $conf->global->PDF_DIFFSIZE_TITLE);
+
+					if ($conf->global->FACTURE_CHQ_NUMBER > 0) {
+						$account = new Account($this->db);
+						$account->fetch($conf->global->FACTURE_CHQ_NUMBER);
+
+						$pdf->SetXY($this->marge_gauche, $posy);
+						$pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
+						$pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $account->proprio), 0, 'L', 0);
+						$posy = $pdf->GetY() + 1;
+
+						if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) {
+							$pdf->SetXY($this->marge_gauche, $posy);
+							$pdf->SetFont('', '', $default_font_size - $diffsizetitle);
+							$pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0);
+							$posy = $pdf->GetY() + 2;
+						}
+					}
+					if ($conf->global->FACTURE_CHQ_NUMBER == -1) {
+						$pdf->SetXY($this->marge_gauche, $posy);
+						$pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
+						$pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $this->emetteur->name), 0, 'L', 0);
+						$posy = $pdf->GetY() + 1;
+
+						if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) {
+							$pdf->SetXY($this->marge_gauche, $posy);
+							$pdf->SetFont('', '', $default_font_size - $diffsizetitle);
+							$pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0);
+							$posy = $pdf->GetY() + 2;
+						}
+					}
+				}
+			}
+
+			// If payment mode not forced or forced to VIR, show payment with BAN
+			if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') {
+				if ($object->fk_account > 0 || $object->fk_bank > 0 || !empty($conf->global->FACTURE_RIB_NUMBER)) {
+					$bankid = ($object->fk_account <= 0 ? $conf->global->FACTURE_RIB_NUMBER : $object->fk_account);
+					if ($object->fk_bank > 0) {
+						$bankid = $object->fk_bank; // For backward compatibility when object->fk_account is forced with object->fk_bank
+					}
+					$account = new Account($this->db);
+					$account->fetch($bankid);
+
+					$curx = $this->marge_gauche;
+					$cury = $posy;
+
+					$posy = pdf_bank($pdf, $outputlangs, $curx, $cury, $account, 0, $default_font_size);
+
+					$posy += 2;
+				}
+			}
+		}
+
+		return $posy;
+	}
+
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *	Show total to pay
+	 *
+	 *	@param	TCPDF		$pdf            Object PDF
+	 *	@param  Facture		$object         Object invoice
+	 *	@param  int			$deja_regle     Amount already paid (in the currency of invoice)
+	 *	@param	int			$posy			Position depart
+	 *	@param	Translate	$outputlangs	Objet langs
+	 *  @param  Translate	$outputlangsbis	Object lang for output bis
+	 *	@return int							Position pour suite
+	 */
+	protected function _tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs, $outputlangsbis)
+	{
+		// phpcs:enable
+		global $conf, $mysoc, $hookmanager;
+
+		$sign = 1;
+		if ($object->type == 2 && !empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) {
+			$sign = -1;
+		}
+
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		$outputlangsbis = null;
+		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && $outputlangs->defaultlang != $conf->global->PDF_USE_ALSO_LANGUAGE_CODE) {
+			$outputlangsbis = new Translate('', $conf);
+			$outputlangsbis->setDefaultLang($conf->global->PDF_USE_ALSO_LANGUAGE_CODE);
+			$outputlangsbis->loadLangs(array("main", "dict", "companies", "bills", "products", "propal"));
+			$default_font_size--;
+		}
+
+		$tab2_top = $posy;
+		$tab2_hl = 4;
+		$pdf->SetFont('', '', $default_font_size - 1);
+
+		// Total table
+		$col1x = 120;
+		$col2x = 170;
+		if ($this->page_largeur < 210) { // To work with US executive format
+			$col1x -= 15;
+			$col2x -= 10;
+		}
+		$largcol2 = ($this->page_largeur - $this->marge_droite - $col2x);
+
+		$useborder = 0;
+		$index = 0;
+
+		// Total HT
+		$pdf->SetFillColor(255, 255, 255);
+		$pdf->SetXY($col1x, $tab2_top + 0);
+		$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities(empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) ? "TotalHT" : "Total").(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transnoentities(empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) ? "TotalHT" : "Total") : ''), 0, 'L', 1);
+
+		$total_ht = ((!empty($conf->multicurrency->enabled) && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ht : $object->total_ht);
+		$pdf->SetXY($col2x, $tab2_top + 0);
+		$pdf->MultiCell($largcol2, $tab2_hl, price($sign * ($total_ht + (!empty($object->remise) ? $object->remise : 0)), 0, $outputlangs), 0, 'R', 1);
+
+		// Show VAT by rates and total
+		$pdf->SetFillColor(248, 248, 248);
+
+		$total_ttc = (!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ttc : $object->total_ttc;
+
+		$this->atleastoneratenotnull = 0;
+		if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT)) {
+			$tvaisnull = ((!empty($this->tva) && count($this->tva) == 1 && isset($this->tva['0.000']) && is_float($this->tva['0.000'])) ? true : false);
+			if (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_IFNULL) && $tvaisnull) {
+				// Nothing to do
+			} else {
+				// FIXME amount of vat not supported with multicurrency
+
+				//Local tax 1 before VAT
+				//if (! empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on')
+				//{
+				foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
+					if (in_array((string) $localtax_type, array('1', '3', '5'))) {
+						continue;
+					}
+
+					foreach ($localtax_rate as $tvakey => $tvaval) {
+						if ($tvakey != 0) {    // On affiche pas taux 0
+							//$this->atleastoneratenotnull++;
+
+							$index++;
+							$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+							$tvacompl = '';
+							if (preg_match('/\*/', $tvakey)) {
+								$tvakey = str_replace('*', '', $tvakey);
+								$tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
+							}
+
+							$totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalLT1", $mysoc->country_code) : '');
+							$totalvat .= ' ';
+							$totalvat .= vatrate(abs($tvakey), 1).$tvacompl;
+							$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+
+							$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+							$pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1);
+						}
+					}
+				}
+				//}
+				//Local tax 2 before VAT
+				//if (! empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on')
+				//{
+				foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
+					if (in_array((string) $localtax_type, array('1', '3', '5'))) {
+						continue;
+					}
+
+					foreach ($localtax_rate as $tvakey => $tvaval) {
+						if ($tvakey != 0) {    // On affiche pas taux 0
+							//$this->atleastoneratenotnull++;
+
+							$index++;
+							$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+							$tvacompl = '';
+							if (preg_match('/\*/', $tvakey)) {
+								$tvakey = str_replace('*', '', $tvakey);
+								$tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
+							}
+							$totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalLT2", $mysoc->country_code) : '');
+							$totalvat .= ' ';
+							$totalvat .= vatrate(abs($tvakey), 1).$tvacompl;
+							$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+
+							$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+							$pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1);
+						}
+					}
+				}
+
+				//}
+
+				// VAT
+				foreach ($this->tva_array as $tvakey => $tvaval) {
+					if ($tvakey != 0) {    // On affiche pas taux 0
+						$this->atleastoneratenotnull++;
+
+						$index++;
+						$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+						$tvacompl = '';
+						if (preg_match('/\*/', $tvakey)) {
+							$tvakey = str_replace('*', '', $tvakey);
+							$tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
+						}
+						$totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : '');
+						$totalvat .= ' ';
+						if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') {
+							$totalvat .= vatrate($tvaval['vatrate'], 1).$tvacompl;
+						} elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') {
+							$totalvat .= $tvaval['vatcode'].$tvacompl;
+						} else {
+							$totalvat .= vatrate($tvaval['vatrate'], 1).($tvaval['vatcode'] ? ' ('.$tvaval['vatcode'].')' : '').$tvacompl;
+						}
+						$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+
+						$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+						$pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1);
+					}
+				}
+
+				//Local tax 1 after VAT
+				//if (! empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on')
+				//{
+				foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
+					if (in_array((string) $localtax_type, array('2', '4', '6'))) {
+						continue;
+					}
+
+					foreach ($localtax_rate as $tvakey => $tvaval) {
+						if ($tvakey != 0) {    // On affiche pas taux 0
+							//$this->atleastoneratenotnull++;
+
+							$index++;
+							$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+							$tvacompl = '';
+							if (preg_match('/\*/', $tvakey)) {
+								$tvakey = str_replace('*', '', $tvakey);
+								$tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
+							}
+							$totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code).' ';
+							$totalvat .= vatrate(abs($tvakey), 1).$tvacompl;
+
+							$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+							$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+							$pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1);
+						}
+					}
+				}
+				//}
+				//Local tax 2 after VAT
+				//if (! empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on')
+				//{
+				foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
+					if (in_array((string) $localtax_type, array('2', '4', '6'))) {
+						continue;
+					}
+
+					foreach ($localtax_rate as $tvakey => $tvaval) {
+						//$this->atleastoneratenotnull++;
+
+						$index++;
+						$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+						$tvacompl = '';
+						if (preg_match('/\*/', $tvakey)) {
+							$tvakey = str_replace('*', '', $tvakey);
+							$tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")";
+						}
+						$totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code).' ';
+
+						$totalvat .= vatrate(abs($tvakey), 1).$tvacompl;
+						$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+
+						$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+						$pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1);
+					}
+				}
+				//}
+
+				// Revenue stamp
+				if (price2num($object->revenuestamp) != 0) {
+					$index++;
+					$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("RevenueStamp"), $useborder, 'L', 1);
+
+					$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($largcol2, $tab2_hl, price($sign * $object->revenuestamp), $useborder, 'R', 1);
+				}
+
+				// Total TTC
+				$index++;
+				$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+				$pdf->SetTextColor(0, 0, 60);
+				$pdf->SetFillColor(224, 224, 224);
+				$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalTTC"), $useborder, 'L', 1);
+
+				$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($largcol2, $tab2_hl, price($sign * $total_ttc, 0, $outputlangs), $useborder, 'R', 1);
+
+				// Retained warranty
+				if ($object->displayRetainedWarranty()) {
+					$pdf->SetTextColor(40, 40, 40);
+					$pdf->SetFillColor(255, 255, 255);
+
+					$retainedWarranty = $object->getRetainedWarrantyAmount();
+					$billedWithRetainedWarranty = $object->total_ttc - $retainedWarranty;
+
+					// Billed - retained warranty
+					$index++;
+					$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("ToPayOn", dol_print_date($object->date_lim_reglement, 'day')), $useborder, 'L', 1);
+
+					$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($largcol2, $tab2_hl, price($billedWithRetainedWarranty), $useborder, 'R', 1);
+
+					// retained warranty
+					$index++;
+					$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+					$retainedWarrantyToPayOn = $outputlangs->transnoentities("RetainedWarranty").' ('.$object->retained_warranty.'%)';
+					$retainedWarrantyToPayOn .= !empty($object->retained_warranty_date_limit) ? ' '.$outputlangs->transnoentities("toPayOn", dol_print_date($object->retained_warranty_date_limit, 'day')) : '';
+
+					$pdf->MultiCell($col2x - $col1x, $tab2_hl, $retainedWarrantyToPayOn, $useborder, 'L', 1);
+					$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($largcol2, $tab2_hl, price($retainedWarranty), $useborder, 'R', 1);
+				}
+			}
+		}
+
+		$pdf->SetTextColor(0, 0, 0);
+		$creditnoteamount = $object->getSumCreditNotesUsed((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0); // Warning, this also include excess received
+		$depositsamount = $object->getSumDepositsUsed((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0);
+		//print "x".$creditnoteamount."-".$depositsamount;exit;
+		$resteapayer = price2num($total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 'MT');
+		if (!empty($object->paye)) {
+			$resteapayer = 0;
+		}
+
+		if (($deja_regle > 0 || $creditnoteamount > 0 || $depositsamount > 0) && empty($conf->global->INVOICE_NO_PAYMENT_DETAILS)) {
+			// Already paid + Deposits
+			$index++;
+			$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+			$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("Paid"), 0, 'L', 0);
+			$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+			$pdf->MultiCell($largcol2, $tab2_hl, price($deja_regle + $depositsamount, 0, $outputlangs), 0, 'R', 0);
+
+			// Credit note
+			if ($creditnoteamount) {
+				$labeltouse = ($outputlangs->transnoentities("CreditNotesOrExcessReceived") != "CreditNotesOrExcessReceived") ? $outputlangs->transnoentities("CreditNotesOrExcessReceived") : $outputlangs->transnoentities("CreditNotes");
+				$index++;
+				$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($col2x - $col1x, $tab2_hl, $labeltouse, 0, 'L', 0);
+				$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($largcol2, $tab2_hl, price($creditnoteamount, 0, $outputlangs), 0, 'R', 0);
+			}
+
+			// Escompte
+			if ($object->close_code == Facture::CLOSECODE_DISCOUNTVAT) {
+				$index++;
+				$pdf->SetFillColor(255, 255, 255);
+
+				$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("EscompteOfferedShort"), $useborder, 'L', 1);
+				$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($largcol2, $tab2_hl, price($object->total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 0, $outputlangs), $useborder, 'R', 1);
+
+				$resteapayer = 0;
+			}
+
+			$index++;
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->SetFillColor(224, 224, 224);
+			$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+			$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("RemainderToPay"), $useborder, 'L', 1);
+			$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+			$pdf->MultiCell($largcol2, $tab2_hl, price($resteapayer, 0, $outputlangs), $useborder, 'R', 1);
+
+			$pdf->SetFont('', '', $default_font_size - 1);
+			$pdf->SetTextColor(0, 0, 0);
+		}
+
+		$index++;
+		return ($tab2_top + ($tab2_hl * $index));
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *   Show table for lines
+	 *
+	 *   @param		TCPDF		$pdf     		Object PDF
+	 *   @param		string		$tab_top		Top position of table
+	 *   @param		string		$tab_height		Height of table (rectangle)
+	 *   @param		int			$nexY			Y (not used)
+	 *   @param		Translate	$outputlangs	Langs object
+	 *   @param		int			$hidetop		1=Hide top bar of array and title, 0=Hide nothing, -1=Hide only title
+	 *   @param		int			$hidebottom		Hide bottom bar of array
+	 *   @param		string		$currency		Currency code
+	 *   @return	void
+	 */
+	protected function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop = 0, $hidebottom = 0, $currency = '')
+	{
+		global $conf;
+
+		// Force to disable hidetop and hidebottom
+		$hidebottom = 0;
+		if ($hidetop) {
+			$hidetop = -1;
+		}
+
+		$currency = !empty($currency) ? $currency : $conf->currency;
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		// Amount in (at tab_top - 1)
+		$pdf->SetTextColor(0, 0, 0);
+		$pdf->SetFont('', '', $default_font_size - 2);
+
+		if (empty($hidetop)) {
+			$titre = $outputlangs->transnoentities("AmountInCurrency", $outputlangs->transnoentitiesnoconv("Currency".$currency));
+			$pdf->SetXY($this->page_largeur - $this->marge_droite - ($pdf->GetStringWidth($titre) + 3), $tab_top - 4);
+			$pdf->MultiCell(($pdf->GetStringWidth($titre) + 3), 2, $titre);
+
+			//$conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR='230,230,230';
+			if (!empty($conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR)) {
+				$pdf->Rect($this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_droite - $this->marge_gauche, 5, 'F', null, explode(',', $conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR));
+			}
+		}
+
+		$pdf->SetDrawColor(128, 128, 128);
+		$pdf->SetFont('', '', $default_font_size - 1);
+
+		// Output Rect
+		$this->printRect($pdf, $this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $tab_height, $hidetop, $hidebottom); // Rect takes a length in 3rd parameter and 4th parameter
+
+		if (empty($hidetop)) {
+			$pdf->line($this->marge_gauche, $tab_top + 5, $this->page_largeur - $this->marge_droite, $tab_top + 5); // line takes a position y in 2nd parameter and 4th parameter
+
+			$pdf->SetXY($this->posxdesc - 1, $tab_top + 1);
+			$pdf->MultiCell(108, 2, $outputlangs->transnoentities("Designation"), '', 'L');
+		}
+
+		if (!empty($conf->global->MAIN_GENERATE_INVOICES_WITH_PICTURE)) {
+			$pdf->line($this->posxpicture - 1, $tab_top, $this->posxpicture - 1, $tab_top + $tab_height);
+			if (empty($hidetop)) {
+				//$pdf->SetXY($this->posxpicture-1, $tab_top+1);
+				//$pdf->MultiCell($this->posxtva-$this->posxpicture-1,2, $outputlangs->transnoentities("Photo"),'','C');
+			}
+		}
+
+		if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) && empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN)) {
+			$pdf->line($this->posxtva - 1, $tab_top, $this->posxtva - 1, $tab_top + $tab_height);
+			if (empty($hidetop)) {
+				$pdf->SetXY($this->posxtva - 3, $tab_top + 1);
+				$pdf->MultiCell($this->posxup - $this->posxtva + 3, 2, $outputlangs->transnoentities("VAT"), '', 'C');
+			}
+		}
+
+		$pdf->line($this->posxup - 1, $tab_top, $this->posxup - 1, $tab_top + $tab_height);
+		if (empty($hidetop)) {
+			$pdf->SetXY($this->posxup - 1, $tab_top + 1);
+			$pdf->MultiCell($this->posxqty - $this->posxup - 1, 2, $outputlangs->transnoentities("PriceUHT"), '', 'C');
+		}
+
+		$pdf->line($this->posxqty - 1, $tab_top, $this->posxqty - 1, $tab_top + $tab_height);
+		if (empty($hidetop)) {
+			$pdf->SetXY($this->posxqty - 1, $tab_top + 1);
+			$pdf->MultiCell($this->posxunit - $this->posxqty - 1, 2, $outputlangs->transnoentities("Qty"), '', 'C');
+		}
+
+		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
+			$pdf->line($this->posxunit - 1, $tab_top, $this->posxunit - 1, $tab_top + $tab_height);
+			if (empty($hidetop)) {
+				$pdf->SetXY($this->posxunit - 1, $tab_top + 1);
+				$pdf->MultiCell($this->posxdiscount - $this->posxunit - 1, 2, $outputlangs->transnoentities("Unit"), '', 'C');
+			}
+		}
+
+		if ($this->atleastonediscount) {
+			$pdf->line($this->posxdiscount - 1, $tab_top, $this->posxdiscount - 1, $tab_top + $tab_height);
+			if (empty($hidetop)) {
+				$pdf->SetXY($this->posxdiscount - 1, $tab_top + 1);
+				$pdf->MultiCell($this->posxprogress - $this->posxdiscount + 1, 2, $outputlangs->transnoentities("ReductionShort"), '', 'C');
+			}
+		}
+
+		if ($this->situationinvoice) {
+			$pdf->line($this->posxprogress - 1, $tab_top, $this->posxprogress - 1, $tab_top + $tab_height);
+			if (empty($hidetop)) {
+				$pdf->SetXY($this->posxprogress, $tab_top + 1);
+				$pdf->MultiCell($this->postotalht - $this->posxprogress, 2, $outputlangs->transnoentities("ProgressShort"), '', 'C');
+			}
+		}
+
+		$pdf->line($this->postotalht, $tab_top, $this->postotalht, $tab_top + $tab_height);
+		if (empty($hidetop)) {
+			$pdf->SetXY($this->postotalht - 1, $tab_top + 1);
+			$pdf->MultiCell(30, 2, $outputlangs->transnoentities("TotalHT"), '', 'C');
+		}
+	}
+
+    protected function getEntityDataById($entity_id)
+    {
+        $entitiArray = [];
+        $sql = "SELECT rowid, label FROM llx_entity WHERE rowid = {$entity_id}";
+        $data = $this->db->query($sql);
+        if (!$data) {
+            return $entitiArray;
+        }
+        while ($row = pg_fetch_all($data)) {
+            return $row;
+        }
+    }
+
+	protected function getEntityVAT($entity_id) {
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_TVAINTRA';";
+		$details = $this->db->query($sql);
+		if (!$details) {
+			return null;
+		}
+		while ($row = pg_fetch_all($details)) {
+			return $row;
+		}
+	}
+
+	protected function getEntityLogo($entity_id) {
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_SOCIETE_LOGO_SMALL';";
+		$details = $this->db->query($sql);
+		if (!$details) {
+			return null;
+		}
+		while ($row = pg_fetch_all($details)) {
+			return $row;
+		}
+	}
+
+	protected function getEntityAddress($entity_id) {
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_SOCIETE_ZIP';";
+		$details = $this->db->query($sql);
+		$row = pg_fetch_all($details);
+		$zip = $row[0]['value'];
+
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_SOCIETE_TOWN';";
+		$details = $this->db->query($sql);
+		$row = pg_fetch_all($details);
+		$city = $row[0]['value'];
+
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_SOCIETE_ADDRESS';";
+		$details = $this->db->query($sql);
+		$row = pg_fetch_all($details);
+		$address = $row[0]['value'];
+
+		return $zip.' '.$city.', '.$address;
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *  Show top header of page.
+	 *
+	 *  @param	TCPDF		$pdf     		Object PDF
+	 *  @param  Facture		$object     	Object to show
+	 *  @param  int	    	$showaddress    0=no, 1=yes
+	 *  @param  Translate	$outputlangs	Object lang for output
+	 *  @param  Translate	$outputlangsbis	Object lang for output bis
+	 *  @return	int							top shift of linked object lines
+	 */
+	protected function _pagehead(&$pdf, $object, $showaddress, $outputlangs, $outputlangsbis = null)
+	{
+		global $conf, $langs;
+
+		$ltrdirection = 'L';
+		if ($outputlangs->trans("DIRECTION") == 'rtl') $ltrdirection = 'R';
+
+		// Load traductions files required by page
+		$outputlangs->loadLangs(array("main", "bills", "propal", "companies"));
+
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		pdf_pagehead($pdf, $outputlangs, $this->page_hauteur);
+
+		$pdf->SetTextColor(0, 0, 60);
+		$pdf->SetFont('', 'B', $default_font_size + 3);
+
+		$w = 110;
+
+		$posy = $this->marge_haute;
+		$posx = $this->page_largeur - $this->marge_droite - $w;
+
+		$pdf->SetXY($this->marge_gauche, $posy);
+
+		// Logo
+		if (empty($conf->global->PDF_DISABLE_MYCOMPANY_LOGO)) {
+			$mycompanyLogo = $this->getEntityLogo($object->entity);
+			//print_r($mycompanyLogo);
+			//die();
+			if ($this->emetteur->logo) {
+				$logodir = $conf->mycompany->dir_output;
+				if (!empty($conf->mycompany->multidir_output[$object->entity])) {
+					$logodir = $conf->mycompany->multidir_output[$object->entity];
+				}
+				if (empty($conf->global->MAIN_PDF_USE_LARGE_LOGO)) {
+					$logo = $logodir.'/logos/thumbs/'.$mycompanyLogo[0]['value'];
+				} else {
+					$logo = $logodir.'/logos/'.$this->emetteur->logo;
+				}
+				if (is_readable($logo)) {
+					$height = pdf_getHeightForLogo($logo);
+					$pdf->Image($logo, $this->marge_gauche, $posy, 0, $height); // width=0 (auto)
+				} else {
+					$pdf->SetTextColor(200, 0, 0);
+					$pdf->SetFont('', 'B', $default_font_size - 2);
+					$pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorLogoFileNotFound", $logo), 0, 'L');
+					$pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorGoToGlobalSetup"), 0, 'L');
+				}
+			} else {
+				$text = $this->emetteur->name;
+				$pdf->MultiCell($w, 4, $outputlangs->convToOutputCharset($text), 0, $ltrdirection);
+			}
+		}
+
+		$pdf->SetFont('', 'B', $default_font_size + 3);
+		$pdf->SetXY($posx, $posy);
+		$pdf->SetTextColor(0, 0, 60);
+		$title = $outputlangs->transnoentities("PdfInvoiceTitle");
+		if ($object->type == 1) {
+			$title = $outputlangs->transnoentities("InvoiceReplacement");
+		}
+		if ($object->type == 2) {
+			$title = $outputlangs->transnoentities("InvoiceAvoir");
+		}
+		if ($object->type == 3) {
+			$title = $outputlangs->transnoentities("InvoiceDeposit");
+		}
+		if ($object->type == 4) {
+			$title = $outputlangs->transnoentities("InvoiceProForma");
+		}
+		if ($this->situationinvoice) {
+			$title = $outputlangs->transnoentities("PDFInvoiceSituation");
+		}
+		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) {
+			$title .= ' - ';
+			if ($object->type == 0) {
+				if ($this->situationinvoice) {
+					$title .= $outputlangsbis->transnoentities("PDFInvoiceSituation");
+				}
+				$title .= $outputlangsbis->transnoentities("PdfInvoiceTitle");
+			} elseif ($object->type == 1) {
+				$title .= $outputlangsbis->transnoentities("InvoiceReplacement");
+			} elseif ($object->type == 2) {
+				$title .= $outputlangsbis->transnoentities("InvoiceAvoir");
+			} elseif ($object->type == 3) {
+				$title .= $outputlangsbis->transnoentities("InvoiceDeposit");
+			} elseif ($object->type == 4) {
+				$title .= $outputlangsbis->transnoentities("InvoiceProForma");
+			}
+		}
+		$title .= ' '.$outputlangs->convToOutputCharset($object->ref);
+		if ($object->statut == $object::STATUS_DRAFT) {
+			$pdf->SetTextColor(128, 0, 0);
+			$title .= ' - '.$outputlangs->transnoentities("NotValidated");
+		}
+
+		$pdf->MultiCell($w, 3, $title, '', 'R');
+
+		$pdf->SetFont('', 'B', $default_font_size);
+
+		/*
+		$posy += 5;
+		$pdf->SetXY($posx, $posy);
+		$pdf->SetTextColor(0, 0, 60);
+		$textref = $outputlangs->transnoentities("Ref")." : ".$outputlangs->convToOutputCharset($object->ref);
+		if ($object->statut == $object::STATUS_DRAFT) {
+			$pdf->SetTextColor(128, 0, 0);
+			$textref .= ' - '.$outputlangs->transnoentities("NotValidated");
+		}
+		$pdf->MultiCell($w, 4, $textref, '', 'R');*/
+
+		$posy += 3;
+		$pdf->SetFont('', '', $default_font_size - 2);
+
+		if ($object->ref_client) {
+			$posy += 4;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer")." : ".$outputlangs->convToOutputCharset($object->ref_client), '', 'R');
+		}
+
+		if (!empty($conf->global->PDF_SHOW_PROJECT_TITLE)) {
+			$object->fetch_projet();
+			if (!empty($object->project->ref)) {
+				$posy += 3;
+				$pdf->SetXY($posx, $posy);
+				$pdf->SetTextColor(0, 0, 60);
+				$pdf->MultiCell($w, 3, $outputlangs->transnoentities("Project")." : ".(empty($object->project->title) ? '' : $object->project->title), '', 'R');
+			}
+		}
+
+		if (!empty($conf->global->PDF_SHOW_PROJECT)) {
+			$object->fetch_projet();
+			if (!empty($object->project->ref)) {
+				$outputlangs->load("projects");
+				$posy += 3;
+				$pdf->SetXY($posx, $posy);
+				$pdf->SetTextColor(0, 0, 60);
+				$pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefProject")." : ".(empty($object->project->ref) ? '' : $object->project->ref), '', 'R');
+			}
+		}
+
+		$objectidnext = $object->getIdReplacingInvoice('validated');
+		if ($object->type == 0 && $objectidnext) {
+			$objectreplacing = new Facture($this->db);
+			$objectreplacing->fetch($objectidnext);
+
+			$posy += 3;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementByInvoice").' : '.$outputlangs->convToOutputCharset($objectreplacing->ref), '', 'R');
+		}
+		if ($object->type == 1) {
+			$objectreplaced = new Facture($this->db);
+			$objectreplaced->fetch($object->fk_facture_source);
+
+			$posy += 4;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementInvoice").' : '.$outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R');
+		}
+		if ($object->type == 2 && !empty($object->fk_facture_source)) {
+			$objectreplaced = new Facture($this->db);
+			$objectreplaced->fetch($object->fk_facture_source);
+
+			$posy += 3;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("CorrectionInvoice").' : '.$outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R');
+		}
+
+		$posy += 4;
+		$pdf->SetXY($posx, $posy);
+		$pdf->SetTextColor(0, 0, 60);
+		$title = $outputlangs->transnoentities("DateInvoice");
+		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) {
+			$title .= ' - '.$outputlangsbis->transnoentities("DateInvoice");
+		}
+		$pdf->MultiCell($w, 3, $title." : ".dol_print_date($object->date, "day", false, $outputlangs, true), '', 'R');
+
+		if (!empty($conf->global->INVOICE_POINTOFTAX_DATE)) {
+			$posy += 4;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("DatePointOfTax")." : ".dol_print_date($object->date_pointoftax, "day", false, $outputlangs), '', 'R');
+		}
+
+		if ($object->type != 2) {
+			$posy += 3;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$title = $outputlangs->transnoentities("DateDue");
+			if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) {
+				$title .= ' - '.$outputlangsbis->transnoentities("DateDue");
+			}
+			$pdf->MultiCell($w, 3, $title." : ".dol_print_date($object->date_lim_reglement, "day", false, $outputlangs, true), '', 'R');
+		}
+
+		if (empty($conf->global->MAIN_PDF_HIDE_CUSTOMER_CODE) && $object->thirdparty->code_client) {
+			$posy += 3;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("CustomerCode")." : ".$outputlangs->transnoentities($object->thirdparty->code_client), '', 'R');
+		}
+
+		// Get contact
+		if (!empty($conf->global->DOC_SHOW_FIRST_SALES_REP)) {
+			$arrayidcontact = $object->getIdContact('internal', 'SALESREPFOLL');
+			if (count($arrayidcontact) > 0) {
+				$usertmp = new User($this->db);
+				$usertmp->fetch($arrayidcontact[0]);
+				$posy += 4;
+				$pdf->SetXY($posx, $posy);
+				$pdf->SetTextColor(0, 0, 60);
+				$pdf->MultiCell($w, 3, $langs->transnoentities("SalesRepresentative")." : ".$usertmp->getFullName($langs), '', 'R');
+			}
+		}
+
+		$posy += 1;
+
+		$top_shift = 0;
+		// Show list of linked objects
+		$current_y = $pdf->getY();
+		$posy = pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, $w, 3, 'R', $default_font_size);
+		if ($current_y < $pdf->getY()) {
+			$top_shift = $pdf->getY() - $current_y;
+		}
+
+		if ($showaddress) {
+			// Sender properties
+			//$carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object);
+			$entityDetails = $this->getEntityVAT($object->entity);
+			$entityAddress = $this->getEntityAddress($object->entity);
+			$entityInfo = $this->getEntityDataById($object->entity);
+			//print_r($entityDetails);
+			//die();
+			$entityName = $entityInfo[0]['label'];
+			$carac_emetteur = "\n".$entityAddress;
+			$carac_emetteur .= "\n\nVAT number: ".$entityDetails[0]['value'];
+
+			// Show sender
+			$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+			$posy += $top_shift;
+			$posx = $this->marge_gauche;
+			if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+				$posx = $this->page_largeur - $this->marge_droite - 80;
+			}
+
+			$hautcadre = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 38 : 40;
+			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
+
+
+			// Show sender frame
+			if (empty($conf->global->MAIN_PDF_NO_SENDER_FRAME)) {
+				$pdf->SetTextColor(0, 0, 0);
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->SetXY($posx, $posy - 5);
+				$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillFrom"), 0, $ltrdirection);
+				$pdf->SetXY($posx, $posy);
+				$pdf->SetFillColor(230, 230, 230);
+				$pdf->MultiCell($widthrecbox, $hautcadre, "", 0, 'R', 1);
+				$pdf->SetTextColor(0, 0, 60);
+			}
+
+			// Show sender name
+			if (empty($conf->global->MAIN_PDF_HIDE_SENDER_NAME)) {
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox - 2, 4, $outputlangs->convToOutputCharset($entityName), 0, $ltrdirection);
+				$posy = $pdf->getY();
+			}
+
+			// Show sender information
+			$pdf->SetXY($posx + 2, $posy);
+			$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]);
+			}
+
+			// 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 = str_replace("?","ő",$carac_client_name);
+			//print_r($carac_client_name);
+			//die();
+
+			$mode =  'target';
+			$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+
+			// Show recipient
+			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 100;
+			if ($this->page_largeur < 210) {
+				$widthrecbox = 84; // To work with US executive format
+			}
+			$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+			$posy += $top_shift;
+			$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+			if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+				$posx = $this->marge_gauche;
+			}
+
+			// Show recipient frame
+			if (empty($conf->global->MAIN_PDF_NO_RECIPENT_FRAME)) {
+				$pdf->SetTextColor(0, 0, 0);
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->SetXY($posx + 2, $posy - 5);
+				$pdf->MultiCell($widthrecbox - 2, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
+				$pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+			}
+
+			// Show recipient name
+			$pdf->SetXY($posx + 2, $posy + 3);
+			//$pdf->SetFont('', 'B', $default_font_size);
+			$pdf->SetFont('freeserif', 'B', $default_font_size);
+			$pdf->MultiCell($widthrecbox - 2, 2, $carac_client_name, 0, $ltrdirection);
+
+			$posy = $pdf->getY();
+
+			// Show recipient information
+			$pdf->SetFont('', '', $default_font_size - 1);
+			$pdf->SetXY($posx + 2, $posy);
+			$pdf->MultiCell($widthrecbox - 2, 4, $carac_client, 0, $ltrdirection);
+		}
+
+		$pdf->SetTextColor(0, 0, 0);
+		return $top_shift;
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *   	Show footer of page. Need this->emetteur object
+	 *
+	 *   	@param	TCPDF		$pdf     			PDF
+	 * 		@param	Facture		$object				Object to show
+	 *      @param	Translate	$outputlangs		Object lang for output
+	 *      @param	int			$hidefreetext		1=Hide free text
+	 *      @return	int								Return height of bottom margin including footer text
+	 */
+	protected function _pagefoot(&$pdf, $object, $outputlangs, $hidefreetext = 0)
+	{
+		global $conf;
+		$showdetails = empty($conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS) ? 0 : $conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS;
+		return pdf_pagefoot($pdf, $outputlangs, 'INVOICE_FREE_TEXT', $this->emetteur, $this->marge_basse, $this->marge_gauche, $this->page_hauteur, $object, $showdetails, $hidefreetext, $this->page_largeur, $this->watermark);
+	}
+}

+ 2354 - 0
core/modules/facture/doc/pdf_crabe_receipt.modules.php

@@ -0,0 +1,2354 @@
+<?php
+/* Copyright (C) 2004-2014	Laurent Destailleur	<eldy@users.sourceforge.net>
+ * Copyright (C) 2005-2012	Regis Houssin		<regis.houssin@inodbox.com>
+ * Copyright (C) 2008		Raphael Bertrand		<raphael.bertrand@resultic.fr>
+ * Copyright (C) 2010-2014	Juanjo Menent		<jmenent@2byte.es>
+ * Copyright (C) 2012		Christophe Battarel	<christophe.battarel@altairis.fr>
+ * Copyright (C) 2012		Cédric Salvador		<csalvador@gpcsolutions.fr>
+ * Copyright (C) 2012-2014	Raphaël Doursenaud	<rdoursenaud@gpcsolutions.fr>
+ * Copyright (C) 2015		Marcos García		<marcosgdf@gmail.com>
+ * Copyright (C) 2017-2018	Ferran Marcet		<fmarcet@2byte.es>
+ * Copyright (C) 2018-2020  Frédéric France     <frederic.france@netlogic.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ * or see https://www.gnu.org/
+ */
+
+/**
+ *	\file       htdocs/core/modules/facture/doc/pdf_crabe_receipt.modules.php
+ *	\ingroup    facture
+ *	\brief      File of class to generate customers invoices from crabe model
+ */
+
+require_once DOL_DOCUMENT_ROOT . '/core/modules/facture/modules_facture.php';
+require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
+require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
+require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
+
+
+/**
+ *	Class to generate the customer invoice PDF with template Receipt
+ */
+class pdf_crabe_receipt extends ModelePDFFactures
+{
+	/**
+	 * @var DoliDb Database handler
+	 */
+	public $db;
+
+	/**
+	 * @var string model name
+	 */
+	public $name;
+
+	/**
+	 * @var string model description (short text)
+	 */
+	public $description;
+
+	/**
+	 * @var int 	Save the name of generated file as the main doc when generating a doc with this template
+	 */
+	public $update_main_doc_field;
+
+	/**
+	 * @var string document type
+	 */
+	public $type;
+
+	/**
+	 * @var array Minimum version of PHP required by module.
+	 * e.g.: PHP ≥ 5.6 = array(5, 6)
+	 */
+	public $phpmin = array(5, 6);
+
+	/**
+	 * Dolibarr version of the loaded document
+	 * @var string
+	 */
+	public $version = 'dolibarr';
+
+	/**
+	 * @var int page_largeur
+	 */
+	public $page_largeur;
+
+	/**
+	 * @var int page_hauteur
+	 */
+	public $page_hauteur;
+
+	/**
+	 * @var array format
+	 */
+	public $format;
+
+	/**
+	 * @var int marge_gauche
+	 */
+	public $marge_gauche;
+
+	/**
+	 * @var int marge_droite
+	 */
+	public $marge_droite;
+
+	/**
+	 * @var int marge_haute
+	 */
+	public $marge_haute;
+
+	/**
+	 * @var int marge_basse
+	 */
+	public $marge_basse;
+
+	/**
+	 * Issuer
+	 * @var Societe Object that emits
+	 */
+	public $emetteur;
+
+	/**
+	 * @var bool Situation invoice type
+	 */
+	public $situationinvoice;
+
+	/**
+	 * @var float X position for the situation progress column
+	 */
+	public $posxprogress;
+
+
+	/**
+	 *	Constructor
+	 *
+	 *  @param		DoliDB		$db      Database handler
+	 */
+	public function __construct($db)
+	{
+		global $conf, $langs, $mysoc;
+
+		// Translations
+		$langs->loadLangs(array("main", "bills"));
+
+		$this->db = $db;
+		$this->name = "receipt";
+		$this->description = $langs->trans('PDFCrabeDescription');
+		$this->update_main_doc_field = 1; // Save the name of generated file as the main doc when generating a doc with this template
+
+		// Dimension page
+		$this->type = 'pdf';
+		$formatarray = pdf_getFormat();
+		$this->page_largeur = $formatarray['width'];
+		$this->page_hauteur = $formatarray['height'];
+		$this->format = array($this->page_largeur, $this->page_hauteur);
+		$this->marge_gauche = isset($conf->global->MAIN_PDF_MARGIN_LEFT) ? $conf->global->MAIN_PDF_MARGIN_LEFT : 10;
+		$this->marge_droite = isset($conf->global->MAIN_PDF_MARGIN_RIGHT) ? $conf->global->MAIN_PDF_MARGIN_RIGHT : 10;
+		$this->marge_haute = isset($conf->global->MAIN_PDF_MARGIN_TOP) ? $conf->global->MAIN_PDF_MARGIN_TOP : 10;
+		$this->marge_basse = isset($conf->global->MAIN_PDF_MARGIN_BOTTOM) ? $conf->global->MAIN_PDF_MARGIN_BOTTOM : 10;
+
+		$this->option_logo = 1; // Display logo
+		$this->option_tva = 1; // Manage the vat option FACTURE_TVAOPTION
+		$this->option_modereg = 1; // Display payment mode
+		$this->option_condreg = 1; // Display payment terms
+		$this->option_multilang = 1; // Available in several languages
+		$this->option_escompte = 1; // Displays if there has been a discount
+		$this->option_credit_note = 1; // Support credit notes
+		$this->option_freetext = 1; // Support add of a personalised text
+		$this->option_draft_watermark = 1; // Support add of a watermark on drafts
+		$this->watermark = '';
+
+		// Get source company
+		$this->emetteur = $mysoc;
+		if (empty($this->emetteur->country_code)) {
+			$this->emetteur->country_code = substr($langs->defaultlang, -2); // By default, if was not defined
+		}
+
+		// Define position of columns
+		$this->posxdesc = $this->marge_gauche + 1;
+		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
+			$this->posxtva = 101; 		//101
+			$this->posxtvaprice = 118;	//118
+			$this->posxup = 135;		//135
+			$this->posxqty = 151;		//151
+			$this->posxunit = 191;		//191
+			$this->posxtva = 200;		//200
+		} else {
+			$this->posxtva = 70;		//70
+			$this->posxtvaprice = 85;	//100
+			$this->posxup = 105;		//116
+			$this->posxqty = 130;		//135
+			$this->posxunit = 140;		//152
+		}
+		$this->posxprogress = 151; // Only displayed for situation invoices
+		$this->posxdiscount = 162;
+		$this->posxprogress = 174;
+		$this->postotalht = 160;		//164
+		if (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) || !empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN)) {
+			$this->posxtva = $this->posxup;
+		}
+		$this->posxpicture = $this->posxtva - (empty($conf->global->MAIN_DOCUMENTS_WITH_PICTURE_WIDTH) ? 20 : $conf->global->MAIN_DOCUMENTS_WITH_PICTURE_WIDTH); // width of images
+		if ($this->page_largeur < 210) { // To work with US executive format
+			$this->posxpicture -= 20;
+			$this->posxtvaprice -= 20;
+			$this->posxtva -= 20;
+			$this->posxup -= 20;
+			$this->posxqty -= 20;
+			$this->posxunit -= 20;
+			$this->posxdiscount -= 20;
+			$this->posxprogress -= 20;
+			$this->postotalht -= 20;
+		}
+
+		$this->tva = array();
+		$this->tva_array = array();
+		$this->localtax1 = array();
+		$this->localtax2 = array();
+		$this->atleastoneratenotnull = 0;
+		$this->atleastonediscount = 0;
+		$this->hoursdiscount = 0;
+		$this->eventDate = '';
+		$this->situationinvoice = false;
+	}
+
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	/**
+	 *  Function to build pdf onto disk
+	 *
+	 *  @param		Facture		$object				Object to generate
+	 *  @param		Translate	$outputlangs		Lang output object
+	 *  @param		string		$srctemplatepath	Full path of source filename for generator using a template file
+	 *  @param		int			$hidedetails		Do not show line details
+	 *  @param		int			$hidedesc			Do not show desc
+	 *  @param		int			$hideref			Do not show ref
+	 *  @return     int         	    			1=OK, 0=KO
+	 */
+	public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0)
+	{
+		// phpcs:enable
+		global $user, $langs, $conf, $mysoc, $hookmanager, $nblines;
+
+		dol_syslog("write_file outputlangs->defaultlang=" . (is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
+
+		if (!is_object($outputlangs)) {
+			$outputlangs = $langs;
+		}
+		// For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO
+		if (!empty($conf->global->MAIN_USE_FPDF)) {
+			$outputlangs->charset_output = 'ISO-8859-1';
+		}
+
+		// Load translation files required by the page
+		$outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies"));
+
+		// Show Draft Watermark
+		if ($object->statut == $object::STATUS_DRAFT && (!empty($conf->global->FACTURE_DRAFT_WATERMARK))) {
+			$this->watermark = $conf->global->FACTURE_DRAFT_WATERMARK;
+		}
+
+		global $outputlangsbis;
+		$outputlangsbis = null;
+		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && $outputlangs->defaultlang != $conf->global->PDF_USE_ALSO_LANGUAGE_CODE) {
+			$outputlangsbis = new Translate('', $conf);
+			$outputlangsbis->setDefaultLang($conf->global->PDF_USE_ALSO_LANGUAGE_CODE);
+			$outputlangsbis->loadLangs(array("main", "bills", "products", "dict", "companies"));
+		}
+
+		$nblines = count($object->lines);
+
+		// Loop on each lines to detect if there is at least one image to show
+		$realpatharray = array();
+		if (!empty($conf->global->MAIN_GENERATE_INVOICES_WITH_PICTURE)) {
+			for ($i = 0; $i < $nblines; $i++) {
+				if (empty($object->lines[$i]->fk_product)) {
+					continue;
+				}
+
+				$objphoto = new Product($this->db);
+				$objphoto->fetch($object->lines[$i]->fk_product);
+
+				$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;
+
+				$realpath = '';
+				foreach ($objphoto->liste_photos($dir, 1) as $key => $obj) {
+					$filename = $obj['photo'];
+					//if ($obj['photo_vignette']) $filename='thumbs/'.$obj['photo_vignette'];
+					$realpath = $dir . $filename;
+					break;
+				}
+
+				if ($realpath) {
+					$realpatharray[$i] = $realpath;
+				}
+			}
+		}
+		if (count($realpatharray) == 0) {
+			$this->posxpicture = $this->posxtva;
+		}
+
+		if ($conf->facture->dir_output) {
+			$object->fetch_thirdparty();
+
+			$deja_regle = $object->getSommePaiement((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0);
+			$amount_credit_notes_included = $object->getSumCreditNotesUsed((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0);
+			$amount_deposits_included = $object->getSumDepositsUsed((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0);
+
+			// Definition of $dir and $file
+			if ($object->specimen) {
+				$dir = empty($conf->facture->multidir_output[$conf->entity]) ? $conf->facture->dir_output : $conf->facture->multidir_output[$conf->entity];
+				$file = $dir . "/SPECIMEN.pdf";
+			} else {
+				$objectref = dol_sanitizeFileName($object->ref);
+				$dir = (empty($conf->facture->multidir_output[$conf->entity]) ? $conf->facture->dir_output : $conf->facture->multidir_output[$conf->entity]) . "/" . $objectref;
+				$file = $dir . "/" . $objectref . ".pdf";
+			}
+			if (!file_exists($dir)) {
+				if (dol_mkdir($dir) < 0) {
+					$this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
+					return 0;
+				}
+			}
+
+			if (file_exists($dir)) {
+				// Add pdfgeneration hook
+				if (!is_object($hookmanager)) {
+					include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
+					$hookmanager = new HookManager($this->db);
+				}
+				$hookmanager->initHooks(array('pdfgeneration'));
+				$parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
+				global $action;
+				$reshook = $hookmanager->executeHooks('beforePDFCreation', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+
+				// Set nblines with the new facture lines content after hook
+				$nblines = count($object->lines);
+				$nbpayments = count($object->getListOfPayments());
+
+				// Create pdf instance
+				$pdf = pdf_getInstance($this->format);
+				$default_font_size = pdf_getPDFFontSize($outputlangs); // Must be after pdf_getInstance
+				$pdf->SetAutoPageBreak(1, 0);
+
+				$heightforinfotot = 50 + (4 * $nbpayments); // Height reserved to output the info and total part and payment part
+				if ($heightforinfotot > 220) {
+					$heightforinfotot = 220;
+				}
+				$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 + 8; // Height reserved to output the footer (value include bottom margin)
+				if (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS)) {
+					$heightforfooter += 6;
+				}
+
+				if (class_exists('TCPDF')) {
+					$pdf->setPrintHeader(false);
+					$pdf->setPrintFooter(false);
+				}
+				$pdf->SetFont(pdf_getPDFFont($outputlangs));
+
+				// Set path to the background PDF File
+				if (!empty($conf->global->MAIN_ADD_PDF_BACKGROUND)) {
+					$logodir = $conf->mycompany->dir_output;
+					if (!empty($conf->mycompany->multidir_output[$object->entity])) {
+						$logodir = $conf->mycompany->multidir_output[$object->entity];
+					}
+					$pagecount = $pdf->setSourceFile($logodir . '/' . $conf->global->MAIN_ADD_PDF_BACKGROUND);
+					$tplidx = $pdf->importPage(1);
+				}
+
+				$pdf->Open();
+				$pagenb = 0;
+				$pdf->SetDrawColor(128, 128, 128);
+
+				$pdf->SetTitle($outputlangs->convToOutputCharset($object->ref));
+				$pdf->SetSubject($outputlangs->transnoentities("PdfInvoiceTitle"));
+				$pdf->SetCreator("Dolibarr " . DOL_VERSION);
+				$pdf->SetAuthor($mysoc->name . ($user->id > 0 ? ' - ' . $outputlangs->convToOutputCharset($user->getFullName($outputlangs)) : ''));
+				$pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref) . " " . $outputlangs->transnoentities("PdfInvoiceTitle") . " " . $outputlangs->convToOutputCharset($object->thirdparty->name));
+				if (!empty($conf->global->MAIN_DISABLE_PDF_COMPRESSION)) {
+					$pdf->SetCompression(false);
+				}
+
+				// Set certificate
+				$cert = empty($user->conf->CERTIFICATE_CRT) ? '' : $user->conf->CERTIFICATE_CRT;
+				$certprivate = empty($user->conf->CERTIFICATE_CRT_PRIVATE) ? '' : $user->conf->CERTIFICATE_CRT_PRIVATE;
+				// If user has no certificate, we try to take the company one
+				if (!$cert) {
+					$cert = empty($conf->global->CERTIFICATE_CRT) ? '' : $conf->global->CERTIFICATE_CRT;
+				}
+				if (!$certprivate) {
+					$certprivate = empty($conf->global->CERTIFICATE_CRT_PRIVATE) ? '' : $conf->global->CERTIFICATE_CRT_PRIVATE;
+				}
+				// If a certificate is found
+				if ($cert) {
+					$info = array(
+						'Name' => $this->emetteur->name,
+						'Location' => getCountry($this->emetteur->country_code, 0),
+						'Reason' => 'INVOICE',
+						'ContactInfo' => $this->emetteur->email
+					);
+					$pdf->setSignature($cert, $certprivate, $this->emetteur->name, '', 2, $info);
+				}
+
+				$pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right
+
+				// Set $this->atleastonediscount if you have at least one discount
+				for ($i = 0; $i < $nblines; $i++) {
+					if ($object->lines[$i]->remise_percent) {
+						$this->atleastonediscount++;
+					}
+					if ($object->lines[$i]->array_options['options_discount_hours']) {
+						$this->hoursdiscount = true;
+					}
+				}
+				/* if (empty($this->atleastonediscount)) {    // retrieve space not used by discount
+					$delta = ($this->posxprogress - $this->posxdiscount);
+					$this->posxpicture += $delta;
+					$this->posxtva += $delta;
+					$this->posxtvaprice += $delta;
+					$this->posxup += $delta;
+					//$this->posxqty += $delta;
+					$this->posxunit += $delta;
+					$this->posxdiscount += $delta;
+					// post of fields after are not modified, stay at same position
+				} */
+
+				//print_r($object);exit;
+
+				$progress_width = 0;
+				// Situation invoice handling
+				if ($object->situation_cycle_ref && empty($conf->global->MAIN_PDF_HIDE_SITUATION)) {
+					$this->situationinvoice = true;
+					$progress_width = 10;
+					$this->posxpicture -= $progress_width;
+					$this->posxtva -= $progress_width;
+					$this->posxtvaprice -= $progress_width;
+					$this->posxup -= $progress_width;
+					//$this->posxqty -= $progress_width;
+					$this->posxunit -= $progress_width;
+					$this->posxdiscount -= $progress_width;
+					$this->posxprogress -= $progress_width;
+				}
+
+				// New page
+				$pdf->AddPage();
+				if (!empty($tplidx)) {
+					$pdf->useTemplate($tplidx);
+				}
+				$pagenb++;
+
+				// Output header (logo, ref and address blocks). This is first call for first page.
+				$top_shift = $this->_pagehead($pdf, $object, 1, $outputlangs);
+				$pdf->SetFont('', '', $default_font_size - 1);
+				$pdf->MultiCell(0, 3, ''); // Set interline to 3
+				$pdf->SetTextColor(0, 0, 0);
+
+				// $pdf->GetY() here can't be used. It is bottom of the second addresse box but first one may be higher
+
+				// $tab_top is y where we must continue content (90 = 42 + 48: 42 is height of logo and ref, 48 is address blocks)
+				$tab_top = 90 + $top_shift;		// top_shift is an addition for linked objects or addons (0 in most cases)
+				$tab_top_newpage = (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD) ? 42 + $top_shift : 10);
+
+				// You can add more thing under header here, if you increase $extra_under_address_shift too.
+				$extra_under_address_shift = 0;
+				$qrcodestring = '';
+				if (!empty($conf->global->INVOICE_ADD_ZATCA_QR_CODE)) {
+					$qrcodestring = $object->buildZATCAQRString();
+				} elseif (!empty($conf->global->INVOICE_ADD_SWISS_QR_CODE)) {
+					$qrcodestring = $object->buildSwitzerlandQRString();
+				}
+
+				$tsForQR = time();
+
+				if ($action !== 'confirm_deleteline' && $object->type !== '2' && $object->type !== '8') {
+					$sql = "SELECT * FROM " . MAIN_DB_PREFIX . "bbus_bbticket WHERE fk_facture = '" . $object->id . "'";
+					$res = $this->db->getRow($sql);
+					if (is_object($res)) {
+						$row = (array) $res;
+
+						$checkSQL = "SELECT * FROM " . MAIN_DB_PREFIX . "bbus_bbticketinvoiceprinting WHERE fk_facture = '" . $object->id . "' AND ticket_id = '" . $row['rowid'] . "'";
+						$resCheck = $this->db->query($checkSQL);
+
+						if ($this->db->num_rows($resCheck) > 0) {
+							while ($checkRow = $this->db->fetch_object($resCheck)) {
+								$rowCheck = (array) $checkRow;
+							}
+						}
+					}
+				}
+				if ($rowCheck['fk_facture'] == '' && intval($object->statut) === 2 && intval($object->array_options['options_app_facture']) === 0 && $object->type !== '2' && $object->type !== '8') {
+					$sqlNOAPP = "SELECT * FROM " . MAIN_DB_PREFIX . "bbus_bbticket WHERE fk_facture = '" . $object->id . "'";
+					$resNOAPP = $this->db->query($sqlNOAPP);
+					if ($this->db->num_rows($resNOAPP) > 0) {
+
+
+						while ($row = $this->db->fetch_object($resNOAPP)) {
+							$allNOAPP[] = $row;
+						}
+						global $user;
+						if (count($allNOAPP) == 1) {
+							foreach ($allNOAPP as $row) {
+								$row = (array) $row;
+								$this->db->begin();
+								$ticketPrintSQL = "INSERT INTO " . $this->db->prefix() . "bbus_bbticketinvoiceprinting 
+							(fk_user_creat,status,fk_facture,fk_user_api_key,ticket_id,printing_date,printing_date_timestamp,product_id, date_creation) VALUES  
+							('" . $row['fk_user_creat'] . "', '0', '" . $row['fk_facture'] . "', '{$user->api_key}', '" . $row['rowid'] . "', '" . date("Y-m-d H:i:s", $tsForQR) . "', '" . $tsForQR . "', '" . $row['ticket_id'] . "', '" . date("Y-m-d H:i:s", $tsForQR) . "');";
+								if ($this->db->query($ticketPrintSQL)) {
+									$this->db->commit();
+								} else {
+									$errorMsg = $this->db->lasterror;
+									$this->db->rollback();
+								}
+							}
+						} else {
+							foreach ($allNOAPP as $row) {
+								$row = (array) $row;
+								$this->db->begin();
+								$ticketPrintSQL1 = "INSERT INTO " . $this->db->prefix() . "bbus_bbticketinvoiceprinting 
+							(fk_user_creat,status,fk_facture,fk_user_api_key,ticket_id,printing_date,printing_date_timestamp,product_id, date_creation) VALUES  
+							('" . $row['fk_user_creat'] . "', '0', '" . $row['fk_facture'] . "', '{$user->api_key}', '" . $row['rowid'] . "', '" . date("Y-m-d H:i:s", $tsForQR) . "', '" . $tsForQR . "', '" . $row['ticket_id'] . "', '" . date("Y-m-d H:i:s", $tsForQR) . "');";
+								//print $ticketPrintSQL1;exit;
+								if ($this->db->query($ticketPrintSQL1)) {
+									$this->db->commit();
+								} else {
+									$errorMsg = $this->db->lasterror;
+									$this->db->rollback();
+								}
+							}
+						}
+					}
+				}
+				$qrcodestring = $object->ref . '_' . $tsForQR;
+
+
+				if ($qrcodestring) {
+					$qrcodecolor = array('25', '25', '25');
+					// set style for QR-code
+					$styleQr = array(
+						'border' => false,
+						'padding' => 0,
+						'fgcolor' => $qrcodecolor,
+						'bgcolor' => false, //array(255,255,255)
+						'module_width' => 1, // width of a single module in points
+						'module_height' => 1 // height of a single module in points
+					);
+					$pdf->write2DBarcode($qrcodestring, 'QRCODE,M', $this->marge_gauche, $tab_top - 5, 25, 25, $styleQr, 'N');
+					$extra_under_address_shift += 25;
+				}
+
+				// Call hook printUnderHeaderPDFline
+				$parameters = array(
+					'object' => $object,
+					'i' => $i,
+					'pdf' => &$pdf,
+					'outputlangs' => $outputlangs,
+					'hidedetails' => $hidedetails
+				);
+				$reshook = $hookmanager->executeHooks('printUnderHeaderPDFline', $parameters, $this); // Note that $object may have been modified by hook
+				if (!empty($hookmanager->resArray['extra_under_address_shift'])) {
+					$extra_under_address_shift += $hookmanager->resArray['extra_under_header_shift'];
+				}
+
+				$tab_top += $extra_under_address_shift;
+				$tab_top_newpage += 0;
+
+				// Incoterm
+				$height_incoterms = 0;
+				if (!empty($conf->incoterm->enabled)) {
+					$desc_incoterms = $object->getIncotermsForPDF();
+					if ($desc_incoterms) {
+						$tab_top -= 2;
+
+						$pdf->SetFont('', '', $default_font_size - 1);
+						$pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top - 1, dol_htmlentitiesbr($desc_incoterms), 0, 1);
+						$nexY = $pdf->GetY();
+						$height_incoterms = $nexY - $tab_top;
+
+						// Rect takes a length in 3rd parameter
+						$pdf->SetDrawColor(192, 192, 192);
+						$pdf->Rect($this->marge_gauche, $tab_top - 1, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $height_incoterms + 1);
+
+						$tab_top = $nexY + 6;
+					}
+				}
+
+				// Display notes
+				$notetoshow = empty($object->note_public) ? '' : $object->note_public;
+				if (!empty($conf->global->MAIN_ADD_SALE_REP_SIGNATURE_IN_NOTE)) {
+					// Get first sale rep
+					if (is_object($object->thirdparty)) {
+						$salereparray = $object->thirdparty->getSalesRepresentatives($user);
+						$salerepobj = new User($this->db);
+						$salerepobj->fetch($salereparray[0]['id']);
+						if (!empty($salerepobj->signature)) {
+							$notetoshow = dol_concatdesc($notetoshow, $salerepobj->signature);
+						}
+					}
+				}
+				// Extrafields in note
+				$extranote = $this->getExtrafieldsInHtml($object, $outputlangs);
+				if (!empty($extranote)) {
+					$notetoshow = dol_concatdesc($notetoshow, $extranote);
+				}
+				if ($notetoshow) {
+					$tab_top -= 2;
+
+					$substitutionarray = pdf_getSubstitutionArray($outputlangs, null, $object);
+					complete_substitutions_array($substitutionarray, $outputlangs, $object);
+
+					$notetoshow = make_substitutions($notetoshow, $substitutionarray, $outputlangs);
+					$notetoshow = convertBackOfficeMediasLinksToPublicLinks($notetoshow);
+
+					$pdf->SetFont('', '', $default_font_size - 1);
+					$pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top - 1, dol_htmlentitiesbr($notetoshow), 0, 1);
+					$nexY = $pdf->GetY();
+					$height_note = $nexY - $tab_top;
+
+					// Rect takes a length in 3rd parameter
+					$pdf->SetDrawColor(192, 192, 192);
+					$pdf->Rect($this->marge_gauche, $tab_top - 1, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $height_note + 1);
+
+					$tab_top = $nexY + 6;
+				}
+
+				$iniY = $tab_top + 7;
+				$curY = $tab_top + 7;
+				$nexY = $tab_top + 7;
+
+				// Loop on each lines
+				for ($i = 0; $i < $nblines; $i++) {
+					global $db;
+					$eventSql = "SELECT ee.date_start FROM public.llx_eventwizard_eventhistory as ee
+					INNER JOIN llx_actioncomm as ac ON ac.id = ee.fk_event
+					WHERE ee.fk_facture = {$object->lines[$i]->fk_facture}";
+
+					$eventResult = $this->db->query($eventSql);
+					if ($this->db->num_rows($eventResult) > 0){
+						while($row = $this->db->fetch_object($eventResult)){
+							$this->eventDate = $row->date_start;
+						}
+					}
+					$curY = $nexY;
+					$pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage
+					$pdf->SetTextColor(0, 0, 0);
+
+					// Define size of image if we need it
+					$imglinesize = array();
+					if (!empty($realpatharray[$i])) {
+						$imglinesize = pdf_getSizeForImage($realpatharray[$i]);
+					}
+
+					$pdf->setTopMargin($tab_top_newpage);
+					$pdf->setPageOrientation('', 1, $heightforfooter + $heightforfreetext + $heightforinfotot); // The only function to edit the bottom margin of current page to set it.
+					$pageposbefore = $pdf->getPage();
+
+					$showpricebeforepagebreak = 1;
+					$posYAfterImage = 0;
+					$posYAfterDescription = 0;
+
+					// We start with Photo of product line
+					if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imglinesize['height']) > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforinfotot))) {	// If photo too high, we moved completely on new page
+						$pdf->AddPage('', '', true);
+						if (!empty($tplidx)) {
+							$pdf->useTemplate($tplidx);
+						}
+						if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+							$this->_pagehead($pdf, $object, 0, $outputlangs);
+						}
+						$pdf->setPage($pageposbefore + 1);
+
+						$curY = $tab_top_newpage;
+
+						// Allows data in the first page if description is long enough to break in multiples pages
+						if (!empty($conf->global->MAIN_PDF_DATA_ON_FIRST_PAGE)) {
+							$showpricebeforepagebreak = 1;
+						} else {
+							$showpricebeforepagebreak = 0;
+						}
+					}
+
+					if (isset($imglinesize['width']) && isset($imglinesize['height'])) {
+						$curX = $this->posxpicture - 1;
+						$pdf->Image($realpatharray[$i], $curX + (($this->posxtva - $this->posxpicture - $imglinesize['width']) / 2), $curY, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi
+						// $pdf->Image does not increase value return by getY, so we save it manually
+						$posYAfterImage = $curY + $imglinesize['height'];
+					}
+
+					// Description of product line
+					$curX = $this->posxdesc - 1;
+
+					$pdf->startTransaction();
+					//$hidedesc = 1;
+					//$hideref = 1;
+					//Termék / Product
+					$object->lines[$i]->description = $object->lines[$i]->product_desc;
+					$object->lines[$i]->desc = $object->lines[$i]->product_desc;
+					$hideref = 1;
+					$hidedesc = 0;
+					pdf_writelinedesc($pdf, $object, $i, $outputlangs, $this->posxpicture - $curX - $progress_width, 3, $curX, $curY, $hideref, $hidedesc);
+					if($this->eventDate !== ''){
+						$pdf->MultiCell(50, 3, $this->eventDate, 0, 'R');
+					}
+					$pageposafter = $pdf->getPage();
+					if ($pageposafter > $pageposbefore) {	// There is a pagebreak
+						$pdf->rollbackTransaction(true);
+						$pageposafter = $pageposbefore;
+						//print $pageposafter.'-'.$pageposbefore;exit;
+						$pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it.
+						pdf_writelinedesc($pdf, $object, $i, $outputlangs, $this->posxpicture - $curX - $progress_width, 3, $curX, $curY, $hideref, $hidedesc);
+						$pageposafter = $pdf->getPage();
+						$posyafter = $pdf->GetY();
+						//var_dump($posyafter); var_dump(($this->page_hauteur - ($heightforfooter+$heightforfreetext+$heightforinfotot))); exit;
+						if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforinfotot))) {	// There is no space left for total+free text
+							if ($i == ($nblines - 1)) {	// No more lines, and no space left to show total, so we create a new page
+								$pdf->AddPage('', '', true);
+								if (!empty($tplidx)) {
+									$pdf->useTemplate($tplidx);
+								}
+								if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+									$this->_pagehead($pdf, $object, 0, $outputlangs);
+								}
+								$pdf->setPage($pageposafter + 1);
+							}
+						} else {
+							// We found a page break
+
+							// Allows data in the first page if description is long enough to break in multiples pages
+							if (!empty($conf->global->MAIN_PDF_DATA_ON_FIRST_PAGE)) {
+								$showpricebeforepagebreak = 1;
+							} else {
+								$showpricebeforepagebreak = 0;
+							}
+						}
+					} else // No pagebreak
+					{
+						$pdf->commitTransaction();
+					}
+					$posYAfterDescription = $pdf->GetY();
+
+					$nexY = $pdf->GetY();
+					$pageposafter = $pdf->getPage();
+					$pdf->setPage($pageposbefore);
+					$pdf->setTopMargin($this->marge_haute);
+					$pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
+
+					// We suppose that a too long description or photo were moved completely on next page
+					if ($pageposafter > $pageposbefore && empty($showpricebeforepagebreak)) {
+						$pdf->setPage($pageposafter);
+						$curY = $tab_top_newpage;
+					}
+
+					$pdf->SetFont('', '', $default_font_size - 1); // We reposition the default font
+
+					// VAT Rate
+					if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) && empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN)) {
+						$vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails);
+						$pdf->SetXY($this->posxtva - 25, $curY);
+						$vat_rate = $this->setCPercent(rtrim($vat_rate, "%"));
+						$pdf->MultiCell($this->posxup - $this->posxtva + 4, 3, $vat_rate, 0, 'R');
+					}
+
+					//print_r($outputlangs);exit;
+
+					// VAT Price
+					/* if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) && empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN)) {
+									   $vat_price = pdf_getlinevatprice($object, $i, $outputlangs, $hidedetails);
+									   $pdf->SetXY($this->posxtvaprice - 5, $curY);
+									   $pdf->MultiCell($this->posxup - $this->posxtvaprice + 4, 3, $vat_price, 0, 'R');
+								   }
+
+								   // Unit price before discount
+								   $up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails);
+								   $pdf->SetXY($this->posxup, $curY);
+								   $pdf->MultiCell($this->posxqty - $this->posxup - 0.8, 3, $up_excl_tax, 0, 'R', 0);
+				*/
+					// Quantity
+					/* $qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
+					$pdf->SetXY($this->posxtvaprice - 5, $curY);
+					$pdf->MultiCell($this->posxunit - $this->posxqty - 0.8, 4, $qty, 0, 'R'); // Enough for 6 chars */
+
+					$qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails);
+					$pdf->SetXY($this->posxtvaprice - 20, $curY);
+					$pdf->MultiCell($this->posxunit - 80, 4, pdf_getlinetotalwithtax($object, $i, $outputlangs, $hidedetails), 0, 'R'); // Enough for 6 chars
+
+					// Unit
+					if (!empty($conf->global->PRODUCT_USE_UNITS)) {
+						$unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails, $hookmanager);
+						$pdf->SetXY($this->posxunit, $curY);
+						$pdf->MultiCell($this->posxdiscount - $this->posxunit - 0.8, 4, $unit, 0, 'L');
+					}
+
+					// Discount on line Hours
+					$pdf->SetXY($this->posxdiscount - 43, $curY);
+					if ($object->lines[$i]->array_options['options_discount_hours']) {
+						$remise_hours = '+' . $object->lines[$i]->array_options['options_discount_hours'] . ' ' . $outputlangs->transnoentities("Hour(s) / Óra");
+						$pdf->MultiCell($this->posxprogress - $this->posxdiscount + 20, 3, $remise_hours, 0, 'R');
+					}
+					// Discount on line Percent
+					$pdf->SetXY($this->posxdiscount - 5, $curY);
+					if ($object->lines[$i]->remise_percent) {
+						$remise_percent = '-' . pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails);
+						$pdf->MultiCell($this->posxprogress - $this->posxdiscount + 2, 3, $remise_percent, 0, 'R');
+					}
+
+					// Situation progress
+					if ($this->situationinvoice) {
+						$progress = pdf_getlineprogress($object, $i, $outputlangs, $hidedetails);
+						$pdf->SetXY($this->posxprogress, $curY);
+						$pdf->MultiCell($this->postotalht - $this->posxprogress + 1, 3, $progress, 0, 'R');
+					}
+
+					// Total HT line
+					//$total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails);
+					//$pdf->SetXY($this->postotalht, $curY);
+					//$pdf->MultiCell($this->page_largeur - $this->marge_droite - $this->postotalht, 3, $total_excl_tax, 0, 'R', 0);
+
+					// Total GROSS line
+					$total_excl_tax = pdf_getlinetotalwithtax($object, $i, $outputlangs, $hidedetails);
+					$pdf->SetXY($this->postotalht, $curY);
+					$pdf->MultiCell($this->page_largeur - $this->marge_droite - $this->postotalht, 3, $total_excl_tax, 0, 'R', 0);
+
+
+					$sign = 1;
+					if (isset($object->type) && $object->type == 2 && !empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) {
+						$sign = -1;
+					}
+					// Collection of totals by value of VAT in $this->tva["taux"]=total_tva
+					$prev_progress = $object->lines[$i]->get_prev_progress($object->id);
+					if ($prev_progress > 0 && !empty($object->lines[$i]->situation_percent)) { // Compute progress from previous situation
+						if (!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) {
+							$tvaligne['HUF'] = $sign * $object->lines[$i]->total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
+							$tvaligne['EUR'] = $sign * $object->lines[$i]->multicurrency_total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
+						} else {
+							$tvaligne['HUF'] = $sign * $object->lines[$i]->total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
+							$tvaligne['EUR'] = $sign * $object->lines[$i]->multicurrency_total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent;
+						}
+					} else {
+						if (!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) {
+							$tvaligne['HUF'] = $sign * $object->lines[$i]->total_tva;
+							$tvaligne['EUR'] = $sign * $object->lines[$i]->multicurrency_total_tva;
+						} else {
+							$tvaligne['HUF'] = $sign * $object->lines[$i]->total_tva;
+							$tvaligne['EUR'] = $sign * $object->lines[$i]->total_tva;
+						}
+					}
+
+					$localtax1ligne = $object->lines[$i]->total_localtax1;
+					$localtax2ligne = $object->lines[$i]->total_localtax2;
+					$localtax1_rate = $object->lines[$i]->localtax1_tx;
+					$localtax2_rate = $object->lines[$i]->localtax2_tx;
+					$localtax1_type = $object->lines[$i]->localtax1_type;
+					$localtax2_type = $object->lines[$i]->localtax2_type;
+
+					if ($object->remise_percent) {
+						$tvaligne['HUF'] -= ($tvaligne['HUF'] * $object->remise_percent) / 100;
+						$tvaligne['EUR'] -= ($tvaligne['EUR'] * $object->remise_percent) / 100;
+					}
+					if ($object->remise_percent) {
+						$localtax1ligne -= ($localtax1ligne * $object->remise_percent) / 100;
+					}
+					if ($object->remise_percent) {
+						$localtax2ligne -= ($localtax2ligne * $object->remise_percent) / 100;
+					}
+
+					$vatrate = (string) $object->lines[$i]->tva_tx;
+
+					// Retrieve type from database for backward compatibility with old records
+					if (
+						(!isset($localtax1_type) || $localtax1_type == '' || !isset($localtax2_type) || $localtax2_type == '') // if tax type not defined
+						&& (!empty($localtax1_rate) || !empty($localtax2_rate))
+					) { // and there is local tax
+						$localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc);
+						$localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : '';
+						$localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : '';
+					}
+
+					// retrieve global local tax
+					if ($localtax1_type && $localtax1ligne != 0) {
+						$this->localtax1[$localtax1_type][$localtax1_rate] += $localtax1ligne;
+					}
+					if ($localtax2_type && $localtax2ligne != 0) {
+						$this->localtax2[$localtax2_type][$localtax2_rate] += $localtax2ligne;
+					}
+
+					if (($object->lines[$i]->info_bits & 0x01) == 0x01) {
+						$vatrate .= '*';
+					}
+
+					// Fill $this->tva and $this->tva_array
+					if (!isset($this->tva[$vatrate])) {
+						$this->tva[$vatrate]['HUF'] = 0;
+						$this->tva[$vatrate]['EUR'] = 0;
+					}
+
+					$this->tva[$vatrate]['HUF'] += $tvaligne['HUF'];	// ->tva is abandonned, we use now ->tva_array that is more complete
+					$this->tva[$vatrate]['EUR'] += $tvaligne['EUR'];	// ->tva is abandonned, we use now ->tva_array that is more complete
+					//print_r($this->tva[$vatrate]);
+					//print_r($tvaligne);
+					$vatcode = $object->lines[$i]->vat_src_code;
+
+					if (empty($this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['amount'])) {
+						$this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['amount'] = 0;
+					}
+					$this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['HUF'] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva[$vatrate]['HUF']);
+					$this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['EUR'] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva[$vatrate]['EUR']);
+
+					//print_r($this->tva_array);
+
+					if ($posYAfterImage > $posYAfterDescription) {
+						$nexY = $posYAfterImage;
+					}
+
+					// Add line
+					if (!empty($conf->global->MAIN_PDF_DASH_BETWEEN_LINES) && $i < ($nblines - 1)) {
+						$pdf->setPage($pageposafter);
+						$pdf->SetLineStyle(array('dash' => '1,1', 'color' => array(80, 80, 80)));
+						//$pdf->SetDrawColor(190,190,200);
+						$pdf->line($this->marge_gauche, $nexY + 1, $this->page_largeur - $this->marge_droite, $nexY + 1);
+						$pdf->SetLineStyle(array('dash' => 0));
+					}
+
+					$nexY += 2; // Add space between lines
+
+					// Detect if some page were added automatically and output _tableau for past pages
+					while ($pagenb < $pageposafter) {
+						$pdf->setPage($pagenb);
+						if ($pagenb == 1) {
+							$this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, 0, 1, $object->multicurrency_code);
+						} else {
+							$this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code);
+						}
+						$this->_pagefoot($pdf, $object, $outputlangs, 1);
+						$pagenb++;
+						$pdf->setPage($pagenb);
+						$pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it.
+						if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+							$this->_pagehead($pdf, $object, 0, $outputlangs);
+						}
+						if (!empty($tplidx)) {
+							$pdf->useTemplate($tplidx);
+						}
+					}
+					if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) {
+						if ($pagenb == 1) {
+							$this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, 0, 1, $object->multicurrency_code);
+						} else {
+							$this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code);
+						}
+						$this->_pagefoot($pdf, $object, $outputlangs, 1);
+						// New page
+						$pdf->AddPage();
+						if (!empty($tplidx)) {
+							$pdf->useTemplate($tplidx);
+						}
+						$pagenb++;
+						if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+							$this->_pagehead($pdf, $object, 0, $outputlangs);
+						}
+					}
+				}
+				//exit;
+
+				// Show square
+				if ($pagenb == 1) {
+					$this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforinfotot - $heightforfreetext - $heightforfooter, 0, $outputlangs, 0, 0, $object->multicurrency_code);
+					$bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforfooter + 1;
+				} else {
+					$this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforinfotot - $heightforfreetext - $heightforfooter, 0, $outputlangs, 1, 0, $object->multicurrency_code);
+					$bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforfooter + 1;
+				}
+				dol_syslog("bottomlasttab=" . $bottomlasttab . " this->page_hauteur=" . $this->page_hauteur . " heightforinfotot=" . $heightforinfotot . " heightforfreetext=" . $heightforfreetext . " heightforfooter=" . $heightforfooter);
+
+				// Display info area
+				$posy = $this->_tableau_info($pdf, $object, $bottomlasttab, $outputlangs, $outputlangsbis);
+
+				// Display total area
+				$posy = $this->_tableau_tot($pdf, $object, $deja_regle, $bottomlasttab, $outputlangs, $outputlangsbis);
+
+				// Display Payments area
+				if (($deja_regle || $amount_credit_notes_included || $amount_deposits_included) && empty($conf->global->INVOICE_NO_PAYMENT_DETAILS)) {
+					$posy = $this->_tableau_versements($pdf, $object, $posy, $outputlangs, $heightforfooter);
+				}
+
+				// Pagefoot
+				$this->_pagefoot($pdf, $object, $outputlangs);
+				if (method_exists($pdf, 'AliasNbPages')) {
+					$pdf->AliasNbPages();
+				}
+
+				$pdf->Close();
+
+				$pdf->Output($file, 'F');
+
+				// Add pdfgeneration hook
+				$hookmanager->initHooks(array('pdfgeneration'));
+				$parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
+				global $action;
+				$reshook = $hookmanager->executeHooks('afterPDFCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
+				if ($reshook < 0) {
+					$this->error = $hookmanager->error;
+					$this->errors = $hookmanager->errors;
+				}
+
+				if (!empty($conf->global->MAIN_UMASK)) {
+					@chmod($file, octdec($conf->global->MAIN_UMASK));
+				}
+
+				$this->result = array('fullpath' => $file);
+
+				return 1; // No error
+			} else {
+				$this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
+				return 0;
+			}
+		} else {
+			$this->error = $langs->transnoentities("ErrorConstantNotDefined", "FAC_OUTPUTDIR");
+			return 0;
+		}
+	}
+
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *  Show payments table
+	 *
+	 *  @param	TCPDF		$pdf            	Object PDF
+	 *  @param  Facture		$object         	Object invoice
+	 *  @param  int			$posy           	Position y in PDF
+	 *  @param  Translate	$outputlangs    	Object langs for output
+	 *  @param  int			$heightforfooter 	Height for footer
+	 *  @return int             				<0 if KO, >0 if OK
+	 */
+	protected function _tableau_versements(&$pdf, $object, $posy, $outputlangs, $heightforfooter = 0)
+	{
+		// phpcs:enable
+		global $conf;
+
+		$sign = 1;
+		if ($object->type == 2 && !empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) {
+			$sign = -1;
+		}
+
+		$current_page = $pdf->getPage();
+		$tab3_posx = 120;
+		$tab3_top = $posy + 8;
+		$tab3_width = 80;
+		$tab3_height = 4;
+		if ($this->page_largeur < 210) { // To work with US executive format
+			$tab3_posx -= 15;
+		}
+
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		$this->_tableau_versements_header($pdf, $object, $outputlangs, $default_font_size, $tab3_posx, $tab3_top, $tab3_width, $tab3_height);
+
+		$y = 0;
+
+		$pdf->SetFont('', '', $default_font_size - 4);
+
+
+		// Loop on each discount available (deposits and credit notes and excess of payment included)
+		$sql = "SELECT re.rowid, re.amount_ht, re.multicurrency_amount_ht, re.amount_tva, re.multicurrency_amount_tva,  re.amount_ttc, re.multicurrency_amount_ttc,";
+		$sql .= " re.description, re.fk_facture_source,";
+		$sql .= " f.type, f.datef";
+		$sql .= " FROM " . MAIN_DB_PREFIX . "societe_remise_except as re, " . MAIN_DB_PREFIX . "facture as f";
+		$sql .= " WHERE re.fk_facture_source = f.rowid AND re.fk_facture = " . ((int) $object->id);
+		$resql = $this->db->query($sql);
+		if ($resql) {
+			$num = $this->db->num_rows($resql);
+			$i = 0;
+			$invoice = new Facture($this->db);
+			while ($i < $num) {
+				$y += 3;
+				if ($tab3_top + $y >= ($this->page_hauteur - $heightforfooter)) {
+					$y = 0;
+					$current_page++;
+					$pdf->AddPage('', '', true);
+					if (!empty($tplidx)) {
+						$pdf->useTemplate($tplidx);
+					}
+					if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+						$this->_pagehead($pdf, $object, 0, $outputlangs);
+					}
+					$pdf->setPage($current_page);
+					$this->_tableau_versements_header($pdf, $object, $outputlangs, $default_font_size, $tab3_posx, $tab3_top + $y - 3, $tab3_width, $tab3_height);
+				}
+
+				$obj = $this->db->fetch_object($resql);
+
+				if ($obj->type == 2) {
+					$text = $outputlangs->transnoentities("CreditNote");
+				} elseif ($obj->type == 3) {
+					$text = $outputlangs->transnoentities("Deposit");
+				} elseif ($obj->type == 0) {
+					$text = $outputlangs->transnoentities("ExcessReceived");
+				} else {
+					$text = $outputlangs->transnoentities("UnknownType");
+				}
+
+				$invoice->fetch($obj->fk_facture_source);
+
+				$pdf->SetXY($tab3_posx, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, dol_print_date($this->db->jdate($obj->datef), 'day', false, $outputlangs, true), 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 21, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, price((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? $obj->multicurrency_amount_ttc : $obj->amount_ttc, 0, $outputlangs), 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 40, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, $text, 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 58, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, $invoice->ref, 0, 'L', 0);
+
+				$pdf->line($tab3_posx, $tab3_top + $y + 3, $tab3_posx + $tab3_width, $tab3_top + $y + 3);
+
+				$i++;
+			}
+		} else {
+			$this->error = $this->db->lasterror();
+			return -1;
+		}
+
+		// Loop on each payment
+		// TODO Call getListOfPaymentsgetListOfPayments instead of hard coded sql
+		$sql = "SELECT p.datep as date, p.fk_paiement, p.num_paiement as num, pf.amount as amount, pf.multicurrency_amount,";
+		$sql .= " cp.code";
+		$sql .= " FROM " . MAIN_DB_PREFIX . "paiement_facture as pf, " . MAIN_DB_PREFIX . "paiement as p";
+		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_paiement as cp ON p.fk_paiement = cp.id";
+		$sql .= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = " . ((int) $object->id);
+		//$sql.= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = 1";
+		$sql .= " ORDER BY p.datep";
+
+		$resql = $this->db->query($sql);
+		if ($resql) {
+			$num = $this->db->num_rows($resql);
+			$i = 0;
+			while ($i < $num) {
+				$y += 3;
+				if ($tab3_top + $y >= ($this->page_hauteur - $heightforfooter)) {
+					$y = 0;
+					$current_page++;
+					$pdf->AddPage('', '', true);
+					if (!empty($tplidx)) {
+						$pdf->useTemplate($tplidx);
+					}
+					if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) {
+						$this->_pagehead($pdf, $object, 0, $outputlangs);
+					}
+					$pdf->setPage($current_page);
+					$this->_tableau_versements_header($pdf, $object, $outputlangs, $default_font_size, $tab3_posx, $tab3_top + $y - 3, $tab3_width, $tab3_height);
+				}
+
+				$row = $this->db->fetch_object($resql);
+
+				$pdf->SetXY($tab3_posx, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, dol_print_date($this->db->jdate($row->date), 'day', false, $outputlangs, true), 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 21, $tab3_top + $y);
+				$pdf->MultiCell(20, 3, price($sign * ((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? $row->multicurrency_amount : $row->amount), 0, $outputlangs), 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 40, $tab3_top + $y);
+				$oper = $outputlangs->transnoentitiesnoconv("PaymentTypeShort" . $row->code);
+
+				$pdf->MultiCell(20, 3, $oper, 0, 'L', 0);
+				$pdf->SetXY($tab3_posx + 58, $tab3_top + $y);
+				$pdf->MultiCell(30, 3, $row->num, 0, 'L', 0);
+
+				$pdf->line($tab3_posx, $tab3_top + $y + 3, $tab3_posx + $tab3_width, $tab3_top + $y + 3);
+
+				$i++;
+			}
+
+			return $tab3_top + $y + 3;
+		} else {
+			$this->error = $this->db->lasterror();
+			return -1;
+		}
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 * Function _tableau_versements_header
+	 *
+	 * @param TCPDF 		$pdf				Object PDF
+	 * @param Facture		$object				Object invoice
+	 * @param Translate		$outputlangs		Object langs for output
+	 * @param int			$default_font_size	Font size
+	 * @param int			$tab3_posx			pos x
+	 * @param int 			$tab3_top			pos y
+	 * @param int 			$tab3_width			width
+	 * @param int 			$tab3_height		height
+	 * @return void
+	 */
+	protected function _tableau_versements_header($pdf, $object, $outputlangs, $default_font_size, $tab3_posx, $tab3_top, $tab3_width, $tab3_height)
+	{
+		// phpcs:enable
+		$title = $outputlangs->transnoentities("PaymentsAlreadyDoneHUEN");
+		if ($object->type == 2) {
+			$title = $outputlangs->transnoentities("PaymentsBackAlreadyDone");
+		}
+
+		$pdf->SetFont('', '', $default_font_size - 3);
+		$pdf->SetXY($tab3_posx, $tab3_top - 4);
+		$pdf->MultiCell(60, 3, $title, 0, 'L', 0);
+
+		$pdf->line($tab3_posx, $tab3_top, $tab3_posx + $tab3_width, $tab3_top);
+
+		$pdf->SetFont('', '', $default_font_size - 4);
+		$pdf->SetXY($tab3_posx, $tab3_top);
+		$pdf->MultiCell(20, 3, $outputlangs->transnoentities("PaymentHUEN"), 0, 'L', 0);
+		$pdf->SetXY($tab3_posx + 21, $tab3_top);
+		$pdf->MultiCell(20, 3, $outputlangs->transnoentities("AmountHUEN"), 0, 'L', 0);
+		$pdf->SetXY($tab3_posx + 40, $tab3_top);
+		$pdf->MultiCell(20, 3, $outputlangs->transnoentities("TypeHUEN"), 0, 'L', 0);
+		$pdf->SetXY($tab3_posx + 58, $tab3_top);
+		$pdf->MultiCell(20, 3, $outputlangs->transnoentities("NumHUEN"), 0, 'L', 0);
+
+		$pdf->line($tab3_posx, $tab3_top - 1 + $tab3_height, $tab3_posx + $tab3_width, $tab3_top - 1 + $tab3_height);
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *   Show miscellaneous information (payment mode, payment term, ...)
+	 *
+	 *   @param		TCPDF		$pdf     		Object PDF
+	 *   @param		Facture		$object			Object to show
+	 *   @param		int			$posy			Y
+	 *   @param		Translate	$outputlangs	Langs object
+	 *   @param  	Translate	$outputlangsbis	Object lang for output bis
+	 *   @return	int							Pos y
+	 */
+	protected function _tableau_info(&$pdf, $object, $posy, $outputlangs, $outputlangsbis)
+	{
+		// phpcs:enable
+		global $conf, $mysoc;
+
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		$pdf->SetFont('', '', $default_font_size - 1);
+
+		// If France, show VAT mention if not applicable
+		if ($this->emetteur->country_code == 'FR' && empty($mysoc->tva_assuj)) {
+			$pdf->SetFont('', 'B', $default_font_size - 2);
+			$pdf->SetXY($this->marge_gauche, $posy);
+			if ($mysoc->forme_juridique_code == 92) {
+				$pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoiceAsso"), 0, 'L', 0);
+			} else {
+				$pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoice"), 0, 'L', 0);
+			}
+
+			$posy = $pdf->GetY() + 4;
+		}
+
+		$posxval = 52;
+
+		// Show payments conditions
+		/* if ($object->type != 2 && ($object->cond_reglement_code || $object->cond_reglement)) {
+				  $pdf->SetFont('', 'B', $default_font_size - 2);
+				  $pdf->SetXY($this->marge_gauche, $posy);
+				  $titre = $outputlangs->transnoentities("PaymentConditions") . ':';
+				  $pdf->MultiCell(43, 4, $titre, 0, 'L');
+
+				  $pdf->SetFont('', '', $default_font_size - 2);
+				  $pdf->SetXY($posxval, $posy);
+				  $lib_condition_paiement = $outputlangs->transnoentities("PaymentCondition" . $object->cond_reglement_code) != ('PaymentCondition' . $object->cond_reglement_code) ? $outputlangs->transnoentities("PaymentCondition" . $object->cond_reglement_code) : $outputlangs->convToOutputCharset($object->cond_reglement_doc ? $object->cond_reglement_doc : $object->cond_reglement_label);
+				  $lib_condition_paiement = str_replace('\n', "\n", $lib_condition_paiement);
+				  $pdf->MultiCell(67, 4, $lib_condition_paiement, 0, 'L');
+
+				  $posy = $pdf->GetY() + 3;	// We need spaces for 2 lines payment conditions
+			  }*/
+
+		if ($object->type != 2) {
+			// Check a payment mode is defined
+			if (
+				empty($object->mode_reglement_code)
+				&& empty($conf->global->FACTURE_CHQ_NUMBER)
+				&& empty($conf->global->FACTURE_RIB_NUMBER)
+			) {
+				$this->error = $outputlangs->transnoentities("ErrorNoPaiementModeConfigured");
+			} elseif (
+				($object->mode_reglement_code == 'CHQ' && empty($conf->global->FACTURE_CHQ_NUMBER) && empty($object->fk_account) && empty($object->fk_bank))
+				|| ($object->mode_reglement_code == 'VIR' && empty($conf->global->FACTURE_RIB_NUMBER) && empty($object->fk_account) && empty($object->fk_bank))
+			) {
+				// Avoid having any valid PDF with setup that is not complete
+				$outputlangs->load("errors");
+
+				$pdf->SetXY($this->marge_gauche, $posy);
+				$pdf->SetTextColor(200, 0, 0);
+				$pdf->SetFont('', 'B', $default_font_size - 2);
+				$this->error = $outputlangs->transnoentities("ErrorPaymentModeDefinedToWithoutSetup", $object->mode_reglement_code);
+				$pdf->MultiCell(80, 3, $this->error, 0, 'L', 0);
+				$pdf->SetTextColor(0, 0, 0);
+
+				$posy = $pdf->GetY() + 1;
+			}
+
+			// Show payment mode
+			if (
+				!empty($object->mode_reglement_code)
+				&& $object->mode_reglement_code != 'CHQ'
+				&& $object->mode_reglement_code != 'VIR'
+			) {
+				$pdf->SetFont('', 'B', $default_font_size - 2);
+				$pdf->SetXY($this->marge_gauche, $posy);
+				$titre = $outputlangs->transnoentities("PaymentFizetesiMode") . ':';
+				$pdf->MultiCell(90, 5, $titre, 0, 'L');
+
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->SetXY($posxval + 10, $posy);
+				$lib_mode_reg = $outputlangs->transnoentities("PaymentTypeHUEN" . $object->mode_reglement_code) != ('PaymentTypeHUEN' . $object->mode_reglement_code) ? $outputlangs->transnoentities("PaymentTypeHUEN" . $object->mode_reglement_code) : $outputlangs->convToOutputCharset($object->mode_reglement);
+				$pdf->MultiCell(90, 5, $lib_mode_reg, 0, 'L');
+
+				//szisz
+				//$currency_code = !empty($currency_code) ? $currency_code : $conf->currency;
+				$currency_code = $object->multicurrency_code;
+				$pdf->SetFont('', 'B', $default_font_size - 2);
+				$currency = $outputlangs->transnoentities("DevizaCurrency") . ': ';
+				$pdf->MultiCell(80, 5, $currency, 0, 'L');
+				$pdf->SetXY($posxval + 10, $posy + 5);
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->MultiCell(80, 5, $currency_code, 0, 'L');
+
+				$posy = $pdf->GetY();
+			}
+
+			// Show online payment link
+			if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CB' || $object->mode_reglement_code == 'VAD') {
+				$useonlinepayment = 0;
+				if (!empty($conf->global->PDF_SHOW_LINK_TO_ONLINE_PAYMENT)) {
+					if (!empty($conf->paypal->enabled)) {
+						$useonlinepayment++;
+					}
+					if (!empty($conf->stripe->enabled)) {
+						$useonlinepayment++;
+					}
+					if (!empty($conf->paybox->enabled)) {
+						$useonlinepayment++;
+					}
+				}
+
+				if ($object->statut != Facture::STATUS_DRAFT && $useonlinepayment) {
+					require_once DOL_DOCUMENT_ROOT . '/core/lib/payments.lib.php';
+					global $langs;
+
+					$langs->loadLangs(array('payment', 'paybox', 'stripe'));
+					$servicename = $langs->transnoentities('Online');
+					$paiement_url = getOnlinePaymentUrl('', 'invoice', $object->ref, '', '', '');
+					$linktopay = $langs->trans("ToOfferALinkForOnlinePayment", $servicename) . ' <a href="' . $paiement_url . '">' . $outputlangs->transnoentities("ClickHere") . '</a>';
+
+					$pdf->SetXY($this->marge_gauche, $posy);
+					$pdf->writeHTMLCell(80, 5, '', '', dol_htmlentitiesbr($linktopay), 0, 1);
+
+					$posy = $pdf->GetY() + 1;
+				}
+			}
+
+			// Show payment mode CHQ
+			if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CHQ') {
+				// If payment mode unregulated or payment mode forced to CHQ
+				if (!empty($conf->global->FACTURE_CHQ_NUMBER)) {
+					$diffsizetitle = (empty($conf->global->PDF_DIFFSIZE_TITLE) ? 3 : $conf->global->PDF_DIFFSIZE_TITLE);
+
+					if ($conf->global->FACTURE_CHQ_NUMBER > 0) {
+						$account = new Account($this->db);
+						$account->fetch($conf->global->FACTURE_CHQ_NUMBER);
+
+						$pdf->SetXY($this->marge_gauche, $posy);
+						$pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
+						$pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $account->proprio), 0, 'L', 0);
+						$posy = $pdf->GetY() + 1;
+
+						if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) {
+							$pdf->SetXY($this->marge_gauche, $posy);
+							$pdf->SetFont('', '', $default_font_size - $diffsizetitle);
+							$pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0);
+							$posy = $pdf->GetY() + 2;
+						}
+					}
+					if ($conf->global->FACTURE_CHQ_NUMBER == -1) {
+						$pdf->SetXY($this->marge_gauche, $posy);
+						$pdf->SetFont('', 'B', $default_font_size - $diffsizetitle);
+						$pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $this->emetteur->name), 0, 'L', 0);
+						$posy = $pdf->GetY() + 1;
+
+						if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) {
+							$pdf->SetXY($this->marge_gauche, $posy);
+							$pdf->SetFont('', '', $default_font_size - $diffsizetitle);
+							$pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0);
+							$posy = $pdf->GetY() + 2;
+						}
+					}
+				}
+			}
+
+			// If payment mode not forced or forced to VIR, show payment with BAN
+			if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') {
+				if ($object->fk_account > 0 || $object->fk_bank > 0 || !empty($conf->global->FACTURE_RIB_NUMBER)) {
+					$bankid = ($object->fk_account <= 0 ? $conf->global->FACTURE_RIB_NUMBER : $object->fk_account);
+					if ($object->fk_bank > 0) {
+						$bankid = $object->fk_bank; // For backward compatibility when object->fk_account is forced with object->fk_bank
+					}
+					$account = new Account($this->db);
+					$account->fetch($bankid);
+
+					$curx = $this->marge_gauche;
+					$cury = $posy;
+
+					$posy = pdf_bank($pdf, $outputlangs, $curx, $cury, $account, 0, $default_font_size);
+
+					$posy += 2;
+				}
+			}
+		}
+		$pdf->SetXY(10, $posy + 35);
+		$lastWarningMessageHU = $outputlangs->transnoentities("LastWarningMessageHU");
+		$pdf->MultiCell(200, 5, $lastWarningMessageHU, 0, 'C');
+		return $posy;
+	}
+
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *	Show total to pay
+	 *
+	 *	@param	TCPDF		$pdf            Object PDF
+	 *	@param  Facture		$object         Object invoice
+	 *	@param  int			$deja_regle     Amount already paid (in the currency of invoice)
+	 *	@param	int			$posy			Position depart
+	 *	@param	Translate	$outputlangs	Objet langs
+	 *  @param  Translate	$outputlangsbis	Object lang for output bis
+	 *	@return int							Position pour suite
+	 */
+	protected function _tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs, $outputlangsbis)
+	{
+		// phpcs:enable
+		global $conf, $mysoc, $hookmanager;
+
+		$sign = 1;
+		if ($object->type == 2 && !empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) {
+			$sign = -1;
+		}
+
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		$outputlangsbis = null;
+		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && $outputlangs->defaultlang != $conf->global->PDF_USE_ALSO_LANGUAGE_CODE) {
+			$outputlangsbis = new Translate('', $conf);
+			$outputlangsbis->setDefaultLang($conf->global->PDF_USE_ALSO_LANGUAGE_CODE);
+			$outputlangsbis->loadLangs(array("main", "dict", "companies", "bills", "products", "propal"));
+			$default_font_size--;
+		}
+
+		$tab2_top = $posy;
+		$tab2_hl = 4;
+		$pdf->SetFont('', '', $default_font_size - 1);
+
+		// Total table
+		$col1x = 120;
+		$col2x = 170;
+		if ($this->page_largeur < 210) { // To work with US executive format
+			$col1x -= 15;
+			$col2x -= 10;
+		}
+		$largcol2 = ($this->page_largeur - $this->marge_droite - $col2x);
+
+		$useborder = 0;
+		$index = 0;
+
+		// Total HT
+		/* $pdf->SetFillColor(255, 255, 255);
+			  $pdf->SetXY($col1x, $tab2_top + 0);
+			  $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities(empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) ? "TotalHT" : "Total") . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transnoentities(empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) ? "TotalHT" : "Total") : ''), 0, 'L', 1);
+
+			  $total_ht = ((!empty($conf->multicurrency->enabled) && isset($object->multicurrency_tx) && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ht : $object->total_ht);
+			  $pdf->SetXY($col2x, $tab2_top + 0);
+			  $pdf->MultiCell($largcol2, $tab2_hl, price($sign * ($total_ht + (!empty($object->remise) ? $object->remise : 0)), 0, $outputlangs), 0, 'R', 1);
+			   */
+		// Show VAT by rates and total
+		$pdf->SetFillColor(248, 248, 248);
+
+		$total_ttc = (!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ttc : $object->total_ttc;
+
+		$this->atleastoneratenotnull = 0;
+		if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT)) {
+			$tvaisnull = ((!empty($this->tva) && count($this->tva) == 1 && isset($this->tva['0.000']) && is_float($this->tva['0.000'])) ? true : false);
+			if (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_IFNULL) && $tvaisnull) {
+				// Nothing to do
+			} else {
+				foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
+					if (in_array((string) $localtax_type, array('1', '3', '5'))) {
+						continue;
+					}
+
+					foreach ($localtax_rate as $tvakey => $tvaval) {
+						if ($tvakey != 0) {    // On affiche pas taux 0
+							$index++;
+							$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+							$tvacompl = '';
+							if (preg_match('/\*/', $tvakey)) {
+								$tvakey = str_replace('*', '', $tvakey);
+								$tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
+							}
+
+							$totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT1", $mysoc->country_code) : '');
+							$totalvat .= ' ';
+							$totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
+							$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+
+							$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+							$pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1);
+						}
+					}
+				}
+				foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
+					if (in_array((string) $localtax_type, array('1', '3', '5'))) {
+						continue;
+					}
+
+					foreach ($localtax_rate as $tvakey => $tvaval) {
+						if ($tvakey != 0) {    // On affiche pas taux 0
+							$index++;
+							$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+							$tvacompl = '';
+							if (preg_match('/\*/', $tvakey)) {
+								$tvakey = str_replace('*', '', $tvakey);
+								$tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
+							}
+							$totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalLT2", $mysoc->country_code) : '');
+							$totalvat .= ' ';
+							$totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
+							$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+
+							$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+							$pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1);
+						}
+					}
+				}
+
+				//}
+
+				// VAT
+				foreach ($this->tva_array as $tvakey => $tvaval) {
+					if ($tvakey != 0) {    // On affiche pas taux 0
+						$this->atleastoneratenotnull++;
+
+						$index++;
+						$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+						$tvacompl = '';
+						if (preg_match('/\*/', $tvakey)) {
+							$tvakey = str_replace('*', '', $tvakey);
+							$tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
+						}
+						$totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : '');
+						$totalvat .= ' ';
+						if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') {
+							$totalvat .= vatrate($tvaval['HUF']['vatrate'], 1) . $tvacompl;
+						} elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') {
+							$totalvat .= $tvaval['HUF']['vatcode'] . $tvacompl;
+						} else {
+							$totalvat .= vatrate($tvaval['HUF']['vatrate'], 1) . ($tvaval['HUF']['vatcode'] ? ' (' . $tvaval['HUF']['vatcode'] . ')' : '') . $tvacompl;
+						}
+						//print_r($tvaval);exit;
+						$totalvat = $this->setCPercent($tvaval['HUF']['vatcode']);
+						$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+
+						$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+						//print price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs);
+						$pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['HUF']['amount'], 'MT'), 0, $outputlangs) . ' HUF', 0, 'R', 1);
+					}
+				}
+
+				if ($object->multicurrency_code == 'EUR') {
+
+					foreach ($this->tva_array as $tvakey => $tvaval) {
+						if ($tvakey != 0) {    // On affiche pas taux 0
+							$this->atleastoneratenotnull++;
+
+							$index++;
+							$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+							$tvacompl = '';
+							if (preg_match('/\*/', $tvakey)) {
+								$tvakey = str_replace('*', '', $tvakey);
+								$tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
+							}
+							$totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code) . (is_object($outputlangsbis) ? ' / ' . $outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : '');
+							$totalvat .= ' ';
+							if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') {
+								$totalvat .= vatrate($tvaval['EUR']['vatrate'], 1) . $tvacompl;
+							} elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') {
+								$totalvat .= $tvaval['EUR']['vatcode'] . $tvacompl;
+							} else {
+								$totalvat .= vatrate($tvaval['EUR']['vatrate'], 1) . ($tvaval['EUR']['vatcode'] ? ' (' . $tvaval['EUR']['vatcode'] . ')' : '') . $tvacompl;
+							}
+							$totalvat = $this->setCPercent($tvaval['EUR']['vatcode']);
+							$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+
+							$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+							//print price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs);
+							$pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['EUR']['amount'], 'MT'), 0, $outputlangs) . ' EUR', 0, 'R', 1);
+						}
+					}
+				}
+
+
+				//Local tax 1 after VAT
+				//if (! empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on')
+				//{
+				foreach ($this->localtax1 as $localtax_type => $localtax_rate) {
+					if (in_array((string) $localtax_type, array('2', '4', '6'))) {
+						continue;
+					}
+
+					foreach ($localtax_rate as $tvakey => $tvaval) {
+						if ($tvakey != 0) {    // On affiche pas taux 0
+							//$this->atleastoneratenotnull++;
+
+							$index++;
+							$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+							$tvacompl = '';
+							if (preg_match('/\*/', $tvakey)) {
+								$tvakey = str_replace('*', '', $tvakey);
+								$tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
+							}
+							$totalvat = $outputlangs->transcountrynoentities("TotalLT1", $mysoc->country_code) . ' ';
+							$totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
+
+							$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+							$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+							$pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1);
+						}
+					}
+				}
+				//}
+				//Local tax 2 after VAT
+				//if (! empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on')
+				//{
+				foreach ($this->localtax2 as $localtax_type => $localtax_rate) {
+					if (in_array((string) $localtax_type, array('2', '4', '6'))) {
+						continue;
+					}
+
+					foreach ($localtax_rate as $tvakey => $tvaval) {
+						//$this->atleastoneratenotnull++;
+
+						$index++;
+						$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+						$tvacompl = '';
+						if (preg_match('/\*/', $tvakey)) {
+							$tvakey = str_replace('*', '', $tvakey);
+							$tvacompl = " (" . $outputlangs->transnoentities("NonPercuRecuperable") . ")";
+						}
+						$totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code) . ' ';
+
+						$totalvat .= vatrate(abs($tvakey), 1) . $tvacompl;
+						$pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1);
+
+						$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+						$pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1);
+					}
+				}
+				//}
+
+				// Revenue stamp
+				if (price2num($object->revenuestamp) != 0) {
+					$index++;
+					$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("RevenueStamp"), $useborder, 'L', 1);
+
+					$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($largcol2, $tab2_hl, price($sign * $object->revenuestamp), $useborder, 'R', 1);
+				}
+
+				// Total TTC
+				$index++;
+				$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+				$pdf->SetTextColor(0, 0, 60);
+				$pdf->SetFillColor(224, 224, 224);
+				$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalTTCGROSS"), $useborder, 'L', 1);
+
+				$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($largcol2, $tab2_hl, price($sign * $total_ttc, 0, $outputlangs), $useborder, 'R', 1);
+
+				// Retained warranty
+				if ($object->displayRetainedWarranty()) {
+					$pdf->SetTextColor(40, 40, 40);
+					$pdf->SetFillColor(255, 255, 255);
+
+					$retainedWarranty = $object->getRetainedWarrantyAmount();
+					$billedWithRetainedWarranty = $object->total_ttc - $retainedWarranty;
+
+					// Billed - retained warranty
+					$index++;
+					$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("ToPayOn", dol_print_date($object->date_lim_reglement, 'day')), $useborder, 'L', 1);
+
+					$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($largcol2, $tab2_hl, price($billedWithRetainedWarranty), $useborder, 'R', 1);
+
+					// retained warranty
+					$index++;
+					$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+
+					$retainedWarrantyToPayOn = $outputlangs->transnoentities("RetainedWarranty") . ' (' . $object->retained_warranty . '%)';
+					$retainedWarrantyToPayOn .= !empty($object->retained_warranty_date_limit) ? ' ' . $outputlangs->transnoentities("toPayOn", dol_print_date($object->retained_warranty_date_limit, 'day')) : '';
+
+					$pdf->MultiCell($col2x - $col1x, $tab2_hl, $retainedWarrantyToPayOn, $useborder, 'L', 1);
+					$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+					$pdf->MultiCell($largcol2, $tab2_hl, price($retainedWarranty), $useborder, 'R', 1);
+				}
+			}
+		}
+
+		$pdf->SetTextColor(0, 0, 0);
+		$creditnoteamount = $object->getSumCreditNotesUsed((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0); // Warning, this also include excess received
+		$depositsamount = $object->getSumDepositsUsed((!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) ? 1 : 0);
+		//print "x".$creditnoteamount."-".$depositsamount;exit;
+		$resteapayer = price2num($total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 'MT');
+		if (!empty($object->paye)) {
+			$resteapayer = 0;
+		}
+
+		if (($deja_regle > 0 || $creditnoteamount > 0 || $depositsamount > 0) && empty($conf->global->INVOICE_NO_PAYMENT_DETAILS)) {
+			// Already paid + Deposits
+			$index++;
+			$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+			$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("Paid"), 0, 'L', 0);
+			$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+			$pdf->MultiCell($largcol2, $tab2_hl, price($deja_regle + $depositsamount, 0, $outputlangs), 0, 'R', 0);
+
+			// Credit note
+			if ($creditnoteamount) {
+				$labeltouse = ($outputlangs->transnoentities("CreditNotesOrExcessReceived") != "CreditNotesOrExcessReceived") ? $outputlangs->transnoentities("CreditNotesOrExcessReceived") : $outputlangs->transnoentities("CreditNotes");
+				$index++;
+				$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($col2x - $col1x, $tab2_hl, $labeltouse, 0, 'L', 0);
+				$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($largcol2, $tab2_hl, price($creditnoteamount, 0, $outputlangs), 0, 'R', 0);
+			}
+
+			// Escompte
+			if ($object->close_code == Facture::CLOSECODE_DISCOUNTVAT) {
+				$index++;
+				$pdf->SetFillColor(255, 255, 255);
+
+				$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("EscompteOfferedShort"), $useborder, 'L', 1);
+				$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+				$pdf->MultiCell($largcol2, $tab2_hl, price($object->total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 0, $outputlangs), $useborder, 'R', 1);
+
+				$resteapayer = 0;
+			}
+
+			$index++;
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->SetFillColor(224, 224, 224);
+			$pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index);
+			$pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("RemainderToPay"), $useborder, 'L', 1);
+			$pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index);
+			$pdf->MultiCell($largcol2, $tab2_hl, price($resteapayer, 0, $outputlangs), $useborder, 'R', 1);
+
+			$pdf->SetFont('', '', $default_font_size - 1);
+			$pdf->SetTextColor(0, 0, 0);
+		}
+
+		$index++;
+		return ($tab2_top + ($tab2_hl * $index));
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *   Show table for lines
+	 *
+	 *   @param		TCPDF		$pdf     		Object PDF
+	 *   @param		string		$tab_top		Top position of table
+	 *   @param		string		$tab_height		Height of table (rectangle)
+	 *   @param		int			$nexY			Y (not used)
+	 *   @param		Translate	$outputlangs	Langs object
+	 *   @param		int			$hidetop		1=Hide top bar of array and title, 0=Hide nothing, -1=Hide only title
+	 *   @param		int			$hidebottom		Hide bottom bar of array
+	 *   @param		string		$currency		Currency code
+	 *   @return	void
+	 */
+	protected function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop = 0, $hidebottom = 0, $currency = '')
+	{
+		global $conf;
+
+		// Force to disable hidetop and hidebottom
+		$hidebottom = 0;
+		if ($hidetop) {
+			$hidetop = -1;
+		}
+
+		$currency = !empty($currency) ? $currency : $conf->currency;
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		// Amount in (at tab_top - 1)
+		$pdf->SetTextColor(0, 0, 0);
+		$pdf->SetFont('', '', $default_font_size - 2);
+
+		/* if (empty($hidetop)) {
+						$titre = $outputlangs->transnoentities("AmountInCurrency", $outputlangs->transnoentitiesnoconv("Currency" . $currency));
+						$pdf->SetXY($this->page_largeur - $this->marge_droite - ($pdf->GetStringWidth($titre) + 3), $tab_top - 4);
+						$pdf->MultiCell(($pdf->GetStringWidth($titre) + 3), 2, $titre);
+
+						//$conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR='230,230,230';
+						if (!empty($conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR)) {
+							$pdf->Rect($this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_droite - $this->marge_gauche, 5, 'F', null, explode(',', $conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR));
+						}
+					} */
+
+		$pdf->SetDrawColor(128, 128, 128);
+		$pdf->SetFont('', '', $default_font_size - 1);
+
+		// Output Rect
+		$this->printRect($pdf, $this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $tab_height, $hidetop, $hidebottom); // Rect takes a length in 3rd parameter and 4th parameter
+
+		if (empty($hidetop)) {
+			$pdf->line($this->marge_gauche, $tab_top + 5, $this->page_largeur - $this->marge_droite, $tab_top + 5); // line takes a position y in 2nd parameter and 4th parameter
+
+			$pdf->SetXY($this->posxdesc - 1, $tab_top + 1);
+			$pdf->MultiCell(108, 2, $outputlangs->transnoentities("TermekProduct"), '', 'L');
+		}
+
+		/* if (!empty($conf->global->MAIN_GENERATE_INVOICES_WITH_PICTURE)) {
+						$pdf->line($this->posxpicture - 1, $tab_top, $this->posxpicture - 1, $tab_top + $tab_height);
+						if (empty($hidetop)) {
+							//$pdf->SetXY($this->posxpicture-1, $tab_top+1);
+							//$pdf->MultiCell($this->posxtva-$this->posxpicture-1,2, $outputlangs->transnoentities("Photo"),'','C');
+						}
+					} */
+
+
+
+		// VAT
+		if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) && empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN)) {
+			$pdf->line($this->posxtva - 5, $tab_top, $this->posxtva - 5, $tab_top + $tab_height);
+			if (empty($hidetop)) {
+				$pdf->SetXY($this->posxtva - 4, $tab_top + 1);
+				$pdf->MultiCell($this->posxup - $this->posxtva - 15, 2, $outputlangs->transnoentities("VAT / ÁFA"), '', 'C');
+			}
+		}
+
+		// VAT PRICE
+		if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) && empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN)) {
+			$pdf->line($this->posxtvaprice - 1, $tab_top, $this->posxtvaprice - 1, $tab_top + $tab_height);
+			if (empty($hidetop)) {
+				$pdf->SetXY($this->posxtvaprice - 35, $tab_top + 1);
+				$pdf->MultiCell($this->posxup, 2, $outputlangs->transnoentities("Egys. ár / Unit price"), '', 'C');
+			}
+		}
+
+		//$pdf->line($this->posxup - 1, $tab_top, $this->posxup - 1, $tab_top + $tab_height);
+			  /* if (empty($hidetop)) {
+				  $pdf->SetXY($this->posxup - 1, $tab_top + 1);
+				  $pdf->MultiCell($this->posxqty - $this->posxup - 1, 2, $outputlangs->transnoentities("PriceUHT"), '', 'C');
+			  } */
+
+			  /* $pdf->line($this->posxqty - 1, $tab_top, $this->posxqty - 1, $tab_top + $tab_height);
+			  if (empty($hidetop)) {
+				  $pdf->SetXY($this->posxqty - 1, $tab_top + 1);
+				  $pdf->MultiCell($this->posxunit - $this->posxqty + 3, 2, $outputlangs->transnoentities("Qty"), '', 'C');
+			  } */
+
+			  /* if (!empty($conf->global->PRODUCT_USE_UNITS)) {
+				  $pdf->line($this->posxunit - 1, $tab_top, $this->posxunit - 1, $tab_top + $tab_height);
+				  if (empty($hidetop)) {
+					  $pdf->SetXY($this->posxunit - 1, $tab_top + 1);
+					  $pdf->MultiCell($this->posxdiscount - $this->posxunit - 1, 2, $outputlangs->transnoentities("Unit"), '', 'C');
+				  }
+			  } */
+
+			  //if ($this->hoursdiscount) {
+				  $pdf->line($this->posxdiscount - 37, $tab_top, $this->posxdiscount - 37, $tab_top + $tab_height);
+				  if (empty($hidetop)) {
+					  $pdf->SetXY($this->posxdiscount - 35, $tab_top + 1);
+					  $pdf->MultiCell($this->posxprogress - $this->posxdiscount + 10, 2, $outputlangs->transnoentities("Kedv. / Disc. "), '', 'C');
+				  }
+			  //}
+			  
+			  //if ($this->atleastonediscount) {
+				$pdf->line($this->posxdiscount - 10, $tab_top, $this->posxdiscount - 10, $tab_top + $tab_height);
+				if (empty($hidetop)) {
+					$pdf->SetXY($this->posxdiscount - 11, $tab_top + 1);
+					$pdf->MultiCell($this->posxprogress - $this->posxdiscount + 10, 2, $outputlangs->transnoentities("Kedv. / Disc."), '', 'C');
+				}
+			//}
+
+			  /* if ($this->situationinvoice) {
+				  $pdf->line($this->posxprogress - 1, $tab_top, $this->posxprogress - 1, $tab_top + $tab_height);
+				  if (empty($hidetop)) {
+					  $pdf->SetXY($this->posxprogress, $tab_top + 1);
+					  $pdf->MultiCell($this->postotalht - $this->posxprogress, 2, $outputlangs->transnoentities("ProgressShort"), '', 'C');
+				  }
+			  } */
+
+		$pdf->line($this->postotalht + 12, $tab_top, $this->postotalht + 12, $tab_top + $tab_height);
+		if (empty($hidetop)) {
+			$pdf->SetXY($this->postotalht - 1, $tab_top + 1);
+			$pdf->MultiCell(50, 2, $outputlangs->transnoentities("TotalGross"), '', 'C');
+		}
+	}
+
+	protected function getEntityDataById($entity_id)
+	{
+		$entitiArray = [];
+		$sql = "SELECT rowid, label FROM llx_entity WHERE rowid = {$entity_id}";
+		$data = $this->db->query($sql);
+		if (!$data) {
+			return $entitiArray;
+		}
+		while ($row = pg_fetch_all($data)) {
+			return $row;
+		}
+	}
+
+	protected function getEntityVAT($entity_id)
+	{
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_TVAINTRA';";
+		$details = $this->db->query($sql);
+		if (!$details) {
+			return null;
+		}
+		while ($row = pg_fetch_all($details)) {
+			return $row;
+		}
+	}
+
+	protected function getEntityLogo($entity_id)
+	{
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_SOCIETE_LOGO_SMALL';";
+		$details = $this->db->query($sql);
+		if (!$details) {
+			return null;
+		}
+		while ($row = pg_fetch_all($details)) {
+			return $row;
+		}
+	}
+
+	protected function getEntityAddress($entity_id)
+	{
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_SOCIETE_ZIP';";
+		$details = $this->db->query($sql);
+		$row = pg_fetch_all($details);
+		$zip = $row[0]['value'];
+
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_SOCIETE_TOWN';";
+		$details = $this->db->query($sql);
+		$row = pg_fetch_all($details);
+		$city = $row[0]['value'];
+
+		$sql = "SELECT * FROM llx_const WHERE entity = {$entity_id} AND name = 'MAIN_INFO_SOCIETE_ADDRESS';";
+		$details = $this->db->query($sql);
+		$row = pg_fetch_all($details);
+		$address = $row[0]['value'];
+
+		return $zip . ' ' . $city . ', ' . $address;
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *  Show top header of page.
+	 *
+	 *  @param	TCPDF		$pdf     		Object PDF
+	 *  @param  Facture		$object     	Object to show
+	 *  @param  int	    	$showaddress    0=no, 1=yes
+	 *  @param  Translate	$outputlangs	Object lang for output
+	 *  @param  Translate	$outputlangsbis	Object lang for output bis
+	 *  @return	int							top shift of linked object lines
+	 */
+	protected function _pagehead(&$pdf, $object, $showaddress, $outputlangs, $outputlangsbis = null)
+	{
+		global $conf, $langs;
+
+		$ltrdirection = 'L';
+		if ($outputlangs->trans("DIRECTION") == 'rtl')
+			$ltrdirection = 'R';
+
+		// Load traductions files required by page
+		$outputlangs->loadLangs(array("main", "bills", "propal", "companies"));
+
+		$default_font_size = pdf_getPDFFontSize($outputlangs);
+
+		pdf_pagehead($pdf, $outputlangs, $this->page_hauteur);
+
+		$pdf->SetTextColor(0, 0, 60);
+		$pdf->SetFont('', 'B', $default_font_size + 3);
+
+		$w = 110;
+
+		$posy = $this->marge_haute;
+		$posx = $this->page_largeur - $this->marge_droite - $w;
+
+		$pdf->SetXY($this->marge_gauche, $posy);
+
+		// Logo
+		if (empty($conf->global->PDF_DISABLE_MYCOMPANY_LOGO)) {
+			$mycompanyLogo = $this->getEntityLogo($object->entity);
+			//print_r($mycompanyLogo);
+			//die();
+			if ($this->emetteur->logo) {
+				$logodir = $conf->mycompany->dir_output;
+				if (!empty($conf->mycompany->multidir_output[$object->entity])) {
+					$logodir = $conf->mycompany->multidir_output[$object->entity];
+				}
+				if (empty($conf->global->MAIN_PDF_USE_LARGE_LOGO)) {
+					$logo = $logodir . '/logos/thumbs/' . $mycompanyLogo[0]['value'];
+				} else {
+					$logo = $logodir . '/logos/' . $this->emetteur->logo;
+				}
+				if (is_readable($logo)) {
+					$height = pdf_getHeightForLogo($logo);
+					$pdf->Image($logo, $this->marge_gauche, $posy, 0, $height); // width=0 (auto)
+				} else {
+					$pdf->SetTextColor(200, 0, 0);
+					$pdf->SetFont('', 'B', $default_font_size - 2);
+					$pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorLogoFileNotFound", $logo), 0, 'L');
+					$pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorGoToGlobalSetup"), 0, 'L');
+				}
+			} else {
+				$text = $this->emetteur->name;
+				$pdf->MultiCell($w, 4, $outputlangs->convToOutputCharset($text), 0, $ltrdirection);
+			}
+		}
+
+		$pdf->SetFont('', 'B', $default_font_size + 3);
+		$pdf->SetXY($posx, $posy);
+		$pdf->SetTextColor(0, 0, 60);
+		//$title = $outputlangs->transnoentities("PdfInvoiceTitle");
+		$title = $outputlangs->transnoentities("ReceiptNo");
+		if ($object->type == 1) {
+			$title = $outputlangs->transnoentities("InvoiceReplacement");
+		}
+		if ($object->type == 2) {
+			$title = $outputlangs->transnoentities("InvoiceAvoir");
+		}
+		if ($object->type == 3) {
+			$title = $outputlangs->transnoentities("InvoiceDeposit");
+		}
+		if ($object->type == 4) {
+			$title = $outputlangs->transnoentities("InvoiceProForma");
+		}
+		if ($this->situationinvoice) {
+			$title = $outputlangs->transnoentities("PDFInvoiceSituation");
+		}
+		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) {
+			$title .= ' - ';
+			if ($object->type == 0) {
+				if ($this->situationinvoice) {
+					$title .= $outputlangsbis->transnoentities("PDFInvoiceSituation");
+				}
+				$title .= $outputlangsbis->transnoentities("PdfInvoiceTitle");
+			} elseif ($object->type == 1) {
+				$title .= $outputlangsbis->transnoentities("InvoiceReplacement");
+			} elseif ($object->type == 2) {
+				$title .= $outputlangsbis->transnoentities("InvoiceAvoir");
+			} elseif ($object->type == 3) {
+				$title .= $outputlangsbis->transnoentities("InvoiceDeposit");
+			} elseif ($object->type == 4) {
+				$title .= $outputlangsbis->transnoentities("InvoiceProForma");
+			}
+		}
+		$title .= ' ' . $outputlangs->convToOutputCharset($object->ref);
+		if ($object->statut == $object::STATUS_DRAFT) {
+			$pdf->SetTextColor(128, 0, 0);
+			$title .= ' - ' . $outputlangs->transnoentities("NotValidated");
+		}
+
+		//$title = 'Receipt';
+
+		$pdf->MultiCell($w, 3, $title, '', 'R');
+
+		$pdf->SetFont('', 'B', $default_font_size);
+
+		/*
+						  $posy += 5;
+						  $pdf->SetXY($posx, $posy);
+						  $pdf->SetTextColor(0, 0, 60);
+						  $textref = $outputlangs->transnoentities("Ref")." : ".$outputlangs->convToOutputCharset($object->ref);
+						  if ($object->statut == $object::STATUS_DRAFT) {
+							  $pdf->SetTextColor(128, 0, 0);
+							  $textref .= ' - '.$outputlangs->transnoentities("NotValidated");
+						  }
+						  $pdf->MultiCell($w, 4, $textref, '', 'R');*/
+
+		$posy += 3;
+		$pdf->SetFont('', '', $default_font_size - 2);
+
+		if ($object->ref_client) {
+			$posy += 4;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer") . " : " . $outputlangs->convToOutputCharset($object->ref_client), '', 'R');
+		}
+
+		if (!empty($conf->global->PDF_SHOW_PROJECT_TITLE)) {
+			$object->fetch_projet();
+			if (!empty($object->project->ref)) {
+				$posy += 3;
+				$pdf->SetXY($posx, $posy);
+				$pdf->SetTextColor(0, 0, 60);
+				$pdf->MultiCell($w, 3, $outputlangs->transnoentities("Project") . " : " . (empty($object->project->title) ? '' : $object->project->title), '', 'R');
+			}
+		}
+
+		if (!empty($conf->global->PDF_SHOW_PROJECT)) {
+			$object->fetch_projet();
+			if (!empty($object->project->ref)) {
+				$outputlangs->load("projects");
+				$posy += 3;
+				$pdf->SetXY($posx, $posy);
+				$pdf->SetTextColor(0, 0, 60);
+				$pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefProject") . " : " . (empty($object->project->ref) ? '' : $object->project->ref), '', 'R');
+			}
+		}
+
+		$objectidnext = $object->getIdReplacingInvoice('validated');
+		if ($object->type == 0 && $objectidnext) {
+			$objectreplacing = new Facture($this->db);
+			$objectreplacing->fetch($objectidnext);
+
+			$posy += 3;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementByInvoice") . ' : ' . $outputlangs->convToOutputCharset($objectreplacing->ref), '', 'R');
+		}
+		if ($object->type == 1) {
+			$objectreplaced = new Facture($this->db);
+			$objectreplaced->fetch($object->fk_facture_source);
+
+			$posy += 4;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementInvoice") . ' : ' . $outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R');
+		}
+		if ($object->type == 2 && !empty($object->fk_facture_source)) {
+			$objectreplaced = new Facture($this->db);
+			$objectreplaced->fetch($object->fk_facture_source);
+
+			$posy += 3;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("CorrectionInvoice") . ' : ' . $outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R');
+		}
+
+		$posy += 4;
+		$pdf->SetXY($posx, $posy);
+		$pdf->SetTextColor(0, 0, 60);
+		$title = $outputlangs->transnoentities("ReceiptDateInvoice");
+		if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) {
+			$title .= ' - ' . $outputlangsbis->transnoentities("ReceiptDateInvoice");
+		}
+		$pdf->MultiCell($w, 3, $title . " : " . dol_print_date($object->date, "day", false, $outputlangs, true), '', 'R');
+
+		if (!empty($conf->global->INVOICE_POINTOFTAX_DATE)) {
+			$posy += 4;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$pdf->MultiCell($w, 3, $outputlangs->transnoentities("DatePointOfTax") . " : " . dol_print_date($object->date_pointoftax, "day", false, $outputlangs), '', 'R');
+		}
+
+		if ($object->type != 2) {
+			$posy += 3;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			$title = $outputlangs->transnoentities("RecieptDateDue");
+			if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) {
+				$title .= ' - ' . $outputlangsbis->transnoentities("RecieptDateDue");
+			}
+			$pdf->MultiCell($w, 3, $title . " : " . dol_print_date($object->date_lim_reglement, "day", false, $outputlangs, true), '', 'R');
+		}
+
+		if (empty($conf->global->MAIN_PDF_HIDE_CUSTOMER_CODE) && $object->thirdparty->code_client) {
+			$posy += 3;
+			$pdf->SetXY($posx, $posy);
+			$pdf->SetTextColor(0, 0, 60);
+			//$pdf->MultiCell($w, 3, $outputlangs->transnoentities("CustomerCode") . " : " . $outputlangs->transnoentities($object->thirdparty->code_client), '', 'R');
+		}
+
+		// Get contact
+		if (!empty($conf->global->DOC_SHOW_FIRST_SALES_REP)) {
+			$arrayidcontact = $object->getIdContact('internal', 'SALESREPFOLL');
+			if (count($arrayidcontact) > 0) {
+				$usertmp = new User($this->db);
+				$usertmp->fetch($arrayidcontact[0]);
+				$posy += 4;
+				$pdf->SetXY($posx, $posy);
+				$pdf->SetTextColor(0, 0, 60);
+				$pdf->MultiCell($w, 3, $langs->transnoentities("SalesRepresentative") . " : " . $usertmp->getFullName($langs), '', 'R');
+			}
+		}
+
+		$posy += 1;
+
+		$top_shift = 0;
+		// Show list of linked objects
+		$current_y = $pdf->getY();
+		$posy = pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, $w, 3, 'R', $default_font_size);
+		if ($current_y < $pdf->getY()) {
+			$top_shift = $pdf->getY() - $current_y;
+		}
+
+		if ($showaddress) {
+			// Sender properties
+			//$carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object);
+			$entityDetails = $this->getEntityVAT($object->entity);
+			$entityAddress = $this->getEntityAddress($object->entity);
+			$entityInfo = $this->getEntityDataById($object->entity);
+			//print_r($entityDetails);
+			//die();
+			$entityName = $entityInfo[0]['label'];
+			$carac_emetteur = "\n" . $entityAddress;
+			$carac_emetteur .= "\n\n" . $outputlangs->transnoentities("VATNumber") . " " . $entityDetails[0]['value'];
+
+			// Show sender
+			$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+			$posy += $top_shift;
+			$posx = $this->marge_gauche;
+			if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+				$posx = $this->page_largeur - $this->marge_droite - 80;
+			}
+
+			$hautcadre = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 38 : 40;
+			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82;
+
+
+			// Show sender frame
+			if (empty($conf->global->MAIN_PDF_NO_SENDER_FRAME)) {
+				$pdf->SetTextColor(0, 0, 0);
+				$pdf->SetFont('', '', $default_font_size - 2);
+				$pdf->SetXY($posx, $posy - 5);
+				//$pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillFromReceipt"), 0, $ltrdirection);
+				$pdf->SetXY($posx, $posy);
+				$pdf->SetFillColor(230, 230, 230);
+				$pdf->MultiCell($widthrecbox, $hautcadre, "", 0, 'R', 1);
+				$pdf->SetTextColor(0, 0, 60);
+			}
+
+			// Show sender name
+			if (empty($conf->global->MAIN_PDF_HIDE_SENDER_NAME)) {
+				$pdf->SetXY($posx + 2, $posy + 3);
+				$pdf->SetFont('', 'B', $default_font_size);
+				$pdf->MultiCell($widthrecbox - 2, 4, $outputlangs->convToOutputCharset($entityName), 0, $ltrdirection);
+				$posy = $pdf->getY();
+			}
+
+			// Show sender information
+			$pdf->SetXY($posx + 2, $posy);
+			$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]);
+			}
+
+			// 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 = str_replace("?","ő",$carac_client_name);
+			//print_r($carac_client_name);
+			//die();
+
+			$mode = 'target';
+			$carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, $mode, $object);
+
+			// Show recipient
+			$widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 100;
+			if ($this->page_largeur < 210) {
+				$widthrecbox = 84; // To work with US executive format
+			}
+			$posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42;
+			$posy += $top_shift;
+			$posx = $this->page_largeur - $this->marge_droite - $widthrecbox;
+			if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) {
+				$posx = $this->marge_gauche;
+			}
+
+			// Show recipient frame
+			/* if (empty($conf->global->MAIN_PDF_NO_RECIPENT_FRAME)) {
+								  $pdf->SetTextColor(0, 0, 0);
+								  $pdf->SetFont('', '', $default_font_size - 2);
+								  $pdf->SetXY($posx + 2, $posy - 5);
+								  $pdf->MultiCell($widthrecbox - 2, 5, $outputlangs->transnoentities("BillTo"), 0, $ltrdirection);
+								  $pdf->Rect($posx, $posy, $widthrecbox, $hautcadre);
+							  }
+
+							  // Show recipient name
+							  $pdf->SetXY($posx + 2, $posy + 3);
+							  //$pdf->SetFont('', 'B', $default_font_size);
+							  $pdf->SetFont('freeserif', 'B', $default_font_size);
+							  $pdf->MultiCell($widthrecbox - 2, 2, $carac_client_name, 0, $ltrdirection);
+
+							  $posy = $pdf->getY();
+
+							  // Show recipient information
+							  $pdf->SetFont('', '', $default_font_size - 1);
+							  $pdf->SetXY($posx + 2, $posy);
+							  $pdf->MultiCell($widthrecbox - 2, 4, $carac_client, 0, $ltrdirection); */
+		}
+
+		$pdf->SetTextColor(0, 0, 0);
+		return $top_shift;
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
+	/**
+	 *   	Show footer of page. Need this->emetteur object
+	 *
+	 *   	@param	TCPDF		$pdf     			PDF
+	 * 		@param	Facture		$object				Object to show
+	 *      @param	Translate	$outputlangs		Object lang for output
+	 *      @param	int			$hidefreetext		1=Hide free text
+	 *      @return	int								Return height of bottom margin including footer text
+	 */
+	protected function _pagefoot(&$pdf, $object, $outputlangs, $hidefreetext = 0)
+	{
+		global $conf;
+		$showdetails = empty($conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS) ? 0 : $conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS;
+		return pdf_pagefoot($pdf, $outputlangs, 'INVOICE_FREE_TEXT', $this->emetteur, $this->marge_basse, $this->marge_gauche, $this->page_hauteur, $object, $showdetails, $hidefreetext, $this->page_largeur, $this->watermark);
+	}
+
+	function setCPercent($percent)
+	{
+		switch ($percent) {
+			case 5:
+				return "A-4.76";
+				break;
+			case 18:
+				return "B-15.25";
+				break;
+			case 27:
+				return "C-21.26";
+				break;
+		}
+	}
+}

+ 32 - 14
core/modules/facture/mod_facture_mercure.php

@@ -26,7 +26,7 @@
  *	\ingroup    facture
  *	\brief      File containing class for numbering module Mercure
  */
-require_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.php';
+require_once DOL_DOCUMENT_ROOT . '/core/modules/facture/modules_facture.php';
 
 
 /**
@@ -59,13 +59,15 @@ class mod_facture_mercure extends ModeleNumRefFactures
 
 		$form = new Form($db);
 
-		$texte = $langs->trans('GenericNumRefModelDesc')."<br>\n";
-		$texte .= '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
-		$texte .= '<input type="hidden" name="token" value="'.newToken().'">';
+		$texte = $langs->trans('GenericNumRefModelDesc') . "<br>\n";
+		$texte .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
+		$texte .= '<input type="hidden" name="token" value="' . newToken() . '">';
 		$texte .= '<input type="hidden" name="action" value="updateMask">';
 		$texte .= '<input type="hidden" name="maskconstinvoice" value="FACTURE_MERCURE_MASK_INVOICE">';
+		$texte .= '<input type="hidden" name="maskconstreceipt" value="FACTURE_MERCURE_MASK_RECEIPT">';
 		$texte .= '<input type="hidden" name="maskconstreplacement" value="FACTURE_MERCURE_MASK_REPLACEMENT">';
 		$texte .= '<input type="hidden" name="maskconstcredit" value="FACTURE_MERCURE_MASK_CREDIT">';
+		$texte .= '<input type="hidden" name="maskconstreceiptcredit" value="FACTURE_MERCURE_MASK_RECEIPT_CREDIT">';
 		$texte .= '<input type="hidden" name="maskconstdeposit" value="FACTURE_MERCURE_MASK_DEPOSIT">';
 		$texte .= '<table class="nobordernopadding" width="100%">';
 
@@ -76,26 +78,38 @@ class mod_facture_mercure extends ModeleNumRefFactures
 		$tooltip .= $langs->trans("GenericMaskCodes5");
 
 		// Setting the prefix
-		$texte .= '<tr><td><span class="opacitymedium">'.$langs->trans("Mask").' ('.$langs->trans("InvoiceStandard").'):</span></td>';
-		$texte .= '<td class="right">'.$form->textwithpicto('<input type="text" class="flat minwidth175" name="maskinvoice" value="'.getDolGlobalString("FACTURE_MERCURE_MASK_INVOICE").'">', $tooltip, 1, 1).'</td>';
+		$texte .= '<tr><td><span class="opacitymedium">' . $langs->trans("Mask") . ' (' . $langs->trans("InvoiceStandard") . '):</span></td>';
+		$texte .= '<td class="right">' . $form->textwithpicto('<input type="text" class="flat minwidth175" name="maskinvoice" value="' . getDolGlobalString("FACTURE_MERCURE_MASK_INVOICE") . '">', $tooltip, 1, 1) . '</td>';
 
-		$texte .= '<td class="left" rowspan="3">&nbsp; <input type="submit" class="button button-edit" name="Button"value="'.$langs->trans("Modify").'"></td>';
+		$texte .= '<td class="left" rowspan="3">&nbsp; <input type="submit" class="button button-edit" name="Button"value="' . $langs->trans("Modify") . '"></td>';
 
 		$texte .= '</tr>';
 
+		// RECEIPT
+		$texte .= '<tr><td><span class="opacitymedium">' . $langs->trans("Mask") . ' (' . $langs->trans("ReceiptStandard") . '):</span></td>';
+		$texte .= '<td class="right">' . $form->textwithpicto('<input type="text" class="flat minwidth175" name="maskreceipt" value="' . getDolGlobalString("FACTURE_MERCURE_MASK_RECEIPT") . '">', $tooltip, 1, 1) . '</td>';
+		$texte .= '</tr>';
+
+
 		// Prefix setting of replacement invoices
-		$texte .= '<tr><td><span class="opacitymedium">'.$langs->trans("Mask").' ('.$langs->trans("InvoiceReplacement").'):</span></td>';
-		$texte .= '<td class="right">'.$form->textwithpicto('<input type="text" class="flat minwidth175" name="maskreplacement" value="'.getDolGlobalString("FACTURE_MERCURE_MASK_REPLACEMENT").'">', $tooltip, 1, 1).'</td>';
+		$texte .= '<tr><td><span class="opacitymedium">' . $langs->trans("Mask") . ' (' . $langs->trans("InvoiceReplacement") . '):</span></td>';
+		$texte .= '<td class="right">' . $form->textwithpicto('<input type="text" class="flat minwidth175" name="maskreplacement" value="' . getDolGlobalString("FACTURE_MERCURE_MASK_REPLACEMENT") . '">', $tooltip, 1, 1) . '</td>';
 		$texte .= '</tr>';
 
 		// Prefix setting of credit note
-		$texte .= '<tr><td><span class="opacitymedium">'.$langs->trans("Mask").' ('.$langs->trans("InvoiceAvoir").'):</span></td>';
-		$texte .= '<td class="right">'.$form->textwithpicto('<input type="text" class="flat minwidth175" name="maskcredit" value="'.getDolGlobalString("FACTURE_MERCURE_MASK_CREDIT").'">', $tooltip, 1, 1).'</td>';
+		$texte .= '<tr><td><span class="opacitymedium">' . $langs->trans("Mask") . ' (' . $langs->trans("InvoiceAvoir") . '):</span></td>';
+		$texte .= '<td class="right">' . $form->textwithpicto('<input type="text" class="flat minwidth175" name="maskcredit" value="' . getDolGlobalString("FACTURE_MERCURE_MASK_CREDIT") . '">', $tooltip, 1, 1) . '</td>';
 		$texte .= '</tr>';
 
+		// RECEIPT CREDIT NOTE
+		$texte .= '<tr><td><span class="opacitymedium">' . $langs->trans("Mask") . ' (' . $langs->trans("ReceiptInvoiceAvoir") . '):</span></td>';
+		$texte .= '<td class="right">' . $form->textwithpicto('<input type="text" class="flat minwidth175" name="maskreceiptcredit" value="' . getDolGlobalString("FACTURE_MERCURE_MASK_RECEIPT_CREDIT") . '">', $tooltip, 1, 1) . '</td>';
+		$texte .= '</tr>';
+
+
 		// Prefix setting of deposit
-		$texte .= '<tr><td><span class="opacitymedium">'.$langs->trans("Mask").' ('.$langs->trans("InvoiceDeposit").'):</span></td>';
-		$texte .= '<td class="right">'.$form->textwithpicto('<input type="text" class="flat minwidth175" name="maskdeposit" value="'.getDolGlobalString("FACTURE_MERCURE_MASK_DEPOSIT").'">', $tooltip, 1, 1).'</td>';
+		$texte .= '<tr><td><span class="opacitymedium">' . $langs->trans("Mask") . ' (' . $langs->trans("InvoiceDeposit") . '):</span></td>';
+		$texte .= '<td class="right">' . $form->textwithpicto('<input type="text" class="flat minwidth175" name="maskdeposit" value="' . getDolGlobalString("FACTURE_MERCURE_MASK_DEPOSIT") . '">', $tooltip, 1, 1) . '</td>';
 		$texte .= '</tr>';
 
 		$texte .= '</table>';
@@ -139,7 +153,7 @@ class mod_facture_mercure extends ModeleNumRefFactures
 	{
 		global $db;
 
-		require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
+		require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
 
 		// Get Mask value
 		$mask = '';
@@ -147,6 +161,10 @@ class mod_facture_mercure extends ModeleNumRefFactures
 			$mask = getDolGlobalString('FACTURE_MERCURE_MASK_REPLACEMENT', getDolGlobalString('FACTURE_MERCURE_MASK_INVOICE'));
 		} elseif (is_object($invoice) && $invoice->type == 2) {
 			$mask = getDolGlobalString('FACTURE_MERCURE_MASK_CREDIT');
+		} elseif (is_object($invoice) && $invoice->type == 7) {
+			$mask = getDolGlobalString('FACTURE_MERCURE_MASK_RECEIPT');
+		} elseif (is_object($invoice) && $invoice->type == 8) {
+			$mask = getDolGlobalString('FACTURE_MERCURE_MASK_RECEIPT_CREDIT');
 		} elseif (is_object($invoice) && $invoice->type == 3) {
 			$mask = getDolGlobalString('FACTURE_MERCURE_MASK_DEPOSIT');
 		} else {

+ 2 - 2
core/modules/modTicket.class.php

@@ -238,7 +238,7 @@ class modTicket extends DolibarrModules
 
 		$this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket',
 			'type' => 'left',
-			'titre' => 'Ticket',
+			'titre' => 'Tickethu',
 			'prefix' => img_picto('', $this->picto, 'class="paddingright pictofixedwidth em092"'),
 			'mainmenu' => 'ticket',
 			'leftmenu' => 'ticket',
@@ -253,7 +253,7 @@ class modTicket extends DolibarrModules
 
 		$this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket,fk_leftmenu=ticket',
 			'type' => 'left',
-			'titre' => 'NewTicket',
+			'titre' => 'NewTickethu',
 			'mainmenu' => 'ticket',
 			'url' => '/ticket/card.php?action=create',
 			'langs' => 'ticket',

+ 21 - 2
core/tpl/extrafields_list_search_input.tpl.php

@@ -31,16 +31,35 @@ if (!empty($extrafieldsobjectkey)) {	// $extrafieldsobject is the $object->table
 				$tmpkey = preg_replace('/'.$search_options_pattern.'/', '', $key);
 				if (in_array($typeofextrafield, array('varchar', 'mail', 'ip', 'url', 'int', 'double')) && empty($extrafields->attributes[$extrafieldsobjectkey]['computed'][$key])) {
 					$searchclass = '';
+					if ($key == 'fk_statut') {
+						$cssforfield .= ($cssforfield ? ' ' : '').'center';
+					}
 					if (in_array($typeofextrafield, array('varchar', 'mail', 'ip', 'url'))) {
 						$searchclass = 'searchstring';
 					}
 					if (in_array($typeofextrafield, array('int', 'double'))) {
 						$searchclass = 'searchnum';
 					}
-					print '<input class="flat'.($searchclass ? ' '.$searchclass : '').'" size="4" type="text" name="'.$search_options_pattern.$tmpkey.'" value="'.dol_escape_htmltag((empty($search_array_options[$search_options_pattern.$tmpkey]) ? '' : $search_array_options[$search_options_pattern.$tmpkey])).'">';
+					if ($key == 'to_email') {
+						$TicketObj = new Ticket($db);
+						$toEmailSql1 = "SELECT to_email FROM " . MAIN_DB_PREFIX . $TicketObj->table_element . "_extrafields GROUP BY to_email";
+						$resultdata = $db->query($toEmailSql1);
+						$toEmailArray = ['allemails' => '-- Select (All) --'];
+						while ($row = $db->fetch_object($resultdata)) {
+							$toEmailArray[$row->to_email] = $row->to_email;
+						}
+						$selectedtoEmailArray = null;
+						if (!empty(GETPOST('search_options_to_email', 'array')) && !GETPOST('button_removefilter_x', 'alpha')) {
+							$selectedtoEmailArray = array_values(GETPOST('search_options_to_email', 'array'));
+						}
+						//print $form->selectarray('search_options_' . $key, $toEmailArray, $search[$key], '', 0, 0, '', 1, 0, 0, '', 'maxwidth200', 1);
+						print Form::multiselectarray('search_options_' . $key, $toEmailArray, $selectedtoEmailArray, 0, 0, 'minwidth100imp maxwidth200', 0, 0, '', '', '');
+					} else {
+						print '<input class="flat' . ($searchclass ? ' ' . $searchclass : '') . '" size="4" type="text" name="' . $search_options_pattern . $tmpkey . '" value="' . dol_escape_htmltag((empty($search_array_options[$search_options_pattern . $tmpkey]) ? '' : $search_array_options[$search_options_pattern . $tmpkey])) . '">';
+					}
 				} elseif (in_array($typeofextrafield, array('datetime', 'timestamp'))) {
 					$morecss = '';
-					echo $extrafields->showInputField($key, (empty($search_array_options[$search_options_pattern.$tmpkey]) ? '' : $search_array_options[$search_options_pattern.$tmpkey]), '', '', $search_options_pattern, $morecss, 0, $extrafieldsobjectkey, 1);
+					//echo $extrafields->showInputField($key, (empty($search_array_options[$search_options_pattern.$tmpkey]) ? '' : $search_array_options[$search_options_pattern.$tmpkey]), '', '', $search_options_pattern, $morecss, 0, $extrafieldsobjectkey, 1);
 				} else {
 					// for the type as 'checkbox', 'chkbxlst', 'sellist' we should use code instead of id (example: I declare a 'chkbxlst' to have a link with dictionnairy, I have to extend it with the 'code' instead 'rowid')
 					$morecss = '';

+ 26 - 11
core/tpl/extrafields_list_search_sql.tpl.php

@@ -21,30 +21,30 @@ if (!empty($extrafieldsobjectkey) && !empty($search_array_options) && is_array($
 
 	foreach ($search_array_options as $key => $val) {
 		$crit = $val;
-		$tmpkey = preg_replace('/'.$search_options_pattern.'/', '', $key);
+		$tmpkey = preg_replace('/' . $search_options_pattern . '/', '', $key);
 		$typ = $extrafields->attributes[$extrafieldsobjectkey]['type'][$tmpkey];
 
 		if ($crit != '' && in_array($typ, array('date', 'datetime', 'timestamp'))) {
 			if (is_numeric($crit)) {
 				if ($typ == 'date') {
-					include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
+					include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
 					$crit = dol_get_first_hour($crit);
 				}
-				$sql .= " AND ".$extrafieldsobjectprefix.$tmpkey." = '".$db->idate($crit)."'";
+				$sql .= " AND " . $extrafieldsobjectprefix . $tmpkey . " = '" . $db->idate($crit) . "'";
 			} elseif (is_array($crit)) {
 				if ($crit['start'] !== '' && $crit['end'] !== '') {
-					$sql .= " AND (".$extrafieldsobjectprefix.$tmpkey." BETWEEN '". $db->idate($crit['start']). "' AND '".$db->idate($crit['end']) . "')";
+					$sql .= " AND (" . $extrafieldsobjectprefix . $tmpkey . " BETWEEN '" . $db->idate($crit['start']) . "' AND '" . $db->idate($crit['end']) . "')";
 				} elseif ($crit['start'] !== '') {
-					$sql .= " AND (".$extrafieldsobjectprefix.$tmpkey." >= '". $db->idate($crit['start'])."')";
+					$sql .= " AND (" . $extrafieldsobjectprefix . $tmpkey . " >= '" . $db->idate($crit['start']) . "')";
 				} elseif ($crit['end'] !== '') {
-					$sql .= " AND (".$extrafieldsobjectprefix.$tmpkey." <= '". $db->idate($crit['end'])."')";
+					$sql .= " AND (" . $extrafieldsobjectprefix . $tmpkey . " <= '" . $db->idate($crit['end']) . "')";
 				}
 			}
 		} elseif (in_array($typ, array('boolean'))) {
 			if ($crit !== '-1' && $crit !== '') {
-				$sql .= " AND (".$extrafieldsobjectprefix.$tmpkey." = '".$db->escape($crit)."'";
+				$sql .= " AND (" . $extrafieldsobjectprefix . $tmpkey . " = '" . $db->escape($crit) . "'";
 				if ($crit == '0') {
-					$sql .= " OR ".$extrafieldsobjectprefix.$tmpkey." IS NULL";
+					$sql .= " OR " . $extrafieldsobjectprefix . $tmpkey . " IS NULL";
 				}
 				$sql .= ")";
 			}
@@ -64,12 +64,27 @@ if (!empty($extrafieldsobjectkey) && !empty($search_array_options) && is_array($
 			}
 			if (is_array($crit)) {
 				$crit = implode(' ', $crit); // natural_search() expects a string
+				if ($key == 'search_options_to_email') {
+					if ($crit == 'allemails') {
+						$TicketObj = new Ticket($db);
+						$toEmailSql2 = "SELECT to_email FROM " . MAIN_DB_PREFIX . $TicketObj->table_element . "_extrafields GROUP BY to_email";
+						$resultdata = $db->query($toEmailSql2);
+						while ($row = $db->fetch_object($resultdata)) {
+							$toEmailArray[] = $row->to_email;
+						}
+						$crit = implode(' ', $toEmailArray);
+					}
+					$roemailString = str_replace(" ", "%' OR ef.to_email LIKE '%", $crit);
+					$sql .= "AND (ef.to_email LIKE '%" . $roemailString . "%')";
+				}
 			} elseif ($typ === 'select' and is_string($crit) and strpos($crit, ',') === false) {
-				$critSelect = "'".implode("','", array_map(array($db, 'escape'), explode(',', $crit)))."'";
-				$sql .= " AND (".$extrafieldsobjectprefix.$tmpkey." IN (".$db->sanitize($critSelect, 1).") )";
+				$critSelect = "'" . implode("','", array_map(array($db, 'escape'), explode(',', $crit))) . "'";
+				$sql .= " AND (" . $extrafieldsobjectprefix . $tmpkey . " IN (" . $db->sanitize($critSelect, 1) . ") )";
 				continue;
 			}
-			$sql .= natural_search($extrafieldsobjectprefix.$tmpkey, $crit, $mode_search);
+			if ($key != 'search_options_to_email') {
+				$sql .= natural_search($extrafieldsobjectprefix . $tmpkey, $crit, $mode_search);
+			}
 		}
 	}
 }

+ 40 - 28
core/tpl/extrafields_view.tpl.php

@@ -44,7 +44,7 @@ if (!isset($parameters) || !is_array($parameters)) {
 	$parameters = array();
 }
 if (!empty($cols)) {
-	$parameters['colspan'] = ' colspan="'.$cols.'"';
+	$parameters['colspan'] = ' colspan="' . $cols . '"';
 }
 if (!empty($cols)) {
 	$parameters['cols'] = $cols;
@@ -99,9 +99,9 @@ if (empty($reshook) && isset($extrafields->attributes[$object->table_element]['l
 			$langs->load($extrafields->attributes[$object->table_element]['langfile'][$tmpkeyextra]);
 		}
 		if ($action == 'edit_extras') {
-			$value = (GETPOSTISSET("options_".$tmpkeyextra) ? GETPOST("options_".$tmpkeyextra) : (isset($object->array_options["options_".$tmpkeyextra]) ? $object->array_options["options_".$tmpkeyextra] : ''));
+			$value = (GETPOSTISSET("options_" . $tmpkeyextra) ? GETPOST("options_" . $tmpkeyextra) : (isset($object->array_options["options_" . $tmpkeyextra]) ? $object->array_options["options_" . $tmpkeyextra] : ''));
 		} else {
-			$value = (isset($object->array_options["options_".$tmpkeyextra]) ? $object->array_options["options_".$tmpkeyextra] : '');
+			$value = (isset($object->array_options["options_" . $tmpkeyextra]) ? $object->array_options["options_" . $tmpkeyextra] : '');
 			//var_dump($tmpkeyextra.' - '.$value);
 		}
 
@@ -112,12 +112,14 @@ if (empty($reshook) && isset($extrafields->attributes[$object->table_element]['l
 			print $extrafields->showSeparator($tmpkeyextra, $object);
 
 			$lastseparatorkeyfound = $tmpkeyextra;
+		} elseif ($tmpkeyextra == 'discount_start_minute' || $tmpkeyextra == 'discount_to_hour' || $tmpkeyextra == 'discount_to_minute') {
+
 		} else {
-			$collapse_group = $extrafields_collapse_num.(!empty($object->id) ? '_'.$object->id : '');
-			print '<tr class="trextrafields_collapse'.$collapse_group;
+			$collapse_group = $extrafields_collapse_num . (!empty($object->id) ? '_' . $object->id : '');
+			print '<tr class="trextrafields_collapse' . $collapse_group;
 			/*if ($extrafields_collapse_num && $extrafields_collapse_num_old && $extrafields_collapse_num != $extrafields_collapse_num_old) {
-				print ' trextrafields_collapse_new';
-			}*/
+						 print ' trextrafields_collapse_new';
+					 }*/
 			if ($extrafields_collapse_num && $i == count($extrafields->attributes[$object->table_element]['label'])) {
 				print ' trextrafields_collapse_last';
 			}
@@ -139,7 +141,7 @@ if (empty($reshook) && isset($extrafields->attributes[$object->table_element]['l
 			if (!empty($extrafields->attributes[$object->table_element]['help'][$tmpkeyextra])) {
 				// You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
 				$tmptooltip = explode(':', $extrafields->attributes[$object->table_element]['help'][$tmpkeyextra]);
-				print $form->textwithpicto($langs->trans($tmplabelextra), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.$tmpkeyextra.'_'.$tmptooltip[1]));
+				print $form->textwithpicto($langs->trans($tmplabelextra), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_' . $tmpkeyextra . '_' . $tmptooltip[1]));
 			} else {
 				print $langs->trans($tmplabelextra);
 			}
@@ -193,42 +195,44 @@ if (empty($reshook) && isset($extrafields->attributes[$object->table_element]['l
 			}
 
 			$isdraft = ((isset($object->statut) && $object->statut == 0) || (isset($object->status) && $object->status == 0));
-			if (($isdraft || !empty($extrafields->attributes[$object->table_element]['alwayseditable'][$tmpkeyextra]))
+			if (
+				($isdraft || !empty($extrafields->attributes[$object->table_element]['alwayseditable'][$tmpkeyextra]))
 				&& $permok && $enabled != 5 && ($action != 'edit_extras' || GETPOST('attribute') != $tmpkeyextra)
-				&& empty($extrafields->attributes[$object->table_element]['computed'][$tmpkeyextra])) {
+				&& empty($extrafields->attributes[$object->table_element]['computed'][$tmpkeyextra])
+			) {
 				$fieldid = empty($forcefieldid) ? 'id' : $forcefieldid;
 				$valueid = empty($forceobjectid) ? $object->id : $forceobjectid;
 				if ($object->table_element == 'societe') {
 					$fieldid = 'socid';
 				}
 
-				print '<td class="right"><a class="reposition editfielda" href="'.$_SERVER['PHP_SELF'].'?'.$fieldid.'='.$valueid.'&action=edit_extras&token='.newToken().'&attribute='.$tmpkeyextra.'&ignorecollapsesetup=1">'.img_edit().'</a></td>';
+				print '<td class="right"><a class="reposition editfielda" href="' . $_SERVER['PHP_SELF'] . '?' . $fieldid . '=' . $valueid . '&action=edit_extras&token=' . newToken() . '&attribute=' . $tmpkeyextra . '&ignorecollapsesetup=1">' . img_edit() . '</a></td>';
 			}
 			print '</tr></table>';
 			print '</td>';
 
-			$html_id = !empty($object->id) ? $object->element.'_extras_'.$tmpkeyextra.'_'.$object->id : '';
+			$html_id = !empty($object->id) ? $object->element . '_extras_' . $tmpkeyextra . '_' . $object->id : '';
 
-			print '<td id="'.$html_id.'" class="valuefield '.$object->element.'_extras_'.$tmpkeyextra.' wordbreak"'.(!empty($cols) ? ' colspan="'.$cols.'"' : '').'>';
+			print '<td id="' . $html_id . '" class="valuefield ' . $object->element . '_extras_' . $tmpkeyextra . ' wordbreak"' . (!empty($cols) ? ' colspan="' . $cols . '"' : '') . '>';
 
 			// Convert date into timestamp format
 			if (in_array($extrafields->attributes[$object->table_element]['type'][$tmpkeyextra], array('date'))) {
-				$datenotinstring = empty($object->array_options['options_'.$tmpkeyextra]) ? '' : $object->array_options['options_'.$tmpkeyextra];
+				$datenotinstring = empty($object->array_options['options_' . $tmpkeyextra]) ? '' : $object->array_options['options_' . $tmpkeyextra];
 				// print 'X'.$object->array_options['options_' . $tmpkeyextra].'-'.$datenotinstring.'x';
-				if (!empty($object->array_options['options_'.$tmpkeyextra]) && !is_numeric($object->array_options['options_'.$tmpkeyextra])) {	// For backward compatibility
+				if (!empty($object->array_options['options_' . $tmpkeyextra]) && !is_numeric($object->array_options['options_' . $tmpkeyextra])) {	// For backward compatibility
 					$datenotinstring = $db->jdate($datenotinstring);
 				}
 				//print 'x'.$object->array_options['options_' . $tmpkeyextra].'-'.$datenotinstring.' - '.dol_print_date($datenotinstring, 'dayhour');
-				$value = GETPOSTISSET("options_".$tmpkeyextra) ? dol_mktime(12, 0, 0, GETPOST("options_".$tmpkeyextra."month", 'int'), GETPOST("options_".$tmpkeyextra."day", 'int'), GETPOST("options_".$tmpkeyextra."year", 'int')) : $datenotinstring;
+				$value = GETPOSTISSET("options_" . $tmpkeyextra) ? dol_mktime(12, 0, 0, GETPOST("options_" . $tmpkeyextra . "month", 'int'), GETPOST("options_" . $tmpkeyextra . "day", 'int'), GETPOST("options_" . $tmpkeyextra . "year", 'int')) : $datenotinstring;
 			}
 			if (in_array($extrafields->attributes[$object->table_element]['type'][$tmpkeyextra], array('datetime'))) {
-				$datenotinstring = empty($object->array_options['options_'.$tmpkeyextra]) ? '' : $object->array_options['options_'.$tmpkeyextra];
+				$datenotinstring = empty($object->array_options['options_' . $tmpkeyextra]) ? '' : $object->array_options['options_' . $tmpkeyextra];
 				// print 'X'.$object->array_options['options_' . $tmpkeyextra].'-'.$datenotinstring.'x';
-				if (!empty($object->array_options['options_'.$tmpkeyextra]) && !is_numeric($object->array_options['options_'.$tmpkeyextra])) {	// For backward compatibility
+				if (!empty($object->array_options['options_' . $tmpkeyextra]) && !is_numeric($object->array_options['options_' . $tmpkeyextra])) {	// For backward compatibility
 					$datenotinstring = $db->jdate($datenotinstring);
 				}
 				//print 'x'.$object->array_options['options_' . $tmpkeyextra].'-'.$datenotinstring.' - '.dol_print_date($datenotinstring, 'dayhour');
-				$value = GETPOSTISSET("options_".$tmpkeyextra) ? dol_mktime(GETPOST("options_".$tmpkeyextra."hour", 'int'), GETPOST("options_".$tmpkeyextra."min", 'int'), GETPOST("options_".$tmpkeyextra."sec", 'int'), GETPOST("options_".$tmpkeyextra."month", 'int'), GETPOST("options_".$tmpkeyextra."day", 'int'), GETPOST("options_".$tmpkeyextra."year", 'int'), 'tzuserrel') : $datenotinstring;
+				$value = GETPOSTISSET("options_" . $tmpkeyextra) ? dol_mktime(GETPOST("options_" . $tmpkeyextra . "hour", 'int'), GETPOST("options_" . $tmpkeyextra . "min", 'int'), GETPOST("options_" . $tmpkeyextra . "sec", 'int'), GETPOST("options_" . $tmpkeyextra . "month", 'int'), GETPOST("options_" . $tmpkeyextra . "day", 'int'), GETPOST("options_" . $tmpkeyextra . "year", 'int'), 'tzuserrel') : $datenotinstring;
 			}
 
 			//TODO Improve element and rights detection
@@ -237,23 +241,31 @@ if (empty($reshook) && isset($extrafields->attributes[$object->table_element]['l
 				if ($object->table_element == 'societe') {
 					$fieldid = 'socid';
 				}
-				print '<form enctype="multipart/form-data" action="'.$_SERVER["PHP_SELF"] . '?' . $fieldid . '=' . $object->id . '" method="post" name="formextra">';
+				print '<form enctype="multipart/form-data" action="' . $_SERVER["PHP_SELF"] . '?' . $fieldid . '=' . $object->id . '" method="post" name="formextra">';
 				print '<input type="hidden" name="action" value="update_extras">';
-				print '<input type="hidden" name="attribute" value="'.$tmpkeyextra.'">';
-				print '<input type="hidden" name="token" value="'.newToken().'">';
-				print '<input type="hidden" name="'.$fieldid.'" value="'.$object->id.'">';
+				print '<input type="hidden" name="attribute" value="' . $tmpkeyextra . '">';
+				print '<input type="hidden" name="token" value="' . newToken() . '">';
+				print '<input type="hidden" name="' . $fieldid . '" value="' . $object->id . '">';
 				print $extrafields->showInputField($tmpkeyextra, $value, '', '', '', 0, $object->id, $object->table_element);
 
-				print '<input type="submit" class="button" value="'.dol_escape_htmltag($langs->trans('Modify')).'">';
+				print '<input type="submit" class="button" value="' . dol_escape_htmltag($langs->trans('Modify')) . '">';
 
 				print '</form>';
 			} else {
 				//var_dump($tmpkeyextra.'-'.$value.'-'.$object->table_element);
-				print $extrafields->showOutputField($tmpkeyextra, $value, '', $object->table_element);
+				if ($tmpkeyextra == 'discount_start_hour') {
+					print $extrafields->showOutputField($tmpkeyextra, $value, '', $object->table_element) . ' 
+					: ' . $extrafields->showOutputField('discount_start_minute', $object->array_options["options_discount_start_minute"], '', $object->table_element) . '
+					- ' . $extrafields->showOutputField('discount_to_hour', $object->array_options["options_discount_to_hour"], '', $object->table_element) . '
+					: ' . $extrafields->showOutputField('discount_to_minute', $object->array_options["options_discount_to_minute"], '', $object->table_element)
+					;
+				} else {
+					print $extrafields->showOutputField($tmpkeyextra, $value, '', $object->table_element);
+				}
 			}
 
 			print '</td>';
-			print '</tr>'."\n";
+			print '</tr>' . "\n";
 		}
 	}
 
@@ -291,8 +303,8 @@ if (empty($reshook) && isset($extrafields->attributes[$object->table_element]['l
 						}
 						setListDependencies();
 				    });
-				</script>'."\n";
+				</script>' . "\n";
 	}
 }
 ?>
-<!-- END PHP TEMPLATE extrafields_view.tpl.php -->
+<!-- END PHP TEMPLATE extrafields_view.tpl.php -->

+ 623 - 556
core/tpl/objectline_create.tpl.php

@@ -103,7 +103,8 @@ print "<!-- BEGIN PHP TEMPLATE objectline_create.tpl.php -->\n";
 $nolinesbefore = (count($this->lines) == 0 || $forcetoshowtitlelines);
 if ($nolinesbefore) {
 	?>
-	<tr class="liste_titre<?php echo (($nolinesbefore || $object->element == 'contrat') ? '' : ' liste_titre_add_') ?> nodrag nodrop">
+	<tr
+		class="liste_titre<?php echo (($nolinesbefore || $object->element == 'contrat') ? '' : ' liste_titre_add_') ?> nodrag nodrop">
 		<?php if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER)) { ?>
 			<td class="linecolnum center"></td>
 		<?php } ?>
@@ -120,7 +121,8 @@ if ($nolinesbefore) {
 		<td class="linecolvat right"><span id="title_vat"><?php echo $langs->trans('VAT'); ?></span></td>
 		<td class="linecoluht right"><span id="title_up_ht"><?php echo $langs->trans('PriceUHT'); ?></span></td>
 		<?php if (isModEnabled("multicurrency") && $this->multicurrency_code != $conf->currency) { ?>
-			<td class="linecoluht_currency right"><span id="title_up_ht_currency"><?php echo $langs->trans('PriceUHTCurrency'); ?></span></td>
+			<td class="linecoluht_currency right"><span
+					id="title_up_ht_currency"><?php echo $langs->trans('PriceUHTCurrency'); ?></span></td>
 		<?php } ?>
 		<?php if (!empty($inputalsopricewithtax) && !getDolGlobalInt('MAIN_NO_INPUT_PRICE_WITH_TAX')) { ?>
 			<td class="linecoluttc right"><span id="title_up_ttc"><?php echo $langs->trans('PriceUTTC'); ?></span></td>
@@ -138,7 +140,7 @@ if ($nolinesbefore) {
 		<?php
 		// Fields for situation invoice
 		if (isset($this->situation_cycle_ref) && $this->situation_cycle_ref) {
-			print '<td class="linecolcycleref right">'.$langs->trans('Progress').'</td>';
+			print '<td class="linecolcycleref right">' . $langs->trans('Progress') . '</td>';
 			print '<td class="linecolcycleref2 right"></td>';
 		}
 		if (!empty($usemargins)) {
@@ -153,10 +155,10 @@ if ($nolinesbefore) {
 				}
 				echo '</td>';
 				if (!empty($conf->global->DISPLAY_MARGIN_RATES)) {
-					echo '<td class="margininfos linecolmargin2 right"><span class="np_marginRate">'.$langs->trans('MarginRate').'</span></td>';
+					echo '<td class="margininfos linecolmargin2 right"><span class="np_marginRate">' . $langs->trans('MarginRate') . '</span></td>';
 				}
 				if (!empty($conf->global->DISPLAY_MARK_RATES)) {
-					echo '<td class="margininfos linecolmargin2 right"><span class="np_markRate">'.$langs->trans('MarkRate').'</span></td>';
+					echo '<td class="margininfos linecolmargin2 right"><span class="np_markRate">' . $langs->trans('MarkRate') . '</span></td>';
 				}
 			}
 		}
@@ -166,7 +168,8 @@ if ($nolinesbefore) {
 	<?php
 }
 ?>
-<tr class="pair nodrag nodrop nohoverpair<?php echo ($nolinesbefore || $object->element == 'contrat') ? '' : ' liste_titre_create'; ?>">
+<tr
+	class="pair nodrag nodrop nohoverpair<?php echo ($nolinesbefore || $object->element == 'contrat') ? '' : ' liste_titre_create'; ?>">
 	<?php
 	$coldisplay = 0;
 	// Adds a line numbering column
@@ -199,7 +202,7 @@ if ($nolinesbefore) {
 				echo ((GETPOST('prod_entry_mode', 'alpha') == 'free' || !empty($conf->global->MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT)) ? ' checked' : '');
 				echo '> ';
 				// Show type selector
-				echo '<span class="textradioforitem">'.$langs->trans("FreeLineOfType").'</span>';
+				echo '<span class="textradioforitem">' . $langs->trans("FreeLineOfType") . '</span>';
 				echo '</label>';
 				echo ' ';
 			} else {
@@ -225,7 +228,7 @@ if ($nolinesbefore) {
 				echo '<span class="prod_entry_mode_predef">';
 			}
 			echo '<label for="prod_entry_mode_predef">';
-			echo '<input type="radio" class="prod_entry_mode_predef" name="prod_entry_mode" id="prod_entry_mode_predef" value="predef"'.(GETPOST('prod_entry_mode') == 'predef' ? ' checked' : '').'> ';
+			echo '<input type="radio" class="prod_entry_mode_predef" name="prod_entry_mode" id="prod_entry_mode_predef" value="predef"' . (GETPOST('prod_entry_mode') == 'predef' ? ' checked' : '') . '> ';
 			$labelforradio = '';
 			if (empty($conf->dol_optimize_smallscreen)) {
 				if (empty($senderissupplier)) {
@@ -248,7 +251,7 @@ if ($nolinesbefore) {
 			} else {
 				$labelforradio = $langs->trans('PredefinedItem');
 			}
-			print '<span class="textradioforitem">'.$labelforradio.'</span>';
+			print '<span class="textradioforitem">' . $labelforradio . '</span>';
 			echo '</label>';
 			echo ' ';
 			$filtertype = '';
@@ -258,7 +261,8 @@ if ($nolinesbefore) {
 			if (empty($senderissupplier)) {
 				$statustoshow = 1;
 				$statuswarehouse = 'warehouseopen,warehouseinternal';
-				if (!empty($conf->global->ENTREPOT_WAREHOUSEINTERNAL_NOT_SELL)) $statuswarehouse = 'warehouseopen';
+				if (!empty($conf->global->ENTREPOT_WAREHOUSEINTERNAL_NOT_SELL))
+					$statuswarehouse = 'warehouseopen';
 				if (!empty($conf->global->ENTREPOT_EXTRA_STATUS)) {
 					// hide products in closed warehouse, but show products for internal transfer
 					$form->select_produits(GETPOST('idprod'), 'idprod', $filtertype, $conf->product->limit_size, $buyer->price_level, $statustoshow, 2, '', 1, array(), $buyer->id, '1', 0, 'maxwidth500', 0, $statuswarehouse, GETPOST('combinations', 'array'));
@@ -267,26 +271,25 @@ if ($nolinesbefore) {
 				}
 				if (!empty($conf->global->MAIN_AUTO_OPEN_SELECT2_ON_FOCUS_FOR_CUSTOMER_PRODUCTS)) {
 					?>
-				<script>
-					$(document).ready(function(){
-						// On first focus on a select2 combo, auto open the menu (this allow to use the keyboard only)
-						$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
-							console.log('focus on a select2');
-							if ($(this).attr('aria-labelledby') == 'select2-idprod-container')
-							{
-								console.log('open combo');
-								$('#idprod').select2('open');
-							}
+					<script>
+						$(document).ready(function () {
+							// On first focus on a select2 combo, auto open the menu (this allow to use the keyboard only)
+							$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
+								console.log('focus on a select2');
+								if ($(this).attr('aria-labelledby') == 'select2-idprod-container') {
+									console.log('open combo');
+									$('#idprod').select2('open');
+								}
+							});
 						});
-					});
-				</script>
+					</script>
 					<?php
 				}
 			} else {
 				// $senderissupplier=2 is the same as 1 but disables test on minimum qty, disable autofill qty with minimum and autofill unit price
 				if ($senderissupplier != 2) {
 					$ajaxoptions = array(
-						'update' => array('qty'=>'qty', 'remise_percent' => 'discount', 'idprod' => 'idprod'), // html id tags that will be edited with each ajax json response key
+						'update' => array('qty' => 'qty', 'remise_percent' => 'discount', 'idprod' => 'idprod'), // html id tags that will be edited with each ajax json response key
 						'option_disabled' => 'idthatdoesnotexists', // html id to disable once select is done
 						'warning' => $langs->trans("NoPriceDefinedForThisSupplier") // translation of an error saved into var 'warning' (for example shown we select a disabled option into combo)
 					);
@@ -302,18 +305,17 @@ if ($nolinesbefore) {
 				$form->select_produits_fournisseurs($object->socid, GETPOST('idprodfournprice'), 'idprodfournprice', '', '', $ajaxoptions, 1, $alsoproductwithnosupplierprice, 'minwidth300imp maxwidth500');
 				if (!empty($conf->global->MAIN_AUTO_OPEN_SELECT2_ON_FOCUS_FOR_SUPPLIER_PRODUCTS)) {
 					?>
-				<script>
-					$(document).ready(function(){
-						// On first focus on a select2 combo, auto open the menu (this allow to use the keyboard only)
-						$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
-							//console.log('focus on a select2');
-							if ($(this).attr('aria-labelledby') == 'select2-idprodfournprice-container')
-							{
-								$('#idprodfournprice').select2('open');
-							}
+					<script>
+						$(document).ready(function () {
+							// On first focus on a select2 combo, auto open the menu (this allow to use the keyboard only)
+							$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
+								//console.log('focus on a select2');
+								if ($(this).attr('aria-labelledby') == 'select2-idprodfournprice-container') {
+									$('#idprodfournprice').select2('open');
+								}
+							});
 						});
-					});
-				</script>
+					</script>
 					<?php
 				}
 			}
@@ -322,18 +324,18 @@ if ($nolinesbefore) {
 		}
 
 		if (!empty($conf->global->MAIN_ADD_LINE_AT_POSITION)) {
-			echo '<br>'.$langs->trans('AddLineOnPosition').' : <input type="number" name="rank" step="1" min="0" style="width: 5em;">';
+			echo '<br>' . $langs->trans('AddLineOnPosition') . ' : <input type="number" name="rank" step="1" min="0" style="width: 5em;">';
 		}
 
 		if (is_object($hookmanager) && empty($senderissupplier)) {
-			$parameters = array('fk_parent_line'=>GETPOST('fk_parent_line', 'int'));
+			$parameters = array('fk_parent_line' => GETPOST('fk_parent_line', 'int'));
 			$reshook = $hookmanager->executeHooks('formCreateProductOptions', $parameters, $object, $action);
 			if (!empty($hookmanager->resPrint)) {
 				print $hookmanager->resPrint;
 			}
 		}
 		if (is_object($hookmanager) && !empty($senderissupplier)) {
-			$parameters = array('htmlname'=>'addproduct');
+			$parameters = array('htmlname' => 'addproduct');
 			$reshook = $hookmanager->executeHooks('formCreateProductSupplierOptions', $parameters, $object, $action);
 			if (!empty($hookmanager->resPrint)) {
 				print $hookmanager->resPrint;
@@ -346,7 +348,7 @@ if ($nolinesbefore) {
 			}
 		}
 		// Editor wysiwyg
-		require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
+		require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
 		$nbrows = ROWS_2;
 		$enabled = (!empty($conf->global->FCKEDITOR_ENABLE_DETAILS) ? $conf->global->FCKEDITOR_ENABLE_DETAILS : 0);
 		if (!empty($conf->global->MAIN_INPUT_DESC_HEIGHT)) {
@@ -361,14 +363,14 @@ if ($nolinesbefore) {
 		// Show autofill date for recurring invoices
 		if (isModEnabled("service") && ($object->element == 'facturerec' || $object->element == 'invoice_supplier_rec')) {
 			echo '<div class="divlinefordates"><br>';
-			echo $langs->trans('AutoFillDateFrom').' ';
+			echo $langs->trans('AutoFillDateFrom') . ' ';
 			if (!empty($conf->global->INVOICE_REC_DATE_TO_YES)) {
 				$line->date_start_fill = 1;
 				$line->date_end_fill = 1;
 			}
 			echo $form->selectyesno('date_start_fill', $line->date_start_fill, 1);
 			echo ' - ';
-			echo $langs->trans('AutoFillDateTo').' ';
+			echo $langs->trans('AutoFillDateTo') . ' ';
 			echo $form->selectyesno('date_end_fill', $line->date_end_fill, 1);
 			echo '</div>';
 		}
@@ -385,12 +387,14 @@ if ($nolinesbefore) {
 		if ($object->element == 'supplier_proposal' || $object->element == 'order_supplier' || $object->element == 'invoice_supplier' || $object->element == 'invoice_supplier_rec') {	// We must have same test in printObjectLines
 			$coldisplay++;
 			?>
-	<td class="nobottom linecolrefsupplier"><input id="fourn_ref" name="fourn_ref" class="flat minwidth50 maxwidth100 maxwidth125onsmartphone" value="<?php echo (GETPOSTISSET("fourn_ref") ? GETPOST("fourn_ref", 'alpha', 2) : ''); ?>"></td>
-		<?php }
+		<td class="nobottom linecolrefsupplier"><input id="fourn_ref" name="fourn_ref"
+				class="flat minwidth50 maxwidth100 maxwidth125onsmartphone"
+				value="<?php echo (GETPOSTISSET("fourn_ref") ? GETPOST("fourn_ref", 'alpha', 2) : ''); ?>"></td>
+	<?php }
 		print '<td class="nobottom linecolvat right">';
 		$coldisplay++;
 		if ($seller->tva_assuj == "0") {
-			echo '<input type="hidden" name="tva_tx" id="tva_tx" value="0">'.vatrate(0, true);
+			echo '<input type="hidden" name="tva_tx" id="tva_tx" value="0">' . vatrate(0, true);
 		} else {
 			echo $form->load_tva('tva_tx', (GETPOSTISSET("tva_tx") ? GETPOST("tva_tx", 'alpha', 2) : -1), $seller, $buyer, 0, 0, '', false, 1);
 		}
@@ -398,7 +402,8 @@ if ($nolinesbefore) {
 	</td>
 
 	<td class="nobottom linecoluht right"><?php $coldisplay++; ?>
-		<input type="text" size="5" name="price_ht" id="price_ht" class="flat right" value="<?php echo (GETPOSTISSET("price_ht") ? GETPOST("price_ht", 'alpha', 2) : ''); ?>">
+		<input type="text" size="5" name="price_ht" id="price_ht" class="flat right"
+			value="<?php echo (GETPOSTISSET("price_ht") ? GETPOST("price_ht", 'alpha', 2) : ''); ?>">
 	</td>
 
 	<?php
@@ -406,7 +411,8 @@ if ($nolinesbefore) {
 		$coldisplay++;
 		?>
 		<td class="nobottom linecoluht_currency right">
-			<input type="text" size="5" name="multicurrency_price_ht" id="multicurrency_price_ht" class="flat right" value="<?php echo (GETPOSTISSET("multicurrency_price_ht") ? GETPOST("multicurrency_price_ht", 'alpha', 2) : ''); ?>">
+			<input type="text" size="5" name="multicurrency_price_ht" id="multicurrency_price_ht" class="flat right"
+				value="<?php echo (GETPOSTISSET("multicurrency_price_ht") ? GETPOST("multicurrency_price_ht", 'alpha', 2) : ''); ?>">
 		</td>
 		<?php
 	}
@@ -414,30 +420,38 @@ if ($nolinesbefore) {
 		$coldisplay++;
 		?>
 		<td class="nobottom linecoluttc right">
-			<input type="text" size="5" name="price_ttc" id="price_ttc" class="flat right" value="<?php echo (GETPOSTISSET("price_ttc") ? GETPOST("price_ttc", 'alpha', 2) : ''); ?>">
+			<input type="text" size="5" name="price_ttc" id="price_ttc" class="flat right"
+				value="<?php echo (GETPOSTISSET("price_ttc") ? GETPOST("price_ttc", 'alpha', 2) : ''); ?>">
 		</td>
 		<?php
 	}
 	$coldisplay++;
 	?>
-	<td class="nobottom linecolqty right">
-	<input type="text" name="qty" id="qty" class="flat width40 right" value="<?php echo (GETPOSTISSET("qty") ? GETPOST("qty", 'alpha', 2) : 1); ?>">
-	</td>
-	<?php
-	if (!empty($conf->global->PRODUCT_USE_UNITS)) {
+	<?php if ($this->element == 'facture') { ?>
+		<td class="nobottom linecolqty right"><input type="text" name="qty" id="qty" class="flat width40 right"
+				value="<?php echo (GETPOSTISSET("qty") ? GETPOST("qty", 'alpha', 2) : 1); ?>"></td>
+	<?php } else { ?>
+		<td class="nobottom linecolqty right"><input type="text" size="2" name="qty" id="qty" class="flat right"
+				value="<?php echo (GETPOSTISSET("qty") ? GETPOST("qty", 'alpha', 2) : 1); ?>">
+		<?php } ?>
+		<?php
+		if (!empty($conf->global->PRODUCT_USE_UNITS)) {
+			$coldisplay++;
+			print '<td class="nobottom linecoluseunit left">';
+			print $form->selectUnits(empty($line->fk_unit) ? $conf->global->PRODUCT_USE_UNITS : $line->fk_unit, "units");
+			print '</td>';
+		}
+		$remise_percent = $buyer->remise_percent;
+		if ($object->element == 'supplier_proposal' || $object->element == 'order_supplier' || $object->element == 'invoice_supplier') {
+			$remise_percent = $seller->remise_supplier_percent;
+		}
 		$coldisplay++;
-		print '<td class="nobottom linecoluseunit left">';
-		print $form->selectUnits(empty($line->fk_unit) ? $conf->global->PRODUCT_USE_UNITS : $line->fk_unit, "units");
-		print '</td>';
-	}
-	$remise_percent = $buyer->remise_percent;
-	if ($object->element == 'supplier_proposal' || $object->element == 'order_supplier' || $object->element == 'invoice_supplier') {
-		$remise_percent = $seller->remise_supplier_percent;
-	}
-	$coldisplay++;
-	?>
+		?>
 
-	<td class="nobottom nowrap linecoldiscount right"><input type="text" name="remise_percent" id="remise_percent" class="flat width40 right" value="<?php echo (GETPOSTISSET("remise_percent") ? GETPOST("remise_percent", 'alpha', 2) : ($remise_percent ? $remise_percent : '')); ?>"><span class="opacitymedium hideonsmartphone">%</span></td>
+	<td class="nobottom nowrap linecoldiscount right"><input type="text" name="remise_percent" id="remise_percent"
+			class="flat width40 right"
+			value="<?php echo (GETPOSTISSET("remise_percent") ? GETPOST("remise_percent", 'alpha', 2) : ($remise_percent ? $remise_percent : '')); ?>"><span
+			class="opacitymedium hideonsmartphone">%</span></td>
 	<?php
 	if (isset($this->situation_cycle_ref) && $this->situation_cycle_ref) {
 		$coldisplay++;
@@ -452,18 +466,20 @@ if ($nolinesbefore) {
 			<td class="nobottom margininfos linecolmargin right">
 				<!-- For predef product -->
 				<?php if (isModEnabled("product") || isModEnabled("service")) { ?>
-					<select id="fournprice_predef" name="fournprice_predef" class="flat minwidth75imp maxwidth150" style="display: none;"></select>
+					<select id="fournprice_predef" name="fournprice_predef" class="flat minwidth75imp maxwidth150"
+						style="display: none;"></select>
 				<?php } ?>
 				<!-- For free product -->
-				<input type="text" id="buying_price" name="buying_price" class="flat maxwidth75 right" value="<?php echo (GETPOSTISSET("buying_price") ? GETPOST("buying_price", 'alpha', 2) : ''); ?>">
+				<input type="text" id="buying_price" name="buying_price" class="flat maxwidth75 right"
+					value="<?php echo (GETPOSTISSET("buying_price") ? GETPOST("buying_price", 'alpha', 2) : ''); ?>">
 			</td>
 			<?php
 			if (!empty($conf->global->DISPLAY_MARGIN_RATES)) {
-				echo '<td class="nobottom nowraponall margininfos right"><input class="flat right width40" type="text" id="np_marginRate" name="np_marginRate" value="'.(GETPOSTISSET("np_marginRate") ? GETPOST("np_marginRate", 'alpha', 2) : '').'"><span class="np_marginRate opacitymedium hideonsmartphone">%</span></td>';
+				echo '<td class="nobottom nowraponall margininfos right"><input class="flat right width40" type="text" id="np_marginRate" name="np_marginRate" value="' . (GETPOSTISSET("np_marginRate") ? GETPOST("np_marginRate", 'alpha', 2) : '') . '"><span class="np_marginRate opacitymedium hideonsmartphone">%</span></td>';
 				$coldisplay++;
 			}
 			if (!empty($conf->global->DISPLAY_MARK_RATES)) {
-				echo '<td class="nobottom nowraponall margininfos right"><input class="flat right width40" type="text" id="np_markRate" name="np_markRate" value="'.(GETPOSTISSET("np_markRate") ? GETPOST("np_markRate", 'alpha', 2) : '').'"><span class="np_markRate opacitymedium hideonsmartphone">%</span></td>';
+				echo '<td class="nobottom nowraponall margininfos right"><input class="flat right width40" type="text" id="np_markRate" name="np_markRate" value="' . (GETPOSTISSET("np_markRate") ? GETPOST("np_markRate", 'alpha', 2) : '') . '"><span class="np_markRate opacitymedium hideonsmartphone">%</span></td>';
 				$coldisplay++;
 			}
 		}
@@ -471,17 +487,18 @@ if ($nolinesbefore) {
 	$coldisplay += $colspan;
 	?>
 	<td class="nobottom linecoledit center valignmiddle" colspan="<?php echo $colspan; ?>">
-		<input type="submit" class="button reposition" value="<?php echo $langs->trans('Add'); ?>" name="addline" id="addline">
+		<input type="submit" class="button reposition" value="<?php echo $langs->trans('Add'); ?>" name="addline"
+			id="addline">
 	</td>
 </tr>
 
 <?php
 if ((isModEnabled("service") || ($object->element == 'contrat')) && $dateSelector && GETPOST('type') != '0') {	// We show date field if required
-	print '<tr id="trlinefordates" class="oddeven">'."\n";
+	print '<tr id="trlinefordates" class="oddeven">' . "\n";
 	if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER)) {
 		print '<td></td>';
 	}
-	print '<td colspan="'.($coldisplay - (empty($conf->global->MAIN_VIEW_LINE_NUMBER) ? 0 : 1)).'">';
+	print '<td colspan="' . ($coldisplay - (empty($conf->global->MAIN_VIEW_LINE_NUMBER) ? 0 : 1)) . '">';
 	$date_start = dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), 0, GETPOST('date_startmonth'), GETPOST('date_startday'), GETPOST('date_startyear'));
 	$date_end = dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), 0, GETPOST('date_endmonth'), GETPOST('date_endday'), GETPOST('date_endyear'));
 
@@ -502,19 +519,20 @@ if ((isModEnabled("service") || ($object->element == 'contrat')) && $dateSelecto
 	}
 
 	if (!empty($object->element) && $object->element == 'contrat') {
-		print $langs->trans("DateStartPlanned").' ';
+		print $langs->trans("DateStartPlanned") . ' ';
 		print $form->selectDate($date_start, "date_start", $usehm, $usehm, 1, "addproduct");
-		print ' &nbsp; '.$langs->trans("DateEndPlanned").' ';
+		print ' &nbsp; ' . $langs->trans("DateEndPlanned") . ' ';
 		print $form->selectDate($date_end, "date_end", $usehm, $usehm, 1, "addproduct");
 	} else {
-		print $langs->trans('ServiceLimitedDuration').' '.$langs->trans('From').' ';
+		print $langs->trans('ServiceLimitedDuration') . ' ' . $langs->trans('From') . ' ';
 		print $form->selectDate($date_start, 'date_start', empty($conf->global->MAIN_USE_HOURMIN_IN_DATE_RANGE) ? 0 : 1, empty($conf->global->MAIN_USE_HOURMIN_IN_DATE_RANGE) ? 0 : 1, 1, "addproduct", 1, 0);
-		print ' '.$langs->trans('to').' ';
+		print ' ' . $langs->trans('to') . ' ';
 		print $form->selectDate($date_end, 'date_end', empty($conf->global->MAIN_USE_HOURMIN_IN_DATE_RANGE) ? 0 : 1, empty($conf->global->MAIN_USE_HOURMIN_IN_DATE_RANGE) ? 0 : 1, 1, "addproduct", 1, 0);
-	};
+	}
+	;
 
 	if ($prefillDates) {
-		echo ' <span class="small"><a href="#" id="prefill_service_dates">'.$langs->trans('FillWithLastServiceDates').'</a></span>';
+		echo ' <span class="small"><a href="#" id="prefill_service_dates">' . $langs->trans('FillWithLastServiceDates') . '</a></span>';
 	}
 
 	print '<script>';
@@ -523,15 +541,15 @@ if ((isModEnabled("service") || ($object->element == 'contrat')) && $dateSelecto
 		?>
 		function prefill_service_dates()
 		{
-			$('#date_start').val("<?php echo dol_escape_js(dol_print_date($date_start_prefill, 'day')); ?>").trigger('change');
-			$('#date_end').val("<?php echo dol_escape_js(dol_print_date($date_end_prefill, 'day')); ?>").trigger('change');
+		$('#date_start').val("<?php echo dol_escape_js(dol_print_date($date_start_prefill, 'day')); ?>").trigger('change');
+		$('#date_end').val("<?php echo dol_escape_js(dol_print_date($date_end_prefill, 'day')); ?>").trigger('change');
 
-			return false; // Prevent default link behaviour (which is go to href URL)
+		return false; // Prevent default link behaviour (which is go to href URL)
 		}
 
 		$(document).ready(function()
 		{
-			$('#prefill_service_dates').click(prefill_service_dates);
+		$('#prefill_service_dates').click(prefill_service_dates);
 		});
 
 		<?php
@@ -539,23 +557,23 @@ if ((isModEnabled("service") || ($object->element == 'contrat')) && $dateSelecto
 
 	if (!$date_start) {
 		if (isset($conf->global->MAIN_DEFAULT_DATE_START_HOUR)) {
-			print 'jQuery("#date_starthour").val("'.$conf->global->MAIN_DEFAULT_DATE_START_HOUR.'");';
+			print 'jQuery("#date_starthour").val("' . $conf->global->MAIN_DEFAULT_DATE_START_HOUR . '");';
 		}
 		if (isset($conf->global->MAIN_DEFAULT_DATE_START_MIN)) {
-			print 'jQuery("#date_startmin").val("'.$conf->global->MAIN_DEFAULT_DATE_START_MIN.'");';
+			print 'jQuery("#date_startmin").val("' . $conf->global->MAIN_DEFAULT_DATE_START_MIN . '");';
 		}
 	}
 	if (!$date_end) {
 		if (isset($conf->global->MAIN_DEFAULT_DATE_END_HOUR)) {
-			print 'jQuery("#date_endhour").val("'.$conf->global->MAIN_DEFAULT_DATE_END_HOUR.'");';
+			print 'jQuery("#date_endhour").val("' . $conf->global->MAIN_DEFAULT_DATE_END_HOUR . '");';
 		}
 		if (isset($conf->global->MAIN_DEFAULT_DATE_END_MIN)) {
-			print 'jQuery("#date_endmin").val("'.$conf->global->MAIN_DEFAULT_DATE_END_MIN.'");';
+			print 'jQuery("#date_endmin").val("' . $conf->global->MAIN_DEFAULT_DATE_END_MIN . '");';
 		}
 	}
 	print '</script>';
 	print '</td>';
-	print '</tr>'."\n";
+	print '</tr>' . "\n";
 }
 
 
@@ -567,13 +585,13 @@ if (!empty($usemargins) && $user->rights->margins->creer) {
 	<?php
 	if (!empty($conf->global->DISPLAY_MARGIN_RATES)) { ?>
 		$("input[name='np_marginRate']:first").blur(function(e) {
-			return checkFreeLine(e, "np_marginRate");
+		return checkFreeLine(e, "np_marginRate");
 		});
 		<?php
 	}
 	if (!empty($conf->global->DISPLAY_MARK_RATES)) { ?>
 		$("input[name='np_markRate']:first").blur(function(e) {
-			return checkFreeLine(e, "np_markRate");
+		return checkFreeLine(e, "np_markRate");
 		});
 		<?php
 	}
@@ -583,580 +601,629 @@ if (!empty($usemargins) && $user->rights->margins->creer) {
 	/* TODO This does not work for number with thousand separator that is , */
 	function checkFreeLine(e, npRate)
 	{
-		var buying_price = $("input[name='buying_price']:first");
-		var remise = $("input[name='remise_percent']:first");
+	var buying_price = $("input[name='buying_price']:first");
+	var remise = $("input[name='remise_percent']:first");
 
-		var rate = $("input[name='"+npRate+"']:first");
-		if (rate.val() == '')
-			return true;
+	var rate = $("input[name='"+npRate+"']:first");
+	if (rate.val() == '')
+	return true;
 
-		if (! $.isNumeric(rate.val().replace(',','.')))
-		{
-			alert('<?php echo dol_escape_js($langs->trans("rateMustBeNumeric")); ?>');
-			e.stopPropagation();
-			setTimeout(function () { rate.focus() }, 50);
-			return false;
-		}
-		if (npRate == "np_markRate" && rate.val() >= 100)
-		{
-			alert('<?php echo dol_escape_js($langs->trans("markRateShouldBeLesserThan100")); ?>');
-			e.stopPropagation();
-			setTimeout(function () { rate.focus() }, 50);
-			return false;
-		}
+	if (! $.isNumeric(rate.val().replace(',','.')))
+	{
+	alert('<?php echo dol_escape_js($langs->trans("rateMustBeNumeric")); ?>');
+	e.stopPropagation();
+	setTimeout(function () { rate.focus() }, 50);
+	return false;
+	}
+	if (npRate == "np_markRate" && rate.val() >= 100)
+	{
+	alert('<?php echo dol_escape_js($langs->trans("markRateShouldBeLesserThan100")); ?>');
+	e.stopPropagation();
+	setTimeout(function () { rate.focus() }, 50);
+	return false;
+	}
 
-		var price = 0;
-		remisejs=price2numjs(remise.val());
+	var price = 0;
+	remisejs=price2numjs(remise.val());
 
-		if (remisejs != 100)	// If a discount not 100 or no discount
-		{
-			if (remisejs == '') remisejs=0;
+	if (remisejs != 100) // If a discount not 100 or no discount
+	{
+	if (remisejs == '') remisejs=0;
 
-			bpjs=price2numjs(buying_price.val());
-			ratejs=price2numjs(rate.val());
+	bpjs=price2numjs(buying_price.val());
+	ratejs=price2numjs(rate.val());
 
-			if (npRate == "np_marginRate")
-				price = ((bpjs * (1 + ratejs / 100)) / (1 - remisejs / 100));
-			else if (npRate == "np_markRate")
-				price = ((bpjs / (1 - ratejs / 100)) / (1 - remisejs / 100));
-		}
+	if (npRate == "np_marginRate")
+	price = ((bpjs * (1 + ratejs / 100)) / (1 - remisejs / 100));
+	else if (npRate == "np_markRate")
+	price = ((bpjs / (1 - ratejs / 100)) / (1 - remisejs / 100));
+	}
 
-		$("input[name='price_ht']:first").val(price);	// TODO Must use a function like php price to have here a formated value
+	$("input[name='price_ht']:first").val(price); // TODO Must use a function like php price to have here a formated value
 
-		return true;
+	return true;
 	}
 
 	<?php
 }
 ?>
 
-	/* JQuery for product free or predefined select */
-	jQuery(document).ready(function() {
-		jQuery("#price_ht").keyup(function(event) {
-			// console.log(event.which);		// discard event tag and arrows
-			if (event.which != 9 && (event.which < 37 ||event.which > 40) && jQuery("#price_ht").val() != '') {
-			jQuery("#price_ttc").val('');
-			jQuery("#multicurrency_subprice").val('');
-		}
+/* JQuery for product free or predefined select */
+jQuery(document).ready(function() {
+jQuery("#price_ht").keyup(function(event) {
+// console.log(event.which); // discard event tag and arrows
+if (event.which != 9 && (event.which < 37 ||event.which> 40) && jQuery("#price_ht").val() != '') {
+	jQuery("#price_ttc").val('');
+	jQuery("#multicurrency_subprice").val('');
+	}
 	});
 	jQuery("#price_ttc").keyup(function(event) {
-		// console.log(event.which);		// discard event tag and arrows
-		if (event.which != 9 && (event.which < 37 || event.which > 40) && jQuery("#price_ttc").val() != '') {
-			jQuery("#price_ht").val('');
-			jQuery("#multicurrency_subprice").val('');
+	// console.log(event.which); // discard event tag and arrows
+	if (event.which != 9 && (event.which < 37 || event.which> 40) && jQuery("#price_ttc").val() != '') {
+		jQuery("#price_ht").val('');
+		jQuery("#multicurrency_subprice").val('');
 		}
-	});
-	jQuery("#multicurrency_subprice").keyup(function(event) {
-		// console.log(event.which);		// discard event tag and arrows
-		if (event.which != 9 && (event.which < 37 || event.which > 40) && jQuery("#price_ttc").val() != '') {
+		});
+		jQuery("#multicurrency_subprice").keyup(function(event) {
+		// console.log(event.which); // discard event tag and arrows
+		if (event.which != 9 && (event.which < 37 || event.which> 40) && jQuery("#price_ttc").val() != '') {
 			jQuery("#price_ht").val('');
 			jQuery("#price_ttc").val('');
-		}
-	});
+			}
+			});
 
-	$("#prod_entry_mode_free").on( "click", function() {
-		setforfree();
-	});
-	$("#select_type").change(function()
-	{
-		setforfree();
+			$("#prod_entry_mode_free").on( "click", function() {
+			setforfree();
+			});
+			$("#select_type").change(function()
+			{
+			setforfree();
 
-		if (jQuery('#select_type').val() >= 0) {
+			if (jQuery('#select_type').val() >= 0) {
 			console.log("Set focus on description field");
 			/* this focus code works on a standard textarea but not if field was replaced with CKEDITOR */
 			jQuery('#dp_desc').focus();
 			/* this focus code works for CKEDITOR */
 			if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") {
-				var editor = CKEDITOR.instances['dp_desc'];
-				if (editor) {
-					editor.focus();
-				}
+			var editor = CKEDITOR.instances['dp_desc'];
+			if (editor) {
+			editor.focus();
+			}
+			}
 			}
-		}
 
-		console.log("Hide/show date according to product type");
-		if (jQuery('#select_type').val() == '0')
-		{
+			console.log("Hide/show date according to product type");
+			if (jQuery('#select_type').val() == '0')
+			{
 			jQuery('#trlinefordates').hide();
 			jQuery('.divlinefordates').hide();
-		}
-		else
-		{
+			}
+			else
+			{
 			jQuery('#trlinefordates').show();
 			jQuery('.divlinefordates').show();
-		}
-	});
+			}
+			});
 
-	$("#prod_entry_mode_predef").on( "click", function() {
-		console.log("click prod_entry_mode_predef");
-		setforpredef();
-		jQuery('#trlinefordates').show();
-	});
+			$("#prod_entry_mode_predef").on( "click", function() {
+			console.log("click prod_entry_mode_predef");
+			setforpredef();
+			jQuery('#trlinefordates').show();
+			});
 
-	<?php
-	if (!$freelines) { ?>
-		$("#prod_entry_mode_predef").click();
-		<?php
-	}
+			<?php
+			if (!$freelines) { ?>
+				$("#prod_entry_mode_predef").click();
+				<?php
+			}
 
-	if (in_array($this->table_element_line, array('propaldet', 'commandedet', 'facturedet'))) { ?>
-	$("#date_start, #date_end").focusout(function() {
-		let type = $(this).attr('type');
-		let mandatoryP = $(this).attr('mandatoryperiod');
-		if (type == 1 && mandatoryP == 1) {
-			if ($(this).val() == ''  && !$(this).hasClass('inputmandatory')) {
+			if (in_array($this->table_element_line, array('propaldet', 'commandedet', 'facturedet'))) { ?>
+				$("#date_start, #date_end").focusout(function() {
+				let type = $(this).attr('type');
+				let mandatoryP = $(this).attr('mandatoryperiod');
+				if (type == 1 && mandatoryP == 1) {
+				if ($(this).val() == '' && !$(this).hasClass('inputmandatory')) {
 				$(this).addClass('inputmandatory');
-			}else{
+				}else{
 				$(this).removeClass('inputmandatory');
-			}
-		}
-	});
-		<?php
-	} ?>
-	/* When changing predefined product, we reload list of supplier prices required for margin combo */
-	$("#idprod, #idprodfournprice").change(function()
-	{
-		console.log("objectline_create.tpl Call method change() after change on #idprod or #idprodfournprice (senderissupplier=<?php echo $senderissupplier; ?>). this.val = "+$(this).val());
+				}
+				}
+				});
+				<?php
+			} ?>
+			/* When changing predefined product, we reload list of supplier prices required for margin combo */
+			$("#idprod, #idprodfournprice").change(function()
+			{
+			console.log("objectline_create.tpl Call method change() after change on #idprod or #idprodfournprice
+			(senderissupplier=<?php echo $senderissupplier; ?>). this.val = "+$(this).val());
 
-		setforpredef();		// TODO Keep vat combo visible and set it to first entry into list that match result of get_default_tva(product)
+			setforpredef(); // TODO Keep vat combo visible and set it to first entry into list that match result of
+			get_default_tva(product)
 
-		jQuery('#trlinefordates').show();
+			jQuery('#trlinefordates').show();
 
-		<?php
-		if (empty($conf->global->MAIN_DISABLE_EDIT_PREDEF_PRICEHT) && empty($senderissupplier)) {
-			?>
-			var pbq = parseInt($('option:selected', this).attr('data-pbq'));	/* If product was selected with a HTML select */
-			if (isNaN(pbq)) { pbq = jQuery('#idprod').attr('data-pbq'); } 		/* If product was selected with a HTML input with autocomplete */
+			<?php
+			if (empty($conf->global->MAIN_DISABLE_EDIT_PREDEF_PRICEHT) && empty($senderissupplier)) {
+				?>
+				var pbq = parseInt($('option:selected', this).attr('data-pbq')); /* If product was selected with a HTML
+				select */
+				if (isNaN(pbq)) { pbq = jQuery('#idprod').attr('data-pbq'); } /* If product was selected with a HTML input
+				with autocomplete */
 
-			if ((jQuery('#idprod').val() > 0 || jQuery('#idprodfournprice').val()) && ! isNaN(pbq) && pbq > 0)
-			{
-				console.log("objectline_create.tpl We are in a price per qty context, we do not call ajax/product, init of fields is done few lines later");
-			} else {
+				if ((jQuery('#idprod').val() > 0 || jQuery('#idprodfournprice').val()) && ! isNaN(pbq) && pbq > 0)
+				{
+				console.log("objectline_create.tpl We are in a price per qty context, we do not call ajax/product, init of
+				fields is done few lines later");
+				} else {
 				<?php if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) { ?>
-					if (isNaN(pbq)) { console.log("We use experimental option PRODUIT_CUSTOMER_PRICES_BY_QTY or PRODUIT_CUSTOMER_PRICES_BY_QTY but we could not get the id of pbq from product combo list, so load of price may be 0 if product has differet prices"); }
+					if (isNaN(pbq)) { console.log("We use experimental option PRODUIT_CUSTOMER_PRICES_BY_QTY or
+					PRODUIT_CUSTOMER_PRICES_BY_QTY but we could not get the id of pbq from product combo list, so load of price
+					may be 0 if product has differet prices"); }
 				<?php } ?>
 				// Get the price for the product and display it
-				console.log("Load unit price without tax and set it into #price_ht for product id="+$(this).val()+" socid=<?php print $object->socid; ?>");
+				console.log("Load unit price without tax and set it into #price_ht for product id="+$(this).val()+"
+				socid=<?php print $object->socid; ?>");
 				$.post('<?php echo DOL_URL_ROOT; ?>/product/ajax/products.php?action=fetch',
-					{ 'id': $(this).val(), 'socid': <?php print $object->socid; ?>, 'token': '<?php print currentToken(); ?>' },
-					function(data) {
-						console.log("objectline_create.tpl Load unit price end, we got value ht="+data.price_ht+" ttc="+data.price_ttc+" pricebasetype="+data.pricebasetype);
-
-						$('#date_start').removeAttr('type');
-						$('#date_end').removeAttr('type');
-						$('#date_start').attr('type', data.type);
-						$('#date_end').attr('type', data.type);
-
-						$('#date_start').removeAttr('mandatoryperiod');
-						$('#date_end').removeAttr('mandatoryperiod');
-						$('#date_start').attr('mandatoryperiod', data.mandatory_period);
-						$('#date_end').attr('mandatoryperiod', data.mandatory_period);
-
-						// service and we setted mandatory_period to true
-						if (data.mandatory_period == 1 && data.type == 1) {
-							jQuery('#date_start').addClass('inputmandatory');
-							jQuery('#date_end').addClass('inputmandatory');
-						} else {
-							jQuery('#date_start').removeClass('inputmandatory');
-							jQuery('#date_end').removeClass('inputmandatory');
-						}
+				{ 'id': $(this).val(), 'socid': <?php print $object->socid; ?>, 'token': '<?php print currentToken(); ?>' },
+				function(data) {
+				console.log("objectline_create.tpl Load unit price end, we got value ht="+data.price_ht+"
+				ttc="+data.price_ttc+" pricebasetype="+data.pricebasetype);
+
+				$('#date_start').removeAttr('type');
+				$('#date_end').removeAttr('type');
+				$('#date_start').attr('type', data.type);
+				$('#date_end').attr('type', data.type);
+
+				$('#date_start').removeAttr('mandatoryperiod');
+				$('#date_end').removeAttr('mandatoryperiod');
+				$('#date_start').attr('mandatoryperiod', data.mandatory_period);
+				$('#date_end').attr('mandatoryperiod', data.mandatory_period);
+
+				// service and we setted mandatory_period to true
+				if (data.mandatory_period == 1 && data.type == 1) {
+				jQuery('#date_start').addClass('inputmandatory');
+				jQuery('#date_end').addClass('inputmandatory');
+				} else {
+				jQuery('#date_start').removeClass('inputmandatory');
+				jQuery('#date_end').removeClass('inputmandatory');
+				}
 
-						if (<?php echo (int) $inputalsopricewithtax; ?> == 1 && data.pricebasetype == 'TTC') {
-							console.log("objectline_create.tpl set content of price_ttc");
-							jQuery("#price_ttc").val(data.price_ttc);
-						} else {
-							console.log("objectline_create.tpl set content of price_ht");
-							jQuery("#price_ht").val(data.price_ht);
+				if (<?php echo (int) $inputalsopricewithtax; ?> == 1 && data.pricebasetype == 'TTC') {
+				console.log("objectline_create.tpl set content of price_ttc");
+				jQuery("#price_ttc").val(data.price_ttc);
+				} else {
+				console.log("objectline_create.tpl set content of price_ht");
+				var discount_hours = [];
+						var discount_percent = [];
+						for (var i = 1; i <= data.array_options.options_discount_hours; i++) {
+    						discount_hours.push(i);
 						}
+						for (var i = 1; i <= data.array_options.options_discount_percent; i++) {
+    						discount_percent.push(i);
+						}
+						$.each(discount_hours, function(index, option) {
+							$('#options_discount_hours').append($('<option>', {
+								value: option,
+								text: option
+							}));
+    					});
+						$.each(discount_percent, function(index, option) {
+							$('#options_discount_percent').append($('<option>', {
+								value: option,
+								text: option
+							}));
+    					});
+				jQuery("#price_ht").val(data.price_ht);
+				}
 
-						var tva_tx = data.tva_tx;
-						var default_vat_code = data.default_vat_code;
+				var tva_tx = data.tva_tx;
+				var default_vat_code = data.default_vat_code;
 
-						// Now set the VAT
-						var stringforvatrateselection = tva_tx;
-						if (typeof default_vat_code != 'undefined' && default_vat_code != null) {
-							stringforvatrateselection = stringforvatrateselection+' ('+default_vat_code+')';
-						}
-						// Set vat rate if field is an input box
-						$('#tva_tx').val(tva_tx);
-						// Set vat rate by selecting the combo
-						//$('#tva_tx option').val(tva_tx);	// This is bugged, it replaces the vat key of all options
-						$('#tva_tx option').removeAttr('selected');
-						console.log("stringforvatrateselection="+stringforvatrateselection+" -> value of option label for this key="+$('#tva_tx option[value="'+stringforvatrateselection+'"]').val());
-						$('#tva_tx option[value="'+stringforvatrateselection+'"]').prop('selected', true);
+				// Now set the VAT
+				var stringforvatrateselection = tva_tx;
+				if (typeof default_vat_code != 'undefined' && default_vat_code != null) {
+				stringforvatrateselection = stringforvatrateselection+' ('+default_vat_code+')';
+				}
+				// Set vat rate if field is an input box
+				$('#tva_tx').val(tva_tx);
+				// Set vat rate by selecting the combo
+				//$('#tva_tx option').val(tva_tx); // This is bugged, it replaces the vat key of all options
+				$('#tva_tx option').removeAttr('selected');
+				console.log("stringforvatrateselection="+stringforvatrateselection+" -> value of option label for this
+				key="+$('#tva_tx option[value="'+stringforvatrateselection+'"]').val());
+				$('#tva_tx option[value="'+stringforvatrateselection+'"]').prop('selected', true);
 
-						<?php
-						if (!empty($conf->global->PRODUIT_AUTOFILL_DESC) && $conf->global->PRODUIT_AUTOFILL_DESC == 1) {
-							if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { ?>
+				<?php
+				if (!empty($conf->global->PRODUIT_AUTOFILL_DESC) && $conf->global->PRODUIT_AUTOFILL_DESC == 1) {
+					if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { ?>
 						var proddesc = data.desc_trans;
-								<?php
-							} else { ?>
+						<?php
+					} else { ?>
 						var proddesc = data.desc;
-								<?php
-							} ?>
-						console.log("objectline_create.tpl Load desciption into text area : "+proddesc);
-							<?php
-							if (!empty($conf->global->FCKEDITOR_ENABLE_DETAILS)) { ?>
+						<?php
+					} ?>
+					console.log("objectline_create.tpl Load desciption into text area : "+proddesc);
+					<?php
+					if (!empty($conf->global->FCKEDITOR_ENABLE_DETAILS)) { ?>
 						if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined")
 						{
-							var editor = CKEDITOR.instances['dp_desc'];
-							if (editor) {
-								editor.setData(proddesc);
-							}
+						var editor = CKEDITOR.instances['dp_desc'];
+						if (editor) {
+						editor.setData(proddesc);
+						}
 						}
-								<?php
-							} else { ?>
+						<?php
+					} else { ?>
 						jQuery('#dp_desc').text(proddesc);
-								<?php
-							} ?>
-							<?php
-						} ?>
 						<?php
-						if (!empty($conf->global->PRODUCT_LOAD_EXTRAFIELD_INTO_OBJECTLINES)) { ?>
-							jQuery.each(data.array_options, function( key, value ) {
-								jQuery('div[class*="det'+key.replace('options_','_extras_')+'"] > #'+key).val(value);
-							});
-							<?php
-						} ?>
-					},
-					'json'
+					} ?>
+					<?php
+				} ?>
+				<?php
+				if (!empty($conf->global->PRODUCT_LOAD_EXTRAFIELD_INTO_OBJECTLINES)) { ?>
+					jQuery.each(data.array_options, function( key, value ) {
+					jQuery('div[class*="det'+key.replace('options_','_extras_')+'"] > #'+key).val(value);
+					});
+					<?php
+				} ?>
+				},
+				'json'
 				);
+				}
+				<?php
 			}
-			<?php
-		}
 
-		if (!empty($usemargins) && $user->rights->margins->creer) {
-			$langs->load('stocks');
-			?>
+			if (!empty($usemargins) && $user->rights->margins->creer) {
+				$langs->load('stocks');
+				?>
 
-			/* Code for margin */
-			$("#fournprice_predef").find("option").remove();
-			$("#fournprice_predef").hide();
-			$("#buying_price").val("").show();
+				/* Code for margin */
+				$("#fournprice_predef").find("option").remove();
+				$("#fournprice_predef").hide();
+				$("#buying_price").val("").show();
 
-			/* Call post to load content of combo list fournprice_predef */
-			var token = '<?php echo currentToken(); ?>';		// For AJAX Call we use old 'token' and not 'newtoken'
-			$.post('<?php echo DOL_URL_ROOT; ?>/fourn/ajax/getSupplierPrices.php?bestpricefirst=1', { 'idprod': $(this).val(), 'token': token }, function(data) {
+				/* Call post to load content of combo list fournprice_predef */
+				var token = '<?php echo currentToken(); ?>'; // For AJAX Call we use old 'token' and not 'newtoken'
+				$.post('<?php echo DOL_URL_ROOT; ?>/fourn/ajax/getSupplierPrices.php?bestpricefirst=1', { 'idprod':
+				$(this).val(), 'token': token }, function(data) {
 				if (data && data.length > 0)
 				{
-					var options = ''; var defaultkey = ''; var defaultprice = ''; var bestpricefound = 0;
+				var options = ''; var defaultkey = ''; var defaultprice = ''; var bestpricefound = 0;
 
-					var bestpriceid = 0; var bestpricevalue = 0;
-					var pmppriceid = 0; var pmppricevalue = 0;
-					var costpriceid = 0; var costpricevalue = 0;
+				var bestpriceid = 0; var bestpricevalue = 0;
+				var pmppriceid = 0; var pmppricevalue = 0;
+				var costpriceid = 0; var costpricevalue = 0;
 
-					/* setup of margin calculation */
-					var defaultbuyprice = '<?php
-					if (isset($conf->global->MARGIN_TYPE)) {
-						if ($conf->global->MARGIN_TYPE == '1') {
-							print 'bestsupplierprice';
-						}
-						if ($conf->global->MARGIN_TYPE == 'pmp') {
-							print 'pmp';
-						}
-						if ($conf->global->MARGIN_TYPE == 'costprice') {
-							print 'costprice';
-						}
-					} ?>';
-					console.log("objectline_create.tpl we will set the field for margin. defaultbuyprice="+defaultbuyprice);
+				/* setup of margin calculation */
+				var defaultbuyprice = '<?php
+				if (isset($conf->global->MARGIN_TYPE)) {
+					if ($conf->global->MARGIN_TYPE == '1') {
+						print 'bestsupplierprice';
+					}
+					if ($conf->global->MARGIN_TYPE == 'pmp') {
+						print 'pmp';
+					}
+					if ($conf->global->MARGIN_TYPE == 'costprice') {
+						print 'costprice';
+					}
+				} ?>';
+				console.log("objectline_create.tpl we will set the field for margin. defaultbuyprice="+defaultbuyprice);
 
-					var i = 0;
-					$(data).each(function() {
-						/* Warning: Lines must be processed in order: best supplier price, then pmpprice line then costprice */
-						if (this.id != 'pmpprice' && this.id != 'costprice')
-						{
-							i++;
-							this.price = parseFloat(this.price); // to fix when this.price >0
-							// If margin is calculated on best supplier price, we set it by defaut (but only if value is not 0)
-							//console.log("id="+this.id+"-price="+this.price+"-"+(this.price > 0));
-							if (bestpricefound == 0 && this.price > 0) { defaultkey = this.id; defaultprice = this.price; bestpriceid = this.id; bestpricevalue = this.price; bestpricefound=1; }	// bestpricefound is used to take the first price > 0
-						}
-						if (this.id == 'pmpprice')
-						{
-							// If margin is calculated on PMP, we set it by defaut (but only if value is not 0)
-							console.log("id="+this.id+"-price="+this.price);
-							if ('pmp' == defaultbuyprice || 'costprice' == defaultbuyprice)
-							{
-								if (this.price > 0) {
-									defaultkey = this.id; defaultprice = this.price; pmppriceid = this.id; pmppricevalue = this.price;
-									//console.log("pmppricevalue="+pmppricevalue);
-								}
-							}
-						}
-						if (this.id == 'costprice')
-						{
-							// If margin is calculated on Cost price, we set it by defaut (but only if value is not 0)
-							console.log("id="+this.id+"-price="+this.price+"-pmppricevalue="+pmppricevalue);
-							if ('costprice' == defaultbuyprice)
-							{
-								if (this.price > 0) { defaultkey = this.id; defaultprice = this.price; costpriceid = this.id; costpricevalue = this.price; }
-								else if (pmppricevalue > 0) { defaultkey = 'pmpprice'; defaultprice = pmppricevalue; }
-							}
-						}
-						options += '<option value="'+this.id+'" price="'+this.price+'">'+this.label+'</option>';
-					});
-					options += '<option value="inputprice" price="'+defaultprice+'"><?php echo $langs->trans("InputPrice").'...'; ?></option>';
+				var i = 0;
+				$(data).each(function() {
+				/* Warning: Lines must be processed in order: best supplier price, then pmpprice line then costprice */
+				if (this.id != 'pmpprice' && this.id != 'costprice')
+				{
+				i++;
+				this.price = parseFloat(this.price); // to fix when this.price >0
+				// If margin is calculated on best supplier price, we set it by defaut (but only if value is not 0)
+				//console.log("id="+this.id+"-price="+this.price+"-"+(this.price > 0));
+				if (bestpricefound == 0 && this.price > 0) { defaultkey = this.id; defaultprice = this.price; bestpriceid =
+				this.id; bestpricevalue = this.price; bestpricefound=1; } // bestpricefound is used to take the first price
+				> 0
+				}
+				if (this.id == 'pmpprice')
+				{
+				// If margin is calculated on PMP, we set it by defaut (but only if value is not 0)
+				console.log("id="+this.id+"-price="+this.price);
+				if ('pmp' == defaultbuyprice || 'costprice' == defaultbuyprice)
+				{
+				if (this.price > 0) {
+				defaultkey = this.id; defaultprice = this.price; pmppriceid = this.id; pmppricevalue = this.price;
+				//console.log("pmppricevalue="+pmppricevalue);
+				}
+				}
+				}
+				if (this.id == 'costprice')
+				{
+				// If margin is calculated on Cost price, we set it by defaut (but only if value is not 0)
+				console.log("id="+this.id+"-price="+this.price+"-pmppricevalue="+pmppricevalue);
+				if ('costprice' == defaultbuyprice)
+				{
+				if (this.price > 0) { defaultkey = this.id; defaultprice = this.price; costpriceid = this.id; costpricevalue
+				= this.price; }
+				else if (pmppricevalue > 0) { defaultkey = 'pmpprice'; defaultprice = pmppricevalue; }
+				}
+				}
+				options += '<option value="'+this.id+'" price="'+this.price+'">'+this.label+'</option>';
+				});
+				options += '<option value="inputprice" price="'+defaultprice+'">
+					<?php echo $langs->trans("InputPrice") . '...'; ?></option>';
 
-					console.log("finally selected defaultkey="+defaultkey+" defaultprice for buying price="+defaultprice);
+				console.log("finally selected defaultkey="+defaultkey+" defaultprice for buying price="+defaultprice);
 
-					$("#fournprice_predef").html(options).show();
-					if (defaultkey != '')
-					{
-						$("#fournprice_predef").val(defaultkey);
-					}
+				$("#fournprice_predef").html(options).show();
+				if (defaultkey != '')
+				{
+				$("#fournprice_predef").val(defaultkey);
+				}
 
-					/* At loading, no product are yet selected, so we hide field of buying_price */
-					$("#buying_price").hide();
+				/* At loading, no product are yet selected, so we hide field of buying_price */
+				$("#buying_price").hide();
 
-					/* Define default price at loading */
-					var defaultprice = $("#fournprice_predef").find('option:selected').attr("price");
-					$("#buying_price").val(defaultprice);
+				/* Define default price at loading */
+				var defaultprice = $("#fournprice_predef").find('option:selected').attr("price");
+				$("#buying_price").val(defaultprice);
 
-					$("#fournprice_predef").change(function() {
-						console.log("change on fournprice_predef");
-						/* Hide field buying_price according to choice into list (if 'inputprice' or not) */
-						var linevalue=$(this).find('option:selected').val();
-						var pricevalue = $(this).find('option:selected').attr("price");
-						if (linevalue != 'inputprice' && linevalue != 'pmpprice') {
-							$("#buying_price").val(pricevalue).hide();	/* We set value then hide field */
-						}
-						if (linevalue == 'inputprice') {
-							$('#buying_price').show();
-						}
-						if (linevalue == 'pmpprice') {
-							$("#buying_price").val(pricevalue);
-							$('#buying_price').hide();
-						}
-					});
+				$("#fournprice_predef").change(function() {
+				console.log("change on fournprice_predef");
+				/* Hide field buying_price according to choice into list (if 'inputprice' or not) */
+				var linevalue=$(this).find('option:selected').val();
+				var pricevalue = $(this).find('option:selected').attr("price");
+				if (linevalue != 'inputprice' && linevalue != 'pmpprice') {
+				$("#buying_price").val(pricevalue).hide(); /* We set value then hide field */
 				}
-			},
-			'json');
-
-			<?php
-		}
-		?>
+				if (linevalue == 'inputprice') {
+				$('#buying_price').show();
+				}
+				if (linevalue == 'pmpprice') {
+				$("#buying_price").val(pricevalue);
+				$('#buying_price').hide();
+				}
+				});
+				}
+				},
+				'json');
 
-		<?php
-		if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
+				<?php
+			}
 			?>
-			/* To process customer price per quantity (PRODUIT_CUSTOMER_PRICES_BY_QTY works only if combo product is not an ajax after x key pressed) */
-			var pbq = parseInt($('option:selected', this).attr('data-pbq'));				// When select is done from HTML select
-			if (isNaN(pbq)) { pbq = jQuery('#idprod').attr('data-pbq');	}					// When select is done from HTML input with autocomplete
-			var pbqup = parseFloat($('option:selected', this).attr('data-pbqup'));
-			if (isNaN(pbqup)) { pbqup = jQuery('#idprod').attr('data-pbqup');	}
-			var pbqbase = $('option:selected', this).attr('data-pbqbase');
-			if (isNaN(pbqbase)) { pbqbase = jQuery('#idprod').attr('data-pbqbase');	}
-			var pbqqty = parseFloat($('option:selected', this).attr('data-pbqqty'));
-			if (isNaN(pbqqty)) { pbqqty = jQuery('#idprod').attr('data-pbqqty');	}
-			var pbqpercent = parseFloat($('option:selected', this).attr('data-pbqpercent'));
-			if (isNaN(pbqpercent)) { pbqpercent = jQuery('#idprod').attr('data-pbqpercent');	}
-
-			if ((jQuery('#idprod').val() > 0) && ! isNaN(pbq) && pbq > 0)
-			{
-				var pbqupht = pbqup;	/* TODO support of price per qty TTC not yet available */
 
-				console.log("We choose a price by quanty price_by_qty id = "+pbq+" price_by_qty upht = "+pbqupht+" price_by_qty qty = "+pbqqty+" price_by_qty percent = "+pbqpercent);
+			<?php
+			if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
+				?>
+				/* To process customer price per quantity (PRODUIT_CUSTOMER_PRICES_BY_QTY works only if combo product is not
+				an ajax after x key pressed) */
+				var pbq = parseInt($('option:selected', this).attr('data-pbq')); // When select is done from HTML select
+				if (isNaN(pbq)) { pbq = jQuery('#idprod').attr('data-pbq'); } // When select is done from HTML input with
+				autocomplete
+				var pbqup = parseFloat($('option:selected', this).attr('data-pbqup'));
+				if (isNaN(pbqup)) { pbqup = jQuery('#idprod').attr('data-pbqup'); }
+				var pbqbase = $('option:selected', this).attr('data-pbqbase');
+				if (isNaN(pbqbase)) { pbqbase = jQuery('#idprod').attr('data-pbqbase'); }
+				var pbqqty = parseFloat($('option:selected', this).attr('data-pbqqty'));
+				if (isNaN(pbqqty)) { pbqqty = jQuery('#idprod').attr('data-pbqqty'); }
+				var pbqpercent = parseFloat($('option:selected', this).attr('data-pbqpercent'));
+				if (isNaN(pbqpercent)) { pbqpercent = jQuery('#idprod').attr('data-pbqpercent'); }
+
+				if ((jQuery('#idprod').val() > 0) && ! isNaN(pbq) && pbq > 0)
+				{
+				var pbqupht = pbqup; /* TODO support of price per qty TTC not yet available */
+
+				console.log("We choose a price by quanty price_by_qty id = "+pbq+" price_by_qty upht = "+pbqupht+"
+				price_by_qty qty = "+pbqqty+" price_by_qty percent = "+pbqpercent);
 				jQuery("#pbq").val(pbq);
 				jQuery("#price_ht").val(pbqupht);
-				if (jQuery("#qty").val() < pbqqty)
-				{
-					jQuery("#qty").val(pbqqty);
-				}
-				if (jQuery("#remise_percent").val() < pbqpercent)
-				{
-					jQuery("#remise_percent").val(pbqpercent);
-				}
-			} else { jQuery("#pbq").val(''); }
-			<?php
-		}
-		?>
+				if (jQuery("#qty").val() < pbqqty) { jQuery("#qty").val(pbqqty); } if (jQuery("#remise_percent").val() <
+					pbqpercent) { jQuery("#remise_percent").val(pbqpercent); } } else { jQuery("#pbq").val(''); } <?php
+			}
+			?>
 
 
-		// Deal with supplier ref price (idprodfournprice = int)
-		if (jQuery('#idprodfournprice').val() > 0)
-		{
-			console.log("objectline_create.tpl #idprodfournprice is is an ID > 0, so we set some properties into page");
+				// Deal with supplier ref price (idprodfournprice = int)
+				if (jQuery('#idprodfournprice').val() > 0)
+				{
+				console.log("objectline_create.tpl #idprodfournprice is is an ID > 0, so we set some properties into
+				page");
 
-			var up = parseFloat($('option:selected', this).attr('data-up')); 							// When select is done from HTML select
-			if (isNaN(up)) { up = parseFloat(jQuery('#idprodfournprice').attr('data-up'));}				// When select is done from HTML input with ajax autocomplete
+				var up = parseFloat($('option:selected', this).attr('data-up')); // When select is done from HTML select
+				if (isNaN(up)) { up = parseFloat(jQuery('#idprodfournprice').attr('data-up'));} // When select is done
+				from HTML input with ajax autocomplete
 
-			var up_locale = $('option:selected', this).attr('data-up-locale');							// When select is done from HTML select
-			if (typeof up_locale === 'undefined') { up_locale = jQuery('#idprodfournprice').attr('data-up-locale');}	// When select is done from HTML input with ajax autocomplete
+				var up_locale = $('option:selected', this).attr('data-up-locale'); // When select is done from HTML
+				select
+				if (typeof up_locale === 'undefined') { up_locale = jQuery('#idprodfournprice').attr('data-up-locale');}
+				// When select is done from HTML input with ajax autocomplete
 
-			var qty = parseFloat($('option:selected', this).attr('data-qty'));
-			if (isNaN(qty)) { qty = parseFloat(jQuery('#idprodfournprice').attr('data-qty'));}
+				var qty = parseFloat($('option:selected', this).attr('data-qty'));
+				if (isNaN(qty)) { qty = parseFloat(jQuery('#idprodfournprice').attr('data-qty'));}
 
-			var discount = parseFloat($('option:selected', this).attr('data-discount'));
-			if (isNaN(discount)) { discount = parseFloat(jQuery('#idprodfournprice').attr('data-discount'));}
+				var discount = parseFloat($('option:selected', this).attr('data-discount'));
+				if (isNaN(discount)) { discount = parseFloat(jQuery('#idprodfournprice').attr('data-discount'));}
 
-			var tva_tx = parseFloat($('option:selected', this).attr('data-tvatx')); 					// When select is done from HTML select
-			if (isNaN(tva_tx)) { tva_tx = parseFloat(jQuery('#idprodfournprice').attr('data-tvatx'));}	// When select is done from HTML input with ajax autocomplete
+				var tva_tx = parseFloat($('option:selected', this).attr('data-tvatx')); // When select is done from HTML
+				select
+				if (isNaN(tva_tx)) { tva_tx = parseFloat(jQuery('#idprodfournprice').attr('data-tvatx'));} // When
+				select is done from HTML input with ajax autocomplete
 
-			var default_vat_code = $('option:selected', this).attr('data-default-vat-code');							 					// When select is done from HTML select
-			if (typeof default_vat_code === 'undefined') { default_vat_code = jQuery('#idprodfournprice').attr('data-default-vat-code');}	// When select is done from HTML input with ajax autocomplete
+				var default_vat_code = $('option:selected', this).attr('data-default-vat-code'); // When select is done
+				from HTML select
+				if (typeof default_vat_code === 'undefined') { default_vat_code =
+				jQuery('#idprodfournprice').attr('data-default-vat-code');} // When select is done from HTML input with
+				ajax autocomplete
 
-			var stringforvatrateselection = tva_tx;
-			if (typeof default_vat_code != 'undefined' && default_vat_code != null) {
+				var stringforvatrateselection = tva_tx;
+				if (typeof default_vat_code != 'undefined' && default_vat_code != null) {
 				stringforvatrateselection = stringforvatrateselection+' ('+default_vat_code+')';
-			}
+				}
 
-			console.log("objectline_create.tpl We find supplier price : up = "+up+", up_locale = "+up_locale+", qty = "+qty+", tva_tx = "+tva_tx+", default_vat_code = "+default_vat_code+", stringforvatrateselection="+stringforvatrateselection+", discount = "+discount+" for product supplier ref id = "+jQuery('#idprodfournprice').val());
+				console.log("objectline_create.tpl We find supplier price : up = "+up+", up_locale = "+up_locale+", qty
+				= "+qty+", tva_tx = "+tva_tx+", default_vat_code = "+default_vat_code+",
+				stringforvatrateselection="+stringforvatrateselection+", discount = "+discount+" for product supplier
+				ref id = "+jQuery('#idprodfournprice').val());
 
-			if (typeof up_locale === 'undefined') {
+				if (typeof up_locale === 'undefined') {
 				jQuery("#price_ht").val(up);
-			} else {
-				jQuery("#price_ht").val(up_locale);
-			}
-
-			// Set vat rate if field is an input box
-			$('#tva_tx').val(tva_tx);
-			// Set vat rate by selecting the combo
-			//$('#tva_tx option').val(tva_tx);	// This is bugged, it replaces the vat key of all options
-			$('#tva_tx option').removeAttr('selected');
-			console.log("stringforvatrateselection="+stringforvatrateselection+" -> value of option label for this key="+$('#tva_tx option[value="'+stringforvatrateselection+'"]').val());
-			$('#tva_tx option[value="'+stringforvatrateselection+'"]').prop('selected', true);
-
-			if (jQuery("#qty").val() < qty)	{
-				jQuery("#qty").val(qty);
-			}
-			if (jQuery("#remise_percent").val() < discount) {
-				jQuery("#remise_percent").val(discount);
-			}
-
-			<?php
-			if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 1) {
-				?>
-			var description = $('option:selected', this).attr('data-description');
-			if (typeof description == 'undefined') { description = jQuery('#idprodfournprice').attr('data-description');	}
-
-			console.log("Load desciption into text area : "+description);
-				<?php
-				if (!empty($conf->global->FCKEDITOR_ENABLE_DETAILS)) {
-					?>
-			if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined")
-			{
-				var editor = CKEDITOR.instances['dp_desc'];
-				if (editor) {
-					editor.setData(description);
-				}
-			}
-					<?php
 				} else {
-					?>
-			jQuery('#dp_desc').text(description);
-					<?php
+				jQuery("#price_ht").val(up_locale);
 				}
-			}
-			?>
-		} else if (jQuery('#idprodfournprice').length > 0) {
-			console.log("objectline_create.tpl #idprodfournprice is not an int but is a string so we set only few properties into page");
-
-			var tva_tx = parseFloat($('option:selected', this).attr('data-tvatx')); 					// When select is done from HTML select
-			if (isNaN(tva_tx)) { tva_tx = parseFloat(jQuery('#idprodfournprice').attr('data-tvatx'));}	// When select is done from HTML input with ajax autocomplete
 
-			var default_vat_code = $('option:selected', this).attr('data-default-vat-code');							 					// When select is done from HTML select
-			if (typeof default_vat_code === 'undefined') { default_vat_code = jQuery('#idprodfournprice').attr('data-default-vat-code');}	// When select is done from HTML input with ajax autocomplete
-
-			var stringforvatrateselection = tva_tx;
-			if (typeof default_vat_code != 'undefined' && default_vat_code != null) {
-				stringforvatrateselection = stringforvatrateselection+' ('+default_vat_code+')';
-			}
+				// Set vat rate if field is an input box
+				$('#tva_tx').val(tva_tx);
+				// Set vat rate by selecting the combo
+				//$('#tva_tx option').val(tva_tx); // This is bugged, it replaces the vat key of all options
+				$('#tva_tx option').removeAttr('selected');
+				console.log("stringforvatrateselection="+stringforvatrateselection+" -> value of option label for this
+				key="+$('#tva_tx option[value="'+stringforvatrateselection+'"]').val());
+				$('#tva_tx option[value="'+stringforvatrateselection+'"]').prop('selected', true);
+
+				if (jQuery("#qty").val() < qty) { jQuery("#qty").val(qty); } if (jQuery("#remise_percent").val() <
+					discount) { jQuery("#remise_percent").val(discount); } <?php
+					if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 1) {
+						?>
+						var description = $('option:selected', this).attr('data-description');
+						if (typeof description == 'undefined') { description =
+						jQuery('#idprodfournprice').attr('data-description'); }
+
+						console.log("Load desciption into text area : "+description);
+						<?php
+						if (!empty($conf->global->FCKEDITOR_ENABLE_DETAILS)) {
+							?>
+							if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined")
+							{
+							var editor = CKEDITOR.instances['dp_desc'];
+							if (editor) {
+							editor.setData(description);
+							}
+							}
+							<?php
+						} else {
+							?>
+							jQuery('#dp_desc').text(description);
+							<?php
+						}
+					}
+					?>
+					} else if (jQuery('#idprodfournprice').length > 0) {
+					console.log("objectline_create.tpl #idprodfournprice is not an int but is a string so we set only
+					few properties into page");
+
+					var tva_tx = parseFloat($('option:selected', this).attr('data-tvatx')); // When select is done from
+					HTML select
+					if (isNaN(tva_tx)) { tva_tx = parseFloat(jQuery('#idprodfournprice').attr('data-tvatx'));} // When
+					select is done from HTML input with ajax autocomplete
+
+					var default_vat_code = $('option:selected', this).attr('data-default-vat-code'); // When select is
+					done from HTML select
+					if (typeof default_vat_code === 'undefined') { default_vat_code =
+					jQuery('#idprodfournprice').attr('data-default-vat-code');} // When select is done from HTML input
+					with ajax autocomplete
+
+					var stringforvatrateselection = tva_tx;
+					if (typeof default_vat_code != 'undefined' && default_vat_code != null) {
+					stringforvatrateselection = stringforvatrateselection+' ('+default_vat_code+')';
+					}
 
-			console.log("objectline_create.tpl We find data for price : tva_tx = "+tva_tx+", default_vat_code = "+default_vat_code+", stringforvatrateselection="+stringforvatrateselection+" for product id = "+jQuery('#idprodfournprice').val());
+					console.log("objectline_create.tpl We find data for price : tva_tx = "+tva_tx+", default_vat_code =
+					"+default_vat_code+", stringforvatrateselection="+stringforvatrateselection+" for product id =
+					"+jQuery('#idprodfournprice').val());
 
-			// Set vat rate if field is an input box
-			$('#tva_tx').val(tva_tx);
-			// Set vat rate by selecting the combo
-			//$('#tva_tx option').val(tva_tx);	// This is bugged, it replaces the vat key of all options
-			$('#tva_tx option').removeAttr('selected');
-			console.log("stringforvatrateselection="+stringforvatrateselection+" -> value of option label for this key="+$('#tva_tx option[value="'+stringforvatrateselection+'"]').val());
-			$('#tva_tx option[value="'+stringforvatrateselection+'"]').prop('selected', true);
+					// Set vat rate if field is an input box
+					$('#tva_tx').val(tva_tx);
+					// Set vat rate by selecting the combo
+					//$('#tva_tx option').val(tva_tx); // This is bugged, it replaces the vat key of all options
+					$('#tva_tx option').removeAttr('selected');
+					console.log("stringforvatrateselection="+stringforvatrateselection+" -> value of option label for
+					this key="+$('#tva_tx option[value="'+stringforvatrateselection+'"]').val());
+					$('#tva_tx option[value="'+stringforvatrateselection+'"]').prop('selected', true);
 
-			<?php
-			if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 1) {
-				if (!empty($conf->global->FCKEDITOR_ENABLE_DETAILS)) {
-					?>
-			if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined")
-			{
-				var editor = CKEDITOR.instances['dp_desc'];
-				if (editor) {
-					editor.setData('');
-				}
-			}
 					<?php
-				} else {
+					if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 1) {
+						if (!empty($conf->global->FCKEDITOR_ENABLE_DETAILS)) {
+							?>
+							if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined")
+							{
+							var editor = CKEDITOR.instances['dp_desc'];
+							if (editor) {
+							editor.setData('');
+							}
+							}
+							<?php
+						} else {
+							?>
+							jQuery('#dp_desc').text('');
+							<?php
+						}
+					}
 					?>
-			jQuery('#dp_desc').text('');
-					<?php
-				}
-			}
-			?>
-		}
+					}
 
 
-		/* To set focus */
-		if (jQuery('#idprod').val() > 0 || jQuery('#idprodfournprice').val() > 0)
-		{
-			/* focus work on a standard textarea but not if field was replaced with CKEDITOR */
-			jQuery('#dp_desc').focus();
-			/* focus if CKEDITOR */
-			if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined")
-			{
-				var editor = CKEDITOR.instances['dp_desc'];
-				if (editor) { editor.focus(); }
-			}
-		}
-	});
+					/* To set focus */
+					if (jQuery('#idprod').val() > 0 || jQuery('#idprodfournprice').val() > 0)
+					{
+					/* focus work on a standard textarea but not if field was replaced with CKEDITOR */
+					jQuery('#dp_desc').focus();
+					/* focus if CKEDITOR */
+					if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined")
+					{
+					var editor = CKEDITOR.instances['dp_desc'];
+					if (editor) { editor.focus(); }
+					}
+					}
+					});
 
-		<?php if (GETPOST('prod_entry_mode') == 'predef') { // When we submit with a predef product and it fails we must start with predef ?>
-		setforpredef();
-		<?php } ?>
-	});
+					<?php if (GETPOST('prod_entry_mode') == 'predef') { // When we submit with a predef product and it fails we must start with predef ?>
+						setforpredef();
+					<?php } ?>
+					});
 
-	/* Function to set fields from choice */
-	function setforfree() {
-		console.log("objectline_create.tpl::setforfree. We show most fields");
-		jQuery("#idprodfournprice").val('0');	// Set cursor on not selected product
-		jQuery("#prod_entry_mode_free").prop('checked',true).change();
-		jQuery("#prod_entry_mode_predef").prop('checked',false).change();
-		jQuery("#search_idprod, #idprod, #search_idprodfournprice, #buying_price").val('');
-		jQuery("#price_ht, #multicurrency_price_ht, #price_ttc, #multicurrency_price_ttc, #fourn_ref, #tva_tx, #buying_price, #title_fourn_ref, #title_vat, #title_up_ht, #title_up_ht_currency, #title_up_ttc, #title_up_ttc_currency").show();
-		jQuery("#np_marginRate, #np_markRate, .np_marginRate, .np_markRate, #units, #title_units").show();
-		jQuery("#fournprice_predef").hide();
-	}
+					/* Function to set fields from choice */
+					function setforfree() {
+					console.log("objectline_create.tpl::setforfree. We show most fields");
+					jQuery("#idprodfournprice").val('0'); // Set cursor on not selected product
+					jQuery("#prod_entry_mode_free").prop('checked',true).change();
+					jQuery("#prod_entry_mode_predef").prop('checked',false).change();
+					jQuery("#search_idprod, #idprod, #search_idprodfournprice, #buying_price").val('');
+					jQuery("#price_ht, #multicurrency_price_ht, #price_ttc, #multicurrency_price_ttc, #fourn_ref,
+					#tva_tx, #buying_price, #title_fourn_ref, #title_vat, #title_up_ht, #title_up_ht_currency,
+					#title_up_ttc, #title_up_ttc_currency").show();
+					jQuery("#np_marginRate, #np_markRate, .np_marginRate, .np_markRate, #units, #title_units").show();
+					jQuery("#fournprice_predef").hide();
+					}
 
-	function setforpredef() {
-		console.log("objectline_create.tpl::setforpredef We hide some fields, show dates");
-		jQuery("#select_type").val(-1);
-		jQuery("#prod_entry_mode_free").prop('checked',false).change();
-		jQuery("#prod_entry_mode_predef").prop('checked',true).change();
-		<?php if (empty($conf->global->MAIN_DISABLE_EDIT_PREDEF_PRICEHT)) { ?>
-			jQuery("#price_ht").val('').show();
-			jQuery("#multicurrency_price_ht").val('').show();
-			jQuery("#title_up_ht, #title_up_ht_currency").show();
-		<?php } else { ?>
-			//jQuery("#price_ht").val('').hide();
-			jQuery("#multicurrency_price_ht").val('').hide();
-			jQuery("#title_up_ht, #title_up_ht_currency").hide();
-		<?php } ?>
-		<?php if (empty($conf->global->MAIN_DISABLE_EDIT_PREDEF_PRICETTC)) { ?>
-			jQuery("#price_ttc").val('').show();
-			jQuery("#multicurrency_price_ttc").val('').show();
-			jQuery("#title_up_ttc, #title_up_ttc_currency").show();
-		<?php } else { ?>
-			jQuery("#price_ttc").val('').hide();
-			jQuery("#multicurrency_price_ttc").val('').hide();
-			jQuery("#title_up_ttc, #title_up_ttc_currency").hide();
-		<?php } ?>
-		jQuery("#fourn_ref, #tva_tx, #title_vat").hide();
-		/* jQuery("#title_fourn_ref").hide(); */
-		jQuery("#np_marginRate, #np_markRate, .np_marginRate, .np_markRate, #units, #title_units").hide();
-		jQuery("#buying_price").show();
-		jQuery('#trlinefordates, .divlinefordates').show();
-	}
+					function setforpredef() {
+					console.log("objectline_create.tpl::setforpredef We hide some fields, show dates");
+					jQuery("#select_type").val(-1);
+					jQuery("#prod_entry_mode_free").prop('checked',false).change();
+					jQuery("#prod_entry_mode_predef").prop('checked',true).change();
+					<?php if (empty($conf->global->MAIN_DISABLE_EDIT_PREDEF_PRICEHT)) { ?>
+						jQuery("#price_ht").val('').show();
+						jQuery("#multicurrency_price_ht").val('').show();
+						jQuery("#title_up_ht, #title_up_ht_currency").show();
+					<?php } else { ?>
+						//jQuery("#price_ht").val('').hide();
+						jQuery("#multicurrency_price_ht").val('').hide();
+						jQuery("#title_up_ht, #title_up_ht_currency").hide();
+					<?php } ?>
+					<?php if (empty($conf->global->MAIN_DISABLE_EDIT_PREDEF_PRICETTC)) { ?>
+						jQuery("#price_ttc").val('').show();
+						jQuery("#multicurrency_price_ttc").val('').show();
+						jQuery("#title_up_ttc, #title_up_ttc_currency").show();
+					<?php } else { ?>
+						jQuery("#price_ttc").val('').hide();
+						jQuery("#multicurrency_price_ttc").val('').hide();
+						jQuery("#title_up_ttc, #title_up_ttc_currency").hide();
+					<?php } ?>
+					jQuery("#fourn_ref, #tva_tx, #title_vat").hide();
+					/* jQuery("#title_fourn_ref").hide(); */
+					jQuery("#np_marginRate, #np_markRate, .np_marginRate, .np_markRate, #units, #title_units").hide();
+					jQuery("#buying_price").show();
+					jQuery('#trlinefordates, .divlinefordates').show();
+					}
 
-<?php
+					<?php
 
-print '</script>';
+					print '</script>';
 
-print "<!-- END PHP TEMPLATE objectline_create.tpl.php -->\n";
+					print "<!-- END PHP TEMPLATE objectline_create.tpl.php -->\n";

+ 3 - 0
emailcollector/class/emailcollector.class.php

@@ -2578,6 +2578,9 @@ class EmailCollector extends CommonObject
 								// This may overwrite any $projecttocreate->xxx properties.
 								$errorforthisaction = $this->overwritePropertiesOfObject($tickettocreate, $operation['actionparam'], $messagetext, $subject, $header, $operationslog);
 
+								$tickettocreate->array_options['to_email'] = $to;
+								//$tickettocreate->updateExtraField('payment_log');
+
 								// Set ticket ref if not yet defined
 								if (empty($tickettocreate->ref)) {
 									// Get next Ref

+ 7 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php

@@ -2,6 +2,13 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php';
+
 use PhpOffice\PhpSpreadsheet\Calculation\Engine\CyclicReferenceStack;
 use PhpOffice\PhpSpreadsheet\Calculation\Engine\Logger;
 use PhpOffice\PhpSpreadsheet\Calculation\Token\Stack;

+ 2 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php

@@ -2,6 +2,8 @@
 
 namespace PhpOffice\PhpSpreadsheet\Cell;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php';
+
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Collection\Cells;
 use PhpOffice\PhpSpreadsheet\Exception;

+ 1 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php

@@ -1,6 +1,7 @@
 <?php
 
 namespace PhpOffice\PhpSpreadsheet\Cell;
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php';
 
 use DateTimeInterface;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;

+ 3 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php

@@ -2,6 +2,9 @@
 
 namespace PhpOffice\PhpSpreadsheet\Collection;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php';
+
+
 use PhpOffice\PhpSpreadsheet\Settings;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 

+ 1 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory.php

@@ -1,6 +1,7 @@
 <?php
 
 namespace PhpOffice\PhpSpreadsheet\Collection;
+require_once DOL_DOCUMENT_ROOT . '/includes/Psr/simple-cache/src/CacheInterface.php';
 
 use Psr\SimpleCache\CacheInterface;
 

+ 18 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php

@@ -2,6 +2,24 @@
 
 namespace PhpOffice\PhpSpreadsheet\Reader;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php';
+
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
 use PhpOffice\PhpSpreadsheet\NamedRange;

+ 2 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php

@@ -2,6 +2,8 @@
 
 namespace PhpOffice\PhpSpreadsheet;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory.php';
+
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Chart\Renderer\IRenderer;
 use PhpOffice\PhpSpreadsheet\Collection\Memory;

+ 9 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php

@@ -2,6 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php';
+
+
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Style\Style;
 use PhpOffice\PhpSpreadsheet\Worksheet\Iterator;

+ 1 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php

@@ -1,6 +1,7 @@
 <?php
 
 namespace PhpOffice\PhpSpreadsheet\Style;
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php';
 
 use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
 

+ 2 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php

@@ -1,6 +1,8 @@
 <?php
 
 namespace PhpOffice\PhpSpreadsheet\Style;
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php';
 
 use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
 

+ 8 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php

@@ -1,6 +1,14 @@
 <?php
 
 namespace PhpOffice\PhpSpreadsheet\Style;
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php'; 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php'; 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php'; 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php'; 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php'; 
+
 
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;

+ 1 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php

@@ -1,6 +1,7 @@
 <?php
 
 namespace PhpOffice\PhpSpreadsheet\Worksheet;
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php';
 
 class RowDimension extends Dimension
 {

+ 18 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php

@@ -2,6 +2,24 @@
 
 namespace PhpOffice\PhpSpreadsheet\Worksheet;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php';
+
+
 use ArrayObject;
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;

+ 3 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php

@@ -2,6 +2,9 @@
 
 namespace PhpOffice\PhpSpreadsheet\Writer;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php';
+
+
 abstract class BaseWriter implements IWriter
 {
     /**

+ 3 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php

@@ -2,6 +2,9 @@
 
 namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php';
+
+
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
 use PhpOffice\PhpSpreadsheet\Shared\Date;

+ 18 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php

@@ -1,6 +1,24 @@
 <?php
 
 namespace PhpOffice\PhpSpreadsheet\Writer;
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php';
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php';
+
 
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;

+ 2 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php

@@ -2,6 +2,8 @@
 
 namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php';
+
 use PhpOffice\PhpSpreadsheet\Shared\File;
 use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;

+ 2 - 0
includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php

@@ -2,6 +2,8 @@
 
 namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
 
+require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php';
+
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\RichText\Run;

+ 2 - 1
includes/restler/framework/Luracast/Restler/RestException.php

@@ -66,7 +66,8 @@ class RestException extends Exception
         502 => 'Bad Gateway',
         503 => 'Service Unavailable',
         504 => 'Gateway Timeout',
-        505 => 'HTTP Version Not Supported'
+        505 => 'HTTP Version Not Supported',
+        506 => 'No available spaces'
     );
     private $details;
     private $stage;

+ 6 - 0
langs/en_GB/stocks.lang

@@ -12,3 +12,9 @@ IsInPackage=Packaged
 ThisSerialAlreadyExistWithDifferentDate=This lot/serial number (<strong>%s</strong>) already exists but with different useby or sellby dates (found <strong>%s</strong> but you entered <strong>%s</strong>).
 OptionMULTIPRICESIsOn=Option "several prices per segment" is on. It means a product has several selling price so total of stock by sales value can't be calculated
 AddNewProductStockWarehouse=Set new limit for alert and desired optimal stock level
+DeviceName=Device name
+SKU=SKU identifier (Roller)
+englishlabel=English label
+uniqueidentifier=Unique identifier (IMEI)
+AssignedDevice= Assigned device
+Devicetype=Device type

+ 6 - 0
langs/hu_HU/stocks.lang

@@ -315,3 +315,9 @@ StockTransferRightRead=Olvassa el a készlettranszfereket
 StockTransferRightCreateUpdate=Készlettranszferek létrehozása/frissítése
 StockTransferRightDelete=Törölje a készlettranszfereket
 BatchNotFound=A tétel/sorozat nem található ehhez a termékhez
+DeviceName=Eszköz neve
+SKU=SKU azonosító (Roller)
+englishlabel=Angol elnevezés
+uniqueidentifier=Egyedi azonosító (IMEI)
+AssignedDevice=Hozzárendelt eszköz
+Devicetype=Eszköz típusa

+ 5 - 1
main.inc.php

@@ -526,7 +526,11 @@ if (!defined('NOTOKENRENEWAL') && !defined('NOSESSION')) {
 //dol_syslog("aaaa - ".defined('NOCSRFCHECK')." - ".$dolibarr_nocsrfcheck." - ".$conf->global->MAIN_SECURITY_CSRF_WITH_TOKEN." - ".$_SERVER['REQUEST_METHOD']." - ".GETPOST('token', 'alpha'));
 
 // Check validity of token, only if option MAIN_SECURITY_CSRF_WITH_TOKEN enabled or if constant CSRFCHECK_WITH_TOKEN is set into page
-if ((!defined('NOCSRFCHECK') && empty($dolibarr_nocsrfcheck) && getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN')) || defined('CSRFCHECK_WITH_TOKEN')) {
+//if ((!defined('NOCSRFCHECK') && empty($dolibarr_nocsrfcheck) && getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN')) || defined('CSRFCHECK_WITH_TOKEN')) {
+	if ((!defined('NOCSRFCHECK') && empty($dolibarr_nocsrfcheck) && !empty($conf->global->MAIN_SECURITY_CSRF_WITH_TOKEN))
+	|| defined('CSRFCHECK_WITH_TOKEN')      // Check validity of token, only if option MAIN_SECURITY_CSRF_WITH_TOKEN enabled or if constant CSRFCHECK_WITH_TOKEN is set into pa$
+	&& ! empty(CSRFCHECK_WITH_TOKEN) )
+{
 	// Array of action code where CSRFCHECK with token will be forced (so token must be provided on url request)
 	$sensitiveget = false;
 	if ((GETPOSTISSET('massaction') || GETPOST('action', 'aZ09')) && getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN') >= 3) {

+ 18 - 3
product/card.php

@@ -464,8 +464,13 @@ if (empty($reshook)) {
 					$error++;
 			}
 		}
+		if(empty($duration_value) && $object->isService()){
+			setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('Duration Value')), null, 'errors');
+			$action = "create";
+			$error++;
+		}
 		if (!empty($duration_value) && empty($duration_unit)) {
-			setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('Unit')), null, 'errors');
+			setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('Duration Unit')), null, 'errors');
 			$action = "create";
 			$error++;
 		}
@@ -834,6 +839,16 @@ if (empty($reshook)) {
 				if ($ret < 0) {
 					$error++;
 				}
+				if(empty($duration_value) && $object->isService()){
+					setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('Value')), null, 'errors');
+					$action = "create";
+					$error++;
+				}
+				if (!empty($duration_value) && empty($duration_unit)) {
+					setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('Unit')), null, 'errors');
+					$action = "create";
+					$error++;
+				}
 
 				if (!$error && $object->check()) {
 					if ($object->update($object->id, $user) > 0) {
@@ -1495,7 +1510,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
 
 		// Duration
 		if ($type == 1) {
-			print '<tr><td>'.$langs->trans("Duration").'</td><td>';
+			print '<tr><td style="font-weight: bold;">'.$langs->trans("Duration").'</td><td>';
 			print '<input name="duration_value" size="4" value="'.GETPOST('duration_value', 'int').'">';
 			print $formproduct->selectMeasuringUnits("duration_unit", "time", (GETPOSTISSET('duration_value') ? GETPOST('duration_value', 'alpha') : 'h'), 0, 1);
 
@@ -2054,7 +2069,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
 
 			if ($object->isService()) {
 				// Duration
-				print '<tr><td>'.$langs->trans("Duration").'</td><td>';
+				print '<tr><td style="font-weight: bold;">'.$langs->trans("Duration").'</td><td>';
 				print '<input name="duration_value" size="5" value="'.$object->duration_value.'"> ';
 				print $formproduct->selectMeasuringUnits("duration_unit", "time", $object->duration_unit, 0, 1);
 

+ 19 - 36
product/inventory/class/inventory.class.php

@@ -25,9 +25,9 @@
  */
 
 // Put here all includes required by your class file
-require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
-require_once DOL_DOCUMENT_ROOT . '/core/class/commonobjectline.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/rollerstorage/class/statuses.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
+require_once DOL_DOCUMENT_ROOT.'/custom/rollerstorage/class/statuses.class.php';
 //require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
 //require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
 
@@ -101,27 +101,10 @@ class Inventory extends CommonObject
 	public $statuses = [];
 
 	public $fields = array(
-		'rowid' => array(
-			'type' => 'integer',
-			'label' => 'TechnicalID',
-			'visible' => -1,
-			'enabled' => 1,
-			'position' => 1,
-			'notnull' => 1,
-			'index' => 1,
-			'comment' => 'Id',
-		),
-		'ref' => array('type' => 'varchar(64)', 'label' => 'Roller neve', 'visible' => 1, 'enabled' => 1, 'position' => 10, 'notnull' => 1, 'index' => 1, 'searchall' => 1, 'comment' => 'Reference of object', 'css' => 'maxwidth200'),
-		'entity' => array(
-			'type' => 'integer',
-			'label' => 'Entity',
-			'visible' => 0,
-			'enabled' => 1,
-			'position' => 20,
-			'notnull' => 1,
-			'index' => 1,
-		),
-		'title' => array('type' => 'varchar(255)', 'label' => 'SKU azonosító', 'visible' => 1, 'enabled' => 1, 'position' => 25, 'notnull' => 1, 'css' => 'minwidth300', 'csslist' => 'tdoverflowmax200'),
+		'rowid' => array('type'=>'integer', 'label'=>'TechnicalID','visible'=>-1,'enabled' => 1,'position' => 1,'notnull' => 1,'index' => 1,'comment' => 'Id',),
+		'ref' => array('type' => 'varchar(64)', 'label' => 'DeviceName', 'visible' => 1, 'enabled' => 1, 'position' => 10, 'notnull' => 1, 'index' => 1, 'searchall' => 1, 'comment' => 'Reference of object', 'css' => 'maxwidth200'),
+		'entity' => array('type' => 'integer','label' => 'Entity','visible' => 0,'enabled' => 1,'position' => 20,'notnull' => 1,'index' => 1,),
+		'title' => array('type' => 'varchar(255)', 'label' => 'SKU', 'visible' => 1, 'enabled' => 1, 'position' => 25, 'notnull' => 1, 'css' => 'minwidth300', 'csslist' => 'tdoverflowmax200'),
 		'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Warehouse', 'visible' => 1, 'enabled' => 1, 'position' => 30, 'notnull' => 1, 'index' => 1, 'help' => 'InventoryForASpecificWarehouse', 'picto' => 'stock', 'css' => 'minwidth300 maxwidth500 widthcentpercentminusx', 'csslist' => 'tdoverflowmax200'),
 		'fk_product' => array('type' => 'integer:Product:product/class/product.class.php', 'label' => 'Product', 'get_name_url_params' => '0::0:-1:0::1', 'visible' => 0, 'enabled' => 1, 'position' => 32, 'index' => 1, 'help' => 'InventoryForASpecificProduct', 'picto' => 'product', 'css' => 'minwidth300 maxwidth500 widthcentpercentminusx', 'csslist' => 'tdoverflowmax200'),
 		'categories_product' => array('type' => 'chkbxlst:categorie:label:rowid::type=0:0:', 'label' => 'OrProductsWithCategories', 'visible' => 3, 'enabled' => 0, 'position' => 33, 'help' => '', 'picto' => 'category', 'css' => 'minwidth300 maxwidth500 widthcentpercentminusx'),
@@ -134,7 +117,7 @@ class Inventory extends CommonObject
 		'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 511, 'csslist' => 'tdoverflowmax200'),
 		'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'visible' => -2, 'enabled' => 1, 'position' => 512, 'csslist' => 'tdoverflowmax200'),
 		'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'index' => 0, 'position' => 1000),
-		'status' => array('type' => 'integer:Statuses:custom/rollerstorage/class/statuses.class.php', 'label' => 'Status', 'visible' => 1, 'enabled' => 1, 'position' => 1000, 'notnull' => 1, 'default' => 110, 'index' => 1,)
+		'status' => array('type' => 'integer:Statuses:custom/rollerstorage/class/statuses.class.php', 'label' => 'Status', 'visible' => 1, 'enabled' => 1, 'position' => 1000, 'notnull' => 1, 'default' => 110, 'index' => 1, )
 		//'status' => array('type' => 'integer', 'label' => 'Status', 'visible' => 1, 'enabled' => 1, 'position' => 1000, 'notnull' => 1, 'default' => 110, 'index' => 1,)
 	);
 
@@ -520,11 +503,11 @@ class Inventory extends CommonObject
 	 * @return int         <0 if KO, 0 if not found, >0 if OK
 	 */
 	/*public function fetchLines()
-	{
-	$this->lines=array();
-	// Load lines with object MyObjectLine
-	return count($this->lines)?1:0;
-	}*/
+	   {
+	   $this->lines=array();
+	   // Load lines with object MyObjectLine
+	   return count($this->lines)?1:0;
+	   }*/
 
 	/**
 	 * Update object into database
@@ -651,18 +634,18 @@ class Inventory extends CommonObject
 		global $langs;
 		global $db;
 
-		$statusObj = new Statuses($db);
-		$statuses = $statusObj::getStatusids($db);
+		/*$statusObj = new Statuses($db);
+			  $statuses = $statusObj::getStatusids($db);*/
 
 		$labelStatus = array();
 		$labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
 		$labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated') . ' (' . $langs->transnoentitiesnoconv('InventoryStartedShort') . ')';
 		$labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
 		$labelStatus[self::STATUS_RECORDED] = $langs->transnoentitiesnoconv('Closed');
-		foreach($statuses as $key => $val){
-			$labelStatus[$key] = $val;	
-			$labelStatusShort[$key] = $val;	
-		}
+		/*foreach($statuses as $key => $val){
+				  $labelStatus[$key] = $val;	
+				  $labelStatusShort[$key] = $val;	
+			  }*/
 		$labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
 		$labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('InventoryStartedShort');
 		$labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');

+ 866 - 0
product/inventory/class/inventory.class.php.bak

@@ -0,0 +1,866 @@
+<?php
+/* Copyright (C) 2007-2019  Laurent Destailleur <eldy@users.sourceforge.net>
+ * Copyright (C) 2014-2016  Juanjo Menent       <jmenent@2byte.es>
+ * Copyright (C) 2015       Florian Henry       <florian.henry@open-concept.pro>
+ * Copyright (C) 2015       Raphaël Doursenaud  <rdoursenaud@gpcsolutions.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file        product/inventory/class/inventory.class.php
+ * \ingroup     inventory
+ * \brief       This file is a CRUD class file for Inventory (Create/Read/Update/Delete)
+ */
+
+// Put here all includes required by your class file
+require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
+require_once DOL_DOCUMENT_ROOT . '/core/class/commonobjectline.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/rollerstorage/class/statuses.class.php';
+//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
+//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
+
+/**
+ * Class for Inventory
+ */
+
+class Inventory extends CommonObject
+{
+	/**
+	 * @var string ID to identify managed object
+	 */
+	public $element = 'inventory';
+
+	/**
+	 * @var string Name of table without prefix where object is stored
+	 */
+	public $table_element = 'inventory';
+
+	/**
+	 * @var array  Does inventory support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
+	 */
+	public $ismultientitymanaged = 1;
+
+	/**
+	 * @var int  Does object support extrafields ? 0=No, 1=Yes
+	 */
+	public $isextrafieldmanaged = 0;
+
+	/**
+	 * @var string String with name of icon for inventory
+	 */
+	public $picto = 'inventory';
+
+	const STATUS_DRAFT = 0; // Draft
+	const STATUS_VALIDATED = 1; // Inventory is in process
+	const STATUS_RECORDED = 2; // Inventory is finisged. Stock movement has been recorded.
+	const STATUS_CANCELED = 9; // Canceled
+
+	/**
+	 *  'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter]]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
+	 *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
+	 *  'label' the translation key.
+	 *  'picto' is code of a picto to show before value in forms
+	 *  'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM)
+	 *  'position' is the sort order of field.
+	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
+	 *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
+	 *  'noteditable' says if field is not editable (1 or 0)
+	 *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
+	 *  'index' if we want an index in database.
+	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
+	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
+	 *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
+	 *  'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'maxwidth200', 'wordbreak', 'tdoverflowmax200', 'minwidth300 maxwidth500 widthcentpercentminusx'
+	 *  'help' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
+	 *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
+	 *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
+	 *  'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar'
+	 *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
+	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.
+	 *
+	 *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
+	 */
+
+	// BEGIN MODULEBUILDER PROPERTIES
+	/**
+	 * @var array  Array with all fields and their property
+	 */
+
+	public $statuses = [];
+
+	public $fields = array(
+		'rowid' => array('type'=>'integer', 'label'=>'TechnicalID','visible'=>-1,'enabled' => 1,'position' => 1,'notnull' => 1,'index' => 1,'comment' => 'Id',),
+		'ref' => array('type' => 'varchar(64)', 'label' => 'Roller neve', 'visible' => 1, 'enabled' => 1, 'position' => 10, 'notnull' => 1, 'index' => 1, 'searchall' => 1, 'comment' => 'Reference of object', 'css' => 'maxwidth200'),
+		'entity' => array('type' => 'integer','label' => 'Entity','visible' => 0,'enabled' => 1,'position' => 20,'notnull' => 1,'index' => 1,),
+		'title' => array('type' => 'varchar(255)', 'label' => 'SKU azonosító', 'visible' => 1, 'enabled' => 1, 'position' => 25, 'notnull' => 1, 'css' => 'minwidth300', 'csslist' => 'tdoverflowmax200'),
+		'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Warehouse', 'visible' => 1, 'enabled' => 1, 'position' => 30, 'notnull' => 1, 'index' => 1, 'help' => 'InventoryForASpecificWarehouse', 'picto' => 'stock', 'css' => 'minwidth300 maxwidth500 widthcentpercentminusx', 'csslist' => 'tdoverflowmax200'),
+		'fk_product' => array('type' => 'integer:Product:product/class/product.class.php', 'label' => 'Product', 'get_name_url_params' => '0::0:-1:0::1', 'visible' => 0, 'enabled' => 1, 'position' => 32, 'index' => 1, 'help' => 'InventoryForASpecificProduct', 'picto' => 'product', 'css' => 'minwidth300 maxwidth500 widthcentpercentminusx', 'csslist' => 'tdoverflowmax200'),
+		'categories_product' => array('type' => 'chkbxlst:categorie:label:rowid::type=0:0:', 'label' => 'OrProductsWithCategories', 'visible' => 3, 'enabled' => 0, 'position' => 33, 'help' => '', 'picto' => 'category', 'css' => 'minwidth300 maxwidth500 widthcentpercentminusx'),
+		'date_inventory' => array('type' => 'date', 'label' => 'DateValue', 'visible' => 1, 'enabled' => '$conf->global->STOCK_INVENTORY_ADD_A_VALUE_DATE', 'position' => 35, 'csslist' => 'nowraponall'),
+		// This date is not used so disabled by default.
+		'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 500, 'csslist' => 'nowraponall'),
+		'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 501, 'csslist' => 'nowraponall'),
+		'date_validation' => array('type' => 'datetime', 'label' => 'DateValidation', 'visible' => -2, 'enabled' => 1, 'position' => 502, 'csslist' => 'nowraponall'),
+		'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 510, 'foreignkey' => 'user.rowid', 'csslist' => 'tdoverflowmax200'),
+		'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 511, 'csslist' => 'tdoverflowmax200'),
+		'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'visible' => -2, 'enabled' => 1, 'position' => 512, 'csslist' => 'tdoverflowmax200'),
+		'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'index' => 0, 'position' => 1000),
+		'status' => array('type' => 'integer:Statuses:custom/rollerstorage/class/statuses.class.php', 'label' => 'Status', 'visible' => 1, 'enabled' => 1, 'position' => 1000, 'notnull' => 1, 'default' => 110, 'index' => 1, )
+		//'status' => array('type' => 'integer', 'label' => 'Status', 'visible' => 1, 'enabled' => 1, 'position' => 1000, 'notnull' => 1, 'default' => 110, 'index' => 1,)
+	);
+
+	/**
+	 * @var int ID
+	 */
+	public $rowid;
+
+	/**
+	 * @var string Ref
+	 */
+	public $ref;
+
+	/**
+	 * @var int Entity
+	 */
+	public $entity;
+
+	/**
+	 * @var int ID
+	 */
+	public $fk_warehouse;
+
+	/**
+	 * @var int ID
+	 */
+	public $fk_product;
+
+	/**
+	 * @var string Categories id separated by comma
+	 */
+	public $categories_product;
+	public $date_inventory;
+	public $title;
+
+	/**
+	 * @var int Status
+	 */
+	public $status;
+
+	/**
+	 * @var integer|string date_creation
+	 */
+	public $date_creation;
+
+	/**
+	 * @var integer|string date_validation
+	 */
+	public $date_validation;
+	public $tms;
+
+	/**
+	 * @var int ID
+	 */
+	public $fk_user_creat;
+
+	/**
+	 * @var int ID
+	 */
+	public $fk_user_modif;
+
+	/**
+	 * @var int ID
+	 */
+	public $fk_user_valid;
+
+	/**
+	 * @var string import key
+	 */
+	public $import_key;
+	// END MODULEBUILDER PROPERTIES
+
+
+
+	// If this object has a subtable with lines
+
+	/**
+	 * @var string    Name of subtable line
+	 */
+	public $table_element_line = 'inventorydet';
+
+	/**
+	 * @var string    Field with ID of parent key if this field has a parent
+	 */
+	public $fk_element = 'fk_inventory';
+
+	/**
+	 * @var string    Name of subtable class that manage subtable lines
+	 */
+	public $class_element_line = 'Inventoryline';
+
+	/**
+	 * @var array	List of child tables. To test if we can delete object.
+	 */
+	protected $childtables = array();
+	/**
+	 * @var array	List of child tables. To know object to delete on cascade.
+	 */
+	protected $childtablesoncascade = array('inventorydet');
+
+	/**
+	 * @var InventoryLine[]     Array of subtable lines
+	 */
+	public $lines = array();
+
+
+
+	/**
+	 * Constructor
+	 *
+	 * @param DoliDb $db Database handler
+	 */
+	public function __construct(DoliDB $db)
+	{
+		global $conf;
+
+		$this->db = $db;
+
+		if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID)) {
+			$this->fields['rowid']['visible'] = 0;
+		}
+		if (!isModEnabled('multicompany')) {
+			$this->fields['entity']['enabled'] = 0;
+		}
+	}
+
+	/**
+	 * Create object into database
+	 *
+	 * @param  User $user      User that creates
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, Id of created object if OK
+	 */
+	public function create(User $user, $notrigger = false)
+	{
+		$result = $this->createCommon($user, $notrigger);
+
+		return $result;
+	}
+
+	/**
+	 * Validate inventory (start it)
+	 *
+	 * @param  User $user      User that creates
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, Id of created object if OK
+	 */
+	public function validate(User $user, $notrigger = false)
+	{
+		global $conf;
+		$this->db->begin();
+
+		$result = 0;
+
+		if ($this->status == self::STATUS_DRAFT) {
+			// Delete inventory
+			$sql = 'DELETE FROM ' . $this->db->prefix() . 'inventorydet WHERE fk_inventory = ' . ((int) $this->id);
+			$resql = $this->db->query($sql);
+			if (!$resql) {
+				$this->error = $this->db->lasterror();
+				$this->db->rollback();
+				return -1;
+			}
+
+			// Scan existing stock to prefill the inventory
+			$sql = "SELECT ps.rowid, ps.fk_entrepot as fk_warehouse, ps.fk_product, ps.reel,";
+			$sql .= " pb.batch, pb.qty";
+			$sql .= " FROM " . $this->db->prefix() . "product_stock as ps";
+			$sql .= " LEFT JOIN " . $this->db->prefix() . "product_batch as pb ON pb.fk_product_stock = ps.rowid,";
+			$sql .= " " . $this->db->prefix() . "product as p, " . $this->db->prefix() . "entrepot as e";
+			$sql .= " WHERE p.entity IN (" . getEntity('product') . ")";
+			$sql .= " AND ps.fk_product = p.rowid AND ps.fk_entrepot = e.rowid";
+			if (empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
+				$sql .= " AND p.fk_product_type = 0";
+			}
+			if ($this->fk_product > 0) {
+				$sql .= " AND ps.fk_product = " . ((int) $this->fk_product);
+			}
+			if ($this->fk_warehouse > 0) {
+				$sql .= " AND ps.fk_entrepot = " . ((int) $this->fk_warehouse);
+			}
+			if (!empty($this->categories_product)) {
+				$sql .= " AND EXISTS (";
+				$sql .= " SELECT cp.fk_product";
+				$sql .= " FROM " . $this->db->prefix() . "categorie_product AS cp";
+				$sql .= " WHERE cp.fk_product = ps.fk_product";
+				$sql .= " AND cp.fk_categorie IN (" . $this->db->sanitize($this->categories_product) . ")";
+				$sql .= ")";
+			}
+
+			$inventoryline = new InventoryLine($this->db);
+
+			$resql = $this->db->query($sql);
+			if ($resql) {
+				$num = $this->db->num_rows($resql);
+
+				$i = 0;
+				while ($i < $num) {
+					$obj = $this->db->fetch_object($resql);
+
+					$inventoryline->fk_inventory = $this->id;
+					$inventoryline->fk_warehouse = $obj->fk_warehouse;
+					$inventoryline->fk_product = $obj->fk_product;
+					$inventoryline->batch = $obj->batch;
+					$inventoryline->datec = dol_now();
+
+					if (isModEnabled('productbatch')) {
+						$inventoryline->qty_stock = ($obj->batch ? $obj->qty : $obj->reel); // If there is batch detail, we take qty for batch, else global qty
+					} else {
+						$inventoryline->qty_stock = $obj->reel;
+					}
+
+					$resultline = $inventoryline->create($user);
+					if ($resultline <= 0) {
+						$this->error = $inventoryline->error;
+						$this->errors = $inventoryline->errors;
+						$result = -1;
+						break;
+					}
+
+					$i++;
+				}
+			} else {
+				$result = -1;
+				$this->error = $this->db->lasterror();
+			}
+		}
+
+		if ($result >= 0) {
+			$result = $this->setStatut($this::STATUS_VALIDATED, null, '', 'INVENTORY_VALIDATED');
+		}
+
+		if ($result > 0) {
+			$this->db->commit();
+		} else {
+			$this->db->rollback();
+		}
+		return $result;
+	}
+
+	/**
+	 * Go back to draft
+	 *
+	 * @param  User $user      User that creates
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, Id of created object if OK
+	 */
+	public function setDraft(User $user, $notrigger = false)
+	{
+		$this->db->begin();
+
+		// Delete inventory
+		$sql = 'DELETE FROM ' . $this->db->prefix() . 'inventorydet WHERE fk_inventory = ' . ((int) $this->id);
+		$resql = $this->db->query($sql);
+		if (!$resql) {
+			$this->error = $this->db->lasterror();
+			$this->db->rollback();
+			return -1;
+		}
+
+		$result = $this->setStatut($this::STATUS_DRAFT, null, '', 'INVENTORY_DRAFT');
+
+		if ($result > 0) {
+			$this->db->commit();
+		} else {
+			$this->db->rollback();
+		}
+		return $result;
+	}
+
+	/**
+	 * Set to inventory to status "Closed". It means all stock movements were recorded.
+	 *
+	 * @param  User $user      User that creates
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, Id of created object if OK
+	 */
+	public function setRecorded(User $user, $notrigger = false)
+	{
+		$this->db->begin();
+
+		$result = $this->setStatut($this::STATUS_RECORDED, null, '', 'INVENTORY_RECORDED');
+
+		if ($result > 0) {
+			$this->db->commit();
+		} else {
+			$this->db->rollback();
+			return -1;
+		}
+		return $result;
+	}
+
+	/**
+	 * Set to Canceled
+	 *
+	 * @param  User $user      User that creates
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, Id of created object if OK
+	 */
+	public function setCanceled(User $user, $notrigger = false)
+	{
+		$this->db->begin();
+
+		$result = $this->setStatut($this::STATUS_CANCELED, null, '', 'INVENTORY_CANCELED');
+
+		if ($result > 0) {
+			$this->db->commit();
+		} else {
+			$this->db->rollback();
+			return -1;
+		}
+		return $result;
+	}
+
+	/**
+	 * Clone and object into another one
+	 *
+	 * @param  	User 	$user      	User that creates
+	 * @param  	int 	$fromid     Id of object to clone
+	 * @return 	mixed 				New object created, <0 if KO
+	 */
+	public function createFromClone(User $user, $fromid)
+	{
+		global $hookmanager, $langs;
+		$error = 0;
+
+		dol_syslog(__METHOD__, LOG_DEBUG);
+
+		$object = new self($this->db);
+
+		$this->db->begin();
+
+		// Load source object
+		$object->fetchCommon($fromid);
+		// Reset some properties
+		unset($object->id);
+		unset($object->fk_user_creat);
+		unset($object->import_key);
+
+		// Clear fields
+		$object->ref = "copy_of_" . $object->ref;
+		$object->title = $langs->trans("CopyOf") . " " . $object->title;
+		// ...
+
+		// Create clone
+		$object->context['createfromclone'] = 'createfromclone';
+		$result = $object->createCommon($user);
+		if ($result < 0) {
+			$error++;
+			$this->error = $object->error;
+			$this->errors = $object->errors;
+		}
+
+		unset($object->context['createfromclone']);
+
+		// End
+		if (!$error) {
+			$this->db->commit();
+			return $object;
+		} else {
+			$this->db->rollback();
+			return -1;
+		}
+	}
+
+	/**
+	 * Load object in memory from the database
+	 *
+	 * @param int    $id   Id object
+	 * @param string $ref  Ref
+	 * @return int         <0 if KO, 0 if not found, >0 if OK
+	 */
+	public function fetch($id, $ref = null)
+	{
+		$result = $this->fetchCommon($id, $ref);
+		//if ($result > 0 && !empty($this->table_element_line)) $this->fetchLines();
+		return $result;
+	}
+
+	/**
+	 * Load object lines in memory from the database
+	 *
+	 * @return int         <0 if KO, 0 if not found, >0 if OK
+	 */
+	/*public function fetchLines()
+	   {
+	   $this->lines=array();
+	   // Load lines with object MyObjectLine
+	   return count($this->lines)?1:0;
+	   }*/
+
+	/**
+	 * Update object into database
+	 *
+	 * @param  User $user      User that modifies
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, >0 if OK
+	 */
+	public function update(User $user, $notrigger = false)
+	{
+		return $this->updateCommon($user, $notrigger);
+	}
+
+	/**
+	 * Delete object in database
+	 *
+	 * @param User $user       User that deletes
+	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, >0 if OK
+	 */
+	public function delete(User $user, $notrigger = false)
+	{
+		return $this->deleteCommon($user, $notrigger);
+	}
+
+	/**
+	 *  Delete a line of object in database
+	 *
+	 *	@param  User	$user       User that delete
+	 *  @param	int		$idline		Id of line to delete
+	 *  @param 	bool 	$notrigger  false=launch triggers after, true=disable triggers
+	 *  @return int         		>0 if OK, <0 if KO
+	 */
+	public function deleteLine(User $user, $idline, $notrigger = false)
+	{
+		if ($this->status < 0) {
+			$this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
+			return -2;
+		}
+
+		return $this->deleteLineCommon($user, $idline, $notrigger);
+	}
+
+	/**
+	 *  Return a link to the object card (with optionaly the picto)
+	 *
+	 *	@param	int		$withpicto					Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
+	 *	@param	string	$option						On what the link point to
+	 *  @param	int  	$notooltip					1=Disable tooltip
+	 *  @param  string  $morecss            		Add more css on link
+	 *  @param  int     $save_lastsearch_value    	-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
+	 *	@return	string								String with URL
+	 */
+	public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
+	{
+		global $db, $conf, $langs;
+		global $dolibarr_main_authentication, $dolibarr_main_demo;
+		global $menumanager;
+
+		if (!empty($conf->dol_no_mouse_hover)) {
+			$notooltip = 1; // Force disable tooltips
+		}
+
+		$result = '';
+		$companylink = '';
+
+		$label = '<u>' . $langs->trans("Inventory") . '</u>';
+		$label .= '<br>';
+		$label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
+
+		$url = dol_buildpath('/product/inventory/card.php', 1) . '?id=' . $this->id;
+
+		$linkclose = '';
+		if (empty($notooltip)) {
+			if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
+				$label = $langs->trans("ShowInventory");
+				$linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
+			}
+			$linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
+			$linkclose .= ' class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"';
+		} else {
+			$linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
+		}
+
+		$linkstart = '<a href="' . $url . '"';
+		$linkstart .= $linkclose . '>';
+		$linkend = '</a>';
+
+		$result .= $linkstart;
+		if ($withpicto) {
+			$result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
+		}
+		if ($withpicto != 2) {
+			$result .= $this->ref;
+		}
+		$result .= $linkend;
+		//if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
+
+		return $result;
+	}
+
+	/**
+	 *  Retourne le libelle du status d'un user (actif, inactif)
+	 *
+	 *  @param	int		$mode          0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
+	 *  @return	string 			       Label of status
+	 */
+	public function getLibStatut($mode = 0)
+	{
+		return $this->LibStatut($this->status, $mode);
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	/**
+	 *  Return the status
+	 *
+	 *  @param	int		$status        	Id status
+	 *  @param  int		$mode          	0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 5=Long label + Picto, 6=Long label + Picto
+	 *  @return string 			       	Label of status
+	 */
+	public static function LibStatut($status, $mode = 0)
+	{
+		// phpcs:enable
+		global $langs;
+		global $db;
+
+		/*$statusObj = new Statuses($db);
+			  $statuses = $statusObj::getStatusids($db);*/
+
+		$labelStatus = array();
+		$labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
+		$labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated') . ' (' . $langs->transnoentitiesnoconv('InventoryStartedShort') . ')';
+		$labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
+		$labelStatus[self::STATUS_RECORDED] = $langs->transnoentitiesnoconv('Closed');
+		/*foreach($statuses as $key => $val){
+				  $labelStatus[$key] = $val;	
+				  $labelStatusShort[$key] = $val;	
+			  }*/
+		$labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
+		$labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('InventoryStartedShort');
+		$labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
+		$labelStatusShort[self::STATUS_RECORDED] = $langs->transnoentitiesnoconv('Closed');
+
+		$statusType = 'status' . $status;
+		if ($status == self::STATUS_RECORDED) {
+			$statusType = 'status6';
+		}
+
+		return dolGetStatus($labelStatus[$status], $labelStatusShort[$status], '', $statusType, $mode);
+	}
+
+	/**
+	 *	Charge les informations d'ordre info dans l'objet commande
+	 *
+	 *	@param  int		$id       Id of order
+	 *	@return	void
+	 */
+	public function info($id)
+	{
+		$sql = "SELECT rowid, date_creation as datec, tms as datem, date_validation as datev,";
+		$sql .= " fk_user_creat, fk_user_modif, fk_user_valid";
+		$sql .= " FROM " . $this->db->prefix() . $this->table_element . " as t";
+		$sql .= " WHERE t.rowid = " . ((int) $id);
+		$result = $this->db->query($sql);
+		if ($result) {
+			if ($this->db->num_rows($result)) {
+				$obj = $this->db->fetch_object($result);
+
+				$this->id = $obj->rowid;
+
+				if ($obj->fk_user_creat > 0) {
+					$cuser = new User($this->db);
+					$cuser->fetch($obj->fk_user_creat);
+					$this->user_creation = $cuser;
+				}
+
+				if ($obj->fk_user_modif > 0) {
+					$muser = new User($this->db);
+					$muser->fetch($obj->fk_user_modif);
+					$this->user_creation = $muser;
+				}
+
+				if ($obj->fk_user_valid > 0) {
+					$vuser = new User($this->db);
+					$vuser->fetch($obj->fk_user_valid);
+					$this->user_validation = $vuser;
+				}
+
+				$this->date_creation = $this->db->jdate($obj->datec);
+				$this->date_modification = $this->db->jdate($obj->datem);
+				$this->date_validation = $this->db->jdate($obj->datev);
+			}
+
+			$this->db->free($result);
+		} else {
+			dol_print_error($this->db);
+		}
+	}
+
+	/**
+	 * Initialise object with example values
+	 * Id must be 0 if object instance is a specimen
+	 *
+	 * @return void
+	 */
+	public function initAsSpecimen()
+	{
+		$this->initAsSpecimenCommon();
+		$this->title = '';
+	}
+}
+
+/**
+ * Class InventoryLine
+ */
+class InventoryLine extends CommonObjectLine
+{
+	/**
+	 * @var string ID to identify managed object
+	 */
+	public $element = 'inventoryline';
+
+	/**
+	 * @var string Name of table without prefix where object is stored
+	 */
+	public $table_element = 'inventorydet';
+
+	/**
+	 * @var array  Does inventory support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
+	 */
+	public $ismultientitymanaged = 0;
+
+	/**
+	 * @var int  Does object support extrafields ? 0=No, 1=Yes
+	 */
+	public $isextrafieldmanaged = 0;
+
+	/**
+	 * @var string String with name of icon for inventory
+	 */
+	public $picto = 'stock';
+
+	/**
+	 *  'type' if the field format.
+	 *  'label' the translation key.
+	 *  'enabled' is a condition when the field must be managed.
+	 *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only. Using a negative value means field is not shown by default on list but can be selected for viewing)
+	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
+	 *  'index' if we want an index in database.
+	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
+	 *  'position' is the sort order of field.
+	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
+	 *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
+	 *  'help' is a string visible as a tooltip on field
+	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.
+	 *  'default' is a default value for creation (can still be replaced by the global setup of default values)
+	 *  'showoncombobox' if field must be shown into the label of combobox
+	 */
+
+	// BEGIN MODULEBUILDER PROPERTIES
+	/**
+	 * @var array  Array with all fields and their property
+	 */
+	public $fields = array(
+		'rowid' => array(
+			'type' => 'integer',
+			'label' => 'TechnicalID',
+			'visible' => -1,
+			'enabled' => 1,
+			'position' => 1,
+			'notnull' => 1,
+			'index' => 1,
+			'comment' => 'Id',
+		),
+		'fk_inventory' => array('type' => 'integer:Inventory:product/inventory/class/inventory.class.php', 'label' => 'Inventory', 'visible' => 1, 'enabled' => 1, 'position' => 30, 'index' => 1, 'help' => 'LinkToInventory'),
+		'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Warehouse', 'visible' => 1, 'enabled' => 1, 'position' => 30, 'index' => 1, 'help' => 'LinkToThirdparty'),
+		'fk_product' => array('type' => 'integer:Product:product/class/product.class.php', 'label' => 'Product', 'visible' => 1, 'enabled' => 1, 'position' => 32, 'index' => 1, 'help' => 'LinkToProduct'),
+		'batch' => array('type' => 'string', 'label' => 'Batch', 'visible' => 1, 'enabled' => 1, 'position' => 32, 'index' => 1, 'help' => 'LinkToProduct'),
+		'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 500),
+		'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 501),
+		'qty_stock' => array('type' => 'double', 'label' => 'QtyFound', 'visible' => 1, 'enabled' => 1, 'position' => 32, 'index' => 1, 'help' => 'Qty we found/want (to define during draft edition)'),
+		'qty_view' => array('type' => 'double', 'label' => 'QtyBefore', 'visible' => 1, 'enabled' => 1, 'position' => 33, 'index' => 1, 'help' => 'Qty before (filled once movements are validated)'),
+		'qty_regulated' => array('type' => 'double', 'label' => 'QtyDelta', 'visible' => 1, 'enabled' => 1, 'position' => 34, 'index' => 1, 'help' => 'Qty aadded or removed (filled once movements are validated)'),
+		'pmp_real' => array('type' => 'double', 'label' => 'PMPReal', 'visible' => 1, 'enabled' => 1, 'position' => 35),
+		'pmp_expected' => array('type' => 'double', 'label' => 'PMPExpected', 'visible' => 1, 'enabled' => 1, 'position' => 36),
+	);
+
+	/**
+	 * @var int ID
+	 */
+	public $rowid;
+
+	public $fk_inventory;
+	public $fk_warehouse;
+	public $fk_product;
+	public $batch;
+	public $datec;
+	public $tms;
+	public $qty_stock;
+	public $qty_view;
+	public $qty_regulated;
+	public $pmp_real;
+	public $pmp_expected;
+
+
+	/**
+	 * Create object in database
+	 *
+	 * @param User $user       User that creates
+	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, >0 if OK
+	 */
+	public function create(User $user, $notrigger = false)
+	{
+		return $this->createCommon($user, $notrigger);
+	}
+
+	/**
+	 * Load object in memory from the database
+	 *
+	 * @param int    $id   Id object
+	 * @param string $ref  Ref
+	 * @return int         <0 if KO, 0 if not found, >0 if OK
+	 */
+	public function fetch($id, $ref = null)
+	{
+		$result = $this->fetchCommon($id, $ref);
+		//if ($result > 0 && !empty($this->table_element_line)) $this->fetchLines();
+		return $result;
+	}
+
+	/**
+	 * Update object into database
+	 *
+	 * @param  User $user      User that modifies
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, >0 if OK
+	 */
+	public function update(User $user, $notrigger = false)
+	{
+		return $this->updateCommon($user, $notrigger);
+	}
+
+	/**
+	 * Delete object in database
+	 *
+	 * @param User $user       User that deletes
+	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, >0 if OK
+	 */
+	public function delete(User $user, $notrigger = false)
+	{
+		return $this->deleteCommon($user, $notrigger);
+		//return $this->deleteCommon($user, $notrigger, 1);
+	}
+}

+ 1 - 1
product/list.php

@@ -468,7 +468,7 @@ if (!empty($conf->global->PRODUCT_USE_UNITS)) {
 	$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units cu ON cu.rowid = p.fk_unit";
 }
 
-$sql .= ' WHERE p.entity IN ('.getEntity('product').')';
+$sql .= ' WHERE p.entity IN ('.getEntity('product').', 0)';
 if ($sall) {
 	$sql .= ' AND (';
 	$sql .= natural_search(array_keys($fieldstosearchall), $sall, 0, 1);

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 260 - 254
user/class/user.class.php


+ 1 - 1
user/perms.php

@@ -27,7 +27,7 @@
  */
 
 if (!defined('CSRFCHECK_WITH_TOKEN')) {
-	define('CSRFCHECK_WITH_TOKEN', '1'); // Force use of CSRF protection with tokens even for GET
+	define('CSRFCHECK_WITH_TOKEN', '0'); // Force use of CSRF protection with tokens even for GET
 }
 
 // Load Dolibarr environment

+ 225 - 213
variants/combinations.php

@@ -20,12 +20,12 @@
 
 // Load Dolibarr environment
 require '../main.inc.php';
-require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
-require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
-require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttribute.class.php';
-require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttributeValue.class.php';
-require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
-require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination2ValuePair.class.php';
+require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
+require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttribute.class.php';
+require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttributeValue.class.php';
+require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination.class.php';
+require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination2ValuePair.class.php';
 
 $langs->loadLangs(array("products", "other"));
 
@@ -70,7 +70,7 @@ if ($id > 0 || $ref) {
 	$object->fetch($id, $ref);
 }
 
-$selectedvariant = !empty($_SESSION['addvariant_'.$object->id]) ? $_SESSION['addvariant_'.$object->id] : array();
+$selectedvariant = !empty($_SESSION['addvariant_' . $object->id]) ? $_SESSION['addvariant_' . $object->id] : array();
 $selected = "";
 // Security check
 if (!isModEnabled('variants')) {
@@ -102,25 +102,25 @@ $usercandelete = (($object->type == Product::TYPE_PRODUCT && $user->rights->prod
 if ($cancel) {
 	$action = '';
 	$massaction = '';
-	unset($_SESSION['addvariant_'.$object->id]);
+	unset($_SESSION['addvariant_' . $object->id]);
 }
 
 if (!$object->isProduct() && !$object->isService()) {
-	header('Location: '.dol_buildpath('/product/card.php?id='.$object->id, 2));
+	header('Location: ' . dol_buildpath('/product/card.php?id=' . $object->id, 2));
 	exit();
 }
 if ($action == 'add') {
 	unset($selectedvariant);
-	unset($_SESSION['addvariant_'.$object->id]);
+	unset($_SESSION['addvariant_' . $object->id]);
 }
 if ($action == 'create' && GETPOST('selectvariant', 'alpha')) {	// We click on select combination
 	$action = 'add';
 	$attribute_id = GETPOST('attribute', 'int');
 	$attribute_value_id = GETPOST('value', 'int');
-	if ($attribute_id> 0 && $attribute_value_id > 0) {
+	if ($attribute_id > 0 && $attribute_value_id > 0) {
 		$feature = $attribute_id . '-' . $attribute_value_id;
 		$selectedvariant[$feature] = $feature;
-		$_SESSION['addvariant_'.$object->id] = $selectedvariant;
+		$_SESSION['addvariant_' . $object->id] = $selectedvariant;
 	}
 }
 if ($action == 'create' && $subaction == 'delete') {	// We click on select combination
@@ -128,7 +128,7 @@ if ($action == 'create' && $subaction == 'delete') {	// We click on select combi
 	$feature = GETPOST('feature', 'intcomma');
 	if (isset($selectedvariant[$feature])) {
 		unset($selectedvariant[$feature]);
-		$_SESSION['addvariant_'.$object->id] = $selectedvariant;
+		$_SESSION['addvariant_' . $object->id] = $selectedvariant;
 	}
 }
 
@@ -140,7 +140,7 @@ $productCombination2ValuePairs1 = array();
 
 if (($action == 'add' || $action == 'create') && empty($massaction) && !GETPOST('selectvariant', 'alpha') && empty($subaction)) {	// We click on Create all defined combinations
 	//$features = GETPOST('features', 'array');
-	$features = !empty($_SESSION['addvariant_'.$object->id]) ? $_SESSION['addvariant_'.$object->id] : array();
+	$features = !empty($_SESSION['addvariant_' . $object->id]) ? $_SESSION['addvariant_' . $object->id] : array();
 
 	if (!$features) {
 		if ($action == 'create') {
@@ -190,10 +190,10 @@ if (($action == 'add' || $action == 'create') && empty($massaction) && !GETPOST(
 			$result = $prodcomb->createProductCombination($user, $object, $sanit_features, array(), $level_price_impact_percent, $level_price_impact, $weight_impact, $reference);
 			if ($result > 0) {
 				setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
-				unset($_SESSION['addvariant_'.$object->id]);
+				unset($_SESSION['addvariant_' . $object->id]);
 
 				$db->commit();
-				header('Location: '.dol_buildpath('/variants/combinations.php?id='.$id, 2));
+				header('Location: ' . dol_buildpath('/variants/combinations.php?id=' . $id, 2));
 				exit();
 			} else {
 				$langs->load("errors");
@@ -307,7 +307,7 @@ if (($action == 'add' || $action == 'create') && empty($massaction) && !GETPOST(
 
 	if ($prodcomb->update($user) > 0) {
 		setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
-		header('Location: '.dol_buildpath('/variants/combinations.php?id='.$id, 2));
+		header('Location: ' . dol_buildpath('/variants/combinations.php?id=' . $id, 2));
 		exit();
 	} else {
 		setEventMessages($prodcomb->error, $prodcomb->errors, 'errors');
@@ -325,7 +325,7 @@ if ($action === 'confirm_deletecombination') {
 		if ($prodcomb->delete($user) > 0 && (empty($delete_product) || ($delete_product == 'on' && $prodstatic->fetch($prodcomb->fk_product_child) > 0 && $prodstatic->delete($user) > 0))) {
 			$db->commit();
 			setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
-			header('Location: '.dol_buildpath('/variants/combinations.php?id='.$object->id, 2));
+			header('Location: ' . dol_buildpath('/variants/combinations.php?id=' . $object->id, 2));
 			exit();
 		}
 
@@ -352,7 +352,7 @@ if ($action === 'confirm_deletecombination') {
 		//To prevent from copying to the same product
 		if ($prodstatic->ref != $object->ref) {
 			if ($prodcomb->copyAll($user, $object->id, $prodstatic) > 0) {
-				header('Location: '.dol_buildpath('/variants/combinations.php?id='.$prodstatic->id, 2));
+				header('Location: ' . dol_buildpath('/variants/combinations.php?id=' . $prodstatic->id, 2));
 				exit();
 			} else {
 				setEventMessages($langs->trans('ErrorCopyProductCombinations'), null, 'errors');
@@ -383,13 +383,13 @@ if (!empty($id) || !empty($ref)) {
 	}
 
 	$head = product_prepare_head($object);
-	$titre = $langs->trans("CardProduct".$object->type);
+	$titre = $langs->trans("CardProduct" . $object->type);
 	$picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
 
 	print dol_get_fiche_head($head, 'combinations', $titre, -1, $picto);
 
-	$linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
-	$object->next_prev_filter = " fk_product_type = ".$object->type;
+	$linkback = '<a href="' . DOL_URL_ROOT . '/product/list.php?type=' . $object->type . '">' . $langs->trans("BackToList") . '</a>';
+	$object->next_prev_filter = " fk_product_type = " . $object->type;
 
 	dol_banner_tab($object, 'ref', $linkback, ($user->socid ? 0 : 1), 'ref', '', '', '', 0, '', '');
 
@@ -400,7 +400,7 @@ if (!empty($id) || !empty($ref)) {
 
 	// Type
 	if (isModEnabled("product") && isModEnabled("service")) {
-		$typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
+		$typeformat = 'select;0:' . $langs->trans("Product") . ',1:' . $langs->trans("Service");
 		print '<tr><td class="titlefieldcreate">';
 		print (empty($conf->global->PRODUCT_DENY_CHANGE_PRODUCT_TYPE)) ? $form->editfieldkey("Type", 'fk_product_type', $object->type, $object, $usercancreate, $typeformat) : $langs->trans('Type');
 		print '</td><td>';
@@ -409,52 +409,52 @@ if (!empty($id) || !empty($ref)) {
 	}
 
 	// TVA
-	print '<tr><td class="titlefieldcreate">'.$langs->trans("DefaultTaxRate").'</td><td>';
+	print '<tr><td class="titlefieldcreate">' . $langs->trans("DefaultTaxRate") . '</td><td>';
 
 	$positiverates = '';
 	if (price2num($object->tva_tx)) {
-		$positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
+		$positiverates .= ($positiverates ? '/' : '') . price2num($object->tva_tx);
 	}
 	if (price2num($object->localtax1_type)) {
-		$positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
+		$positiverates .= ($positiverates ? '/' : '') . price2num($object->localtax1_tx);
 	}
 	if (price2num($object->localtax2_type)) {
-		$positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
+		$positiverates .= ($positiverates ? '/' : '') . price2num($object->localtax2_tx);
 	}
 	if (empty($positiverates)) {
 		$positiverates = '0';
 	}
-	echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), '%', $object->tva_npr);
+	echo vatrate($positiverates . ($object->default_vat_code ? ' (' . $object->default_vat_code . ')' : ''), '%', $object->tva_npr);
 	/*
-	if ($object->default_vat_code)
-	{
-		print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
-	}
-	else print vatrate($object->tva_tx, true, $object->tva_npr, true);*/
+	   if ($object->default_vat_code)
+	   {
+		   print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
+	   }
+	   else print vatrate($object->tva_tx, true, $object->tva_npr, true);*/
 	print '</td></tr>';
 
 	// Price
-	print '<tr><td>'.$langs->trans("SellingPrice").'</td><td>';
+	print '<tr><td>' . $langs->trans("SellingPrice") . '</td><td>';
 	if ($object->price_base_type == 'TTC') {
-		print price($object->price_ttc).' '.$langs->trans($object->price_base_type);
+		print price($object->price_ttc) . ' ' . $langs->trans($object->price_base_type);
 	} else {
-		print price($object->price).' '.$langs->trans($object->price_base_type);
+		print price($object->price) . ' ' . $langs->trans($object->price_base_type);
 	}
 	print '</td></tr>';
 
 	// Price minimum
-	print '<tr><td>'.$langs->trans("MinPrice").'</td><td>';
+	print '<tr><td>' . $langs->trans("MinPrice") . '</td><td>';
 	if ($object->price_base_type == 'TTC') {
-		print price($object->price_min_ttc).' '.$langs->trans($object->price_base_type);
+		print price($object->price_min_ttc) . ' ' . $langs->trans($object->price_base_type);
 	} else {
-		print price($object->price_min).' '.$langs->trans($object->price_base_type);
+		print price($object->price_min) . ' ' . $langs->trans($object->price_base_type);
 	}
 	print '</td></tr>';
 
 	// Weight
-	print '<tr><td>'.$langs->trans("Weight").'</td><td>';
+	print '<tr><td>' . $langs->trans("Weight") . '</td><td>';
 	if ($object->weight != '') {
-		print $object->weight." ".measuringUnitString(0, "weight", $object->weight_units);
+		print $object->weight . " " . measuringUnitString(0, "weight", $object->weight_units);
 	} else {
 		print '&nbsp;';
 	}
@@ -474,7 +474,7 @@ if (!empty($id) || !empty($ref)) {
 		if ($action == 'add') {
 			$title = $langs->trans('NewProductCombination');
 			// print dol_get_fiche_head();
-			$features = !empty($_SESSION['addvariant_'.$object->id]) ? $_SESSION['addvariant_'.$object->id] : array();
+			$features = !empty($_SESSION['addvariant_' . $object->id]) ? $_SESSION['addvariant_' . $object->id] : array();
 			//First, sanitize
 			$listofvariantselected = '<div id="parttoaddvariant">';
 			if (!empty($features)) {
@@ -484,8 +484,8 @@ if (!empty($id) || !empty($ref)) {
 					if ($prodattr->fetch($explode[0]) <= 0 || $prodattr_val->fetch($explode[1]) <= 0) {
 						continue;
 					}
-					$toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #ddd;">' . $prodattr->label.' : '.$prodattr_val->value .
-						' <a class="reposition" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=create&subaction=delete&feature='.urlencode($feature).'">' . img_delete() . '</a></li>';
+					$toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #ddd;">' . $prodattr->label . ' : ' . $prodattr_val->value .
+						' <a class="reposition" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=create&subaction=delete&feature=' . urlencode($feature) . '">' . img_delete() . '</a></li>';
 				}
 				$listofvariantselected .= '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
 			}
@@ -505,79 +505,86 @@ if (!empty($id) || !empty($ref)) {
 			$prodattr_alljson = array();
 
 			foreach ($prodattr_all as $each) {
-				$prodattr_alljson[$each->id] = $each;
+				$prodattr_alljson[$each->id] = [];
+				foreach ($each->fields as $field => $info) {
+					if ($field == 'rowid') {
+						$field = 'id';
+					}
+					$prodattr_alljson[$each->id][$field] = $each->$field;
+				}
+				// $prodattr_alljson[$each->id]['_'] = $each;
 			}
 			?>
 
-		<script type="text/javascript">
-
-			variants_available = <?php echo json_encode($prodattr_alljson); ?>;
-			variants_selected = {
-				index: [],
-				info: []
-			};
+			<script type="text/javascript">
 
-			<?php
-			foreach ($productCombination2ValuePairs1 as $pc2v) {
-				$prodattr_val->fetch($pc2v->fk_prod_attr_val);
-				?>
-				variants_selected.index.push(<?php echo $pc2v->fk_prod_attr ?>);
-				variants_selected.info[<?php echo $pc2v->fk_prod_attr ?>] = {
-					attribute: variants_available[<?php echo $pc2v->fk_prod_attr ?>],
-					value: {
-						id: <?php echo $pc2v->fk_prod_attr_val ?>,
-						label: '<?php echo $prodattr_val->value ?>'
-					}
+				variants_available = <?php echo (($prodattr_alljson) ? json_encode($prodattr_alljson) : '{}'); ?>;
+				variants_selected = {
+					index: [],
+					info: []
 				};
-				<?php
-			}
-			?>
-
-			restoreAttributes = function() {
-				jQuery("select[name=attribute]").empty().append('<option value="-1">&nbsp;</option>');
 
-				jQuery.each(variants_available, function (key, val) {
-					if (jQuery.inArray(val.id, variants_selected.index) == -1) {
-						jQuery("select[name=attribute]").append('<option value="' + val.id + '">' + val.label + '</option>');
-					}
-				});
-			};
+				<?php
+				foreach ($productCombination2ValuePairs1 as $pc2v) {
+					$prodattr_val->fetch($pc2v->fk_prod_attr_val);
+					?>
+					variants_selected.index.push(<?php echo $pc2v->fk_prod_attr ?>);
+					variants_selected.info[<?php echo $pc2v->fk_prod_attr ?>] = {
+						attribute: variants_available[<?php echo $pc2v->fk_prod_attr ?>],
+						value: {
+							id: <?php echo $pc2v->fk_prod_attr_val ?>,
+							label: '<?php echo $prodattr_val->value ?>'
+						}
+					};
+					<?php
+				}
+				?>
 
+				restoreAttributes = function () {
+					jQuery("select[name=attribute]").empty().append('<option value="-1">&nbsp;</option>');
 
-			jQuery(document).ready(function() {
-				jQuery("select#attribute").change(function () {
-					console.log("Change of field variant attribute");
-					var select = jQuery("select#value");
+					jQuery.each(variants_available, function (key, val) {
+						if (jQuery.inArray(val.id, variants_selected.index) == -1) {
+							jQuery("select[name=attribute]").append('<option value="' + val.id + '">' + val.label + '</option>');
+						}
+					});
+				};
 
-					if (!jQuery(this).val().length || jQuery(this).val() == '-1') {
-						select.empty();
-						select.append('<option value="-1">&nbsp;</option>');
-						return;
-					}
 
-					select.empty().append('<option value="">Loading...</option>');
+				jQuery(document).ready(function () {
+					jQuery("select#attribute").change(function () {
+						console.log("Change of field variant attribute");
+						var select = jQuery("select#value");
 
-					jQuery.getJSON("ajax/get_attribute_values.php", {
-						id: jQuery(this).val()
-					}, function(data) {
-						if (data.error) {
+						if (!jQuery(this).val().length || jQuery(this).val() == '-1') {
 							select.empty();
 							select.append('<option value="-1">&nbsp;</option>');
-							return alert(data.error);
+							return;
 						}
 
-						select.empty();
-						select.append('<option value="-1">&nbsp;</option>');
+						select.empty().append('<option value="">Loading...</option>');
 
-						jQuery(data).each(function (key, val) {
-							keyforoption = val.id
-							valforoption = val.value
-							select.append('<option value="' + keyforoption + '">' + valforoption + '</option>');
+						jQuery.getJSON("ajax/get_attribute_values.php", {
+							id: jQuery(this).val()
+						}, function (data) {
+							if (data.error) {
+								select.empty();
+								select.append('<option value="-1">&nbsp;</option>');
+								return alert(data.error);
+							}
+
+							select.empty();
+							select.append('<option value="-1">&nbsp;</option>');
+
+							jQuery(data).each(function (key, val) {
+								keyforoption = val.id
+								valforoption = val.value
+								select.append('<option value="' + keyforoption + '">' + valforoption + '</option>');
+							});
 						});
 					});
 				});
-			});
-		</script>
+			</script>
 
 			<?php
 		}
@@ -586,11 +593,11 @@ if (!empty($id) || !empty($ref)) {
 
 		print load_fiche_titre($title);
 
-		print '<form method="post" id="combinationform" action="'.$_SERVER["PHP_SELF"] .'?id='.$object->id.'">'."\n";
-		print '<input type="hidden" name="token" value="'.newToken().'">';
-		print '<input type="hidden" name="action" value="'.(($valueid > 0) ? "update" : "create").'">'."\n";
+		print '<form method="post" id="combinationform" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '">' . "\n";
+		print '<input type="hidden" name="token" value="' . newToken() . '">';
+		print '<input type="hidden" name="action" value="' . (($valueid > 0) ? "update" : "create") . '">' . "\n";
 		if ($valueid > 0) {
-			print '<input type="hidden" name="valueid" value="'.$valueid.'">'."\n";
+			print '<input type="hidden" name="valueid" value="' . $valueid . '">' . "\n";
 		}
 
 		print dol_get_fiche_head();
@@ -600,14 +607,14 @@ if (!empty($id) || !empty($ref)) {
 			print '<table class="border" style="width: 100%">';
 			print "<!--  Variant -->\n";
 			print '<tr>';
-			print '<td class="titlefieldcreate fieldrequired"><label for="attribute">'.$langs->trans('ProductAttribute').'</label></td>';
+			print '<td class="titlefieldcreate fieldrequired"><label for="attribute">' . $langs->trans('ProductAttribute') . '</label></td>';
 			print '<td>';
 			if (is_array($prodattr_all)) {
 				print '<select class="flat minwidth100" id="attribute" name="attribute">';
 				print '<option value="-1">&nbsp;</option>';
 				foreach ($prodattr_all as $attr) {
 					//print '<option value="'.$attr->id.'"'.($attr->id == GETPOST('attribute', 'int') ? ' selected="selected"' : '').'>'.$attr->label.'</option>';
-					print '<option value="'.$attr->id.'">'.$attr->label.'</option>';
+					print '<option value="' . $attr->id . '">' . $attr->label . '</option>';
 				}
 				print '</select>';
 			}
@@ -615,8 +622,8 @@ if (!empty($id) || !empty($ref)) {
 			$htmltext = $langs->trans("GoOnMenuToCreateVairants", $langs->transnoentities("Product"), $langs->transnoentities("VariantAttributes"));
 			print $form->textwithpicto('', $htmltext);
 			/*print ' &nbsp; &nbsp; <a href="'.DOL_URL_ROOT.'/variants/create.php?action=create&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=add&token='.newToken().'&id='.$object->id).'">';
-			print $langs->trans("Create");
-			print '</a>';*/
+					 print $langs->trans("Create");
+					 print '</a>';*/
 
 			print '</td>';
 			print '</tr>';
@@ -632,16 +639,18 @@ if (!empty($id) || !empty($ref)) {
 					$htmltext = $langs->trans("GoOnMenuToCreateVairants", $langs->transnoentities("Product"), $langs->transnoentities("VariantAttributes"));
 					print $form->textwithpicto('', $htmltext);
 					/*
-						print ' &nbsp; &nbsp; <a href="'.DOL_URL_ROOT.'/variants/create.php?action=create&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=add&token='.newToken().'&id='.$object->id).'">';
-						print $langs->trans("Create");
-						print '</a>';
-					*/
+									   print ' &nbsp; &nbsp; <a href="'.DOL_URL_ROOT.'/variants/create.php?action=create&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=add&token='.newToken().'&id='.$object->id).'">';
+									   print $langs->trans("Create");
+									   print '</a>';
+								   */
 					?>
 				</td>
 			</tr>
 			<tr>
-				<td></td><td>
-					<input type="submit" class="button" name="selectvariant" id="selectvariant" value="<?php echo dol_escape_htmltag($langs->trans("SelectCombination")); ?>">
+				<td></td>
+				<td>
+					<input type="submit" class="button" name="selectvariant" id="selectvariant"
+						value="<?php echo dol_escape_htmltag($langs->trans("SelectCombination")); ?>">
 				</td>
 			</tr>
 			<?php
@@ -664,17 +673,17 @@ if (!empty($id) || !empty($ref)) {
 					<td class="titlefieldcreate tdtop"><label for="features"><?php echo $langs->trans('Combination') ?></label></td>
 					<td class="tdtop">
 						<div class="inline-block valignmiddle quatrevingtpercent">
-					<?php
-					foreach ($productCombination2ValuePairs1 as $key => $val) {
-						$result1 = $prodattr->fetch($val->fk_prod_attr);
-						$result2 = $prodattr_val->fetch($val->fk_prod_attr_val);
-						//print 'rr'.$result1.' '.$result2;
-						if ($result1 > 0 && $result2 > 0) {
-							print $prodattr->label.' - '.$prodattr_val->value.'<br>';
-							// TODO Add delete link
-						}
-					}
-					?>
+							<?php
+							foreach ($productCombination2ValuePairs1 as $key => $val) {
+								$result1 = $prodattr->fetch($val->fk_prod_attr);
+								$result2 = $prodattr_val->fetch($val->fk_prod_attr_val);
+								//print 'rr'.$result1.' '.$result2;
+								if ($result1 > 0 && $result2 > 0) {
+									print $prodattr->label . ' - ' . $prodattr_val->value . '<br>';
+									// TODO Add delete link
+								}
+							}
+							?>
 						</div>
 						<!-- <div class="inline-block valignmiddle">
 						<a href="#" class="inline-block valignmiddle button" id="delfeature"><?php echo img_edit_remove() ?></a>
@@ -693,25 +702,26 @@ if (!empty($id) || !empty($ref)) {
 			<?php
 			if (empty($conf->global->PRODUIT_MULTIPRICES)) {
 				?>
-			<tr>
-				<td><label for="price_impact"><?php echo $langs->trans('PriceImpact') ?></label></td>
-				<td><input type="text" id="price_impact" name="price_impact" value="<?php echo price($price_impact) ?>">
-				<input type="checkbox" id="price_impact_percent" name="price_impact_percent" <?php echo $price_impact_percent ? ' checked' : '' ?>> <label for="price_impact_percent"><?php echo $langs->trans('PercentageVariation') ?></label>
-				</td>
-			</tr>
+				<tr>
+					<td><label for="price_impact"><?php echo $langs->trans('PriceImpact') ?></label></td>
+					<td><input type="text" id="price_impact" name="price_impact" value="<?php echo price($price_impact) ?>">
+						<input type="checkbox" id="price_impact_percent" name="price_impact_percent" <?php echo $price_impact_percent ? ' checked' : '' ?>> <label
+							for="price_impact_percent"><?php echo $langs->trans('PercentageVariation') ?></label>
+					</td>
+				</tr>
 				<?php
 			} else {
 				$prodcomb->fetchCombinationPriceLevels();
 
 				for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
 					print '<tr>';
-					print '<td><label for="level_price_impact_'.$i.'">'.$langs->trans('ImpactOnPriceLevel', $i).'</label>';
+					print '<td><label for="level_price_impact_' . $i . '">' . $langs->trans('ImpactOnPriceLevel', $i) . '</label>';
 					if ($i === 1) {
-						print ' <a id="apply-price-impact-to-all-level" class="classfortooltip" href="#" title="'.$langs->trans('ApplyToAllPriceImpactLevelHelp').'">('.$langs->trans('ApplyToAllPriceImpactLevel').')</a>';
+						print ' <a id="apply-price-impact-to-all-level" class="classfortooltip" href="#" title="' . $langs->trans('ApplyToAllPriceImpactLevelHelp') . '">(' . $langs->trans('ApplyToAllPriceImpactLevel') . ')</a>';
 					}
 					print '</td>';
-					print '<td><input type="text" class="level_price_impact" id="level_price_impact_'.$i.'" name="level_price_impact['.$i.']" value="'.price($prodcomb->combination_price_levels[$i]->variation_price).'">';
-					print '<input type="checkbox" class="level_price_impact_percent" id="level_price_impact_percent_'.$i.'" name="level_price_impact_percent['.$i.']" '.(!empty($prodcomb->combination_price_levels[$i]->variation_price_percentage) ? ' checked' : '').'> <label for="level_price_impact_percent_'.$i.'">'.$langs->trans('PercentageVariation').'</label>';
+					print '<td><input type="text" class="level_price_impact" id="level_price_impact_' . $i . '" name="level_price_impact[' . $i . ']" value="' . price($prodcomb->combination_price_levels[$i]->variation_price) . '">';
+					print '<input type="checkbox" class="level_price_impact_percent" id="level_price_impact_percent_' . $i . '" name="level_price_impact_percent[' . $i . ']" ' . (!empty($prodcomb->combination_price_levels[$i]->variation_price_percentage) ? ' checked' : '') . '> <label for="level_price_impact_percent_' . $i . '">' . $langs->trans('PercentageVariation') . '</label>';
 
 					print '</td>';
 					print '</tr>';
@@ -720,8 +730,8 @@ if (!empty($id) || !empty($ref)) {
 
 			if ($object->isProduct()) {
 				print '<tr>';
-				print '<td><label for="weight_impact">'.$langs->trans('WeightImpact').'</label></td>';
-				print '<td><input type="text" id="weight_impact" name="weight_impact" value="'.price($weight_impact).'"></td>';
+				print '<td><label for="weight_impact">' . $langs->trans('WeightImpact') . '</label></td>';
+				print '<td><input type="text" id="weight_impact" name="weight_impact" value="' . price($weight_impact) . '"></td>';
 				print '</tr>';
 			}
 
@@ -731,18 +741,18 @@ if (!empty($id) || !empty($ref)) {
 		if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
 			?>
 			<script>
-				$(document).ready(function() {
+				$(document).ready(function () {
 					// Apply level 1 impact to all prices impact levels
-					$('body').on('click', '#apply-price-impact-to-all-level', function(e) {
+					$('body').on('click', '#apply-price-impact-to-all-level', function (e) {
 						e.preventDefault();
-						let priceImpact = $( "#level_price_impact_1" ).val();
-						let priceImpactPrecent = $( "#level_price_impact_percent_1" ).prop("checked");
+						let priceImpact = $("#level_price_impact_1").val();
+						let priceImpactPrecent = $("#level_price_impact_percent_1").prop("checked");
 
 						var multipricelimit = <?php print intval($conf->global->PRODUIT_MULTIPRICES_LIMIT); ?>
 
 						for (let i = 2; i <= multipricelimit; i++) {
-							$( "#level_price_impact_" + i ).val(priceImpact);
-							$( "#level_price_impact_percent_" + i  ).prop("checked", priceImpactPrecent);
+							$("#level_price_impact_" + i).val(priceImpact);
+							$("#level_price_impact_percent_" + i).prop("checked", priceImpactPrecent);
 						}
 					});
 				});
@@ -754,11 +764,13 @@ if (!empty($id) || !empty($ref)) {
 		?>
 
 		<div style="text-align: center">
-		<input type="submit" name="create" <?php if (!is_array($productCombination2ValuePairs1)) {
-			print ' disabled="disabled"';
-										   } ?> value="<?php echo $action == 'add' ? $langs->trans('Create') : $langs->trans("Save") ?>" class="button button-save">
-		&nbsp;
-		<input type="submit" name="cancel" value="<?php echo $langs->trans("Cancel"); ?>" class="button button-cancel">
+			<input type="submit" name="create" <?php if (!is_array($productCombination2ValuePairs1)) {
+				print ' disabled="disabled"';
+			} ?>
+				value="<?php echo $action == 'add' ? $langs->trans('Create') : $langs->trans("Save") ?>"
+				class="button button-save">
+			&nbsp;
+			<input type="submit" name="cancel" value="<?php echo $langs->trans("Cancel"); ?>" class="button button-cancel">
 		</div>
 
 		<?php
@@ -770,17 +782,17 @@ if (!empty($id) || !empty($ref)) {
 				$prodstatic->fetch($prodcomb->fk_product_child);
 
 				print $form->formconfirm(
-					"combinations.php?id=".urlencode($id)."&valueid=".urlencode($valueid),
+					"combinations.php?id=" . urlencode($id) . "&valueid=" . urlencode($valueid),
 					$langs->trans('Delete'),
 					$langs->trans('ProductCombinationDeleteDialog', $prodstatic->ref),
 					"confirm_deletecombination",
-					array(array('label'=> $langs->trans('DeleteLinkedProduct'), 'type'=> 'checkbox', 'name' => 'delete_product', 'value' => false)),
+					array(array('label' => $langs->trans('DeleteLinkedProduct'), 'type' => 'checkbox', 'name' => 'delete_product', 'value' => false)),
 					0,
 					1
 				);
 			}
 		} elseif ($action === 'copy') {
-			print $form->formconfirm('combinations.php?id='.$id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneProductCombinations'), 'confirm_copycombination', array(array('type' => 'text', 'label' => $langs->trans('CloneDestinationReference'), 'name' => 'dest_product')), 0, 1);
+			print $form->formconfirm('combinations.php?id=' . $id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneProductCombinations'), 'confirm_copycombination', array(array('type' => 'text', 'label' => $langs->trans('CloneDestinationReference'), 'name' => 'dest_product')), 0, 1);
 		}
 
 		$comb2val = new ProductCombination2ValuePair($db);
@@ -789,9 +801,9 @@ if (!empty($id) || !empty($ref)) {
 			?>
 
 			<script type="text/javascript">
-				jQuery(document).ready(function() {
+				jQuery(document).ready(function () {
 
-					jQuery('input[name="select_all"]').click(function() {
+					jQuery('input[name="select_all"]').click(function () {
 
 						if (jQuery(this).prop('checked')) {
 							var checked = true;
@@ -802,7 +814,7 @@ if (!empty($id) || !empty($ref)) {
 						jQuery('table.liste input[type="checkbox"]').prop('checked', checked);
 					});
 
-					jQuery('input[name^="select["]').click(function() {
+					jQuery('input[name^="select["]').click(function () {
 						jQuery('input[name="select_all"]').prop('checked', false);
 					});
 
@@ -817,10 +829,10 @@ if (!empty($id) || !empty($ref)) {
 
 		print '	<div class="inline-block divButAction">';
 
-		print '<a href="combinations.php?id='.$object->id.'&action=add&token='.newToken().'" class="butAction">'.$langs->trans('NewProductCombination').'</a>'; // NewVariant
+		print '<a href="combinations.php?id=' . $object->id . '&action=add&token=' . newToken() . '" class="butAction">' . $langs->trans('NewProductCombination') . '</a>'; // NewVariant
 
 		if ($productCombinations) {
-			print '<a href="combinations.php?id='.$object->id.'&action=copy&token='.newToken().'" class="butAction">'.$langs->trans('PropagateVariant').'</a>';
+			print '<a href="combinations.php?id=' . $object->id . '&action=copy&token=' . newToken() . '" class="butAction">' . $langs->trans('PropagateVariant') . '</a>';
 		}
 
 		print '	</div>';
@@ -833,34 +845,34 @@ if (!empty($id) || !empty($ref)) {
 
 
 		// List of variants
-		print '<form method="POST" action="'.$_SERVER["PHP_SELF"] .'?id='.$object->id.'">';
-		print '<input type="hidden" name="token" value="'.newToken().'">';
+		print '<form method="POST" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '">';
+		print '<input type="hidden" name="token" value="' . newToken() . '">';
 		print '<input type="hidden" name="action" value="massaction">';
-		print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
+		print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
 
 		// List of mass actions available
 		/*
-		$arrayofmassactions =  array(
-			'presend'=>$langs->trans("SendByMail"),
-			'builddoc'=>$langs->trans("PDFMerge"),
-		);
-		if ($user->rights->product->supprimer) $arrayofmassactions['predelete']='<span class="fa fa-trash paddingrightonly"></span>'.$langs->trans("Delete");
-		if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array();
-		$massactionbutton=$form->selectMassAction('', $arrayofmassactions);
-		*/
+			  $arrayofmassactions =  array(
+				  'presend'=>$langs->trans("SendByMail"),
+				  'builddoc'=>$langs->trans("PDFMerge"),
+			  );
+			  if ($user->rights->product->supprimer) $arrayofmassactions['predelete']='<span class="fa fa-trash paddingrightonly"></span>'.$langs->trans("Delete");
+			  if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array();
+			  $massactionbutton=$form->selectMassAction('', $arrayofmassactions);
+			  */
 
 		$aaa = '';
 		if (count($productCombinations)) {
 			$aaa = '<select id="bulk_action" name="massaction" class="flat">';
 			$aaa .= '	<option value="nothing">&nbsp;</option>';
-			$aaa .= '	<option value="not_buy" data-html="'.dol_escape_htmltag(img_picto($langs->trans("SetToStatus"), 'stop-circle', 'class="pictofixedwidth"').$langs->trans('SetToStatus', $langs->transnoentitiesnoconv('ProductStatusNotOnBuy'))).'">'.$langs->trans('ProductStatusNotOnBuy').'</option>';
-			$aaa .= '	<option value="not_sell" data-html="'.dol_escape_htmltag(img_picto($langs->trans("SetToStatus"), 'stop-circle', 'class="pictofixedwidth"').$langs->trans('SetToStatus', $langs->transnoentitiesnoconv('ProductStatusNotOnSell'))).'">'.$langs->trans('ProductStatusNotOnSell').'</option>';
-			$aaa .= '	<option value="on_buy" data-html="'.dol_escape_htmltag(img_picto($langs->trans("SetToStatus"), 'stop-circle', 'class="pictofixedwidth"').$langs->trans('SetToStatus', $langs->transnoentitiesnoconv('ProductStatusOnBuy'))).'">'.$langs->trans('ProductStatusOnBuy').'</option>';
-			$aaa .= '	<option value="on_sell" data-html="'.dol_escape_htmltag(img_picto($langs->trans("SetToStatus"), 'stop-circle', 'class="pictofixedwidth"').$langs->trans('SetToStatus', $langs->transnoentitiesnoconv('ProductStatusOnSell'))).'">'.$langs->trans('ProductStatusOnSell').'</option>';
-			$aaa .= '	<option value="delete" data-html="'.dol_escape_htmltag(img_picto($langs->trans("Delete"), 'delete', 'class="pictofixedwidth"').$langs->trans('Delete')).'">'.$langs->trans('Delete').'</option>';
+			$aaa .= '	<option value="not_buy" data-html="' . dol_escape_htmltag(img_picto($langs->trans("SetToStatus"), 'stop-circle', 'class="pictofixedwidth"') . $langs->trans('SetToStatus', $langs->transnoentitiesnoconv('ProductStatusNotOnBuy'))) . '">' . $langs->trans('ProductStatusNotOnBuy') . '</option>';
+			$aaa .= '	<option value="not_sell" data-html="' . dol_escape_htmltag(img_picto($langs->trans("SetToStatus"), 'stop-circle', 'class="pictofixedwidth"') . $langs->trans('SetToStatus', $langs->transnoentitiesnoconv('ProductStatusNotOnSell'))) . '">' . $langs->trans('ProductStatusNotOnSell') . '</option>';
+			$aaa .= '	<option value="on_buy" data-html="' . dol_escape_htmltag(img_picto($langs->trans("SetToStatus"), 'stop-circle', 'class="pictofixedwidth"') . $langs->trans('SetToStatus', $langs->transnoentitiesnoconv('ProductStatusOnBuy'))) . '">' . $langs->trans('ProductStatusOnBuy') . '</option>';
+			$aaa .= '	<option value="on_sell" data-html="' . dol_escape_htmltag(img_picto($langs->trans("SetToStatus"), 'stop-circle', 'class="pictofixedwidth"') . $langs->trans('SetToStatus', $langs->transnoentitiesnoconv('ProductStatusOnSell'))) . '">' . $langs->trans('ProductStatusOnSell') . '</option>';
+			$aaa .= '	<option value="delete" data-html="' . dol_escape_htmltag(img_picto($langs->trans("Delete"), 'delete', 'class="pictofixedwidth"') . $langs->trans('Delete')) . '">' . $langs->trans('Delete') . '</option>';
 			$aaa .= '</select>';
 			$aaa .= ajax_combobox("bulk_action");
-			$aaa .= '<input type="submit" value="'.dol_escape_htmltag($langs->trans("Apply")).'" class="button small">';
+			$aaa .= '<input type="submit" value="' . dol_escape_htmltag($langs->trans("Apply")) . '" class="button small">';
 		}
 		$massactionbutton = $aaa;
 
@@ -876,7 +888,7 @@ if (!empty($id) || !empty($ref)) {
 				<td class="liste_titre"><?php echo $langs->trans('Combination') ?></td>
 				<td class="liste_titre right"><?php echo $langs->trans('PriceImpact') ?></td>
 				<?php if ($object->isProduct()) {
-					print'<td class="liste_titre right">'.$langs->trans('WeightImpact').'</td>';
+					print '<td class="liste_titre right">' . $langs->trans('WeightImpact') . '</td>';
 				} ?>
 				<td class="liste_titre center"><?php echo $langs->trans('OnSell') ?></td>
 				<td class="liste_titre center"><?php echo $langs->trans('OnBuy') ?></td>
@@ -888,52 +900,52 @@ if (!empty($id) || !empty($ref)) {
 				print '</td>';
 				?>
 			</tr>
-		<?php
+			<?php
 
-		if (count($productCombinations)) {
-			foreach ($productCombinations as $currcomb) {
-				$prodstatic->fetch($currcomb->fk_product_child);
-				print '<tr class="oddeven">';
-				print '<td>'.$prodstatic->getNomUrl(1).'</td>';
-				print '<td>';
-
-				$productCombination2ValuePairs = $comb2val->fetchByFkCombination($currcomb->id);
-				$iMax = count($productCombination2ValuePairs);
-
-				for ($i = 0; $i < $iMax; $i++) {
-					echo dol_htmlentities($productCombination2ValuePairs[$i]);
-					if ($i !== ($iMax - 1)) {
-						echo ', ';
+			if (count($productCombinations)) {
+				foreach ($productCombinations as $currcomb) {
+					$prodstatic->fetch($currcomb->fk_product_child);
+					print '<tr class="oddeven">';
+					print '<td>' . $prodstatic->getNomUrl(1) . '</td>';
+					print '<td>';
+
+					$productCombination2ValuePairs = $comb2val->fetchByFkCombination($currcomb->id);
+					$iMax = count($productCombination2ValuePairs);
+
+					for ($i = 0; $i < $iMax; $i++) {
+						echo dol_htmlentities($productCombination2ValuePairs[$i]);
+						if ($i !== ($iMax - 1)) {
+							echo ', ';
+						}
 					}
-				}
-				print '</td>';
-				print '<td class="right">'.($currcomb->variation_price >= 0 ? '+' : '').price($currcomb->variation_price).($currcomb->variation_price_percentage ? ' %' : '').'</td>';
-				if ($object->isProduct()) {
-					print '<td class="right">'.($currcomb->variation_weight >= 0 ? '+' : '').price($currcomb->variation_weight).' '.measuringUnitString(0, 'weight', $prodstatic->weight_units).'</td>';
-				}
-				print '<td class="center">'.$prodstatic->getLibStatut(2, 0).'</td>';
-				print '<td class="center">'.$prodstatic->getLibStatut(2, 1).'</td>';
-				print '<td class="right">';
-				print '<a class="paddingleft paddingright editfielda" href="'.$_SERVER["PHP_SELF"].'?id='.$id.'&action=edit&token='.newToken().'&valueid='.$currcomb->id.'">'.img_edit().'</a>';
-				print '<a class="paddingleft paddingright" href="'.$_SERVER["PHP_SELF"].'?id='.$id.'&action=delete&token='.newToken().'&valueid='.$currcomb->id.'">'.img_delete().'</a>';
-				print '</td>';
-				print '<td class="nowrap center">';
-				if (!empty($productCombinations) || $massactionbutton || $massaction) {   // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
-					$selected = 0;
-					if (in_array($prodstatic->id, $arrayofselected)) {
-						$selected = 1;
+					print '</td>';
+					print '<td class="right">' . ($currcomb->variation_price >= 0 ? '+' : '') . price($currcomb->variation_price) . ($currcomb->variation_price_percentage ? ' %' : '') . '</td>';
+					if ($object->isProduct()) {
+						print '<td class="right">' . ($currcomb->variation_weight >= 0 ? '+' : '') . price($currcomb->variation_weight) . ' ' . measuringUnitString(0, 'weight', $prodstatic->weight_units) . '</td>';
+					}
+					print '<td class="center">' . $prodstatic->getLibStatut(2, 0) . '</td>';
+					print '<td class="center">' . $prodstatic->getLibStatut(2, 1) . '</td>';
+					print '<td class="right">';
+					print '<a class="paddingleft paddingright editfielda" href="' . $_SERVER["PHP_SELF"] . '?id=' . $id . '&action=edit&token=' . newToken() . '&valueid=' . $currcomb->id . '">' . img_edit() . '</a>';
+					print '<a class="paddingleft paddingright" href="' . $_SERVER["PHP_SELF"] . '?id=' . $id . '&action=delete&token=' . newToken() . '&valueid=' . $currcomb->id . '">' . img_delete() . '</a>';
+					print '</td>';
+					print '<td class="nowrap center">';
+					if (!empty($productCombinations) || $massactionbutton || $massaction) {   // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
+						$selected = 0;
+						if (in_array($prodstatic->id, $arrayofselected)) {
+							$selected = 1;
+						}
+						print '<input id="cb' . $prodstatic->id . '" class="flat checkforselect" type="checkbox" name="toselect[]" value="' . $prodstatic->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
 					}
-					print '<input id="cb'.$prodstatic->id.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$prodstatic->id.'"'.($selected ? ' checked="checked"' : '').'>';
+					print '</td>';
+					print '</tr>';
 				}
-				print '</td>';
-				print '</tr>';
+			} else {
+				print '<tr><td colspan="8"><span class="opacitymedium">' . $langs->trans("None") . '</span></td></tr>';
 			}
-		} else {
-			 print '<tr><td colspan="8"><span class="opacitymedium">'.$langs->trans("None").'</span></td></tr>';
-		}
-		print '</table>';
-		print '</div>';
-		print '</form>';
+			print '</table>';
+			print '</div>';
+			print '</form>';
 	}
 }
 

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels