浏览代码

Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop

Laurent Destailleur 3 年之前
父节点
当前提交
45fb57c413

+ 51 - 24
htdocs/admin/ticket.php

@@ -26,6 +26,7 @@ require '../main.inc.php';
 require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
 require_once DOL_DOCUMENT_ROOT."/ticket/class/ticket.class.php";
 require_once DOL_DOCUMENT_ROOT."/core/lib/ticket.lib.php";
+require_once DOL_DOCUMENT_ROOT."/core/class/html.formcategory.class.php";
 
 // Load translation files required by the page
 $langs->loadLangs(array("admin", "ticket"));
@@ -157,6 +158,14 @@ if ($action == 'setvarworkflow') {
 	}
 }
 
+if ($action == 'setvarworkflowother' || $action == 'setvarworkflow') {
+	$param_ticket_product_category = GETPOST('product_category_id', 'int');
+	$res = dolibarr_set_const($db, 'TICKET_PRODUCT_CATEGORY', $param_ticket_product_category, 'chaine', 0, '', $conf->entity);
+	if (!($res > 0)) {
+		$error++;
+	}
+}
+
 if ($action == 'setvarother') {
 	$param_must_exists = GETPOST('TICKET_EMAIL_MUST_EXISTS', 'alpha');
 	$res = dolibarr_set_const($db, 'TICKET_EMAIL_MUST_EXISTS', $param_must_exists, 'chaine', 0, '', $conf->entity);
@@ -219,7 +228,7 @@ if ($action == 'setvarother') {
 
 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
 
-$form = new Form($db);
+$formcategory = new FormCategory($db);
 
 $help_url = "FR:Module_Ticket";
 $page_name = "TicketSetup";
@@ -326,7 +335,7 @@ foreach ($dirmodels as $reldir) {
 						}
 
 						print '<td class="center">';
-						print $form->textwithpicto('', $htmltooltip, 1, 0);
+						print $formcategory->textwithpicto('', $htmltooltip, 1, 0);
 						print '</td>';
 
 						print '</tr>';
@@ -464,7 +473,7 @@ foreach ($dirmodels as $reldir) {
 
 
 								print '<td class="center">';
-								print $form->textwithpicto('', $htmltooltip, 1, 0);
+								print $formcategory->textwithpicto('', $htmltooltip, 1, 0);
 								print '</td>';
 
 								// Preview
@@ -489,8 +498,7 @@ foreach ($dirmodels as $reldir) {
 print '</table>';
 print '</div><br>';
 
-
-if (!$conf->use_javascript_ajax) {
+if (empty($conf->use_javascript_ajax)) {
 	print '<form method="post" action="'.$_SERVER['PHP_SELF'].'" enctype="multipart/form-data" >';
 	print '<input type="hidden" name="token" value="'.newToken().'">';
 	print '<input type="hidden" name="action" value="setvarworkflow">';
@@ -512,11 +520,11 @@ if ($conf->use_javascript_ajax) {
 	print ajax_constantonoff('TICKET_AUTO_READ_WHEN_CREATED_FROM_BACKEND');
 } else {
 	$arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes"));
-	print $form->selectarray("TICKET_AUTO_READ_WHEN_CREATED_FROM_BACKEND", $arrval, $conf->global->TICKET_AUTO_READ_WHEN_CREATED_FROM_BACKEND);
+	print $formcategory->selectarray("TICKET_AUTO_READ_WHEN_CREATED_FROM_BACKEND", $arrval, $conf->global->TICKET_AUTO_READ_WHEN_CREATED_FROM_BACKEND);
 }
 print '</td>';
 print '<td class="center">';
-print $form->textwithpicto('', $langs->trans("TicketsAutoReadTicketHelp"), 1, 'help');
+print $formcategory->textwithpicto('', $langs->trans("TicketsAutoReadTicketHelp"), 1, 'help');
 print '</td>';
 print '</tr>';
 
@@ -528,11 +536,11 @@ if ($conf->use_javascript_ajax) {
 	print ajax_constantonoff('TICKET_AUTO_ASSIGN_USER_CREATE');
 } else {
 	$arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes"));
-	print $form->selectarray("TICKET_AUTO_ASSIGN_USER_CREATE", $arrval, $conf->global->TICKET_AUTO_ASSIGN_USER_CREATE);
+	print $formcategory->selectarray("TICKET_AUTO_ASSIGN_USER_CREATE", $arrval, $conf->global->TICKET_AUTO_ASSIGN_USER_CREATE);
 }
 print '</td>';
 print '<td class="center">';
-print $form->textwithpicto('', $langs->trans("TicketsAutoAssignTicketHelp"), 1, 'help');
+print $formcategory->textwithpicto('', $langs->trans("TicketsAutoAssignTicketHelp"), 1, 'help');
 print '</td>';
 print '</tr>';
 
@@ -543,18 +551,33 @@ if ($conf->use_javascript_ajax) {
 	print ajax_constantonoff('TICKET_NOTIFY_AT_CLOSING');
 } else {
 	$arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes"));
-	print $form->selectarray("TICKET_NOTIFY_AT_CLOSING", $arrval, $conf->global->TICKET_NOTIFY_AT_CLOSING);
+	print $formcategory->selectarray("TICKET_NOTIFY_AT_CLOSING", $arrval, $conf->global->TICKET_NOTIFY_AT_CLOSING);
 }
 print '</td>';
 print '<td class="center">';
-print $form->textwithpicto('', $langs->trans("TicketsAutoNotifyCloseHelp"), 1, 'help');
+print $formcategory->textwithpicto('', $langs->trans("TicketsAutoNotifyCloseHelp"), 1, 'help');
 print '</td>';
 print '</tr>';
 
-if (!$conf->use_javascript_ajax) {
-	print '</form>';
+// Choose which product category is used for tickets
+if ($conf->use_javascript_ajax) {
+	print '<form method="post" action="'.$_SERVER['PHP_SELF'].'" enctype="multipart/form-data" >';
+	print '<input type="hidden" name="token" value="'.newToken().'">';
+	print '<input type="hidden" name="action" value="setvarworkflowother">';
 }
 
+print '<tr class="oddeven"><td>'.$langs->trans("TicketChooseProductCategory").'</td>';
+print '<td class="left">';
+$formcategory->selectProductCategory($conf->global->TICKET_PRODUCT_CATEGORY, 'product_category_id');
+if ($conf->use_javascript_ajax) {
+	print ajax_combobox('select_'.$htmlname);
+}
+print '</td>';
+print '<td class="center">';
+print $formcategory->textwithpicto('', $langs->trans("TicketChooseProductCategoryHelp"), 1, 'help');
+print '</td>';
+print '</tr>';
+
 // Define wanted maximum time elapsed before answers to tickets
 print '<form method="post" action="'.$_SERVER['PHP_SELF'].'" enctype="multipart/form-data" >';
 print '<input type="hidden" name="action" value="setvarother">';
@@ -566,7 +589,7 @@ print '<td class="left">
 	<input type="submit" class="button small" value="'.$langs->trans("Save").'">
 	</td>';
 print '<td class="center">';
-print $form->textwithpicto('', $langs->trans("TicketsDelayBeforeFirstAnswerHelp"), 1, 'help');
+print $formcategory->textwithpicto('', $langs->trans("TicketsDelayBeforeFirstAnswerHelp"), 1, 'help');
 print '</td>';
 print '</tr>';
 
@@ -577,14 +600,18 @@ print '<td class="left">
 	<input type="submit" class="button small" value="'.$langs->trans("Save").'">
 	</td>';
 print '<td class="center">';
-print $form->textwithpicto('', $langs->trans("TicketsDelayBetweenAnswersHelp"), 1, 'help');
+print $formcategory->textwithpicto('', $langs->trans("TicketsDelayBetweenAnswersHelp"), 1, 'help');
 print '</td>';
 print '</tr>';
 
-print '</form>';
-
 print '</table><br>';
 
+print '<div class="center">';
+print '<input type="submit" class="button button-save" value="'.$langs->trans("Save").'">';
+print '</div>';
+
+print '</form>';
+
 
 // Admin var of module
 print load_fiche_titre($langs->trans("Notification"), '', '');
@@ -612,7 +639,7 @@ print '<tr class="oddeven"><td>'.$langs->trans("TicketEmailNotificationFrom").'<
 print '<td class="left">';
 print '<input type="text" class="minwidth200" name="TICKET_NOTIFICATION_EMAIL_FROM" value="'.$conf->global->TICKET_NOTIFICATION_EMAIL_FROM.'"></td>';
 print '<td class="center">';
-print $form->textwithpicto('', $langs->trans("TicketEmailNotificationFromHelp"), 1, 'help');
+print $formcategory->textwithpicto('', $langs->trans("TicketEmailNotificationFromHelp"), 1, 'help');
 print '</td>';
 print '</tr>';
 
@@ -621,7 +648,7 @@ print '<tr class="oddeven"><td>'.$langs->trans("TicketEmailNotificationTo").' ('
 print '<td class="left">';
 print '<input type="text" name="TICKET_NOTIFICATION_EMAIL_TO" value="'.(!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) ? $conf->global->TICKET_NOTIFICATION_EMAIL_TO : '').'"></td>';
 print '<td class="center">';
-print $form->textwithpicto('', $langs->trans("TicketEmailNotificationToHelp"), 1, 'help');
+print $formcategory->textwithpicto('', $langs->trans("TicketEmailNotificationToHelp"), 1, 'help');
 print '</td>';
 print '</tr>';
 
@@ -633,11 +660,11 @@ if ($conf->global->MAIN_FEATURES_LEVEL >= 2) {
 		print ajax_constantonoff('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS');
 	} else {
 		$arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes"));
-		print $form->selectarray("TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS", $arrval, $conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS);
+		print $formcategory->selectarray("TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS", $arrval, $conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS);
 	}
 	print '</td>';
 	print '<td class="center">';
-	print $form->textwithpicto('', $langs->trans("TicketsEmailAlsoSendToMainAddressHelp"), 1, 'help');
+	print $formcategory->textwithpicto('', $langs->trans("TicketsEmailAlsoSendToMainAddressHelp"), 1, 'help');
 	print '</td>';
 	print '</tr>';
 }
@@ -651,7 +678,7 @@ $doleditor = new DolEditor('TICKET_MESSAGE_MAIL_INTRO', $mail_intro, '100%', 120
 $doleditor->Create();
 print '</td>';
 print '<td class="center">';
-print $form->textwithpicto('', $langs->trans("TicketMessageMailIntroHelpAdmin"), 1, 'help');
+print $formcategory->textwithpicto('', $langs->trans("TicketMessageMailIntroHelpAdmin"), 1, 'help');
 print '</td></tr>';
 
 // Texte de signature
@@ -663,12 +690,12 @@ $doleditor = new DolEditor('TICKET_MESSAGE_MAIL_SIGNATURE', $mail_signature, '10
 $doleditor->Create();
 print '</td>';
 print '<td class="center">';
-print $form->textwithpicto('', $langs->trans("TicketMessageMailSignatureHelpAdmin"), 1, 'help');
+print $formcategory->textwithpicto('', $langs->trans("TicketMessageMailSignatureHelpAdmin"), 1, 'help');
 print '</td></tr>';
 
 print '</table>';
 
-print $form->buttonsSaveCancel("Save", '');
+print $formcategory->buttonsSaveCancel("Save", '');
 
 print '</form>';
 

+ 20 - 1
htdocs/admin/workflow.php

@@ -161,7 +161,21 @@ $workflowcodes = array(
 		'position' => 90,
 		'enabled' => ! empty($conf->expedition->enabled) && ! empty($conf->facture->enabled),
 		'picto' => 'shipment'
-	)
+	),
+
+	// Automatic link ticket -> contract
+	'WORKFLOW_TICKET_LINK_CONTRACT' => array(
+		'family' => 'link_ticket',
+		'position' => 75,
+		'enabled' => ! empty($conf->ticket->enabled) && ! empty($conf->contract->enabled),
+		'picto' => 'ticket'
+	),
+	'WORKFLOW_TICKET_USE_PARENT_COMPANY_CONTRACTS' => array(
+		'family' => 'link_ticket',
+		'position' => 76,
+		'enabled' => ! empty($conf->ticket->enabled) && ! empty($conf->contract->enabled),
+		'picto' => 'ticket'
+	),
 );
 
 if (!empty($conf->modules_parts['workflow']) && is_array($conf->modules_parts['workflow'])) {
@@ -237,6 +251,11 @@ foreach ($workflowcodes as $key => $params) {
 			if ($reg[1] == 'shipping') {
 				$header .= ' - '.$langs->trans('Shipment');
 			}
+		} elseif (preg_match('/link_(.*)/', $params['family'], $reg)) {
+			$header = $langs->trans("AutomaticLinking");
+			if ($reg[1] == 'ticket') {
+				$header .= ' - '.$langs->trans('Ticket');
+			}
 		} else {
 			$header = $langs->trans("Description");
 		}

+ 15 - 7
htdocs/contrat/class/contrat.class.php

@@ -2136,19 +2136,27 @@ class Contrat extends CommonObject
 	/**
 	 *  Return list of other contracts for same company than current contract
 	 *
-	 *	@param	string		$option		'all' or 'others'
-	 *  @return array|int   			Array of contracts id or <0 if error
+	 *	@param	string		$option					'all' or 'others'
+	 *	@param	array		$status					sort contracts having these status
+	 *	@param  array		$product_categories		sort contracts containing these product categories
+	 *	@param	array		$line_status			sort contracts where lines have these status
+	 *  @return array|int   						Array of contracts id or <0 if error
 	 */
-	public function getListOfContracts($option = 'all')
+	public function getListOfContracts($option = 'all', $status = [], $product_categories = [], $line_status = [])
 	{
 		$tab = array();
 
 		$sql = "SELECT c.rowid, c.ref";
 		$sql .= " FROM ".MAIN_DB_PREFIX."contrat as c";
-		$sql .= " WHERE fk_soc =".((int) $this->socid);
-		if ($option == 'others') {
-			$sql .= " AND c.rowid <> ".((int) $this->id);
-		}
+		if (!empty($product_categories)) {
+			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."contratdet as cd ON cd.fk_contrat = c.rowid";
+			$sql .= " INNER JOIN ".MAIN_DB_PREFIX."categorie_product as cp ON cp.fk_product = cd.fk_product AND cp.fk_categorie IN (".$this->db->sanitize(implode(', ', $product_categories)).")";
+		}
+		$sql .= " WHERE c.fk_soc =".((int) $this->socid);
+		$sql .= ($option == 'others') ? " AND c.rowid <> ".((int) $this->id) : "";
+		$sql .= (!empty($status)) ? " AND c.statut IN (".$this->db->sanitize(implode(', ', $status)).")" : "";
+		$sql .= (!empty($line_status)) ? " AND cd.statut IN (".$this->db->sanitize(implode(', ', $line_status)).")" : "";
+		$sql .= " GROUP BY c.rowid";
 
 		dol_syslog(get_class($this)."::getOtherContracts()", LOG_DEBUG);
 		$resql = $this->db->query($sql);

+ 0 - 1
htdocs/core/class/html.form.class.php

@@ -3708,7 +3708,6 @@ class Form
 		}
 	}
 
-
 	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
 	/**
 	 *      Load into cache list of payment terms

+ 40 - 0
htdocs/core/class/html.formcategory.class.php

@@ -60,4 +60,44 @@ class FormCategory extends Form
 
 		return $filter;
 	}
+
+	/**
+	 *    Prints a select form for products categories
+	 *    @param    string	$selected          	Id category pre-selection
+	 *    @param    string	$htmlname          	Name of HTML field
+	 *    @param    int		$showempty         	Add an empty field
+	 *    @return	integer|null
+	 */
+	public function selectProductCategory($selected = 0, $htmlname = 'product_category_id', $showempty = 0)
+	{
+		global $conf;
+
+		$sql = "SELECT cp.fk_categorie as cat_index, cat.label FROM `llx_categorie_product` as cp INNER JOIN llx_categorie as cat ON cat.rowid = cp.fk_categorie GROUP BY cp.fk_categorie;";
+
+		dol_syslog(get_class($this)."::selectProductCategory", LOG_DEBUG);
+		$resql = $this->db->query($sql);
+		if ($resql) {
+			print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
+			if ($showempty) {
+				print '<option value="0">&nbsp;</option>';
+			}
+
+			$i = 0;
+			$num_rows = $this->db->num_rows($resql);
+			while ($i < $num_rows) {
+				$category = $this->db->fetch_object($resql);
+				if ($selected && $selected == $category->cat_index) {
+					print '<option value="'.$category->cat_index.'" selected>'.$category->label.'</option>';
+				} else {
+					print '<option value="'.$category->cat_index.'">'.$category->label.'</option>';
+				}
+				$i++;
+			}
+			print ('</select>');
+
+			return $num_rows;
+		} else {
+			dol_print_error($this->db);
+		}
+	}
 }

+ 1 - 0
htdocs/core/modules/modTicket.class.php

@@ -111,6 +111,7 @@ class modTicket extends DolibarrModules
 			5 => array('TICKET_DELAY_BEFORE_FIRST_RESPONSE', 'chaine', '0', 'Maximum wanted elapsed time before a first answer to a ticket (in hours). Display a warning in tickets list if not respected.', 0),
 			6 => array('TICKET_DELAY_SINCE_LAST_RESPONSE', 'chaine', '0', 'Maximum wanted elapsed time between two answers on the same ticket (in hours). Display a warning in tickets list if not respected.', 0),
 			7 => array('TICKET_NOTIFY_AT_CLOSING', 'chaine', '0', 'Default notify contacts when closing a module', 0),
+			8 => array('TICKET_PRODUCT_CATEGORY', 'chaine', 0, 'The category of product that is being used for ticket accounting', 0)
 		);
 
 

+ 3 - 1
htdocs/core/modules/modWorkflow.class.php

@@ -93,7 +93,9 @@ class modWorkflow extends DolibarrModules
 			6=>array('WORKFLOW_ORDER_CLASSIFY_RECEIVED_RECEPTION', 'chaine', '1', 'WORKFLOW_ORDER_CLASSIFY_RECEIVED_RECEPTION', 0, 'current', 0),
 			7=>array('WORKFLOW_ORDER_CLASSIFY_RECEIVED_RECEPTION_CLOSED', 'chaine', '1', 'WORKFLOW_ORDER_CLASSIFY_RECEIVED_RECEPTION_CLOSED', 0, 'current', 0),
 			8=>array('WORKFLOW_INVOICE_AMOUNT_CLASSIFY_BILLED_SUPPLIER_ORDER', 'chaine', '1', 'WORKFLOW_INVOICE_AMOUNT_CLASSIFY_BILLED_SUPPLIER_ORDER', 0, 'current', 0),
-			9=>array('WORKFLOW_BILL_ON_RECEPTION', 'chaine', '1', 'WORKFLOW_BILL_ON_RECEPTION', 0, 'current', 0)
+			9=>array('WORKFLOW_BILL_ON_RECEPTION', 'chaine', '1', 'WORKFLOW_BILL_ON_RECEPTION', 0, 'current', 0),
+			10=>array('WORKFLOW_TICKET_LINK_CONTRACT', 'chaine', '0', 'Automatically link a ticket to available contracts', 0, 'current', 0),
+			11=>array('WORKFLOW_TICKET_USE_PARENT_COMPANY_CONTRACTS', 'chaine', '0', 'Search among parent companies contracts when automatically linking a ticket to available contracts', 0, 'current', 0)
 		);
 
 		// Boxes

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

@@ -424,6 +424,40 @@ class InterfaceWorkflowManager extends DolibarrTriggers
 			}
 		}
 
