diff --git a/htdocs/contact/project.php b/htdocs/contact/project.php
new file mode 100644
index 00000000000..b05709f019a
--- /dev/null
+++ b/htdocs/contact/project.php
@@ -0,0 +1,117 @@
+
+ *
+ * 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 .
+ */
+
+/**
+ * \file htdocs/contact/project.php
+ * \ingroup contact
+ * \brief Page of third party projects
+ */
+
+require '../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
+
+$langs->loadLangs(array("contacts", "companies", "projects"));
+
+// Security check
+$id = GETPOST('id', 'int');
+$result = restrictedArea($user, 'contact', $id, 'socpeople&societe');
+
+// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
+$hookmanager->initHooks(array('projectcontact'));
+
+/*
+ * Actions
+ */
+
+$parameters = array('id' => $id);
+$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) {
+ setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+}
+
+/*
+ * View
+ */
+
+$form = new Form($db);
+
+if ($id) {
+ require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
+ require_once DOL_DOCUMENT_ROOT.'/core/lib/contact.lib.php';
+
+ $object = new Contact($db);
+
+ $result = $object->fetch($id);
+ if (empty($object->thirdparty)) {
+ $object->fetch_thirdparty();
+ }
+ $socid = $object->thirdparty->id;
+ $title = $langs->trans("Projects");
+ if (! empty($conf->global->MAIN_HTML_TITLE) && preg_match('/thirdpartynameonly/', $conf->global->MAIN_HTML_TITLE) && $object->name) {
+ $title = $object->name." - ".$title;
+ }
+ llxHeader('', $title);
+
+ if (! empty($conf->notification->enabled)) {
+ $langs->load("mails");
+ }
+ $head = contact_prepare_head($object);
+
+ print dol_get_fiche_head($head, 'project', $langs->trans("Contact"), -1, 'contact');
+
+ $linkback = ''.$langs->trans("BackToList").'';
+
+ $morehtmlref = '
';
+ if (empty($conf->global->SOCIETE_DISABLE_CONTACTS) && !empty($socid)) {
+ $object->thirdparty->fetch($socid);
+ // Thirdparty
+ $morehtmlref .= $langs->trans('ThirdParty').' : ';
+ if ($object->thirdparty->id > 0) {
+ $morehtmlref .= $object->thirdparty->getNomUrl(1, 'contact');
+ } else {
+ $morehtmlref .= $langs->trans("ContactNotLinkedToCompany");
+ }
+ }
+ $morehtmlref .= '
';
+
+ dol_banner_tab($object, 'id', $linkback, ($user->socid ? 0 : 1), 'rowid', 'nom', $morehtmlref);
+
+ print '';
+
+ print '
';
+ print '
';
+
+ // Civility
+ print '| '.$langs->trans("UserTitle").' | ';
+ print $object->getCivilityLabel();
+ print ' |
';
+
+ print '
';
+
+ print '
';
+
+ print dol_get_fiche_end();
+ print '
';
+
+ // Projects list
+ $result = show_contacts_projects($conf, $langs, $db, $object, $_SERVER["PHP_SELF"].'?id='.$object->id, 1);
+}
+
+// End of page
+llxFooter();
+$db->close();
diff --git a/htdocs/core/lib/contact.lib.php b/htdocs/core/lib/contact.lib.php
index c60cb0c8c18..5a0c6c376fb 100644
--- a/htdocs/core/lib/contact.lib.php
+++ b/htdocs/core/lib/contact.lib.php
@@ -57,6 +57,40 @@ function contact_prepare_head(Contact $object)
$head[$tab][2] = 'perso';
$tab++;
+ if (!empty($conf->projet->enabled) && (!empty($user->rights->projet->lire))) {
+ $nbProject = 0;
+ // Enable caching of thirdrparty count projects
+ require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
+ $cachekey = 'count_projects_contact_'.$object->id;
+ $dataretrieved = dol_getcache($cachekey);
+
+ if (!is_null($dataretrieved)) {
+ $nbProject = $dataretrieved;
+ } else {
+ $sql = 'SELECT COUNT(n.rowid) as nb';
+ $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as n';
+ $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'element_contact as cc ON (n.rowid = cc.element_id)';
+ $sql .= ' WHERE cc.fk_socpeople = '.((int) $object->id);
+ $sql .= ' AND cc.fk_c_type_contact IN (SELECT rowid FROM '.MAIN_DB_PREFIX.'c_type_contact WHERE element="project" AND source="external")';
+ $sql .= ' AND n.entity IN ('.getEntity('project').')';
+ $resql = $db->query($sql);
+ if ($resql) {
+ $obj = $db->fetch_object($resql);
+ $nbProject = $obj->nb;
+ } else {
+ dol_print_error($db);
+ }
+ dol_setcache($cachekey, $nbProject, 120); // If setting cache fails, this is not a problem, so we do not test result.
+ }
+ $head[$tab][0] = DOL_URL_ROOT.'/contact/project.php?id='.$object->id;
+ $head[$tab][1] = $langs->trans("Projects");
+ if ($nbProject > 0) {
+ $head[$tab][1] .= ''.$nbProject.'';
+ }
+ $head[$tab][2] = 'project';
+ $tab++;
+ }
+
// Related items
if (!empty($conf->commande->enabled) || !empty($conf->propal->enabled) || !empty($conf->facture->enabled) || !empty($conf->ficheinter->enabled) || (!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)) {
$head[$tab][0] = DOL_URL_ROOT.'/contact/consumption.php?id='.$object->id;
@@ -117,3 +151,131 @@ function contact_prepare_head(Contact $object)
return $head;
}
+
+/**
+ * Show html area for list of projects
+ *
+ * @param Conf $conf Object conf
+ * @param Translate $langs Object langs
+ * @param DoliDB $db Database handler
+ * @param Object $object Third party object
+ * @param string $backtopage Url to go once contact is created
+ * @param int $nocreatelink 1=Hide create project link
+ * @param string $morehtmlright More html on right of title
+ * @return int
+ */
+function show_contacts_projects($conf, $langs, $db, $object, $backtopage = '', $nocreatelink = 0, $morehtmlright = '')
+{
+ global $user;
+
+ $i = -1;
+
+ if (!empty($conf->projet->enabled) && $user->rights->projet->lire) {
+ $langs->load("projects");
+
+ $newcardbutton = '';
+ if (!empty($conf->projet->enabled) && $user->rights->projet->creer && empty($nocreatelink)) {
+ $newcardbutton .= dolGetButtonTitle($langs->trans('AddProject'), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/projet/card.php?socid='.$object->id.'&action=create&backtopage='.urlencode($backtopage));
+ }
+
+ print "\n";
+ print load_fiche_titre($langs->trans("ProjectsHavingThisContact"), $newcardbutton.$morehtmlright, '');
+ print '';
+ print "\n".'
';
+
+ $sql = 'SELECT p.rowid as id, p.entity, p.title, p.ref, p.public, p.dateo as do, p.datee as de, p.fk_statut as status, p.fk_opp_status, p.opp_amount, p.opp_percent, p.tms as date_update, p.budget_amount';
+ $sql .= ', cls.code as opp_status_code, ctc.libelle';
+ $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as p';
+ $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_lead_status as cls on p.fk_opp_status = cls.rowid';
+ $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'element_contact as cc ON (p.rowid = cc.element_id)';
+ $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'c_type_contact as ctc ON (ctc.rowid = cc.fk_c_type_contact)';
+ $sql .= ' WHERE cc.fk_socpeople = '.((int) $object->id);
+ $sql .= ' AND ctc.element="project" AND ctc.source="external"';
+ $sql .= ' AND p.entity IN ('.getEntity('project').')';
+ $sql .= ' ORDER BY p.dateo DESC';
+
+ $result = $db->query($sql);
+ if ($result) {
+ $num = $db->num_rows($result);
+
+ print '';
+ print '| '.$langs->trans("Ref").' | ';
+ print ''.$langs->trans("Name").' | ';
+ print ''.$langs->trans("ContactType").' | ';
+ print ''.$langs->trans("DateStart").' | ';
+ print ''.$langs->trans("DateEnd").' | ';
+ print ''.$langs->trans("OpportunityAmountShort").' | ';
+ print ''.$langs->trans("OpportunityStatusShort").' | ';
+ print ''.$langs->trans("OpportunityProbabilityShort").' | ';
+ print ''.$langs->trans("Status").' | ';
+ print '
';
+
+ if ($num > 0) {
+ require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
+
+ $projecttmp = new Project($db);
+
+ $i = 0;
+
+ while ($i < $num) {
+ $obj = $db->fetch_object($result);
+ $projecttmp->fetch($obj->id);
+
+ // To verify role of users
+ $userAccess = $projecttmp->restrictedProjectArea($user);
+
+ if ($user->rights->projet->lire && $userAccess > 0) {
+ print '';
+
+ // Ref
+ print '| ';
+ print $projecttmp->getNomUrl(1);
+ print ' | ';
+
+ // Label
+ print ''.$obj->title.' | ';
+ print ''.$obj->libelle.' | ';
+ // Date start
+ print ''.dol_print_date($db->jdate($obj->do), "day").' | ';
+ // Date end
+ print ''.dol_print_date($db->jdate($obj->de), "day").' | ';
+ // Opp amount
+ print '';
+ if ($obj->opp_status_code) {
+ print price($obj->opp_amount, 1, '', 1, -1, -1, '');
+ }
+ print ' | ';
+ // Opp status
+ print '';
+ if ($obj->opp_status_code) {
+ print $langs->trans("OppStatus".$obj->opp_status_code);
+ }
+ print ' | ';
+ // Opp percent
+ print '';
+ if ($obj->opp_percent) {
+ print price($obj->opp_percent, 1, '', 1, 0).'%';
+ }
+ print ' | ';
+ // Status
+ print ''.$projecttmp->getLibStatut(5).' | ';
+
+ print '
';
+ }
+ $i++;
+ }
+ } else {
+ print '| '.$langs->trans("None").' |
';
+ }
+ $db->free($result);
+ } else {
+ dol_print_error($db);
+ }
+ print "
";
+ print '
';
+
+ print "
\n";
+ }
+
+ return $i;
+}
diff --git a/htdocs/langs/en_US/projects.lang b/htdocs/langs/en_US/projects.lang
index 9d3a83c7a61..e73a21dea26 100644
--- a/htdocs/langs/en_US/projects.lang
+++ b/htdocs/langs/en_US/projects.lang
@@ -284,4 +284,5 @@ PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE_help=Note: existing projects with al
SelectLinesOfTimeSpentToInvoice=Select lines of time spent that are unbilled, then bulk action "Generate Invoice" to bill them
ProjectTasksWithoutTimeSpent=Project tasks without time spent
FormForNewLeadDesc=Thanks to fill the following form to contact us. You can also send us an email directly to %s.
+ProjectsHavingThisContact=Projects having this contact
StartDateCannotBeAfterEndDate=End date cannot be before start date
diff --git a/htdocs/langs/fr_FR/projects.lang b/htdocs/langs/fr_FR/projects.lang
index 1b3c6ec7662..950462968ed 100644
--- a/htdocs/langs/fr_FR/projects.lang
+++ b/htdocs/langs/fr_FR/projects.lang
@@ -284,4 +284,5 @@ PROJECT_CLASSIFY_CLOSED_WHEN_ALL_TASKS_DONE_help=Remarque : les projets existant
SelectLinesOfTimeSpentToInvoice=Sélectionnez les lignes de temps passé non facturées, puis l'action groupée "Générer la facture" pour les facturer
ProjectTasksWithoutTimeSpent=Tâches de projet sans temps consommé
FormForNewLeadDesc=Veuillez remplir ce formulaire de contact ou écrivez un e-mail à %s.
+ProjectsHavingThisContact=Projets ayant ce contact associé
StartDateCannotBeAfterEndDate=La date de fin ne peux être avant la date de début