dolibarr 24.0.0-beta
index.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2001-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2022 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2010 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2019 Nicolas ZABOURI <info@inovea-conseil.com>
6 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
7 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
29// Load Dolibarr environment
30require '../main.inc.php';
38require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
39require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
40require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
41require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
42require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
43
44// Load translation files required by the page
45$langs->loadLangs(array('projects', 'companies'));
46
47// Initialize a technical object to manage hooks. Note that conf->hooks_modules contains array
48$hookmanager->initHooks(array('projectsindex'));
49
50$action = GETPOST('action', 'aZ09');
51$search_project_user = GETPOST('search_project_user');
52$mine = (GETPOST('mode', 'aZ09') == 'mine' || $search_project_user == $user->id) ? 1 : 0;
53if ($mine == 0 && $search_project_user === '') {
54 $search_project_user = getDolGlobalString('MAIN_SEARCH_PROJECT_USER_PROJECTSINDEX');
55}
56if ($search_project_user == $user->id) {
57 $mine = 1;
58}
59
60$sortfield = GETPOST('sortfield', 'aZ09comma');
61$sortorder = GETPOST('sortorder', 'aZ09comma');
62
63$max = getDolGlobalInt('MAIN_SIZE_SHORTLIST_LIMIT', 5);
64
65// Security check
66$socid = 0;
67//if ($user->socid > 0) $socid = $user->socid; // For external user, no check is done on company because readability is managed by public status of project and assignment.
68if (!$user->hasRight('projet', 'lire')) {
70}
71
72
73/*
74 * Actions
75 */
76
77$parameters = array();
78$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
79if ($reshook < 0) {
80 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
81}
82if (empty($reshook)) {
83 if ($action == 'refresh_search_project_user' && $user->hasRight('projet', 'lire')) {
84 $search_project_user = GETPOSTINT('search_project_user');
85 $tabparam = array("MAIN_SEARCH_PROJECT_USER_PROJECTSINDEX" => $search_project_user);
86
87 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
88 $result = dol_set_user_param($db, $conf, $user, $tabparam);
89 }
90}
91
92
93/*
94 * View
95 */
96
97$companystatic = new Societe($db);
98$projectstatic = new Project($db);
99$form = new Form($db);
100$formfile = new FormFile($db);
101
102$projectset = ($mine ? $mine : (!$user->hasRight('projet', 'all', 'lire') ? 0 : 2));
103$projectsListId = $projectstatic->getProjectsAuthorizedForUser($user, $projectset, 1);
104//var_dump($projectsListId);
105
106
107$title = $langs->trans('ProjectsArea');
108
109$help_url = 'EN:Module_Projects|FR:Module_Projets|ES:M&oacute;dulo_Proyectos|DE:Modul_Projekte';
110
111llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-project page-dashboard');
112
113
114//if ($mine) $title=$langs->trans("MyProjectsArea");
115
116
117// Title for combo list see all projects
118$titleall = $langs->trans("AllAllowedProjects");
119if ($user->hasRight('projet', 'all', 'lire') && !$socid) {
120 $titleall = $langs->trans("AllProjects");
121} else {
122 $titleall = $langs->trans("AllAllowedProjects").'<br><br>';
123}
124
125$morehtml = '<form name="projectform" method="POST" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
126$morehtml .= '<input type="hidden" name="token" value="'.newToken().'">';
127$morehtml .= '<input type="hidden" name="action" value="refresh_search_project_user">';
128
129$morehtml .= '<select name="search_project_user" id="search_project_user">';
130$morehtml .= '<option name="all" value="0"'.($mine ? '' : ' selected').'>'.$titleall.'</option>';
131$morehtml .= '<option name="mine" value="'.$user->id.'"'.(($search_project_user == $user->id) ? ' selected' : '').'>'.$langs->trans("ProjectsImContactFor").'</option>';
132$morehtml .= '</select>';
133$morehtml .= ajax_combobox("search_project_user", array(), 0, 0, 'resolve', '-1', 'small');
134$morehtml .= '<input type="submit" class="button smallpaddingimp" name="refresh" value="'.$langs->trans("Refresh").'">';
135$morehtml .= '</form>';
136
137if ($mine) {
138 $htmltooltip = $langs->trans("MyProjectsDesc");
139} else {
140 if ($user->hasRight('projet', 'all', 'lire') && !$socid) {
141 $htmltooltip = $langs->trans("ProjectsDesc");
142 } else {
143 $htmltooltip = $langs->trans("ProjectsPublicDesc");
144 }
145}
146
147print_barre_liste($form->textwithpicto($title, $htmltooltip), 0, $_SERVER["PHP_SELF"], '', '', '', '', 0, -1, 'project', 0, $morehtml);
148
149
150// Get list of ponderated percent and colors for each status
151include DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/theme_vars.inc.php';
160// Available from theme_vars:
161'
162@phan-var-force string $badgeStatus0
163@phan-var-force string $badgeStatus1
164@phan-var-force string $badgeStatus2
165@phan-var-force string $badgeStatus3
166@phan-var-force string $badgeStatus4
167@phan-var-force string $badgeStatus5
168@phan-var-force string $badgeStatus6
169@phan-var-force string $badgeStatus7
170@phan-var-force string $badgeStatus8
171@phan-var-force string $badgeStatus9
172';
173$listofoppstatus = array();
174$listofopplabel = array();
175$listofoppcode = array();
176$colorseries = array();
177$sql = "SELECT cls.rowid, cls.code, cls.percent, cls.label";
178$sql .= " FROM ".MAIN_DB_PREFIX."c_lead_status as cls";
179$sql .= " WHERE active=1";
180$resql = $db->query($sql);
181if ($resql) {
182 $num = $db->num_rows($resql);
183 $i = 0;
184
185 while ($i < $num) {
186 $objp = $db->fetch_object($resql);
187 $listofoppstatus[$objp->rowid] = $objp->percent;
188 $listofopplabel[$objp->rowid] = $objp->label; // default label if translation from "OppStatus".code not found.
189 $listofoppcode[$objp->rowid] = $objp->code;
190 switch ($objp->code) {
191 case 'PROSP':
192 $colorseries[$objp->rowid] = "-".$badgeStatus0;
193 break;
194 case 'QUAL':
195 $colorseries[$objp->rowid] = "-".$badgeStatus1;
196 break;
197 case 'PROPO':
198 $colorseries[$objp->rowid] = $badgeStatus1;
199 break;
200 case 'NEGO':
201 $colorseries[$objp->rowid] = $badgeStatus4;
202 break;
203 case 'LOST':
204 $colorseries[$objp->rowid] = $badgeStatus9;
205 break;
206 case 'WON':
207 $colorseries[$objp->rowid] = $badgeStatus6;
208 break;
209 default:
210 $colorseries[$objp->rowid] = $badgeStatus2;
211 break;
212 }
213 $i++;
214 }
215} else {
217}
218
219
220print '<div class="fichecenter">';
221
222print '<div class="twocolumns">';
223
224print '<div class="firstcolumn fichehalfleft boxhalfleft" id="boxhalfleft">';
225
226
227// Statistics
228include DOL_DOCUMENT_ROOT.'/projet/graph_opportunities.inc.php';
229
230// List of draft projects
231print_projecttasks_array($db, $form, $socid, $projectsListId, 0, 0, $listofoppstatus, array('projectlabel', 'plannedworkload', 'declaredprogress', 'prospectionstatus', 'projectstatus'), $max);
232
233
234print '</div><div class="secondcolumn fichehalfright boxhalfright" id="boxhalfright">';
235
236
237// Latest modified projects
238$sql = "SELECT p.rowid, p.ref, p.title, p.dateo as date_start, p.datee as date_end, p.fk_statut as status, p.tms as datem";
239$sql .= ", s.rowid as socid, s.nom as name, s.name_alias";
240$sql .= ", s.code_client, s.code_compta, s.client";
241$sql .= ", s.code_fournisseur, s.code_compta_fournisseur, s.fournisseur";
242$sql .= ", s.logo, s.email, s.entity";
243$sql .= ", s.canvas, s.status as thirdpartystatus";
244$sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
245$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
246$sql .= " WHERE p.entity IN (".getEntity('project').")";
247if ($mine || !$user->hasRight('projet', 'all', 'lire')) {
248 $sql .= " AND p.rowid IN (".$db->sanitize($projectsListId).")"; // If we have this test true, it also means projectset is not 2
249}
250if ($socid) {
251 $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
252}
253$sql .= " ORDER BY p.tms DESC";
254$sql .= $db->plimit($max, 0);
255
256$resql = $db->query($sql);
257if ($resql) {
258 startSimpleTable($langs->trans("LatestModifiedProjects", $max), "projet/list.php", "sortfield=p.tms&sortorder=DESC", 3, -1, 'project');
259
260 $num = $db->num_rows($resql);
261
262 if ($num) {
263 $i = 0;
264 while ($i < $num) {
265 $obj = $db->fetch_object($resql);
266
267 print '<tr class="oddeven">';
268 print '<td class="nowrap">';
269
270 $projectstatic->id = $obj->rowid;
271 $projectstatic->ref = $obj->ref;
272 $projectstatic->title = $obj->title;
273 $projectstatic->thirdparty_name = $obj->name;
274 $projectstatic->status = $obj->status;
275 $projectstatic->date_start = $db->jdate($obj->date_start);
276 $projectstatic->date_end = $db->jdate($obj->date_end);
277
278 $companystatic->id = $obj->socid;
279 $companystatic->name = $obj->name;
280 $companystatic->name_alias = $obj->name_alias;
281 $companystatic->client = $obj->client;
282 $companystatic->fournisseur = $obj->fournisseur;
283
284 //$companystatic->code_client = $obj->code_client;
285 $companystatic->code_compta = $obj->code_compta;
286 $companystatic->code_compta_client = $obj->code_compta;
287 $companystatic->code_compta_fournisseur = $obj->code_compta_fournisseur;
288
289 $companystatic->logo = $obj->logo;
290 $companystatic->email = $obj->email;
291 $companystatic->entity = $obj->entity;
292 $companystatic->canvas = $obj->canvas;
293 $companystatic->status = $obj->thirdpartystatus;
294
295 print '<table class="nobordernopadding"><tr class="nocellnopadd">';
296 print '<td width="96" class="nobordernopadding nowraponall">';
297 print $projectstatic->getNomUrl(1);
298 print '</td>';
299
300 print '<td width="16" class="nobordernopadding nowrap">';
301 print '&nbsp;';
302 print '</td>';
303
304 print '<td width="16" class="right nobordernopadding hideonsmartphone">';
305 $filename = dol_sanitizeFileName($obj->ref);
306 $filedir = $conf->project->dir_output.'/'.dol_sanitizeFileName($obj->ref);
307 print $formfile->getDocumentsLink($projectstatic->element, $filename, $filedir);
308 print '</td></tr></table>';
309
310 print '</td>';
311
312 // Label
313 print '<td class="tdoverflowmax200" title="'.dolPrintHTMLForAttribute($obj->title).'">';
314 print dolPrintHTML($projectstatic->title);
315 print '</td>';
316
317 // Thirdparty
318 print '<td class="tdoverflowmax125" title="'.dolPrintHTMLForAttribute($companystatic->name).'">';
319 if ($companystatic->id > 0) {
320 print $companystatic->getNomUrl(1, 'company', 16);
321 }
322 print '</td>';
323
324 // Date
325 $datem = $db->jdate($obj->datem);
326 print '<td class="center" title="'.dolPrintHTMLForAttribute($langs->trans("DateModification").': '.dol_print_date($datem, 'dayhour', 'tzuserrel')).'">';
327 print dol_print_date($datem, 'day', 'tzuserrel');
328 print '</td>';
329
330 // Status
331 print '<td class="right">'.$projectstatic->LibStatut($obj->status, 3).'</td>';
332 print '</tr>';
333 $i++;
334 }
335 } else {
336 print '<tr><td colspan="4"><span class="opacitymedium">'.$langs->trans("None").'</span></td></tr>';
337 }
338
339 finishSimpleTable(true);
340} else {
342}
343
344
345$companystatic = new Societe($db); // We need a clean new object for next loop because current one has some properties set.
346
347if (empty($sortfield)) {
348 $sortfield = 'nb';
349 $sortorder = 'desc';
350}
351
352// List of open projects per thirdparty
353$sql = "SELECT COUNT(p.rowid) as nb, SUM(p.opp_amount)";
354$sql .= ", s.rowid as socid, s.nom as name, s.name_alias";
355$sql .= ", s.code_client, s.code_compta, s.client";
356$sql .= ", s.code_fournisseur, s.code_compta_fournisseur, s.fournisseur";
357$sql .= ", s.logo, s.email, s.entity";
358$sql .= ", s.canvas, s.status";
359$sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
360$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s on p.fk_soc = s.rowid";
361$sql .= " WHERE p.entity IN (".getEntity('project').")";
362$sql .= " AND p.fk_statut = 1";
363if ($mine || !$user->hasRight('projet', 'all', 'lire')) {
364 $sql .= " AND p.rowid IN (".$db->sanitize($projectsListId).")"; // If we have this test true, it also means projectset is not 2
365}
366if ($socid > 0) {
367 $sql .= " AND (p.fk_soc IS NULL OR p.fk_soc = 0 OR p.fk_soc = ".((int) $socid).")";
368}
369$sql .= " GROUP BY s.rowid, s.nom, s.name_alias, s.code_client, s.code_compta, s.client, s.code_fournisseur, s.code_compta_fournisseur, s.fournisseur, s.logo, s.email, s.entity, s.canvas, s.status";
370$sql .= $db->order($sortfield, $sortorder);
371//$sql .= $db->plimit($max + 1, 0);
372
373$resql = $db->query($sql);
374if ($resql) {
375 $num = $db->num_rows($resql);
376 $i = 0;
377 $othernb = 0;
378
379 if ($num) {
380 // Open project per thirdparty
381 print '<div class="div-table-responsive-no-min">';
382 print '<table class="noborder centpercent">';
383 print '<tr class="liste_titre">';
384 print_liste_field_titre("OpenedProjectsByThirdparties", $_SERVER["PHP_SELF"], "", "", "", '', $sortfield, $sortorder);
385 print_liste_field_titre("Number", $_SERVER["PHP_SELF"], "nb", "", "", '', $sortfield, $sortorder, 'right ');
386 print "</tr>\n";
387 }
388
389 while ($i < $num) {
390 $obj = $db->fetch_object($resql);
391
392 if ($i >= $max) {
393 $othernb += $obj->nb;
394 $i++;
395 continue;
396 }
397
398 print '<tr class="oddeven">';
399 print '<td class="nowraponall tdoverflowmax100">';
400 if ($obj->socid > 0) {
401 $companystatic->id = $obj->socid;
402 $companystatic->name = $obj->name;
403 $companystatic->name_alias = $obj->name_alias;
404 $companystatic->code_client = $obj->code_client;
405 $companystatic->code_compta = $obj->code_compta;
406 $companystatic->code_compta_client = $obj->code_compta;
407 $companystatic->client = $obj->client;
408 $companystatic->code_fournisseur = $obj->code_fournisseur;
409 $companystatic->code_compta_fournisseur = $obj->code_compta_fournisseur;
410 $companystatic->fournisseur = $obj->fournisseur;
411 $companystatic->logo = $obj->logo;
412 $companystatic->email = $obj->email;
413 $companystatic->entity = $obj->entity;
414 $companystatic->canvas = $obj->canvas;
415 $companystatic->status = $obj->status;
416
417 print $companystatic->getNomUrl(1);
418 } else {
419 print $langs->trans("OthersNotLinkedToThirdParty");
420 }
421 print '</td>';
422 print '<td class="right">';
423 if ($obj->socid) {
424 print '<a href="'.DOL_URL_ROOT.'/projet/list.php?socid='.$obj->socid.'&search_status=1">'.$obj->nb.'</a>';
425 } else {
426 print '<a href="'.DOL_URL_ROOT.'/projet/list.php?search_societe='.urlencode('^$').'&search_status=1">'.$obj->nb.'</a>';
427 }
428 print '</td>';
429 print "</tr>\n";
430
431 $i++;
432 }
433 if ($othernb) {
434 print '<tr class="oddeven">';
435 print '<td class="nowrap">';
436 print '<span class="opacitymedium">'.$langs->trans("More").'...</span>';
437 print '</td>';
438 print '<td class="nowrap right">';
439 print $othernb;
440 print '</td>';
441 print "</tr>\n";
442 }
443
444 if ($num) {
445 print "</table>";
446 print '</div>';
447 }
448
449 $db->free($resql);
450} else {
452}
453
454if ((!getDolGlobalInt('PROJECT_USE_OPPORTUNITIES') || getDolGlobalInt('PROJECT_SHOW_OPEN_PROJECTS_LIST_ON_PROJECT_AREA')) && !getDolGlobalInt('PROJECT_HIDE_OPEN_PROJECTS_LIST_ON_PROJECT_AREA')) {
455 // This list is surely very long and useless when we are using opportunities, so we hide it for this use case, but we allow to show it if
456 // we really want it and to allow interface backward compatibility.
457 print '<br>';
458
459 print_projecttasks_array($db, $form, $socid, $projectsListId, 0, 1, $listofoppstatus, array());
460}
461
462print '</div></div></div>';
463
464$parameters = array('user' => $user);
465$reshook = $hookmanager->executeHooks('dashboardProjects', $parameters, $projectstatic); // Note that $action and $object may have been modified by hook
466
467// End of page
468llxFooter();
469$db->close();
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:476
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to offer components to list and upload files.
Class to manage generation of HTML components Only common components must be here.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_set_user_param($db, $conf, &$user, $tab, $entity=-1)
Save personal parameter.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
print_liste_field_titre($name, $file="", $field="", $begin="", $param="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolPrintHTML($s, $allowiframe=0, $moreallowedtags=array())
Return a string (that can be on several lines) ready to be output on a HTML page.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
print_projecttasks_array($db, $form, $socid, $projectsListId, $mytasks=0, $status=-1, $listofoppstatus=array(), $hiddenfields=array(), $max=0)
Return HTML table with list of projects and number of opened tasks.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.