+		if ($action == 'TICKET_CREATE') {
+			dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
+			// Auto link contract
+			if (!empty($conf->contract->enabled) && !empty($conf->ticket->enabled) && !empty($conf->ficheinter->enabled) && !empty($conf->workflow->enabled) && !empty($conf->global->WORKFLOW_TICKET_LINK_CONTRACT) && !empty($conf->global->TICKET_PRODUCT_CATEGORY) && !empty($object->fk_soc)) {
+				$societe = new Societe($this->db);
+				$company_ids = (empty($conf->global->WORKFLOW_TICKET_USE_PARENT_COMPANY_CONTRACTS)) ? [$object->fk_soc] : $societe->getParentsForCompany($object->fk_soc, [$object->fk_soc]);
+
+				$contrat = new Contrat($this->db);
+				$number_contracts_found = 0;
+				foreach ($company_ids as $company_id) {
+					$contrat->socid = $company_id;
+
+					$list = $contrat->getListOfContracts($option = 'all', $status = [Contrat::STATUS_DRAFT, Contrat::STATUS_VALIDATED], $product_categories = [$conf->global->TICKET_PRODUCT_CATEGORY], $line_status = [ContratLigne::STATUS_INITIAL, ContratLigne::STATUS_OPEN]);
+					if (is_array($list) && !empty($list)) {
+						$number_contracts_found = count($list);
+						if ($number_contracts_found == 1) {
+							$contractid = $list[0]->id;
+							$object->setContract($contractid);
+							break;
+						} elseif ($number_contracts_found > 1) {
+							foreach ($list as $linked_contract) {
+								$object->setContract($linked_contract->id);
+								// don't set '$contractid' so it is not used when creating an intervention.
+							}
+							if (empty(NOLOGIN)) setEventMessage($langs->trans('TicketManyContractsLinked'), 'warnings');
+							break;
+						}
+					}
+				}
+				if ($number_contracts_found == 0) {
+					if (empty(NOLOGIN)) setEventMessage($langs->trans('TicketNoContractFoundToLink'), 'mesgs');
+				}
+			}
+		}
 		return 0;
 	}
 

