2 // +----------------------------------------------------------------------+
3 // | Anuko Time Tracker
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
6 // +----------------------------------------------------------------------+
7 // | LIBERAL FREEWARE LICENSE: This source code document may be used
8 // | by anyone for any purpose, and freely redistributed alone or in
9 // | combination with other software, provided that the license is obeyed.
11 // | There are only two ways to violate the license:
13 // | 1. To redistribute this code in source form, with the copyright
14 // | notice or license removed or altered. (Distributing in compiled
15 // | forms without embedded copyright notices is permitted).
17 // | 2. To redistribute modified versions of this code in *any* form
18 // | that bears insufficient indications that the modifications are
19 // | not the work of the original author(s).
21 // | This license applies to this document only, not any other software
22 // | that it may be combined with.
24 // +----------------------------------------------------------------------+
26 // | https://www.anuko.com/time_tracker/credits.htm
27 // +----------------------------------------------------------------------+
29 import('ttClientHelper');
30 import('DateAndTime');
32 import('ttTimeHelper');
34 require_once(dirname(__FILE__).'/../../plugins/CustomFields.class.php');
36 // Class ttReportHelper is used for help with reports.
37 class ttReportHelper {
39 // getWhere prepares a WHERE clause for a report query.
40 static function getWhere($bean) {
43 // Prepare dropdown parts.
45 if ($bean->getAttribute('client'))
46 $dropdown_parts .= ' and l.client_id = '.$bean->getAttribute('client');
47 elseif ($user->isClient() && $user->client_id)
48 $dropdown_parts .= ' and l.client_id = '.$user->client_id;
49 if ($bean->getAttribute('option')) $dropdown_parts .= ' and l.id in(select log_id from tt_custom_field_log where status = 1 and option_id = '.$bean->getAttribute('option').')';
50 if ($bean->getAttribute('project')) $dropdown_parts .= ' and l.project_id = '.$bean->getAttribute('project');
51 if ($bean->getAttribute('task')) $dropdown_parts .= ' and l.task_id = '.$bean->getAttribute('task');
52 if ($bean->getAttribute('include_records')=='1') $dropdown_parts .= ' and l.billable = 1';
53 if ($bean->getAttribute('include_records')=='2') $dropdown_parts .= ' and l.billable = 0';
54 if ($bean->getAttribute('invoice')=='1') $dropdown_parts .= ' and l.invoice_id is not NULL';
55 if ($bean->getAttribute('invoice')=='2') $dropdown_parts .= ' and l.invoice_id is NULL';
57 // Prepare user list part.
59 if (($user->canManageTeam() || $user->isClient()) && is_array($bean->getAttribute('users')))
60 $userlist = join(',', $bean->getAttribute('users'));
61 // Prepare sql query part for user list.
62 $user_list_part = null;
63 if ($user->canManageTeam() || $user->isClient())
64 $user_list_part = " and l.user_id in ($userlist)";
66 $user_list_part = " and l.user_id = ".$user->id;
68 // Prepare sql query part for where.
69 if ($bean->getAttribute('period'))
70 $period = new Period($bean->getAttribute('period'), new DateAndTime($user->date_format));
72 $period = new Period();
74 new DateAndTime($user->date_format, $bean->getAttribute('start_date')),
75 new DateAndTime($user->date_format, $bean->getAttribute('end_date')));
77 $where = " where l.status = 1 and l.date >= '".$period->getStartDate(DB_DATEFORMAT)."' and l.date <= '".$period->getEndDate(DB_DATEFORMAT)."'".
78 " $user_list_part $dropdown_parts";
82 // getFavWhere prepares a WHERE clause for a favorite report query.
83 static function getFavWhere($report) {
86 // Prepare dropdown parts.
88 if ($report['client_id'])
89 $dropdown_parts .= ' and l.client_id = '.$report['client_id'];
90 elseif ($user->isClient() && $user->client_id)
91 $dropdown_parts .= ' and l.client_id = '.$user->client_id;
92 if ($report['cf_1_option_id']) $dropdown_parts .= ' and l.id in(select log_id from tt_custom_field_log where status = 1 and option_id = '.$report['cf_1_option_id'].')';
93 if ($report['project_id']) $dropdown_parts .= ' and l.project_id = '.$report['project_id'];
94 if ($report['task_id']) $dropdown_parts .= ' and l.task_id = '.$report['task_id'];
95 if ($report['billable']=='1') $dropdown_parts .= ' and l.billable = 1';
96 if ($report['billable']=='2') $dropdown_parts .= ' and l.billable = 0';
97 if ($report['invoice']=='1') $dropdown_parts .= ' and l.invoice_id is not NULL';
98 if ($report['invoice']=='2') $dropdown_parts .= ' and l.invoice_id is NULL';
100 // Prepare user list part.
102 if (($user->canManageTeam() || $user->isClient())) {
103 if ($report['users'])
104 $userlist = $report['users'];
106 $active_users = ttTeamHelper::getActiveUsers();
107 foreach ($active_users as $single_user)
108 $users[] = $single_user['id'];
109 $userlist = join(',', $users);
112 // Prepare sql query part for user list.
113 $user_list_part = null;
114 if ($user->canManageTeam() || $user->isClient())
115 $user_list_part = " and l.user_id in ($userlist)";
117 $user_list_part = " and l.user_id = ".$user->id;
119 // Prepare sql query part for where.
120 if ($report['period'])
121 $period = new Period($report['period'], new DateAndTime($user->date_format));
123 $period = new Period();
125 new DateAndTime($user->date_format, $report['period_start']),
126 new DateAndTime($user->date_format, $report['period_end']));
128 $where = " where l.status = 1 and l.date >= '".$period->getStartDate(DB_DATEFORMAT)."' and l.date <= '".$period->getEndDate(DB_DATEFORMAT)."'".
129 " $user_list_part $dropdown_parts";
133 // getExpenseWhere prepares WHERE clause for expenses query in a report.
134 static function getExpenseWhere($bean) {
137 // Prepare dropdown parts.
138 $dropdown_parts = '';
139 if ($bean->getAttribute('client'))
140 $dropdown_parts .= ' and ei.client_id = '.$bean->getAttribute('client');
141 elseif ($user->isClient() && $user->client_id)
142 $dropdown_parts .= ' and ei.client_id = '.$user->client_id;
143 if ($bean->getAttribute('project')) $dropdown_parts .= ' and ei.project_id = '.$bean->getAttribute('project');
144 if ($bean->getAttribute('invoice')=='1') $dropdown_parts .= ' and ei.invoice_id is not NULL';
145 if ($bean->getAttribute('invoice')=='2') $dropdown_parts .= ' and ei.invoice_id is NULL';
147 // Prepare user list part.
149 if (($user->canManageTeam() || $user->isClient()) && is_array($bean->getAttribute('users')))
150 $userlist = join(',', $bean->getAttribute('users'));
151 // Prepare sql query part for user list.
152 $user_list_part = null;
153 if ($user->canManageTeam() || $user->isClient())
154 $user_list_part = " and ei.user_id in ($userlist)";
156 $user_list_part = " and ei.user_id = ".$user->id;
158 // Prepare sql query part for where.
159 if ($bean->getAttribute('period'))
160 $period = new Period($bean->getAttribute('period'), new DateAndTime($user->date_format));
162 $period = new Period();
164 new DateAndTime($user->date_format, $bean->getAttribute('start_date')),
165 new DateAndTime($user->date_format, $bean->getAttribute('end_date')));
167 $where = " where ei.status = 1 and ei.date >= '".$period->getStartDate(DB_DATEFORMAT)."' and ei.date <= '".$period->getEndDate(DB_DATEFORMAT)."'".
168 " $user_list_part $dropdown_parts";
172 // getFavExpenseWhere prepares a WHERE clause for expenses query in a favorite report.
173 static function getFavExpenseWhere($report) {
176 // Prepare dropdown parts.
177 $dropdown_parts = '';
178 if ($report['client_id'])
179 $dropdown_parts .= ' and ei.client_id = '.$report['client_id'];
180 elseif ($user->isClient() && $user->client_id)
181 $dropdown_parts .= ' and ei.client_id = '.$user->client_id;
182 if ($report['project_id']) $dropdown_parts .= ' and ei.project_id = '.$report['project_id'];
183 if ($report['invoice']=='1') $dropdown_parts .= ' and ei.invoice_id is not NULL';
184 if ($report['invoice']=='2') $dropdown_parts .= ' and ei.invoice_id is NULL';
186 // Prepare user list part.
188 if (($user->canManageTeam() || $user->isClient())) {
189 if ($report['users'])
190 $userlist = $report['users'];
192 $active_users = ttTeamHelper::getActiveUsers();
193 foreach ($active_users as $single_user)
194 $users[] = $single_user['id'];
195 $userlist = join(',', $users);
198 // Prepare sql query part for user list.
199 $user_list_part = null;
200 if ($user->canManageTeam() || $user->isClient())
201 $user_list_part = " and ei.user_id in ($userlist)";
203 $user_list_part = " and ei.user_id = ".$user->id;
205 // Prepare sql query part for where.
206 if ($report['period'])
207 $period = new Period($report['period'], new DateAndTime($user->date_format));
209 $period = new Period();
211 new DateAndTime($user->date_format, $report['period_start']),
212 new DateAndTime($user->date_format, $report['period_end']));
214 $where = " where ei.status = 1 and ei.date >= '".$period->getStartDate(DB_DATEFORMAT)."' and ei.date <= '".$period->getEndDate(DB_DATEFORMAT)."'".
215 " $user_list_part $dropdown_parts";
219 // getItems retrieves all items associated with a report.
220 // It combines tt_log and tt_expense_items in one array for presentation in one table using mysql union all.
221 // Expense items use the "note" field for item name.
222 static function getItems($bean) {
224 $mdb2 = getConnection();
226 $group_by_option = $bean->getAttribute('group_by');
227 $convertTo12Hour = ('%I:%M %p' == $user->time_format) && ($bean->getAttribute('chstart') || $bean->getAttribute('chfinish'));
229 // Prepare a query for time items in tt_log table.
230 $fields = array(); // An array of fields for database query.
231 array_push($fields, 'l.id as id');
232 array_push($fields, '1 as type'); // Type 1 is for tt_log entries.
233 array_push($fields, 'l.date as date');
234 if($user->canManageTeam() || $user->isClient())
235 array_push($fields, 'u.name as user');
236 // Add client name if it is selected.
237 if ($bean->getAttribute('chclient') || 'client' == $group_by_option)
238 array_push($fields, 'c.name as client');
239 // Add project name if it is selected.
240 if ($bean->getAttribute('chproject') || 'project' == $group_by_option)
241 array_push($fields, 'p.name as project');
242 // Add task name if it is selected.
243 if ($bean->getAttribute('chtask') || 'task' == $group_by_option)
244 array_push($fields, 't.name as task');
246 $include_cf_1 = $bean->getAttribute('chcf_1') || 'cf_1' == $group_by_option;
248 $custom_fields = new CustomFields($user->team_id);
249 $cf_1_type = $custom_fields->fields[0]['type'];
250 if ($cf_1_type == CustomFields::TYPE_TEXT) {
251 array_push($fields, 'cfl.value as cf_1');
252 } elseif ($cf_1_type == CustomFields::TYPE_DROPDOWN) {
253 array_push($fields, 'cfo.value as cf_1');
257 if ($bean->getAttribute('chstart')) {
258 array_push($fields, "l.start as unformatted_start");
259 array_push($fields, "TIME_FORMAT(l.start, '%k:%i') as start");
262 if ($bean->getAttribute('chfinish'))
263 array_push($fields, "TIME_FORMAT(sec_to_time(time_to_sec(l.start) + time_to_sec(l.duration)), '%k:%i') as finish");
265 if ($bean->getAttribute('chduration'))
266 array_push($fields, "TIME_FORMAT(l.duration, '%k:%i') as duration");
268 if ($bean->getAttribute('chnote'))
269 array_push($fields, 'l.comment as note');
271 $includeCost = $bean->getAttribute('chcost');
273 if (MODE_TIME == $user->tracking_mode)
274 array_push($fields, "cast(l.billable * coalesce(u.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2)) as cost"); // Use default user rate.
276 array_push($fields, "cast(l.billable * coalesce(upb.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2)) as cost"); // Use project rate for user.
277 array_push($fields, "null as expense");
280 if ($user->canManageTeam() && $bean->getAttribute('chpaid'))
281 array_push($fields, 'l.paid as paid');
283 // Add invoice name if it is selected.
284 if (($user->canManageTeam() || $user->isClient()) && $bean->getAttribute('chinvoice'))
285 array_push($fields, 'i.name as invoice');
287 // Prepare sql query part for left joins.
289 if ($bean->getAttribute('chclient') || 'client' == $group_by_option)
290 $left_joins .= " left join tt_clients c on (c.id = l.client_id)";
291 if (($user->canManageTeam() || $user->isClient()) && $bean->getAttribute('chinvoice'))
292 $left_joins .= " left join tt_invoices i on (i.id = l.invoice_id and i.status = 1)";
293 if ($user->canManageTeam() || $user->isClient() || $user->isPluginEnabled('ex'))
294 $left_joins .= " left join tt_users u on (u.id = l.user_id)";
295 if ($bean->getAttribute('chproject') || 'project' == $group_by_option)
296 $left_joins .= " left join tt_projects p on (p.id = l.project_id)";
297 if ($bean->getAttribute('chtask') || 'task' == $group_by_option)
298 $left_joins .= " left join tt_tasks t on (t.id = l.task_id)";
300 if ($cf_1_type == CustomFields::TYPE_TEXT)
301 $left_joins .= " left join tt_custom_field_log cfl on (l.id = cfl.log_id and cfl.status = 1)";
302 elseif ($cf_1_type == CustomFields::TYPE_DROPDOWN) {
303 $left_joins .= " left join tt_custom_field_log cfl on (l.id = cfl.log_id and cfl.status = 1)".
304 " left join tt_custom_field_options cfo on (cfl.option_id = cfo.id)";
307 if ($includeCost && MODE_TIME != $user->tracking_mode)
308 $left_joins .= " left join tt_user_project_binds upb on (l.user_id = upb.user_id and l.project_id = upb.project_id)";
310 $where = ttReportHelper::getWhere($bean);
312 // Construct sql query for tt_log items.
313 $sql = "select ".join(', ', $fields)." from tt_log l $left_joins $where";
314 // If we don't have expense items (such as when the Expenses plugin is desabled), the above is all sql we need,
315 // with an exception of sorting part, that is added in the end.
317 // However, when we have expenses, we need to do a union with a separate query for expense items from tt_expense_items table.
318 if ($bean->getAttribute('chcost') && $user->isPluginEnabled('ex')) { // if ex(penses) plugin is enabled
320 $fields = array(); // An array of fields for database query.
321 array_push($fields, 'ei.id');
322 array_push($fields, '2 as type'); // Type 2 is for tt_expense_items entries.
323 array_push($fields, 'ei.date');
324 if($user->canManageTeam() || $user->isClient())
325 array_push($fields, 'u.name as user');
326 // Add client name if it is selected.
327 if ($bean->getAttribute('chclient') || 'client' == $group_by_option)
328 array_push($fields, 'c.name as client');
329 // Add project name if it is selected.
330 if ($bean->getAttribute('chproject') || 'project' == $group_by_option)
331 array_push($fields, 'p.name as project');
332 if ($bean->getAttribute('chtask') || 'task' == $group_by_option)
333 array_push($fields, 'null'); // null for task name. We need to match column count for union.
334 if ($bean->getAttribute('chcf_1') || 'cf_1' == $group_by_option)
335 array_push($fields, 'null'); // null for cf_1.
336 if ($bean->getAttribute('chstart')) {
337 array_push($fields, 'null'); // null for unformatted_start.
338 array_push($fields, 'null'); // null for start.
340 if ($bean->getAttribute('chfinish'))
341 array_push($fields, 'null'); // null for finish.
342 if ($bean->getAttribute('chduration'))
343 array_push($fields, 'null'); // null for duration.
344 // Use the note field to print item name.
345 if ($bean->getAttribute('chnote'))
346 array_push($fields, 'ei.name as note');
347 array_push($fields, 'ei.cost as cost');
348 array_push($fields, 'ei.cost as expense');
349 // Add invoice name if it is selected.
350 if (($user->canManageTeam() || $user->isClient()) && $bean->getAttribute('chinvoice'))
351 array_push($fields, 'i.name as invoice');
353 // Prepare sql query part for left joins.
355 if ($user->canManageTeam() || $user->isClient())
356 $left_joins .= " left join tt_users u on (u.id = ei.user_id)";
357 if ($bean->getAttribute('chclient') || 'client' == $group_by_option)
358 $left_joins .= " left join tt_clients c on (c.id = ei.client_id)";
359 if ($bean->getAttribute('chproject') || 'project' == $group_by_option)
360 $left_joins .= " left join tt_projects p on (p.id = ei.project_id)";
361 if (($user->canManageTeam() || $user->isClient()) && $bean->getAttribute('chinvoice'))
362 $left_joins .= " left join tt_invoices i on (i.id = ei.invoice_id and i.status = 1)";
364 $where = ttReportHelper::getExpenseWhere($bean);
366 // Construct sql query for expense items.
367 $sql_for_expense_items = "select ".join(', ', $fields)." from tt_expense_items ei $left_joins $where";
369 // Construct a union.
370 $sql = "($sql) union all ($sql_for_expense_items)";
373 // Determine sort part.
374 $sort_part = ' order by ';
375 if ('no_grouping' == $group_by_option || 'date' == $group_by_option)
376 $sort_part .= 'date';
378 $sort_part .= $group_by_option.', date';
379 if (($user->canManageTeam() || $user->isClient()) && is_array($bean->getAttribute('users')) && 'user' != $group_by_option)
380 $sort_part .= ', user, type';
381 if ($bean->getAttribute('chstart'))
382 $sort_part .= ', unformatted_start';
383 $sort_part .= ', id';
386 // By now we are ready with sql.
388 // Obtain items for report.
389 $res = $mdb2->query($sql);
390 if (is_a($res, 'PEAR_Error')) die($res->getMessage());
392 while ($val = $res->fetchRow()) {
393 if ($convertTo12Hour) {
394 if($val['start'] != '')
395 $val['start'] = ttTimeHelper::to12HourFormat($val['start']);
396 if($val['finish'] != '')
397 $val['finish'] = ttTimeHelper::to12HourFormat($val['finish']);
399 if (isset($val['cost'])) {
400 if ('.' != $user->decimal_mark)
401 $val['cost'] = str_replace('.', $user->decimal_mark, $val['cost']);
403 if (isset($val['expense'])) {
404 if ('.' != $user->decimal_mark)
405 $val['expense'] = str_replace('.', $user->decimal_mark, $val['expense']);
407 if ('no_grouping' != $group_by_option) {
408 $val['grouped_by'] = $val[$group_by_option];
409 if ('date' == $group_by_option) {
410 // This is needed to get the date in user date format.
411 $o_date = new DateAndTime(DB_DATEFORMAT, $val['grouped_by']);
412 $val['grouped_by'] = $o_date->toString($user->date_format);
417 // This is needed to get the date in user date format.
418 $o_date = new DateAndTime(DB_DATEFORMAT, $val['date']);
419 $val['date'] = $o_date->toString($user->date_format);
423 $report_items[] = $row;
426 return $report_items;
429 // getFavItems retrieves all items associated with a favorite report.
430 // It combines tt_log and tt_expense_items in one array for presentation in one table using mysql union all.
431 // Expense items use the "note" field for item name.
432 static function getFavItems($report) {
434 $mdb2 = getConnection();
436 $group_by_option = $report['group_by'];
437 $convertTo12Hour = ('%I:%M %p' == $user->time_format) && ($report['show_start'] || $report['show_end']);
439 // Prepare a query for time items in tt_log table.
440 $fields = array(); // An array of fields for database query.
441 array_push($fields, 'l.id as id');
442 array_push($fields, '1 as type'); // Type 1 is for tt_log entries.
443 array_push($fields, 'l.date as date');
444 if($user->canManageTeam() || $user->isClient())
445 array_push($fields, 'u.name as user');
446 // Add client name if it is selected.
447 if ($report['show_client'] || 'client' == $group_by_option)
448 array_push($fields, 'c.name as client');
449 // Add project name if it is selected.
450 if ($report['show_project'] || 'project' == $group_by_option)
451 array_push($fields, 'p.name as project');
452 // Add task name if it is selected.
453 if ($report['show_task'] || 'task' == $group_by_option)
454 array_push($fields, 't.name as task');
456 $include_cf_1 = $report['show_custom_field_1'] || 'cf_1' == $group_by_option;
458 $custom_fields = new CustomFields($user->team_id);
459 $cf_1_type = $custom_fields->fields[0]['type'];
460 if ($cf_1_type == CustomFields::TYPE_TEXT) {
461 array_push($fields, 'cfl.value as cf_1');
462 } elseif ($cf_1_type == CustomFields::TYPE_DROPDOWN) {
463 array_push($fields, 'cfo.value as cf_1');
467 if ($report['show_start']) {
468 array_push($fields, "l.start as unformatted_start");
469 array_push($fields, "TIME_FORMAT(l.start, '%k:%i') as start");
472 if ($report['show_end'])
473 array_push($fields, "TIME_FORMAT(sec_to_time(time_to_sec(l.start) + time_to_sec(l.duration)), '%k:%i') as finish");
475 if ($report['show_duration'])
476 array_push($fields, "TIME_FORMAT(l.duration, '%k:%i') as duration");
478 if ($report['show_note'])
479 array_push($fields, 'l.comment as note');
481 $includeCost = $report['show_cost'];
483 if (MODE_TIME == $user->tracking_mode)
484 array_push($fields, "cast(l.billable * coalesce(u.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2)) as cost"); // Use default user rate.
486 array_push($fields, "cast(l.billable * coalesce(upb.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2)) as cost"); // Use project rate for user.
487 array_push($fields, "null as expense");
489 // Add invoice name if it is selected.
490 if (($user->canManageTeam() || $user->isClient()) && $report['show_invoice'])
491 array_push($fields, 'i.name as invoice');
493 // Prepare sql query part for left joins.
495 if ($report['show_client'] || 'client' == $group_by_option)
496 $left_joins .= " left join tt_clients c on (c.id = l.client_id)";
497 if (($user->canManageTeam() || $user->isClient()) && $report['show_invoice'])
498 $left_joins .= " left join tt_invoices i on (i.id = l.invoice_id and i.status = 1)";
499 if ($user->canManageTeam() || $user->isClient() || $user->isPluginEnabled('ex'))
500 $left_joins .= " left join tt_users u on (u.id = l.user_id)";
501 if ($report['show_project'] || 'project' == $group_by_option)
502 $left_joins .= " left join tt_projects p on (p.id = l.project_id)";
503 if ($report['show_task'] || 'task' == $group_by_option)
504 $left_joins .= " left join tt_tasks t on (t.id = l.task_id)";
506 if ($cf_1_type == CustomFields::TYPE_TEXT)
507 $left_joins .= " left join tt_custom_field_log cfl on (l.id = cfl.log_id and cfl.status = 1)";
508 elseif ($cf_1_type == CustomFields::TYPE_DROPDOWN) {
509 $left_joins .= " left join tt_custom_field_log cfl on (l.id = cfl.log_id and cfl.status = 1)".
510 " left join tt_custom_field_options cfo on (cfl.option_id = cfo.id)";
513 if ($includeCost && MODE_TIME != $user->tracking_mode)
514 $left_joins .= " left join tt_user_project_binds upb on (l.user_id = upb.user_id and l.project_id = upb.project_id)";
516 $where = ttReportHelper::getFavWhere($report);
518 // Construct sql query for tt_log items.
519 $sql = "select ".join(', ', $fields)." from tt_log l $left_joins $where";
520 // If we don't have expense items (such as when the Expenses plugin is desabled), the above is all sql we need,
521 // with an exception of sorting part, that is added in the end.
523 // However, when we have expenses, we need to do a union with a separate query for expense items from tt_expense_items table.
524 if ($report['show_cost'] && $user->isPluginEnabled('ex')) { // if ex(penses) plugin is enabled
526 $fields = array(); // An array of fields for database query.
527 array_push($fields, 'ei.id');
528 array_push($fields, '2 as type'); // Type 2 is for tt_expense_items entries.
529 array_push($fields, 'ei.date');
530 if($user->canManageTeam() || $user->isClient())
531 array_push($fields, 'u.name as user');
532 // Add client name if it is selected.
533 if ($report['show_client'] || 'client' == $group_by_option)
534 array_push($fields, 'c.name as client');
535 // Add project name if it is selected.
536 if ($report['show_project'] || 'project' == $group_by_option)
537 array_push($fields, 'p.name as project');
538 if ($report['show_task'] || 'task' == $group_by_option)
539 array_push($fields, 'null'); // null for task name. We need to match column count for union.
540 if ($report['show_custom_field_1'] || 'cf_1' == $group_by_option)
541 array_push($fields, 'null'); // null for cf_1.
542 if ($report['show_start']) {
543 array_push($fields, 'null'); // null for unformatted_start.
544 array_push($fields, 'null'); // null for start.
546 if ($report['show_end'])
547 array_push($fields, 'null'); // null for finish.
548 if ($report['show_duration'])
549 array_push($fields, 'null'); // null for duration.
550 // Use the note field to print item name.
551 if ($report['show_note'])
552 array_push($fields, 'ei.name as note');
553 array_push($fields, 'ei.cost as cost');
554 array_push($fields, 'ei.cost as expense');
555 // Add invoice name if it is selected.
556 if (($user->canManageTeam() || $user->isClient()) && $report['show_invoice'])
557 array_push($fields, 'i.name as invoice');
559 // Prepare sql query part for left joins.
561 if ($user->canManageTeam() || $user->isClient())
562 $left_joins .= " left join tt_users u on (u.id = ei.user_id)";
563 if ($report['show_client'] || 'client' == $group_by_option)
564 $left_joins .= " left join tt_clients c on (c.id = ei.client_id)";
565 if ($report['show_project'] || 'project' == $group_by_option)
566 $left_joins .= " left join tt_projects p on (p.id = ei.project_id)";
567 if (($user->canManageTeam() || $user->isClient()) && $report['show_invoice'])
568 $left_joins .= " left join tt_invoices i on (i.id = ei.invoice_id and i.status = 1)";
570 $where = ttReportHelper::getFavExpenseWhere($report);
572 // Construct sql query for expense items.
573 $sql_for_expense_items = "select ".join(', ', $fields)." from tt_expense_items ei $left_joins $where";
575 // Construct a union.
576 $sql = "($sql) union all ($sql_for_expense_items)";
579 // Determine sort part.
580 $sort_part = ' order by ';
581 if ($group_by_option == null || 'no_grouping' == $group_by_option || 'date' == $group_by_option) // TODO: fix DB for NULL values in group_by field.
582 $sort_part .= 'date';
584 $sort_part .= $group_by_option.', date';
585 if (($user->canManageTeam() || $user->isClient()) /*&& is_array($bean->getAttribute('users'))*/ && 'user' != $group_by_option)
586 $sort_part .= ', user, type';
587 if ($report['show_start'])
588 $sort_part .= ', unformatted_start';
589 $sort_part .= ', id';
592 // By now we are ready with sql.
594 // Obtain items for report.
595 $res = $mdb2->query($sql);
596 if (is_a($res, 'PEAR_Error')) die($res->getMessage());
598 while ($val = $res->fetchRow()) {
599 if ($convertTo12Hour) {
600 if($val['start'] != '')
601 $val['start'] = ttTimeHelper::to12HourFormat($val['start']);
602 if($val['finish'] != '')
603 $val['finish'] = ttTimeHelper::to12HourFormat($val['finish']);
605 if (isset($val['cost'])) {
606 if ('.' != $user->decimal_mark)
607 $val['cost'] = str_replace('.', $user->decimal_mark, $val['cost']);
609 if (isset($val['expense'])) {
610 if ('.' != $user->decimal_mark)
611 $val['expense'] = str_replace('.', $user->decimal_mark, $val['expense']);
613 if ('no_grouping' != $group_by_option) {
614 $val['grouped_by'] = $val[$group_by_option];
615 if ('date' == $group_by_option) {
616 // This is needed to get the date in user date format.
617 $o_date = new DateAndTime(DB_DATEFORMAT, $val['grouped_by']);
618 $val['grouped_by'] = $o_date->toString($user->date_format);
623 // This is needed to get the date in user date format.
624 $o_date = new DateAndTime(DB_DATEFORMAT, $val['date']);
625 $val['date'] = $o_date->toString($user->date_format);
629 $report_items[] = $row;
632 return $report_items;
635 // getSubtotals calculates report items subtotals when a report is grouped by.
636 // Without expenses, it's a simple select with group by.
637 // With expenses, it becomes a select with group by from a combined set of records obtained with "union all".
638 static function getSubtotals($bean) {
641 $group_by_option = $bean->getAttribute('group_by');
642 if ('no_grouping' == $group_by_option) return null;
644 $mdb2 = getConnection();
646 // Start with sql to obtain subtotals for time items. This simple sql will be used when we have no expenses.
648 // Determine group by field and a required join.
649 switch ($group_by_option) {
651 $group_field = 'l.date';
655 $group_field = 'u.name';
656 $group_join = 'left join tt_users u on (l.user_id = u.id) ';
659 $group_field = 'c.name';
660 $group_join = 'left join tt_clients c on (l.client_id = c.id) ';
663 $group_field = 'p.name';
664 $group_join = 'left join tt_projects p on (l.project_id = p.id) ';
667 $group_field = 't.name';
668 $group_join = 'left join tt_tasks t on (l.task_id = t.id) ';
671 $group_field = 'cfo.value';
672 $custom_fields = new CustomFields($user->team_id);
673 if ($custom_fields->fields[0]['type'] == CustomFields::TYPE_TEXT)
674 $group_join = 'left join tt_custom_field_log cfl on (l.id = cfl.log_id and cfl.status = 1) left join tt_custom_field_options cfo on (cfl.value = cfo.id) ';
675 elseif ($custom_fields->fields[0]['type'] == CustomFields::TYPE_DROPDOWN)
676 $group_join = 'left join tt_custom_field_log cfl on (l.id = cfl.log_id and cfl.status = 1) left join tt_custom_field_options cfo on (cfl.option_id = cfo.id) ';
680 $where = ttReportHelper::getWhere($bean);
681 if ($bean->getAttribute('chcost')) {
682 if (MODE_TIME == $user->tracking_mode) {
683 if ($group_by_option != 'user')
684 $left_join = 'left join tt_users u on (l.user_id = u.id)';
685 $sql = "select $group_field as group_field, sum(time_to_sec(l.duration)) as time,
686 sum(cast(l.billable * coalesce(u.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10, 2))) as cost,
687 null as expenses from tt_log l
688 $group_join $left_join $where group by $group_field";
690 // If we are including cost and tracking projects, our query (the same as above) needs to join the tt_user_project_binds table.
691 $sql = "select $group_field as group_field, sum(time_to_sec(l.duration)) as time,
692 sum(cast(l.billable * coalesce(upb.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2))) as cost,
693 null as expenses from tt_log l
695 left join tt_user_project_binds upb on (l.user_id = upb.user_id and l.project_id = upb.project_id) $where group by $group_field";
698 $sql = "select $group_field as group_field, sum(time_to_sec(l.duration)) as time, null as expenses from tt_log l
699 $group_join $where group by $group_field";
701 // By now we have sql for time items.
703 // However, when we have expenses, we need to do a union with a separate query for expense items from tt_expense_items table.
704 if ($bean->getAttribute('chcost') && $user->isPluginEnabled('ex')) { // if ex(penses) plugin is enabled
706 // Determine group by field and a required join.
708 $group_field = 'null';
709 switch ($group_by_option) {
711 $group_field = 'ei.date';
715 $group_field = 'u.name';
716 $group_join = 'left join tt_users u on (ei.user_id = u.id) ';
719 $group_field = 'c.name';
720 $group_join = 'left join tt_clients c on (ei.client_id = c.id) ';
723 $group_field = 'p.name';
724 $group_join = 'left join tt_projects p on (ei.project_id = p.id) ';
728 $where = ttReportHelper::getExpenseWhere($bean);
729 $sql_for_expenses = "select $group_field as group_field, null as time, sum(ei.cost) as cost, sum(ei.cost) as expenses from tt_expense_items ei
731 // Add a "group by" clause if we are grouping.
732 if ('null' != $group_field) $sql_for_expenses .= " group by $group_field";
734 // Create a combined query.
735 $sql = "select group_field, sum(time) as time, sum(cost) as cost, sum(expenses) as expenses from (($sql) union all ($sql_for_expenses)) t group by group_field";
739 $res = $mdb2->query($sql);
740 if (is_a($res, 'PEAR_Error')) die($res->getMessage());
742 while ($val = $res->fetchRow()) {
743 if ('date' == $group_by_option) {
744 // This is needed to get the date in user date format.
745 $o_date = new DateAndTime(DB_DATEFORMAT, $val['group_field']);
746 $val['group_field'] = $o_date->toString($user->date_format);
749 $time = $val['time'] ? sec_to_time_fmt_hm($val['time']) : null;
750 if ($bean->getAttribute('chcost')) {
751 if ('.' != $user->decimal_mark) {
752 $val['cost'] = str_replace('.', $user->decimal_mark, $val['cost']);
753 $val['expenses'] = str_replace('.', $user->decimal_mark, $val['expenses']);
755 $subtotals[$val['group_field']] = array('name'=>$val['group_field'],'time'=>$time,'cost'=>$val['cost'],'expenses'=>$val['expenses']);
757 $subtotals[$val['group_field']] = array('name'=>$val['group_field'],'time'=>$time);
763 // getFavSubtotals calculates report items subtotals when a favorite report is grouped by.
764 // Without expenses, it's a simple select with group by.
765 // With expenses, it becomes a select with group by from a combined set of records obtained with "union all".
766 static function getFavSubtotals($report) {
769 $group_by_option = $report['group_by'];
770 if ('no_grouping' == $group_by_option) return null;
772 $mdb2 = getConnection();
774 // Start with sql to obtain subtotals for time items. This simple sql will be used when we have no expenses.
776 // Determine group by field and a required join.
777 switch ($group_by_option) {
779 $group_field = 'l.date';
783 $group_field = 'u.name';
784 $group_join = 'left join tt_users u on (l.user_id = u.id) ';
787 $group_field = 'c.name';
788 $group_join = 'left join tt_clients c on (l.client_id = c.id) ';
791 $group_field = 'p.name';
792 $group_join = 'left join tt_projects p on (l.project_id = p.id) ';
795 $group_field = 't.name';
796 $group_join = 'left join tt_tasks t on (l.task_id = t.id) ';
799 $group_field = 'cfo.value';
800 $custom_fields = new CustomFields($user->team_id);
801 if ($custom_fields->fields[0]['type'] == CustomFields::TYPE_TEXT)
802 $group_join = 'left join tt_custom_field_log cfl on (l.id = cfl.log_id and cfl.status = 1) left join tt_custom_field_options cfo on (cfl.value = cfo.id) ';
803 elseif ($custom_fields->fields[0]['type'] == CustomFields::TYPE_DROPDOWN)
804 $group_join = 'left join tt_custom_field_log cfl on (l.id = cfl.log_id and cfl.status = 1) left join tt_custom_field_options cfo on (cfl.option_id = cfo.id) ';
808 $where = ttReportHelper::getFavWhere($report);
809 if ($report['show_cost']) {
810 if (MODE_TIME == $user->tracking_mode) {
811 if ($group_by_option != 'user')
812 $left_join = 'left join tt_users u on (l.user_id = u.id)';
813 $sql = "select $group_field as group_field, sum(time_to_sec(l.duration)) as time,
814 sum(cast(l.billable * coalesce(u.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10, 2))) as cost,
815 null as expenses from tt_log l
816 $group_join $left_join $where group by $group_field";
818 // If we are including cost and tracking projects, our query (the same as above) needs to join the tt_user_project_binds table.
819 $sql = "select $group_field as group_field, sum(time_to_sec(l.duration)) as time,
820 sum(cast(l.billable * coalesce(upb.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2))) as cost,
821 null as expenses from tt_log l
823 left join tt_user_project_binds upb on (l.user_id = upb.user_id and l.project_id = upb.project_id) $where group by $group_field";
826 $sql = "select $group_field as group_field, sum(time_to_sec(l.duration)) as time, null as expenses from tt_log l
827 $group_join $where group by $group_field";
829 // By now we have sql for time items.
831 // However, when we have expenses, we need to do a union with a separate query for expense items from tt_expense_items table.
832 if ($report['show_cost'] && $user->isPluginEnabled('ex')) { // if ex(penses) plugin is enabled
834 // Determine group by field and a required join.
836 $group_field = 'null';
837 switch ($group_by_option) {
839 $group_field = 'ei.date';
843 $group_field = 'u.name';
844 $group_join = 'left join tt_users u on (ei.user_id = u.id) ';
847 $group_field = 'c.name';
848 $group_join = 'left join tt_clients c on (ei.client_id = c.id) ';
851 $group_field = 'p.name';
852 $group_join = 'left join tt_projects p on (ei.project_id = p.id) ';
856 $where = ttReportHelper::getFavExpenseWhere($report);
857 $sql_for_expenses = "select $group_field as group_field, null as time, sum(ei.cost) as cost, sum(ei.cost) as expenses from tt_expense_items ei
859 // Add a "group by" clause if we are grouping.
860 if ('null' != $group_field) $sql_for_expenses .= " group by $group_field";
862 // Create a combined query.
863 $sql = "select group_field, sum(time) as time, sum(cost) as cost, sum(expenses) as expenses from (($sql) union all ($sql_for_expenses)) t group by group_field";
867 $res = $mdb2->query($sql);
868 if (is_a($res, 'PEAR_Error')) die($res->getMessage());
870 while ($val = $res->fetchRow()) {
871 if ('date' == $group_by_option) {
872 // This is needed to get the date in user date format.
873 $o_date = new DateAndTime(DB_DATEFORMAT, $val['group_field']);
874 $val['group_field'] = $o_date->toString($user->date_format);
877 $time = $val['time'] ? sec_to_time_fmt_hm($val['time']) : null;
878 if ($report['show_cost']) {
879 if ('.' != $user->decimal_mark) {
880 $val['cost'] = str_replace('.', $user->decimal_mark, $val['cost']);
881 $val['expenses'] = str_replace('.', $user->decimal_mark, $val['expenses']);
883 $subtotals[$val['group_field']] = array('name'=>$val['group_field'],'time'=>$time,'cost'=>$val['cost'],'expenses'=>$val['expenses']);
885 $subtotals[$val['group_field']] = array('name'=>$val['group_field'],'time'=>$time);
891 // getTotals calculates total hours and cost for all report items.
892 static function getTotals($bean)
896 $mdb2 = getConnection();
898 $where = ttReportHelper::getWhere($bean);
900 // Start with a query for time items.
901 if ($bean->getAttribute('chcost')) {
902 if (MODE_TIME == $user->tracking_mode) {
903 $sql = "select sum(time_to_sec(l.duration)) as time,
904 sum(cast(l.billable * coalesce(u.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2))) as cost,
907 left join tt_users u on (l.user_id = u.id) $where";
909 $sql = "select sum(time_to_sec(l.duration)) as time,
910 sum(cast(l.billable * coalesce(upb.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2))) as cost,
913 left join tt_user_project_binds upb on (l.user_id = upb.user_id and l.project_id = upb.project_id) $where";
916 $sql = "select sum(time_to_sec(l.duration)) as time, null as cost, null as expenses from tt_log l $where";
918 // If we have expenses, query becomes a bit more complex.
919 if ($bean->getAttribute('chcost') && $user->isPluginEnabled('ex')) {
920 $where = ttReportHelper::getExpenseWhere($bean);
921 $sql_for_expenses = "select null as time, sum(cost) as cost, sum(cost) as expenses from tt_expense_items ei $where";
922 // Create a combined query.
923 $sql = "select sum(time) as time, sum(cost) as cost, sum(expenses) as expenses from (($sql) union all ($sql_for_expenses)) t";
927 $res = $mdb2->query($sql);
928 if (is_a($res, 'PEAR_Error')) die($res->getMessage());
930 $val = $res->fetchRow();
931 $total_time = $val['time'] ? sec_to_time_fmt_hm($val['time']) : null;
932 if ($bean->getAttribute('chcost')) {
933 $total_cost = $val['cost'];
934 if (!$total_cost) $total_cost = '0.00';
935 if ('.' != $user->decimal_mark)
936 $total_cost = str_replace('.', $user->decimal_mark, $total_cost);
937 $total_expenses = $val['expenses'];
938 if (!$total_expenses) $total_expenses = '0.00';
939 if ('.' != $user->decimal_mark)
940 $total_expenses = str_replace('.', $user->decimal_mark, $total_expenses);
943 if ($bean->getAttribute('period'))
944 $period = new Period($bean->getAttribute('period'), new DateAndTime($user->date_format));
946 $period = new Period();
948 new DateAndTime($user->date_format, $bean->getAttribute('start_date')),
949 new DateAndTime($user->date_format, $bean->getAttribute('end_date')));
952 $totals['start_date'] = $period->getStartDate();
953 $totals['end_date'] = $period->getEndDate();
954 $totals['time'] = $total_time;
955 $totals['cost'] = $total_cost;
956 $totals['expenses'] = $total_expenses;
961 // getFavTotals calculates total hours and cost for all favorite report items.
962 static function getFavTotals($report)
966 $mdb2 = getConnection();
968 $where = ttReportHelper::getFavWhere($report);
970 // Start with a query for time items.
971 if ($report['show_cost']) {
972 if (MODE_TIME == $user->tracking_mode) {
973 $sql = "select sum(time_to_sec(l.duration)) as time,
974 sum(cast(l.billable * coalesce(u.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2))) as cost,
977 left join tt_users u on (l.user_id = u.id) $where";
979 $sql = "select sum(time_to_sec(l.duration)) as time,
980 sum(cast(l.billable * coalesce(upb.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2))) as cost,
983 left join tt_user_project_binds upb on (l.user_id = upb.user_id and l.project_id = upb.project_id) $where";
986 $sql = "select sum(time_to_sec(l.duration)) as time, null as cost, null as expenses from tt_log l $where";
988 // If we have expenses, query becomes a bit more complex.
989 if ($report['show_cost'] && $user->isPluginEnabled('ex')) {
990 $where = ttReportHelper::getFavExpenseWhere($report);
991 $sql_for_expenses = "select null as time, sum(cost) as cost, sum(cost) as expenses from tt_expense_items ei $where";
992 // Create a combined query.
993 $sql = "select sum(time) as time, sum(cost) as cost, sum(expenses) as expenses from (($sql) union all ($sql_for_expenses)) t";
997 $res = $mdb2->query($sql);
998 if (is_a($res, 'PEAR_Error')) die($res->getMessage());
1000 $val = $res->fetchRow();
1001 $total_time = $val['time'] ? sec_to_time_fmt_hm($val['time']) : null;
1002 if ($report['show_cost']) {
1003 $total_cost = $val['cost'];
1004 if (!$total_cost) $total_cost = '0.00';
1005 if ('.' != $user->decimal_mark)
1006 $total_cost = str_replace('.', $user->decimal_mark, $total_cost);
1007 $total_expenses = $val['expenses'];
1008 if (!$total_expenses) $total_expenses = '0.00';
1009 if ('.' != $user->decimal_mark)
1010 $total_expenses = str_replace('.', $user->decimal_mark, $total_expenses);
1013 if ($report['period'])
1014 $period = new Period($report['period'], new DateAndTime($user->date_format));
1016 $period = new Period();
1018 new DateAndTime($user->date_format, $report['period_start']),
1019 new DateAndTime($user->date_format, $report['period_end']));
1022 $totals['start_date'] = $period->getStartDate();
1023 $totals['end_date'] = $period->getEndDate();
1024 $totals['time'] = $total_time;
1025 $totals['cost'] = $total_cost;
1026 $totals['expenses'] = $total_expenses;
1031 // The assignToInvoice assigns a set of records to a specific invoice.
1032 static function assignToInvoice($invoice_id, $time_log_ids, $expense_item_ids)
1034 $mdb2 = getConnection();
1035 if ($time_log_ids) {
1036 $sql = "update tt_log set invoice_id = ".$mdb2->quote($invoice_id).
1037 " where id in(".join(', ', $time_log_ids).")";
1038 $affected = $mdb2->exec($sql);
1039 if (is_a($affected, 'PEAR_Error')) die($affected->getMessage());
1041 if ($expense_item_ids) {
1042 $sql = "update tt_expense_items set invoice_id = ".$mdb2->quote($invoice_id).
1043 " where id in(".join(', ', $expense_item_ids).")";
1044 $affected = $mdb2->exec($sql);
1045 if (is_a($affected, 'PEAR_Error')) die($affected->getMessage());
1049 // prepareReportBody - prepares an email body for report.
1050 static function prepareReportBody($bean, $comment)
1055 $items = ttReportHelper::getItems($bean);
1056 $group_by = $bean->getAttribute('group_by');
1057 if ($group_by && 'no_grouping' != $group_by)
1058 $subtotals = ttReportHelper::getSubtotals($bean);
1059 $totals = ttReportHelper::getTotals($bean);
1061 // Use custom fields plugin if it is enabled.
1062 if ($user->isPluginEnabled('cf'))
1063 $custom_fields = new CustomFields($user->team_id);
1065 // Define some styles to use in email.
1066 $style_title = 'text-align: center; font-size: 15pt; font-family: Arial, Helvetica, sans-serif;';
1067 $tableHeader = 'font-weight: bold; background-color: #a6ccf7; text-align: left;';
1068 $tableHeaderCentered = 'font-weight: bold; background-color: #a6ccf7; text-align: center;';
1069 $rowItem = 'background-color: #ffffff;';
1070 $rowItemAlt = 'background-color: #f5f5f5;';
1071 $rowSubtotal = 'background-color: #e0e0e0;';
1072 $cellLeftAligned = 'text-align: left; vertical-align: top;';
1073 $cellRightAligned = 'text-align: right; vertical-align: top;';
1074 $cellLeftAlignedSubtotal = 'font-weight: bold; text-align: left; vertical-align: top;';
1075 $cellRightAlignedSubtotal = 'font-weight: bold; text-align: right; vertical-align: top;';
1077 // Start creating email body.
1079 $body .= '<head><meta http-equiv="content-type" content="text/html; charset='.CHARSET.'"></head>';
1083 $body .= '<p style="'.$style_title.'">'.$i18n->getKey('form.mail.report_subject').': '.$totals['start_date'].' - '.$totals['end_date'].'</p>';
1086 if ($comment) $body .= '<p>'.htmlspecialchars($comment).'</p>';
1088 if ($bean->getAttribute('chtotalsonly')) {
1089 // Totals only report. Output subtotals.
1091 // Determine group_by header.
1092 if ('cf_1' == $group_by)
1093 $group_by_header = htmlspecialchars($custom_fields->fields[0]['label']);
1095 $key = 'label.'.$group_by;
1096 $group_by_header = $i18n->getKey($key);
1099 $body .= '<table border="0" cellpadding="4" cellspacing="0" width="100%">';
1101 $body .= '<td style="'.$tableHeader.'">'.$group_by_header.'</td>';
1102 if ($bean->getAttribute('chduration'))
1103 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.duration').'</td>';
1104 if ($bean->getAttribute('chcost'))
1105 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.cost').'</td>';
1107 foreach($subtotals as $subtotal) {
1108 $body .= '<tr style="'.$rowSubtotal.'">';
1109 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($subtotal['name'] ? htmlspecialchars($subtotal['name']) : ' ').'</td>';
1110 if ($bean->getAttribute('chduration')) {
1111 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1112 if ($subtotal['time'] <> '0:00') $body .= $subtotal['time'];
1115 if ($bean->getAttribute('chcost')) {
1116 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1117 $body .= ($user->canManageTeam() || $user->isClient()) ? $subtotal['cost'] : $subtotal['expenses'];
1124 $body .= '<tr><td> </td></tr>';
1125 $body .= '<tr style="'.$rowSubtotal.'">';
1126 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.$i18n->getKey('label.total').'</td>';
1127 if ($bean->getAttribute('chduration')) {
1128 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1129 if ($totals['time'] <> '0:00') $body .= $totals['time'];
1132 if ($bean->getAttribute('chcost')) {
1133 $body .= '<td nowrap style="'.$cellRightAlignedSubtotal.'">'.htmlspecialchars($user->currency).' ';
1134 $body .= ($user->canManageTeam() || $user->isClient()) ? $totals['cost'] : $totals['expenses'];
1139 $body .= '</table>';
1143 // Print table header.
1144 $body .= '<table border="0" cellpadding="4" cellspacing="0" width="100%">';
1146 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.date').'</td>';
1147 if ($user->canManageTeam() || $user->isClient())
1148 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.user').'</td>';
1149 if ($bean->getAttribute('chclient'))
1150 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.client').'</td>';
1151 if ($bean->getAttribute('chproject'))
1152 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.project').'</td>';
1153 if ($bean->getAttribute('chtask'))
1154 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.task').'</td>';
1155 if ($bean->getAttribute('chcf_1'))
1156 $body .= '<td style="'.$tableHeader.'">'.htmlspecialchars($custom_fields->fields[0]['label']).'</td>';
1157 if ($bean->getAttribute('chstart'))
1158 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.start').'</td>';
1159 if ($bean->getAttribute('chfinish'))
1160 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.finish').'</td>';
1161 if ($bean->getAttribute('chduration'))
1162 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.duration').'</td>';
1163 if ($bean->getAttribute('chnote'))
1164 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.note').'</td>';
1165 if ($bean->getAttribute('chcost'))
1166 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.cost').'</td>';
1167 if ($bean->getAttribute('chinvoice'))
1168 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.invoice').'</td>';
1171 // Initialize variables to print subtotals.
1172 if ($items && 'no_grouping' != $group_by) {
1173 $print_subtotals = true;
1175 $prev_grouped_by = '';
1176 $cur_grouped_by = '';
1178 // Initialize variables to alternate color of rows for different dates.
1181 $row_style = $rowItem;
1183 // Print report items.
1184 if (is_array($items)) {
1185 foreach ($items as $record) {
1186 $cur_date = $record['date'];
1187 // Print a subtotal row after a block of grouped items.
1188 if ($print_subtotals) {
1189 $cur_grouped_by = $record['grouped_by'];
1190 if ($cur_grouped_by != $prev_grouped_by && !$first_pass) {
1191 $body .= '<tr style="'.$rowSubtotal.'">';
1192 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.$i18n->getKey('label.subtotal').'</td>';
1193 $subtotal_name = htmlspecialchars($subtotals[$prev_grouped_by]['name']);
1194 if ($user->canManageTeam() || $user->isClient()) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'user' ? $subtotal_name : '').'</td>';
1195 if ($bean->getAttribute('chclient')) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'client' ? $subtotal_name : '').'</td>';
1196 if ($bean->getAttribute('chproject')) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'project' ? $subtotal_name : '').'</td>';
1197 if ($bean->getAttribute('chtask')) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'task' ? $subtotal_name : '').'</td>';
1198 if ($bean->getAttribute('chcf_1')) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'cf_1' ? $subtotal_name : '').'</td>';
1199 if ($bean->getAttribute('chstart')) $body .= '<td></td>';
1200 if ($bean->getAttribute('chfinish')) $body .= '<td></td>';
1201 if ($bean->getAttribute('chduration')) $body .= '<td style="'.$cellRightAlignedSubtotal.'">'.$subtotals[$prev_grouped_by]['time'].'</td>';
1202 if ($bean->getAttribute('chnote')) $body .= '<td></td>';
1203 if ($bean->getAttribute('chcost')) {
1204 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1205 $body .= ($user->canManageTeam() || $user->isClient()) ? $subtotals[$prev_grouped_by]['cost'] : $subtotals[$prev_grouped_by]['expenses'];
1208 if ($bean->getAttribute('chinvoice')) $body .= '<td></td>';
1210 $body .= '<tr><td> </td></tr>';
1212 $first_pass = false;
1215 // Print a regular row.
1216 if ($cur_date != $prev_date)
1217 $row_style = ($row_style == $rowItem) ? $rowItemAlt : $rowItem;
1218 $body .= '<tr style="'.$row_style.'">';
1219 $body .= '<td style="'.$cellLeftAligned.'">'.$record['date'].'</td>';
1220 if ($user->canManageTeam() || $user->isClient())
1221 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['user']).'</td>';
1222 if ($bean->getAttribute('chclient'))
1223 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['client']).'</td>';
1224 if ($bean->getAttribute('chproject'))
1225 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['project']).'</td>';
1226 if ($bean->getAttribute('chtask'))
1227 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['task']).'</td>';
1228 if ($bean->getAttribute('chcf_1'))
1229 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['cf_1']).'</td>';
1230 if ($bean->getAttribute('chstart'))
1231 $body .= '<td nowrap style="'.$cellRightAligned.'">'.$record['start'].'</td>';
1232 if ($bean->getAttribute('chfinish'))
1233 $body .= '<td nowrap style="'.$cellRightAligned.'">'.$record['finish'].'</td>';
1234 if ($bean->getAttribute('chduration'))
1235 $body .= '<td style="'.$cellRightAligned.'">'.$record['duration'].'</td>';
1236 if ($bean->getAttribute('chnote'))
1237 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['note']).'</td>';
1238 if ($bean->getAttribute('chcost'))
1239 $body .= '<td style="'.$cellRightAligned.'">'.$record['cost'].'</td>';
1240 if ($bean->getAttribute('chinvoice'))
1241 $body .= '<td style="'.$cellRightAligned.'">'.htmlspecialchars($record['invoice']).'</td>';
1244 $prev_date = $record['date'];
1245 if ($print_subtotals)
1246 $prev_grouped_by = $record['grouped_by'];
1250 // Print a terminating subtotal.
1251 if ($print_subtotals) {
1252 $body .= '<tr style="'.$rowSubtotal.'">';
1253 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.$i18n->getKey('label.subtotal').'</td>';
1254 $subtotal_name = htmlspecialchars($subtotals[$cur_grouped_by]['name']);
1255 if ($user->canManageTeam() || $user->isClient()) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'user' ? $subtotal_name : '').'</td>';
1256 if ($bean->getAttribute('chclient')) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'client' ? $subtotal_name : '').'</td>';
1257 if ($bean->getAttribute('chproject')) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'project' ? $subtotal_name : '').'</td>';
1258 if ($bean->getAttribute('chtask')) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'task' ? $subtotal_name : '').'</td>';
1259 if ($bean->getAttribute('chcf_1')) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'cf_1' ? $subtotal_name : '').'</td>';
1260 if ($bean->getAttribute('chstart')) $body .= '<td></td>';
1261 if ($bean->getAttribute('chfinish')) $body .= '<td></td>';
1262 if ($bean->getAttribute('chduration')) $body .= '<td style="'.$cellRightAlignedSubtotal.'">'.$subtotals[$cur_grouped_by]['time'].'</td>';
1263 if ($bean->getAttribute('chnote')) $body .= '<td></td>';
1264 if ($bean->getAttribute('chcost')) {
1265 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1266 $body .= ($user->canManageTeam() || $user->isClient()) ? $subtotals[$cur_grouped_by]['cost'] : $subtotals[$cur_grouped_by]['expenses'];
1269 if ($bean->getAttribute('chinvoice')) $body .= '<td></td>';
1274 $body .= '<tr><td> </td></tr>';
1275 $body .= '<tr style="'.$rowSubtotal.'">';
1276 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.$i18n->getKey('label.total').'</td>';
1277 if ($user->canManageTeam() || $user->isClient()) $body .= '<td></td>';
1278 if ($bean->getAttribute('chclient')) $body .= '<td></td>';
1279 if ($bean->getAttribute('chproject')) $body .= '<td></td>';
1280 if ($bean->getAttribute('chtask')) $body .= '<td></td>';
1281 if ($bean->getAttribute('chcf_1')) $body .= '<td></td>';
1282 if ($bean->getAttribute('chstart')) $body .= '<td></td>';
1283 if ($bean->getAttribute('chfinish')) $body .= '<td></td>';
1284 if ($bean->getAttribute('chduration')) $body .= '<td style="'.$cellRightAlignedSubtotal.'">'.$totals['time'].'</td>';
1285 if ($bean->getAttribute('chnote')) $body .= '<td></td>';
1286 if ($bean->getAttribute('chcost')) {
1287 $body .= '<td nowrap style="'.$cellRightAlignedSubtotal.'">'.htmlspecialchars($user->currency).' ';
1288 $body .= ($user->canManageTeam() || $user->isClient()) ? $totals['cost'] : $totals['expenses'];
1291 if ($bean->getAttribute('chinvoice')) $body .= '<td></td>';
1294 $body .= '</table>';
1298 if (!defined('REPORT_FOOTER') || !(REPORT_FOOTER == false))
1299 $body .= '<p style="text-align: center;">'.$i18n->getKey('form.mail.footer').'</p>';
1301 // Finish creating email body.
1302 $body .= '</body></html>';
1307 // checkFavReportCondition - checks whether it is okay to send fav report.
1308 static function checkFavReportCondition($report, $condition)
1310 $items = ttReportHelper::getFavItems($report);
1312 $condition = str_replace('count', '', $condition);
1313 $count_required = intval(trim(str_replace('>', '', $condition)));
1315 if (count($items) > $count_required)
1316 return true; // Condition ok.
1321 // prepareFavReportBody - prepares an email body for a favorite report.
1322 static function prepareFavReportBody($report)
1327 $items = ttReportHelper::getFavItems($report);
1328 $group_by = $report['group_by'];
1329 if ($group_by && 'no_grouping' != $group_by)
1330 $subtotals = ttReportHelper::getFavSubtotals($report);
1331 $totals = ttReportHelper::getFavTotals($report);
1333 // Use custom fields plugin if it is enabled.
1334 if ($user->isPluginEnabled('cf'))
1335 $custom_fields = new CustomFields($user->team_id);
1337 // Define some styles to use in email.
1338 $style_title = 'text-align: center; font-size: 15pt; font-family: Arial, Helvetica, sans-serif;';
1339 $tableHeader = 'font-weight: bold; background-color: #a6ccf7; text-align: left;';
1340 $tableHeaderCentered = 'font-weight: bold; background-color: #a6ccf7; text-align: center;';
1341 $rowItem = 'background-color: #ffffff;';
1342 $rowItemAlt = 'background-color: #f5f5f5;';
1343 $rowSubtotal = 'background-color: #e0e0e0;';
1344 $cellLeftAligned = 'text-align: left; vertical-align: top;';
1345 $cellRightAligned = 'text-align: right; vertical-align: top;';
1346 $cellLeftAlignedSubtotal = 'font-weight: bold; text-align: left; vertical-align: top;';
1347 $cellRightAlignedSubtotal = 'font-weight: bold; text-align: right; vertical-align: top;';
1349 // Start creating email body.
1351 $body .= '<head><meta http-equiv="content-type" content="text/html; charset='.CHARSET.'"></head>';
1355 $body .= '<p style="'.$style_title.'">'.$i18n->getKey('form.mail.report_subject').': '.$totals['start_date'].' - '.$totals['end_date'].'</p>';
1358 // if ($comment) $body .= '<p>'.htmlspecialchars($comment).'</p>'; // No comment for fav. reports.
1360 if ($report['show_totals_only']) {
1361 // Totals only report. Output subtotals.
1363 // Determine group_by header.
1364 if ('cf_1' == $group_by)
1365 $group_by_header = htmlspecialchars($custom_fields->fields[0]['label']);
1367 $key = 'label.'.$group_by;
1368 $group_by_header = $i18n->getKey($key);
1371 $body .= '<table border="0" cellpadding="4" cellspacing="0" width="100%">';
1373 $body .= '<td style="'.$tableHeader.'">'.$group_by_header.'</td>';
1374 if ($report['show_duration'])
1375 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.duration').'</td>';
1376 if ($report['show_cost'])
1377 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.cost').'</td>';
1379 foreach($subtotals as $subtotal) {
1380 $body .= '<tr style="'.$rowSubtotal.'">';
1381 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($subtotal['name'] ? htmlspecialchars($subtotal['name']) : ' ').'</td>';
1382 if ($report['show_duration']) {
1383 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1384 if ($subtotal['time'] <> '0:00') $body .= $subtotal['time'];
1387 if ($report['show_cost']) {
1388 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1389 $body .= ($user->canManageTeam() || $user->isClient()) ? $subtotal['cost'] : $subtotal['expenses'];
1396 $body .= '<tr><td> </td></tr>';
1397 $body .= '<tr style="'.$rowSubtotal.'">';
1398 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.$i18n->getKey('label.total').'</td>';
1399 if ($report['show_duration']) {
1400 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1401 if ($totals['time'] <> '0:00') $body .= $totals['time'];
1404 if ($report['show_cost']) {
1405 $body .= '<td nowrap style="'.$cellRightAlignedSubtotal.'">'.htmlspecialchars($user->currency).' ';
1406 $body .= ($user->canManageTeam() || $user->isClient()) ? $totals['cost'] : $totals['expenses'];
1411 $body .= '</table>';
1415 // Print table header.
1416 $body .= '<table border="0" cellpadding="4" cellspacing="0" width="100%">';
1418 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.date').'</td>';
1419 if ($user->canManageTeam() || $user->isClient())
1420 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.user').'</td>';
1421 if ($report['show_client'])
1422 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.client').'</td>';
1423 if ($report['show_project'])
1424 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.project').'</td>';
1425 if ($report['show_task'])
1426 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.task').'</td>';
1427 if ($report['show_custom_field_1'])
1428 $body .= '<td style="'.$tableHeader.'">'.htmlspecialchars($custom_fields->fields[0]['label']).'</td>';
1429 if ($report['show_start'])
1430 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.start').'</td>';
1431 if ($report['show_end'])
1432 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.finish').'</td>';
1433 if ($report['show_duration'])
1434 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.duration').'</td>';
1435 if ($report['show_note'])
1436 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.note').'</td>';
1437 if ($report['show_cost'])
1438 $body .= '<td style="'.$tableHeaderCentered.'" width="5%">'.$i18n->getKey('label.cost').'</td>';
1439 if ($report['show_invoice'])
1440 $body .= '<td style="'.$tableHeader.'">'.$i18n->getKey('label.invoice').'</td>';
1443 // Initialize variables to print subtotals.
1444 if ($items && 'no_grouping' != $group_by) {
1445 $print_subtotals = true;
1447 $prev_grouped_by = '';
1448 $cur_grouped_by = '';
1450 // Initialize variables to alternate color of rows for different dates.
1453 $row_style = $rowItem;
1455 // Print report items.
1456 if (is_array($items)) {
1457 foreach ($items as $record) {
1458 $cur_date = $record['date'];
1459 // Print a subtotal row after a block of grouped items.
1460 if ($print_subtotals) {
1461 $cur_grouped_by = $record['grouped_by'];
1462 if ($cur_grouped_by != $prev_grouped_by && !$first_pass) {
1463 $body .= '<tr style="'.$rowSubtotal.'">';
1464 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.$i18n->getKey('label.subtotal').'</td>';
1465 $subtotal_name = htmlspecialchars($subtotals[$prev_grouped_by]['name']);
1466 if ($user->canManageTeam() || $user->isClient()) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'user' ? $subtotal_name : '').'</td>';
1467 if ($report['show_client']) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'client' ? $subtotal_name : '').'</td>';
1468 if ($report['show_project']) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'project' ? $subtotal_name : '').'</td>';
1469 if ($report['show_task']) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'task' ? $subtotal_name : '').'</td>';
1470 if ($report['show_custom_field_1']) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'cf_1' ? $subtotal_name : '').'</td>';
1471 if ($report['show_start']) $body .= '<td></td>';
1472 if ($report['show_end']) $body .= '<td></td>';
1473 if ($report['show_duration']) $body .= '<td style="'.$cellRightAlignedSubtotal.'">'.$subtotals[$prev_grouped_by]['time'].'</td>';
1474 if ($report['show_note']) $body .= '<td></td>';
1475 if ($report['show_cost']) {
1476 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1477 $body .= ($user->canManageTeam() || $user->isClient()) ? $subtotals[$prev_grouped_by]['cost'] : $subtotals[$prev_grouped_by]['expenses'];
1480 if ($report['show_invoice']) $body .= '<td></td>';
1482 $body .= '<tr><td> </td></tr>';
1484 $first_pass = false;
1487 // Print a regular row.
1488 if ($cur_date != $prev_date)
1489 $row_style = ($row_style == $rowItem) ? $rowItemAlt : $rowItem;
1490 $body .= '<tr style="'.$row_style.'">';
1491 $body .= '<td style="'.$cellLeftAligned.'">'.$record['date'].'</td>';
1492 if ($user->canManageTeam() || $user->isClient())
1493 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['user']).'</td>';
1494 if ($report['show_client'])
1495 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['client']).'</td>';
1496 if ($report['show_project'])
1497 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['project']).'</td>';
1498 if ($report['show_task'])
1499 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['task']).'</td>';
1500 if ($report['show_custom_field_1'])
1501 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['cf_1']).'</td>';
1502 if ($report['show_start'])
1503 $body .= '<td nowrap style="'.$cellRightAligned.'">'.$record['start'].'</td>';
1504 if ($report['show_end'])
1505 $body .= '<td nowrap style="'.$cellRightAligned.'">'.$record['finish'].'</td>';
1506 if ($report['show_duration'])
1507 $body .= '<td style="'.$cellRightAligned.'">'.$record['duration'].'</td>';
1508 if ($report['show_note'])
1509 $body .= '<td style="'.$cellLeftAligned.'">'.htmlspecialchars($record['note']).'</td>';
1510 if ($report['show_cost'])
1511 $body .= '<td style="'.$cellRightAligned.'">'.$record['cost'].'</td>';
1512 if ($report['show_invoice'])
1513 $body .= '<td style="'.$cellRightAligned.'">'.htmlspecialchars($record['invoice']).'</td>';
1516 $prev_date = $record['date'];
1517 if ($print_subtotals)
1518 $prev_grouped_by = $record['grouped_by'];
1522 // Print a terminating subtotal.
1523 if ($print_subtotals) {
1524 $body .= '<tr style="'.$rowSubtotal.'">';
1525 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.$i18n->getKey('label.subtotal').'</td>';
1526 $subtotal_name = htmlspecialchars($subtotals[$cur_grouped_by]['name']);
1527 if ($user->canManageTeam() || $user->isClient()) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'user' ? $subtotal_name : '').'</td>';
1528 if ($report['show_client']) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'client' ? $subtotal_name : '').'</td>';
1529 if ($report['show_project']) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'project' ? $subtotal_name : '').'</td>';
1530 if ($report['show_task']) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'task' ? $subtotal_name : '').'</td>';
1531 if ($report['show_custom_field_1']) $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.($group_by == 'cf_1' ? $subtotal_name : '').'</td>';
1532 if ($report['show_start']) $body .= '<td></td>';
1533 if ($report['show_end']) $body .= '<td></td>';
1534 if ($report['show_duration']) $body .= '<td style="'.$cellRightAlignedSubtotal.'">'.$subtotals[$cur_grouped_by]['time'].'</td>';
1535 if ($report['show_note']) $body .= '<td></td>';
1536 if ($report['show_cost']) {
1537 $body .= '<td style="'.$cellRightAlignedSubtotal.'">';
1538 $body .= ($user->canManageTeam() || $user->isClient()) ? $subtotals[$cur_grouped_by]['cost'] : $subtotals[$cur_grouped_by]['expenses'];
1541 if ($report['show_invoice']) $body .= '<td></td>';
1546 $body .= '<tr><td> </td></tr>';
1547 $body .= '<tr style="'.$rowSubtotal.'">';
1548 $body .= '<td style="'.$cellLeftAlignedSubtotal.'">'.$i18n->getKey('label.total').'</td>';
1549 if ($user->canManageTeam() || $user->isClient()) $body .= '<td></td>';
1550 if ($report['show_client']) $body .= '<td></td>';
1551 if ($report['show_project']) $body .= '<td></td>';
1552 if ($report['show_task']) $body .= '<td></td>';
1553 if ($report['show_custom_field_1']) $body .= '<td></td>';
1554 if ($report['show_start']) $body .= '<td></td>';
1555 if ($report['show_end']) $body .= '<td></td>';
1556 if ($report['show_duration']) $body .= '<td style="'.$cellRightAlignedSubtotal.'">'.$totals['time'].'</td>';
1557 if ($report['show_note']) $body .= '<td></td>';
1558 if ($report['show_cost']) {
1559 $body .= '<td nowrap style="'.$cellRightAlignedSubtotal.'">'.htmlspecialchars($user->currency).' ';
1560 $body .= ($user->canManageTeam() || $user->isClient()) ? $totals['cost'] : $totals['expenses'];
1563 if ($report['show_invoice']) $body .= '<td></td>';
1566 $body .= '</table>';
1570 if (!defined('REPORT_FOOTER') || !(REPORT_FOOTER == false))
1571 $body .= '<p style="text-align: center;">'.$i18n->getKey('form.mail.footer').'</p>';
1573 // Finish creating email body.
1574 $body .= '</body></html>';
1579 // sendFavReport - sends a favorite report to a specified email, called from cron.php
1580 static function sendFavReport($report, $subject, $email, $cc) {
1581 // We are called from cron.php, we have no $bean in session.
1582 // cron.php sets global $user and $i18n objects to match our favorite report user.
1586 // Prepare report body.
1587 $body = ttReportHelper::prepareFavReportBody($report);
1589 import('mail.Mailer');
1590 $mailer = new Mailer();
1591 $mailer->setCharSet(CHARSET);
1592 $mailer->setContentType('text/html');
1593 $mailer->setSender(SENDER);
1595 $mailer->setReceiverCC($cc);
1596 if (!empty($user->bcc_email))
1597 $mailer->setReceiverBCC($user->bcc_email);
1598 $mailer->setReceiver($email);
1599 $mailer->setMailMode(MAIL_MODE);
1600 if (empty($subject)) $subject = $report['name'];
1601 if (!$mailer->send($subject, $body))