+ 2 - 0
htdocs/install/mysql/migration/15.0.0-16.0.0.sql

@@ -116,6 +116,8 @@ INSERT INTO llx_c_action_trigger (code,label,description,elementtype,rang) value
 
 ALTER TABLE llx_ticket ADD COLUMN date_last_msg_sent datetime AFTER date_read;
 
+UPDATE llx_const SET name = 'WORKFLOW_TICKET_LINK_CONTRACT' WHERE name = 'TICKET_AUTO_ASSIGN_CONTRACT_CREATE';
+
 CREATE TABLE llx_stock_mouvement_extrafields (
     rowid integer AUTO_INCREMENT PRIMARY KEY,
     tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

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

@@ -66,3 +66,4 @@ RepeatableIntervention=Template of intervention
 ToCreateAPredefinedIntervention=To create a predefined or recurring intervention, create a common intervention and convert it into intervention template
 ConfirmReopenIntervention=Are you sure you want to open back the intervention <b>%s</b>?
 GenerateInter=Generate intervention
+FichinterNoContractLinked=Intervention %s has been created without a linked contract.

+ 4 - 0
htdocs/langs/en_US/ticket.lang

@@ -145,6 +145,8 @@ TicketsDelayBetweenAnswersHelp=If an unresolved ticket that has already received
 TicketsAutoNotifyClose=Automatically notify thirdparty when closing a ticket
 TicketsAutoNotifyCloseHelp=When closing a ticket, you will be proposed to send a message to one of thirdparty's contacts. On mass closing, a message will be sent to one contact of the thirdparty linked to the ticket.
 TicketWrongContact=Provided contact is not part of current ticket contacts. Email not sent.
+TicketChooseProductCategory=Product category for ticket support
+TicketChooseProductCategoryHelp=Select the product category of ticket support. This will be used to automatically link a contract to a ticket.
 
 #
 # Index & list page
@@ -258,6 +260,8 @@ TicketNotCreatedFromPublicInterface=Not available. Ticket was not created from p
 ErrorTicketRefRequired=Ticket reference name is required
 TicketsDelayForFirstResponseTooLong=Too much time elapsed since ticket opening without any answer.
 TicketsDelayFromLastResponseTooLong=Too much time elapsed since last answer on this ticket.
+TicketNoContractFoundToLink=No contract was found to be automatically linked to this ticket. Please link a contract manually.
+TicketManyContractsLinked=Many contracts have been automatically linked to this ticket. Make sure to verify which should be chosen.
 
 #
 # Logs

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

@@ -22,9 +22,14 @@ descWORKFLOW_ORDER_CLASSIFY_RECEIVED_RECEPTION=Classify linked source purchase o
 descWORKFLOW_ORDER_CLASSIFY_RECEIVED_RECEPTION_CLOSED=Classify linked source purchase order as received when a reception is closed (and if the quantity received by all rceptions is the same as in the purchase order to update)
 # Autoclassify purchase invoice
 descWORKFLOW_BILL_ON_RECEPTION=Classify receptions to "billed" when a linked supplier order is validated
+# Automatically link ticket to contract
+descWORKFLOW_TICKET_LINK_CONTRACT=When creating a ticket, link available contracts of matching thirdparty
+descWORKFLOW_TICKET_USE_PARENT_COMPANY_CONTRACTS=When linking contracts, search among those of parents companies
 # Autoclose intervention
 descWORKFLOW_TICKET_CLOSE_INTERVENTION=Close all interventions linked to the ticket when a ticket is closed 
 AutomaticCreation=Automatic creation
 AutomaticClassification=Automatic classification
 # Autoclassify shipment
-descWORKFLOW_SHIPPING_CLASSIFY_CLOSED_INVOICE=Classify linked source shipment as closed when customer invoice is validated
+descWORKFLOW_SHIPPING_CLASSIFY_CLOSED_INVOICE=Classify linked source shipment as closed when customer invoice is validated
+AutomaticClosing=Automatic closing
+AutomaticLinking=Automatic linking

+ 31 - 0
htdocs/societe/class/societe.class.php

@@ -3464,6 +3464,37 @@ class Societe extends CommonObject
 		}
 	}
 
+	/**
+	 *	Get parents for company
+	 *
+	 * @param   int         $company_id     ID of company to search parent
+	 * @param   array       $parents        List of companies ID found
+	 * @return	array
+	 */
+	public function getParentsForCompany($company_id, $parents = [])
+	{
+		global $langs;
+
+		if ($company_id > 0) {
+			$sql = "SELECT parent FROM " . MAIN_DB_PREFIX . "societe WHERE rowid = $company_id";
+			$resql = $this->db->query($sql);
+			if ($resql) {
+				if ($obj = $this->db->fetch_object($resql)) {
+					$parent = $obj->parent;
+					if ($parent > 0 && !in_array($parent, $parents)) {
+						$parents[] = $parent;
+						return $this->getParentsForCompany($parent, $parents);
+					} else {
+						return $parents;
+					}
+				}
+				$this->db->free($resql);
+			} else {
+				setEventMessage($langs->trans('GetCompanyParentsError', $this->db->lasterror()), 'errors');
+			}
+		}
+	}
+
 	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
 	/**
 	 *  Returns if a profid sould be verified to be unique

+ 1 - 17
htdocs/ticket/card.php

@@ -255,28 +255,12 @@ if (empty($reshook)) {
 					$object->add_contact($user->id, "SUPPORTTEC", 'internal');
 				}
 
-				// Auto assign contrat
-				$contractid = 0;
-				if (!empty($conf->global->TICKET_AUTO_ASSIGN_CONTRACT_CREATE)) {
-					$contrat = new Contrat($db);
-					$contrat->socid = $object->fk_soc;
-					$list = $contrat->getListOfContracts();
-
-					if (is_array($list) && !empty($list)) {
-						if (count($list) == 1) {
-							$contractid = $list[0]->id;
-							$object->setContract($contractid);
-						} else {
-						}
-					}
-				}
-
 				// Auto create fiche intervention
 				if (!empty($conf->global->TICKET_AUTO_CREATE_FICHINTER_CREATE)) {
 					$fichinter = new Fichinter($db);
 					$fichinter->socid = $object->fk_soc;
 					$fichinter->fk_project = $projectid;
-					$fichinter->fk_contrat = $contractid;
+					$fichinter->fk_contrat = $object->fk_contract;
 					$fichinter->author = $user->id;
 					$fichinter->model_pdf = 'soleil';
 					$fichinter->origin = $object->element;