Odyssey
cuissues_data.prg
1 <?php
2 // File: cuissues_data.v2 (partially replaces cuissues_report)
3 // Contains functions for CRUD operations in the cuissues_edit.v2 and cuissues_report.v2 files and functions for sending emails.
4 // SPB 10/6/2015-- Replaced previous cuissues_report for kendo upgrade and billing workflow expansion.
5 
6 $monLibrary = dirname(__FILE__) . "/../library";
7 $sharedLibrary = dirname(__FILE__) . "/../../shared/library";
8 $billingLibrary = dirname(__FILE__) . "/../../billing/library";
9 require_once("$monLibrary/cu_top.i");
10 require_once("$monLibrary/ck_hticket.i");
11 require_once("$monLibrary/monitorView.i");
12 require_once("$sharedLibrary/commonPhpFunctions.i");
13 
14 require_once("$sharedLibrary/errormail.i");
15 require_once("$billingLibrary/billingEmailFunctions.i");
16 require_once("$billingLibrary/configuration.i");
17 
18 if (!CheckPerm($link, $Hu, basename($_SERVER['SCRIPT_NAME']), $_SERVER['REMOTE_ADDR'])) {
19  // ** Permissions failed
20  // ** redirect to new page
21  header("Location: /hcuadm/hcu_noperm.prg");
22  exit;
23 }
24 
25 dms_import_v2($DATA_PARAMETERS, "TOP_LEVEL", array("operation" => "string"));
26 $operation = $DATA_PARAMETERS["TOP_LEVEL"]["operation"];
27 
28 switch ($operation) {
29  case "readCuissues":
30  $returnArray = ReadCuissues($dbh);
31  break;
32  case "readIssueEdit":
33  $returnArray = ReadIssueEdit($dbh, $Hu);
34  break;
35  case "readIssueAdd":
36  $returnArray = ReadIssueAdd($dbh);
37  break;
38  case "saveIssueAdd":
39  $returnArray = SaveIssue($dbh, $Hu, true);
40  break;
41  case "saveIssueEdit":
42  $returnArray = SaveIssue($dbh, $Hu, false);
43  break;
44  case "addNewContact":
45  $returnArray = AddNewContact($dbh);
46  break;
47  case "getComments":
48  $returnArray = GetComments($dbh, $Hu);
49  break;
50  case "readTasks":
51  $returnArray = ReadTasks($dbh);
52  break;
53  case "createTask":
54  $returnArray = ModifyTask($dbh, true, $Hu);
55  break;
56  case "updateTask":
57  $returnArray = ModifyTask($dbh, false, $Hu);
58  break;
59  case "deleteTask":
60  $returnArray = DeleteTask($dbh, $Hu);
61  break;
62  case "changeTaskStatus":
63  $returnArray = ChangeTaskStatus($dbh, $Hu);
64  break;
65  case "reorderTasks":
66  $returnArray = ReorderTasks($dbh, $Hu);
67  break;
68  case "addTaskComment":
69  $returnArray = ModifyTaskComment($dbh, $Hu, true);
70  break;
71  case "updateTaskComment":
72  $returnArray = ModifyTaskComment($dbh, $Hu, false);
73  break;
74  case "promoteTask":
75  $returnArray = PromoteTask($dbh, $Hu);
76  break;
77  case "addIssueFromTemplate":
78  $returnArray = AddIssueFromTemplate($dbh, $Hu);
79  break;
80  case "deleteTaskComment":
81  $returnArray = DeleteTaskComment($dbh);
82  break;
83  case "pullInTaskTemplates":
84  $returnArray = PullInTaskTemplates($dbh);
85  break;
86  case "setReportCookie":
87  $returnArray = SetReportCookie($SYSENV);
88  break;
89  case "clearReportCookie":
90  $returnArray = ClearReportCookie($SYSENV);
91  break;
92  default: $returnArray = array("error" => array("Operation not specified: '$operation'"), "record" => "", "operation" => "");
93 }
94 
95 header('Content-type: application/json');
96 print HCU_JsonEncode($returnArray);
97 
98 /////////////////////////////////////
99 // Literals
100 
101 // Gets state model for issue statuses.
102 function GetIssueStatuses() {
103  return array(
104  array("value" => "Active", "currentStatus" => "Active"),
105  array("value" => "Suspended", "currentStatus" => "Active"),
106  array("value" => "Completed", "currentStatus" => "Active"),
107  array("value" => "Deleted", "currentStatus" => "Active"),
108  array("value" => "Suspended", "currentStatus" => "Suspended"),
109  array("value" => "Active", "currentStatus" => "Suspended"),
110  array("value" => "Deleted", "currentStatus" => "Suspended"),
111  array("value" => "Completed", "currentStatus" => "Completed"),
112  array("value" => "Active", "currentStatus" => "Completed"),
113  array("value" => "Suspended", "currentStatus" => "Completed"),
114  array("value" => "Deleted", "currentStatus" => "Completed"),
115  array("value" => "Deleted", "currentStatus" => "Deleted"),
116  array("value" => "Active", "currentStatus" => "Deleted"),
117  array("value" => "Unrecognized", "currentStatus" => "Unrecognized"),
118  array("value" => "Active", "currentStatus" => "Unrecognized"),
119  array("value" => "Suspended", "currentStatus" => "Unrecognized"),
120  array("value" => "Completed", "currentStatus" => "Unrecognized"),
121  array("value" => "Deleted", "currentStatus" => "Unrecognized"));
122 }
123 
124 // Get task statuses. Values are in order that they appear in the tasks.
125 function GetTaskStatuses() {
126  return array("1" => "TODO", "0" => "Done", "2" => "N/A");
127 }
128 
129 // Gets the contact roles from cu_top.
130 function GetContactRoles() {
131  global $cu_contact_role; // from cu_top
132  $roles = array();
133  foreach($cu_contact_role as $key => $value) {
134  $roles[] = array("text" => $value, "value" => $key);
135  }
136  return $roles;
137 }
138 // End Literals
139 ////////////////////////////////////
140 
141 /* Retrieves list of issues and the next task needing to be completed. */
142 function ReadCuissues($dbh)
143 {
144  $parameters = array();
145  dms_import_v2($parameters, "BOTTOM_LEVEL", array("cu" => "string", "showCompleted" => "string", "setDDLs" => "string", "showDeleted" => "string"));
146 
147 
148 
149  $sqls= array();
150  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
151  array("cu" => array("type" => "string", "required" => false, "maxlength" => 12),
152  "showCompleted" => array("type" => "boolean", "required" => false),
153  "showDeleted" => array("type" => "boolean", "required" => false),
154  "setDDLs" => array("type" => "boolean", "required" => false)));
155 
156  $cu= strtoupper($parameters["BOTTOM_LEVEL"]["cu"]);
157  $showCompleted= $parameters["BOTTOM_LEVEL"]["showCompleted"] == "Y";
158  $showDeleted= $parameters["BOTTOM_LEVEL"]["showDeleted"] == "Y";
159  $setDDLs= $parameters["BOTTOM_LEVEL"]["setDDLs"] == "Y";
160 
161  $where= array();
162  $notStatusArray= array();
163  $completeDatePiece= "";
164  if (!$showCompleted)
165  {
166  $completeDatePiece= "and t.complete_date is null";
167  $notStatusArray[]= "Completed";
168  $notStatusArray[]= "Billed";
169  }
170  if (!$showDeleted)
171  $notStatusArray[]= "Deleted";
172  if (count($notStatusArray) > 0)
173  $where[]= "t.status not in ('" . implode("', '", $notStatusArray) . "') $completeDatePiece";
174  if ($cu != "")
175  $where[]= "upper(t.user_name) = '$cu'";
176  $whereSQL= count($where) == 0 ? "" : "where (" . implode(") and (", $where) . ")";
177 
178  $sql= "select t.track_id, temp.number, t.user_name, t.issue, t.status, t.assigned_to, t.contact, t.entry_date, t.last_activity_date, i.name, temp.title, temp.assigned_to as tassigned_to,
179  t.target_date, t.target_date_type, t.billing_feature_id, t.drop_dead_date, t.issue_id from cutrack t inner join cuinfo i on lower(t.user_name)= lower(i.user_name)
180  left join (select row_number() over (partition by ti.track_id, ti.parent order by ti.view_order, ti.trackitem_id) as number, ti.title, ti.assigned_to, ti.track_id
181  from cutrackitem ti where ti.task_status <> 2 and ti.complete_date is null and ti.parent = 0) temp on t.track_id= temp.track_id and temp.number = 1 $whereSQL";
182  $sqlResults= runSelectStatement($sql, $dbh, array("track_id" => "trackId", "user_name" => "cuCode", "issue" => "issue", "assigned_to" => "issueOwner", "entry_date" => "entryDate",
183  "target_date_type" => "targetDateType", "last_activity_date" => "lastActivityDate", "name" => "cuName", "title" => "nextTask", "tassigned_to" => "nextTaskOwner",
184  "status" => "status", "target_date" => "targetDate", "billing_feature_id" => "billingFeatureId", "drop_dead_date" => "dropDeadDate", "issue_id" => "issueId"));
185 
186  $cloneSQL= "select distinct ti.track_id, '|cloneJob|' from cutrackitem ti where lower(ti.title) like '%clone a server%' and ti.complete_date is null and ti.task_status <> 2";
187  $cloneMapResults= runSelectStatementMap($cloneSQL, $dbh);
188 
189  $unattachedSQL= "select t.track_id, '|unattached|' from cutrack t left join cubillfeaturedetail fd on t.billing_feature_id = fd.id where fd.id is null";
190  $unattachedMapResults= runSelectStatementMap($unattachedSQL, $dbh);
191 
192  $records= $sqlResults["record"];
193  for($i=0, $count=count($records); $i != $count; $i++)
194  {
195  $trackId= $records[$i]["trackId"];
196  $issueId= $records[$i]["issueId"];
197 
198  switch(trim(strtoupper($records[$i]["targetDateType"])))
199  {
200  case "":
201  $records[$i]["targetDateTypeSort"]= 1;
202  break;
203  case "TBD":
204  $records[$i]["targetDateTypeSort"]= 3;
205  break;
206  case "FLEX":
207  $records[$i]["targetDateTypeSort"]= 2;
208  break;
209  default:
210  $records[$i]["targetDateTypeSort"]= 4; // Won't get here except for bad data.
211  }
212 
213  /* Numbers are the particular issues that are determined to be implementation-based issues:
214  * 35-- Batch Home Banking Setup, 10-- VR Cloud Based Setup VOIP, 20-- Live HomeBanking Setup, 5-- Upgrade Batch to Live
215  */
216  if (HCU_array_key_exists($trackId, $cloneMapResults['record'])) {
217  $records[$i]["jobType"]= strval($cloneMapResults["record"][$trackId]) . strval($unattachedMapResults["record"][$trackId]) . (in_array($issueId, array(35, 10, 20, 5)) ? "|imp|" : "");
218  }
219  }
220 
221  $ddlSQL= "select issue_id, code from cuissues order by code";
222  $ddlResults= runSelectStatement($ddlSQL, $dbh, array("issue_id" => "value", "code" => "text"));
223 
224  global $SYS_TYPE_CLOSED; // From cu_top
225  $cuSQL= "select i.user_name, i.name from cuinfo i where i.system_options & $SYS_TYPE_CLOSED = 0 order by i.user_name";
226  $cuResults= runSelectStatement($cuSQL, $dbh, array("user_name" => "value", "name" => "cuName"));
227 
228  $errors= array_merge($errors, $sqlResults["error"], $cloneMapResults["error"], $ddlResults["error"], $cuResults["error"]);
229  $sqls= array($sql, $cloneSQL, $unattachedSQL, $ddlSQL, $cuSQL);
230 
231  $cuDDL= array();
232  foreach($cuResults["record"] as $record)
233  {
234  $record["text"]= $record["cuName"] . " (" . $record["value"] . ")";
235  $cuDDL[]= $record;
236  }
237 
238  return array("error" => $errors, "sql" => $sqls, "operation" => "read", "record" => $records, "templateDDL" => $ddlResults["record"], "cuDDL" => $cuDDL);
239 }
240 
241 /* Retrieves DDLs and data needed for a preexisting issue. Will also retrieve comments on that issue by the logged in user. */
242 function readIssueEdit($dbh, $staffId)
243 {
244  $parameters= array();
245  dms_import_v2($parameters, "BOTTOM_LEVEL", array("trackId" => "string", "cu" => "string"));
246 
247  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
248  array("trackId" => array("type" => "int", "required" => true)));
249  $trackId= $parameters["BOTTOM_LEVEL"]["trackId"];
250 
251  $sql= "select t.track_id, t.user_name, t.issue, t.status, t.assigned_to, t.entry_date, t.last_activity_date, t.next_contact_date, t.contact_id, i.name, t.issue_id, t.contact, t.contact_phone,
252  t.contact_email, t.notes, t.target_date, t.complete_date, t.target_date_type, i.www_server, i.vendor, t.billing_feature_id, t.drop_dead_date, ci.gen_for_product, fd.id as test_id,
253  t.billed
254  from cutrack t inner join cuinfo i on lower(t.user_name)= lower(i.user_name) left join cuissues ci on t.issue_id = ci.issue_id
255  left join cubillfeaturedetail fd on t.billing_feature_id = fd.id
256  where t.track_id= $trackId";
257  $results= runSelectStatement($sql, $dbh, array("track_id" => "trackId", "name" => "cuName", "user_name" => "cu", "issue" => "issue", "status" => "status", "assigned_to" => "issueOwner",
258  "last_activity_date" => "lastActivity", "entry_date" => "enteredOn", "target_date" => "targetDate", "complete_date" => "completedOn",
259  "contact_id" => "contactId", "target_date_type" => "targetDateType", "issue_id" => "templateId", "www_server" => "server", "vendor" => "vendor",
260  "contact" => "contactName", "contact_phone" => "contactPhone", "contact_email" => "contactEmail", "notes" => "oldComments",
261  "billing_feature_id" => "billingFeatureId", "drop_dead_date" => "dropDeadDate", "gen_for_product" => "product", "billed" => "billed",
262  "test_id" => "testId"));
263  $errors= array_merge($errors, $results["error"]);
264 
265  if (count($errors) == 0 && count($results["record"]) == 0)
266  {
267  $errors[]= "Issue with track id provided does not exist.";
268  $cu= "";
269  $record= array();
270  }
271  else
272  {
273  $record= $results["record"][0];
274  $cu= $record["cu"];
275  }
276 
277  $commentsSQL= "select row_number() over(order by messageid) as number, n.messageid, n.author, n.createddate, n.messagetext, n.activitydate, n.trackitem_id, ti.title from cuinfo_notes n
278  left join cutrackitem ti on n.trackitem_id = ti.trackitem_id where n.track_id = $trackId";
279 
280  $results= runSelectStatement($commentsSQL, $dbh,
281  array("messageid" => "commentId", "author" => "author", "createddate" => "createdDate", "messagetext" => "text", "number" => "mockCommentId",
282  "trackitem_id" => "trackItemId", "title" => "trackItemName"));
283 
284  $comments= $results["record"];
285  for($i=0, $count=count($comments); $i != $count; $i++)
286  {
287  $comments[$i]["groupId"]= intval($comments[$i]["trackItemId"]) == 0 ? 0 : 1;
288  }
289  $errors= array_merge($errors, $results["error"]);
290 
291  $returnArray= array("error" => $errors, "sql" => array($sql, $commentsSQL), "operation" => "read", "record" => $record, "comments" => $comments);
292  getIssueDDLs($dbh, $returnArray, $cu, $record["contactName"], $record["contactPhone"], $record["contactEmail"], $record["product"]);
293 
294  $returnArray["selectSpecial"]= ($record["contactName"] != "" && $record["contactId"] == "0") || ($record["contactId"] == "-1");
295 
296  return $returnArray;
297 }
298 
299 /* Retrieves DDLs needed for a new issue. */
300 function readIssueAdd($dbh)
301 {
302  $parameters= array();
303  dms_import_v2($parameters, "BOTTOM_LEVEL", array("cu" => "string"));
304 
305  $sqls= array();
306  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"], array("cu" => array("type" => "string", "required" => false, "maxlength" => 12)));
307  $returnArray= array("error" => $errors, "sql" => array(), "operation" => "read");
308  $cu= $parameters["BOTTOM_LEVEL"]["cu"];
309  getIssueDDLs($dbh, $returnArray, $cu);
310  $returnArray["selectSpecial"]= false;
311  return $returnArray;
312 }
313 
314 /* Gets a list of CUs that are active plus the current Cu, owners, contacts, roles, issue and task statuses, and the name of the cu. */
315 function getIssueDDLs($dbh, &$returnArray, $cu, $contactName= "", $contactPhone= "", $contactEmail= "", $product= "")
316 {
317  global $SYS_TYPE_CLOSED; // From cu_top
318  $cu= strtoupper($cu);
319  $cuSQL= "select i.user_name, i.name, i.www_server, i.vendor from cuinfo i where upper(i.user_name) = '$cu' or i.system_options & $SYS_TYPE_CLOSED = 0 order by i.user_name";
320  $cuResults= runSelectStatement($cuSQL, $dbh, array("user_name" => "value", "name" => "text", "www_server" => "server", "vendor" => "vendor"));
321 
322  $ownerSQL= "select u.user_name from dmsmonitorusers u order by lower(u.user_name)";
323  $ownerResults= runSelectStatement($ownerSQL, $dbh, array("user_name" => "value"));
324 
325  $contactSQL= "select c.id, coalesce(c.fname, '') || ' ' || coalesce(c.lname, '') as full_name, c.phone, c.email, c.user_name from monitor_contact c";
326  if ($cu != "")
327  $contactSQL.= " where upper(c.user_name) = '$cu'";
328  $contactResults= runSelectStatement($contactSQL, $dbh, array("id" => "contactId", "full_name" => "text", "phone" => "phone", "email" => "email", "user_name" => "cu"));
329 
330  $contactDDL= $contactResults["record"];
331 
332  for($i=0, $count=count($contactDDL); $i != $count; $i++)
333  {
334  $contactDDL[$i]["sortNumber"]= 3;
335  }
336 
337  array_unshift($contactDDL,
338  array("contactId" => "", "text" => "NONE", "fullName" => "", "phone" => "", "email" => "", "cu" => "", "sortNumber" => 0),
339  array("contactId" => "", "text" => "NEW", "fullName" => "", "phone" => "", "email" => "", "cu" => "", "sortNumber" => 1),
340  array("contactId" => "-1", "text" => "SPECIAL", "fullName" => $contactName, "phone" => $contactPhone, "email" => $contactEmail, "cu" => "", "sortNumber" => 2));
341 
342  $ownerDDL= $ownerResults["record"];
343  for($i=0, $count=count($ownerDDL); $i != $count; $i++)
344  {
345  $ownerDDL[$i]["text"]= $ownerDDL[$i]["value"];
346  $ownerDDL[$i]["filterNumber"]= 0;
347  }
348 
349  array_unshift($ownerDDL, array("value" => "", "text" => "(Job Owner)", "filterNumber" => 1), array("value" => "", "text" => "(Parent Owner)", "filterNumber" => 2));
350 
351  $returnArray["error"]= array_merge($returnArray["error"], $cuResults["error"], $ownerResults["error"], $contactResults["error"]);
352  $returnArray["cuDDL"]= $cuResults["record"];
353  $returnArray["ownerDDL"]= $ownerDDL;
354  $returnArray["statusDDL"]= getIssueStatuses();
355  $returnArray["contactDDL"]= $contactDDL;
356  $returnArray["roleDDL"]= getContactRoles();
357 
358  $taskStatusDDL= array();
359  foreach(getTaskStatuses() as $value => $text)
360  {
361  $taskStatusDDL[]= array("text" => $text, "value" => $value);
362  }
363  $returnArray["taskStatusDDL"]= $taskStatusDDL;
364 
365  $unattachedFeatureDetailsSQL= "select fd.id, si.display_name, coalesce(p.home_cu_desc, cd.prod_id) as prod_desc, c.cu_id, si.billing_system_id from cubillfeaturedetail fd
366  inner join cubillsalesorderdetail cd on fd.sales_order_detail_id = cd.id inner join cubillsalesorder c on cd.sales_order_id = c.id inner join cubillsalesitem si on fd.sales_item_id = si.id
367  left join cuprodlist p on si.prod_id = p.home_cu_code left join cutrack t on fd.id = t.billing_feature_id";
368  $where= array("t.track_id is null");
369  if ($cu != "")
370  $where[]= "upper(c.cu_id) = '$cu'";
371  if ($product != "")
372  $where[]= "cd.prod_id = '$product'";
373  $unattachedFeatureDetailsSQL.= " where (" . implode(") and (", $where) . ")";
374 
375  $returnArray["sql"]= array_merge($returnArray["sql"], array($cuSQL, $ownerSQL, $contactSQL, $unattachedFeatureDetailsSQL));
376 
377  $results= runSelectStatement($unattachedFeatureDetailsSQL, $dbh, array("id" => "value", "display_name" => "itemDesc", "prod_desc" => "prodDesc", "cu_id" => "cu",
378  "billing_system_id" => "billingSystemId"));
379  $returnArray["error"]= array_merge($returnArray["error"], $results["error"]);
380  $sqls[]= $unattachedFeatureDetailsSQL;
381  $unattachedFeatureDetailsDDL= array();
382  $setupRecurring= getSetupRecurring();
383  foreach($results["record"] as $record)
384  {
385  $isSetup= trim($setupRecurring[$record["billingSystemId"]]) == "setup";
386  if ($isSetup) // Workflows are only attached to setups.
387  $unattachedFeatureDetailsDDL[]= array("text" => strval($record["prodDesc"]) . ": " . strval($record["itemDesc"]), "value" => $record["value"], "cu" => strtolower($record["cu"]));
388  }
389  array_unshift($unattachedFeatureDetailsDDL, array("text" => "(None)", "value" => "", "cu" => ""));
390  $returnArray["unattachedFeatureDetailsDDL"]= $unattachedFeatureDetailsDDL;
391 
392  if ($cu != "")
393  {
394  foreach($cuResults["record"] as $record)
395  {
396  if (strtoupper($record["value"]) == $cu)
397  {
398  $returnArray["cuName"]= $record["text"];
399  break;
400  }
401  }
402  }
403 }
404 
405 /* Saves the issue. If new, it will get all columns and insert. If preexisting, it will only update the columns changed. Any new comments will be added, any changed comments updated, and
406 any deleted comments deleted. If the status of the issue changes to Active, email will be sent off to the next task owner (if that isn't the same as before and it isn't the logged in user.)
407 If the status of the issue changes to Complete, corresponding feature in billing will advance to Active if applicable. */
408 function saveIssue($dbh, $staffId, $isAdd)
409 {
410  $parameters= array();
411 
412  $allowed= array("contactId" => "string", "issue" => "string", "status" => "string", "owner" => "string", "targetDate" => "string", "comments" => "string", "deletedCommentIds" => "string",
413  "cu" => "string", "cuName" => "string", "targetDateType" => "string", "contactName" => "string", "contactPhone" => "string", "contactEmail" => "string", "billingFeatureId" => "string",
414  "dropDeadDate" => "string");
415  if (!$isAdd)
416  {
417  $allowed["changedIssue"]= "string";
418  $allowed["changedStatus"]= "string";
419  $allowed["changedOwner"]= "string";
420  $allowed["targetDateChanged"]= "string";
421  $allowed["trackId"]= "digits";
422  $allowed["changedContactId"]= "string";
423  $allowed["changedDropDeadDate"]= "string";
424  }
425 
426  dms_import_v2($parameters, "BOTTOM_LEVEL", $allowed);
427 
428  $cu= strtolower($parameters["BOTTOM_LEVEL"]["cu"]);
429  $cuName= $parameters["BOTTOM_LEVEL"]["cuName"];
430  $issue= $parameters["BOTTOM_LEVEL"]["issue"];
431  $status= $parameters["BOTTOM_LEVEL"]["status"];
432  $comments= $parameters["BOTTOM_LEVEL"]["comments"];
433 
434  $sqls= array();
435  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
436  array("cu" => array("type" => "string", "required" => true, "maxlength" => 12),
437  "cuName" => array("type" => "string", "required" => true, "maxlength" => 40),
438  "contactId" => array("type" => "int", "required" => false, "allowNegatives" => true),
439  "issue" => array("type" => "string", "required" => true, "maxlength" => 90),
440  "status" => array("type" => "string", "required" => true, "maxlength" => 40),
441  "owner" => array("type" => "string", "required" => true, "maxlength" => 30),
442  "targetDate" => array("type" => "date", "required" => false),
443  "trackId" => array("type" => "int", "required" => true),
444  "deletedCommentIds" => array("type" => "string", "required" => false),
445  "changedIssue" => array("type" => "boolean", "required" => false),
446  "changedStatus" => array("type" => "boolean", "required" => false),
447  "changedOwner" => array("type" => "boolean", "required" => false),
448  "targetDateChanged" => array("type" => "boolean", "required" => false),
449  "changedContactId" => array("type" => "boolean", "required" => false),
450  "targetDateType" => array("type" => "string", "required" => false, "maxlength" => 10),
451  "contactName" => array("type" => "string", "required" => false, "maxlength" => 80),
452  "contactPhone" => array("type" => "phone", "required" => false, "maxlength" => 20),
453  "contactEmail" => array("type" => "email", "required" => false, "maxlength" => 50),
454  "billingFeatureId" => array("type" => "int", "required" => false),
455  "dropDeadDate" => array("type" => "date", "required" => false),
456  "changedDropDeadDate" => array("type" => "boolean", "required" => false)));
457  $cuClean= strtolower($parameters["BOTTOM_LEVEL"]["cu"]);
458  $contactId= $parameters["BOTTOM_LEVEL"]["contactId"];
459  $issueClean= $parameters["BOTTOM_LEVEL"]["issue"];
460  $statusClean= $parameters["BOTTOM_LEVEL"]["status"];
461  $owner= $parameters["BOTTOM_LEVEL"]["owner"];
462  $targetDate= strval($parameters["BOTTOM_LEVEL"]["targetDate"]);
463  $trackId= isset($parameters["BOTTOM_LEVEL"]["trackId"]) ? intval($parameters["BOTTOM_LEVEL"]["trackId"]) : 0;
464  $deletedCommentIds= $parameters["BOTTOM_LEVEL"]["deletedCommentIds"];
465  $changedIssue= isset($parameters["BOTTOM_LEVEL"]["changedIssue"]) ? ($parameters["BOTTOM_LEVEL"]["changedIssue"] == "Y") : false;
466  $changedStatus= isset($parameters["BOTTOM_LEVEL"]["changedStatus"]) ? ($parameters["BOTTOM_LEVEL"]["changedStatus"] == "Y") : false;
467  $changedOwner= isset($parameters["BOTTOM_LEVEL"]["changedOwner"]) ? ($parameters["BOTTOM_LEVEL"]["changedOwner"] == "Y") : false;
468  $changedTargetDate= isset($parameters["BOTTOM_LEVEL"]["targetDateChanged"]) ? ($parameters["BOTTOM_LEVEL"]["targetDateChanged"] == "Y") : false;
469  $changedContactId= isset($parameters["BOTTOM_LEVEL"]["changedContactId"]) ? ($parameters["BOTTOM_LEVEL"]["changedContactId"] == "Y") : false;
470  $targetDateType= strval($parameters["BOTTOM_LEVEL"]["targetDateType"]);
471  $contactPhone= $parameters["BOTTOM_LEVEL"]["contactPhone"];
472  $contactEmail= $parameters["BOTTOM_LEVEL"]["contactEmail"];
473  $contactName= $parameters["BOTTOM_LEVEL"]["contactName"];
474  $billingFeatureId= $parameters["BOTTOM_LEVEL"]["billingFeatureId"];
475  $dropDeadDate= isset($parameters["BOTTOM_LEVEL"]["dropDeadDate"]) ? $parameters["BOTTOM_LEVEL"]["dropDeadDate"] : "";
476  $changedDropDeadDate= isset($parameters["BOTTOM_LEVEL"]["changedDropDeadDate"]) ? ($parameters["BOTTOM_LEVEL"]["changedDropDeadDate"] == "Y") : false;
477 
478  if (false === array_search($targetDateType, array("", "TBD", "FLEX")))
479  $errors[]= "Target Date Type is not recognized.";
480 
481  $contactId= $contactId == "" ? "null" : intval($contactId);
482  $targetDate = $targetDate == "" ? "null" : "'$targetDate'";
483  $targetDateType= $targetDateType == "" ? "null" : "'$targetDateType'";
484  $contactPhone= $contactPhone == "" ? "null" : "'$contactPhone'";
485  $contactEmail= $contactEmail == "" ? "null" : "'$contactEmail'";
486  $contactName= $contactName == "" ? "null" : "'$contactName'";
487  $dropDeadDate= $dropDeadDate == "" ? "null" : "'$dropDeadDate'";
488 
489  $transactionSQLs= array();
490  $issueEmails= array();
491  if ($isAdd)
492  {
493  // Either Target Date and Target Date Type are exclusive.
494  if (($targetDate == "null" && $targetDateType == "null") || ($targetDate != "null" && $targetDateType != "null"))
495  $errors[]= "Target Date and Target Date Type are out of sync.";
496 
497  $execSQL= "select nextval('cutrack_track_id_seq'::text::regclass) as next";
498  $sqls[]= $execSQL;
499  $execResults= runSelectStatement($execSQL, $dbh, array("next" => "next"));
500  $trackId= intval($execResults["record"][0]["next"]);
501  $errors= array_merge($errors, $execResults["error"]);
502 
503  $transactionSQLs[]= "insert into cutrack (track_id, user_name, issue, status, assigned_to, contact_id, contact, contact_phone, contact_email, target_date, target_date_type,
504  drop_dead_date, entry_date, last_activity_date) values ($trackId, '$cuClean', '$issueClean', '$statusClean', '$owner', $contactId, $contactName, $contactPhone, $contactEmail, $targetDate,
505  $targetDateType, $dropDeadDate, now(), now())";
506  }
507  else
508  {
509  $nowActive= false;
510  $nowComplete= false;
511  $nowDelete= false;
512 
513  // Will only update what has been changed.
514  $updateList= array();
515  if ($changedContactId)
516  {
517  $updateList[]= "contact_id= $contactId";
518  if ($contactId == "null" || $contactId == "-1")
519  {
520  $updateList[]= "contact= $contactName";
521  $updateList[]= "contact_phone= $contactPhone";
522  $updateList[]= "contact_email= $contactEmail";
523  }
524  }
525 
526  if ($changedIssue)
527  $updateList[]= "issue= '$issueClean'";
528  if ($changedStatus)
529  {
530  $updateList[]= "status= '$statusClean'";
531  if ($status == "Active")
532  $nowActive= true;
533  else if ($status == "Completed")
534  {
535  $nowComplete= true;
536  $updateList[]= "complete_date= now()";
537  }
538  else if ($status == "Deleted")
539  {
540  $nowDelete= true;
541  $updateList[]= "billing_feature_id=0"; // Unlink billing
542 
543  $startOfMonth= new DateTime();
544  $startOfMonth= $startOfMonth->format("Y-m-01");
545  $transactionSQLs[]= // Set the feature to "No Workflow" but only if it is the pending state. If it already active, then ignore.
546  // If the feature has already been partially billed, set it to status of 5 instead. ASSUMPTION: if the date is past, then it has already been billed.
547  "update cubillfeaturedetail set billing_status= case when partially_billed_date is not null and partially_billed_date < '$startOfMonth' then
548  5 else 4 end from cutrack t where billing_status=1 and cubillfeaturedetail.id= t.billing_feature_id and t.track_id= $trackId";
549  }
550 
551  if (!$nowComplete && !$nowDelete)
552  $updateList[]= "complete_date= null"; // clear this out when job was completed at some time but then the status was changed. Job Report query will filter out these jobs otherwise.
553  }
554 
555  if ($changedOwner)
556  $updateList[]= "assigned_to = '$owner'";
557 
558  if ($changedDropDeadDate)
559  $updateList[]= "drop_dead_date = $dropDeadDate";
560  if ($changedTargetDate)
561  {
562  // Either Target Date and Target Date Type are exclusive.
563  if (($targetDate == "null" && $targetDateType == "null") || ($targetDate != "null" && $targetDateType != "null"))
564  $errors[]= "Target Date and Target Date Type are out of sync.";
565  $updateList[]= "target_date = $targetDate, target_date_type = $targetDateType";
566  }
567  if ($billingFeatureId != "")
568  {
569  $updateList[]= "billing_feature_id=$billingFeatureId";
570  $transactionSQLs[]= "update cubillfeaturedetail set billing_status= case when billing_status = 5 or partially_billed_date is null then 1 else 0 end where id= $billingFeatureId";
571  // Update from status 4: no workflow to 1: pending.
572 
573  // Need to update the billing_ids of the triggered sales items.
574  $transactionSQLs[]= "update cutrackitem set billing_item_id= coalesce(fd.id, 0) from cutrackitem ti inner join cutrack t on ti.track_id= t.track_id and t.track_id= $trackId
575  left join cuissuesitem ii on ti.issue_item_id= ii.item_id and ii.issue_id= t.issue_id left join cubillfeaturedetail tfd on tfd.id= $billingFeatureId left join cubillfeaturedetail fd
576  on fd.sales_order_detail_id= tfd.sales_order_detail_id and fd.sales_item_id= ii.sales_item_id where ti.trackitem_id= cutrackitem.trackitem_id";
577  }
578 
579 
580  if (count($updateList) != 0 || $comments != "" || $comments != "[]" || $deletedCommentIds != "" || $deletedCommentIds != "[]")
581  {
582  $updateList[]= "last_activity_date= now()";
583  $transactionSQLs[]= "update cutrack set " . implode(", ", $updateList) . " where track_id= $trackId";
584 
585  if ($nowComplete)
586  {
587  updateBilling($dbh, $trackId, $transactionSQLs);
588 
589  $sql= "select trackitem_id, billing_item_id from cutrackitem where track_id = $trackId and billing_item_id <> 0 and complete_date is null";
590  $results= runSelectStatementMap($sql, $dbh);
591  $sqls[]= $sql;
592  $errors= array_merge($errors, $results["error"]);
593 
594  foreach($results["record"] as $key => $value)
595  {
596  updateBillingTaskTrigger($dbh, "", $value, $transactionSQLs);
597  }
598  }
599 
600  $dateChangeText= array();
601  if ($changedDropDeadDate)
602  {
603  $date= new DateTime(str_replace("'", "", $dropDeadDate));
604  $dateChangeText[]= "<b>Drop Dead Date</b> was changed to " . $date->format("m/d/Y") . ".";
605  }
606  if ($changedTargetDate)
607  {
608  if ($targetDateType != "null")
609  $formattedDate= $targetDateType;
610  else
611  {
612  $date= new DateTime(str_replace("'", "", $targetDate));
613  $formattedDate= $date->format("m/d/Y");
614  }
615  $dateChangeText[]= "<b>Target Date</b> was changed to $formattedDate.";
616  }
617  if (count($dateChangeText) > 0 && $staffId != $owner) {
618  $results = CreateToLine($dbh, array(trim($owner)));
619  if ($results["status"] !== "000") {
620  $errors[] = $results["error"];
621  }
622  $issueEmails[] = array("to" => $results["data"], "subject" => "Job dates were changed!", "text" => implode("<br />", $dateChangeText),
623  "issueName" => $issueClean, "issueId" => $trackId, "status" => $status);
624  }
625  }
626  }
627 
628  if ($comments != "")
629  {
630  $comments= json_decode($comments, true);
631  if (is_array($comments))
632  {
633  foreach($comments as $comment)
634  {
635  $theseErrors= cleanValuesForDatabase($comment,
636  array("commentId" => array("type" => "int", "required" => false),
637  "text" => array("type" => "string", "required" => false)));
638  $commentId= intval($comment["commentId"]);
639  $text= $comment["text"];
640 
641  $text= $text == "" ? "null" : "'$text'";
642 
643  $errors= array_merge($errors, $theseErrors);
644 
645  if ($commentId == 0)
646  $transactionSQLs[]= "insert into cuinfo_notes (messagetext, cu, author, createddate, activitydate, trackitem_id, track_id) values ($text, '$cu', '$staffId', now(), now(), 0, $trackId)";
647  else
648  $transactionSQLs[]= "update cuinfo_notes set messagetext= $text, activitydate= now() where messageid= $commentId";
649  }
650  }
651  else
652  {
653  $errors[]= "Comments do not form a valid array";
654  }
655  }
656 
657  if ($deletedCommentIds != "")
658  {
659  $idArray= json_decode($deletedCommentIds, true);
660  if (is_array($idArray))
661  {
662  if (count($idArray) > 0)
663  {
664  foreach($idArray as $id)
665  {
666  $cleanedId= cleanInteger($id);
667  if ($cleanedId == "ERROR")
668  {
669  $errors[]= "deletedCommentIds has some invalid ids";
670  break;
671  }
672  }
673 
674  $transactionSQLs[]= "delete from cuinfo_notes where messageid in (" . implode(", ", $idArray) . ")";
675  }
676  }
677  else
678  $errors[]= "ignoreIds is not a valid array";
679  }
680 
681  // Create issue owner email
682  if (strtolower($staffId) != strtolower($owner)) {
683  if ($isAdd || $changedOwner) {
684 
685  $results = CreateToLine($dbh, array(trim($owner)));
686  if ($results["status"] !== "000") {
687  $errors[] = $results["error"];
688  }
689  $subject = "Owner changed!";
690  $text = "Congratulations! You are the next owner of the job!";
691  $issueEmails[] = array("to" => $results["data"], "subject" => $subject, "text" => $text,
692  "issueName" => $issue, "issueId" => $trackId, "status" => $status);
693  }
694  }
695 
696  $sqls= array_merge($sqls, $transactionSQLs);
697  if (count($errors) == 0) {
698  $results = runExecStatement(implode(";", $transactionSQLs) . ";", $dbh);
699  $errors = array_merge($errors, $results["error"]);
700  if (count($errors) == 0) {
701  foreach($issueEmails as $issueEmail) {
702  CreateIssueEmail($issueEmail["to"], $issueEmail["subject"], $issueEmail["text"], $issueEmail["issueName"], $issueEmail["status"],
703  $cuName, $cu, $issueEmail["issueId"]);
704  }
705  }
706  }
707 
708 
709 
710 
711  return array("error" => $errors, "sql" => $sqls, "trackId" => $trackId);
712 }
713 
714 /* Will add a new contact in the monitor contact table. */
715 function addNewContact($dbh)
716 {
717  $parameters= array();
718  dms_import_v2($parameters, "BOTTOM_LEVEL",
719  array("firstName" => "string", "lastName" => "string", "role" => "string", "phone" => "string", "email" => "string", "comment" => "string", "decider" => "string", "cu" => "string"));
720 
721  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
722  array("firstName" => array("type" => "string", "required" => true, "maxlength" => 40),
723  "lastName" => array("type" => "string", "required" => true, "maxlength" => 25),
724  "role" => array("type" => "string", "required" => true, "maxlength" => 10),
725  "phone" => array("type" => "phone", "required" => false),
726  "email" => array("type" => "email", "required" => false, "maxlength" => 50),
727  "decider" => array("type" => "boolean", "required" => false),
728  "comment" => array("type" => "string", "required" => false),
729  "cu" => array("type" => "string", "required" => true, "maxlength" => 12)));
730 
731  $firstName= $parameters["BOTTOM_LEVEL"]["firstName"];
732  $lastName= $parameters["BOTTOM_LEVEL"]["lastName"];
733  $role= $parameters["BOTTOM_LEVEL"]["role"];
734  $phone= $parameters["BOTTOM_LEVEL"]["phone"];
735  $email= $parameters["BOTTOM_LEVEL"]["email"];
736  $decider= $parameters["BOTTOM_LEVEL"]["decider"];
737  $comment= $parameters["BOTTOM_LEVEL"]["comment"];
738  $cu= strtolower($parameters["BOTTOM_LEVEL"]["cu"]);
739 
740  $fullName= "$firstName $lastName";
741  $firstName= $firstName == "" ? "null" : "'$firstName'";
742  $lastName= $lastName == "" ? "null" : "'$lastName'";
743  $role= $role == "" ? "null" : "'$role'";
744  $phone= $phone == "" ? "null" : "'$phone'";
745  $email= $email == "" ? "null" : "'$email'";
746  $decider= $decider == "" ? "null" : "'$decider'";
747  $comment= $comment == "" ? "null" : "'$comment'";
748  $cu= $cu == "" ? "null" : "'$cu'";
749 
750  // This only has one insert statement but if more are added in the future, then it too will need a transaction.
751  $insertSQL= "insert into monitor_contact (fname, lname, role, phone, email, decider, comment, user_name) values ($firstName, $lastName, $role, $phone, $email, $decider, $comment, $cu);
752  select currval('monitor_contact_id_seq'::regclass) as next;";
753  $returnArray= array("sql" => array($insertSQL), "error" => $errors, "operation" => "add");
754  if (count($errors) == 0)
755  {
756  $results= runSelectStatement($insertSQL, $dbh, array("next" => "next"));
757  $returnArray["error"]= $results["error"];
758  $contactId= intval($results["record"][0]["next"]);
759 
760  $returnArray["newDDLRecord"]= array("contactId" => $contactId, "text" => $fullName, "phone" => $parameters["BOTTOM_LEVEL"]["phone"], "email" => $parameters["BOTTOM_LEVEL"]["email"],
761  "cu" => $parameters["BOTTOM_LEVEL"]["cu"], "sortNumber" => 3);
762  }
763  return $returnArray;
764 }
765 
766 /* This will get the comments for the current issue. "readIssueEdit" gets the list on page load. This function is called when the "Show All" button is clicked. So it toggles back and forth
767 between only the logged in user's comments for the issue to any comments for that issue and tasks underneath that issue. */
768 function getComments($dbh, $staffId)
769 {
770  $parameters= array();
771  dms_import_v2($parameters, "BOTTOM_LEVEL",
772  array("getAll" => "string", "trackId" => "string", "trackItemId" => "string", "ignoreIds" => "string"));
773 
774  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
775  array("getAll" => array("type" => "boolean", "required" => false),
776  "trackId" => array("type" => "int", "required" => true),
777  "trackItemId" => array("type" => "int", "required" => false),
778  "ignoreIds" => array("type" => "string", "required" => false)));
779 
780  $getAll= $parameters["BOTTOM_LEVEL"]["getAll"] == "Y";
781  $trackId= $parameters["BOTTOM_LEVEL"]["trackId"];
782  $trackItemId= $parameters["BOTTOM_LEVEL"]["trackItemId"];
783  $ignoreIds= $parameters["BOTTOM_LEVEL"]["ignoreIds"];
784 
785  // If looking at issue comments, ALL includes track item comments
786  $trackItemId= intval($trackItemId);
787  $trackItemIdSQLPiece= $trackItemId == 0 ? ($getAll ? "" : "and trackitem_id= 0") : "and trackitem_id = $trackItemId";
788 
789  $insensitiveStaffId= strtolower($staffId);
790  $authorSQLPiece= $getAll ? "" : "and lower(author) = '$insensitiveStaffId'";
791  $limitIdsSQLPiece= "";
792 
793  if ($ignoreIds != "")
794  {
795  $idArray= json_decode($ignoreIds, true);
796  if (is_array($idArray))
797  {
798  if (count($idArray) > 0)
799  {
800  foreach($idArray as $id)
801  {
802  $cleanedId= cleanInteger($id);
803  if ($cleanedId == "ERROR")
804  {
805  $errors[]= "ignoreIds has some invalid ids";
806  break;
807  }
808  }
809 
810  $limitIdsSQLPiece= "and messageid not in (" . implode(", ", $idArray) . ")";
811  }
812  }
813  else
814  $errors[]= "ignoreIds is not a valid array";
815  }
816 
817  $commentsSQL= "select row_number() over(order by messageid) as number, messageid, author, createddate, messagetext, trackitem_id from cuinfo_notes
818  where track_id = $trackId $trackItemIdSQLPiece $authorSQLPiece $limitIdsSQLPiece order by createddate";
819 
820  $record= array();
821  if (count($errors) == 0)
822  {
823  $results= runSelectStatement($commentsSQL, $dbh, array("messageid" => "commentId", "author" => "author", "createddate" => "createdDate", "messagetext" => "text",
824  "trackitem_id" => "trackItemId", "number" => "mockCommentId")); // For differentiating when showing all issue comments.
825  $errors= $results["error"];
826  $record= $results["record"];
827  }
828  return array("sql" => array($commentsSQL), "error" => $errors, "record" => $record, "operation" => "read");
829 }
830 
831 /* Retrieves a list of tasks for the particular issue. This is called on load and when the "Show All" button is clicked. Doing that will toggle between showing N/A tasks. */
832 function readTasks($dbh)
833 {
834  $parameters= array();
835  dms_import_v2($parameters, "BOTTOM_LEVEL", array("trackId" => "string"));
836 
837  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
838  array("trackId" => array("type" => "int", "required" => true)));
839 
840  $trackId= $parameters["BOTTOM_LEVEL"]["trackId"];
841 
842  $sql= "select i.trackitem_id, i.title, i.item_desc, i.complete_date, i.task_status, i.parent, i.assigned_to, i.last_status_changed,
843  row_number() over(partition by i.parent order by i.view_order, i.trackitem_id) - 1 as position, i.issue_item_id, i.billing_item_id
844  from cutrackitem i where i.track_id= $trackId order by i.parent, i.task_status, i.view_order, i.trackitem_id";
845 
846  $taskTemplatesSQL= "select ii.item_id, ii.title from cuissuesitem ii inner join cutrack t on ii.issue_id = t.issue_id and t.track_id= $trackId";
847  $sqls= array($taskTemplatesSQL, $sql);
848 
849  $newRecords= array();
850  $masterTasks= array();
851  if (count($errors) == 0)
852  {
853  $taskResults= runSelectStatement($taskTemplatesSQL, $dbh, array("item_id" => "taskTemplateId", "title" => "title"));
854  $masterTasks= $taskResults["record"];
855 
856  $results= runSelectStatement($sql, $dbh, array("trackitem_id" => "taskId", "title" => "title", "item_desc" => "description", "complete_date" => "completedOn", "position" => "position",
857  "task_status" => "statusId", "parent" => "parentTaskId", "assigned_to" => "ownerId", "last_status_changed" => "statusChangedDate",
858  "issue_item_id" => "taskTemplateId", "billing_item_id" => "billingItemId"));
859  $errors= array_merge($taskResults["error"], $results["error"]);
860  $taskStatuses= getTaskStatuses();
861 
862  $newRecords= array();
863 
864  foreach($results["record"] as $record)
865  {
866  $record["statusName"]= $taskStatuses[$record["statusId"]];
867  $newRecords[]= $record;
868  }
869  }
870 
871  return array("record" => $newRecords, "operation" => "read", "error" => $errors, "sql" => $sqls, "masterTasks" => $masterTasks);
872 }
873 
874 /* This will save a new task or edit a preexisting one. If the owner is changed, then an email will be sent to the new owner (provided that the issue is Active.) */
875 function ModifyTask($dbh, $isAdd, $staffId)
876 {
877  $parameters= array();
878  $allowed= array("title" => "string", "description" => "string", "ownerId" => "string", "issueId" => "string", "issueOwnerId" => "string",
879  "cu" => "string", "cuName" => "string", "issue" => "string", "parentTaskOwnerId" => "string", "isNextTask" => "string", "isFirstTask" => "string");
880  if ($isAdd)
881  {
882  $allowed["parentTaskId"]= "string";
883  $allowed["position"]= "string";
884  }
885  else
886  {
887  $allowed["taskId"]= "string";
888  $allowed["previousOwnerId"]= "string";
889  }
890 
891  dms_import_v2($parameters, "BOTTOM_LEVEL", $allowed);
892 
893  $title= $parameters["BOTTOM_LEVEL"]["title"];
894  $description= $parameters["BOTTOM_LEVEL"]["description"];
895  $issue= $parameters["BOTTOM_LEVEL"]["issue"];
896  $cuCode= strtolower($parameters["BOTTOM_LEVEL"]["cu"]);
897  $cuName= $parameters["BOTTOM_LEVEL"]["cuName"];
898  $parentTaskOwnerId= trim($parameters["BOTTOM_LEVEL"]["parentTaskOwnerId"]);
899  $ownerId= trim($parameters["BOTTOM_LEVEL"]["ownerId"]);
900  $issueOwnerId= trim($parameters["BOTTOM_LEVEL"]["issueOwnerId"]);
901  $previousOwnerId= isset($parameters["BOTTOM_LEVEL"]["previousOwnerId"]) ? trim($parameters["BOTTOM_LEVEL"]["previousOwnerId"]) : "";
902 
903  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
904  array("title" => array("required" => false, "type" => "string", "maxlength" => 80),
905  "description" => array("required" => false, "type" => "string"),
906  "taskId" => array("required" => true, "type" => "int"),
907  "issueId" => array("required" => true, "type" => "int"),
908  "ownerId" => array("required" => false, "type" => "string", "maxlength" => 12),
909  "parentTaskId" => array("required" => false, "type" => "int"),
910  "issueOwnerId" => array("required" => true, "type" => "string", "maxlength" => 30),
911  "previousOwnerId" => array("required" => false, "type" => "string", "maxlength" => 12),
912  "cu" => array("type" => "string", "required" => true, "maxlength" => 12),
913  "cuName" => array("type" => "string", "required" => true, "maxlength" => 40),
914  "issue" => array("type" => "string", "required" => true, "maxlength" => 90),
915  "position" => array("type" => "int", "required" => true),
916  "isNextTask" => array("type" => "boolean", "required" => false),
917  "isFirstTask" => array("type" => "boolean", "required" => false)));
918 
919  $titleClean= $parameters["BOTTOM_LEVEL"]["title"];
920  $descriptionClean= $parameters["BOTTOM_LEVEL"]["description"];
921  $taskId= isset($parameters["BOTTOM_LEVEL"]["taskId"]) ? intval($parameters["BOTTOM_LEVEL"]["taskId"]) : 0;
922  $issueId= $parameters["BOTTOM_LEVEL"]["issueId"];
923  $ownerIdClean= $parameters["BOTTOM_LEVEL"]["ownerId"];
924  $parentTaskId= isset($parameters["BOTTOM_LEVEL"]["parentTaskId"]) ? intval($parameters["BOTTOM_LEVEL"]["parentTaskId"]) : 0;
925  $position= isset($parameters["BOTTOM_LEVEL"]["position"]) ? $parameters["BOTTOM_LEVEL"]["position"] : 0;
926  $isNextTask= $parameters["BOTTOM_LEVEL"]["isNextTask"] == "Y";
927  $isFirstTask= $parameters["BOTTOM_LEVEL"]["isFirstTask"] == "Y";
928 
929  $titleForDatabase= $title == "" ? "null" : "'$titleClean'";
930  $descriptionForDatabase= $description == "" ? "null" : "'$descriptionClean'";
931 
932  $transactionSQLs= array();
933  $sqls= array();
934 
935  if (count($errors) == 0)
936  {
937  if ($isAdd)
938  {
939  $sql= "select nextval('cutrackitem_trackitem_id_seq'::text::regclass) as next";
940 
941  $sqls[]= $sql;
942  $results= runSelectStatement($sql, $dbh, array("next" => "next"));
943  $taskId= intval($results["record"][0]["next"]);
944 
945  $transactionSQLs[]= "insert into cutrackitem (trackitem_id, title, item_desc, assigned_to, track_id, parent, view_order, last_status_changed)
946  values ($taskId, $titleForDatabase, $descriptionForDatabase, '$ownerIdClean', $issueId, $parentTaskId, $position, now())";
947 
948  // Ensure that there is a previous task owner! Otherwise a task reorder operation will send an email to blank.
949  if ($isFirstTask)
950  {
951  $transactionSQLs[]= "update cutrack set previous_task_owner= '$ownerIdClean' where track_id= $issueId";
952  }
953  }
954  else
955  {
956  $transactionSQLs[]= "update cutrackitem set title=$titleForDatabase, item_desc=$descriptionForDatabase, assigned_to='$ownerIdClean' where trackitem_id=$taskId";
957  }
958 
959  $transactionSQLs[]= "update cutrack set last_activity_date= now() where track_id= $issueId";
960  }
961 
962  $sqls= array_merge($sqls, $transactionSQLs);
963 
964  if (count($errors) == 0)
965  {
966  $results= runExecStatement(implode(";", $transactionSQLs) . ";", $dbh);
967  $errors= array_merge($errors, $results["error"]);
968  }
969 
970  // Find effective ownerId (if subtask is set to default and parent task is set to default, it would be the issue owner)
971  if ($ownerId == "")
972  $ownerId= $parentTaskOwnerId;
973  if ($ownerId == "")
974  $ownerId= $issueOwnerId;
975 
976  if ($isNextTask && !$isAdd && strtolower($ownerId) != strtolower($staffId) && count($errors) == 0) {
977  if ($isAdd || $ownerId != $previousOwnerId) {
978 
979  $text = "Congratulations! $staffId has assigned a task to you. Please work on this task.";
980  $text2 = "If you have any questions, please contact $issueOwnerId.";
981 
982  $results = CreateToLine($dbh, array(trim($ownerId)));
983  if ($results["status"] !== "000") {
984  $errors[] = $results["error"];
985  }
986  $subject = "You have been assigned a task!";
987 
988  CreateTaskEmail($results["data"], $subject, $text, $issue, $title, $description, $cuName, $cuCode, $issueId, $taskId, $text2);
989  }
990  }
991 
992  $requerySQL= "select i.trackitem_id, i.title, i.item_desc, i.complete_date, i.task_status, i.parent, i.assigned_to, i.last_status_changed, i.billing_item_id from cutrackitem i
993  where i.trackitem_id= $taskId";
994  $sqls[]= $requerySQL;
995  $requeryResults= runSelectStatement($requerySQL, $dbh, array("trackitem_id" => "taskId", "title" => "title", "item_desc" => "description", "complete_date" => "completedOn",
996  "task_status" => "statusId", "parent" => "parentTaskId", "assigned_to" => "ownerId", "last_status_changed" => "statusChangedDate", "billing_item_id" => "billingItemId"));
997  $errors= array_merge($errors, $requeryResults["error"]);
998 
999  $records= array();
1000  $taskStatuses= getTaskStatuses();
1001  if (isset($requeryResults["record"][0]))
1002  {
1003  $record= $requeryResults["record"][0];
1004  $record["statusName"]= $taskStatuses[$record["statusId"]];
1005  $record["position"]= $record["taskId"];
1006  $records[]= $record;
1007  }
1008 
1009  return array("record" => $records, "sql" => $sqls, "error" => $errors, "operation" => $isAdd ? "add" : "update");
1010 }
1011 
1012 /* This will remove a task from the database. */
1013 function deleteTask($dbh, $staffId)
1014 {
1015  $parameters= array();
1016  dms_import_v2($parameters, "BOTTOM_LEVEL", array("taskId" => "string", "deleteSubTasks" => "string", "cu" => "string", "cuName" => "string", "isNew" => "string",
1017  "trackId" => "string"));
1018 
1019  $cu= strtoupper($parameters["BOTTOM_LEVEL"]["cu"]);
1020  $cuName= $parameters["BOTTOM_LEVEL"]["cuName"];
1021 
1022  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
1023  array("taskId" => array("required" => true, "type" => "int"),
1024  "deleteSubTasks" => array("required" => false, "type" => "boolean"),
1025  "cu" => array("type" => "string", "required" => true, "maxlength" => 12),
1026  "cuName" => array("type" => "string", "required" => true, "maxlength" => 40),
1027  "isNew" => array("required" => false, "type" => "boolean"),
1028  "trackId" => array("required" => true, "type" => "int"),
1029  "issue" => array("type" => "string", "required" => true, "maxlength" => 90)));
1030  $taskId= $parameters["BOTTOM_LEVEL"]["taskId"];
1031  $deleteSubTasks= $parameters["BOTTOM_LEVEL"]["deleteSubTasks"] == "Y";
1032  $isNew= $parameters["BOTTOM_LEVEL"]["isNew"] == "Y";
1033  $issueId= intval($parameters["BOTTOM_LEVEL"]["trackId"]);
1034 
1035  $sqls= array();
1036 
1037  if ($taskId == 0 && $deleteSubTasks)
1038  $errors[]= "Not allowed to delete all top-level tasks.";
1039 
1040  $transactionSQLs= array();
1041 
1042  $transactionSQLs[]= "delete from cutrackitem where trackitem_id = $taskId";
1043  $transactionSQLs[]= "delete from cutrackitem where parent = $taskId";
1044  $transactionSQLs[]= "update cutrack set last_activity_date= now() where track_id= $issueId";
1045 
1046  $taskEmails= array();
1047  $issueEmails= array();
1048  if (!$isNew)
1049  notifyNextTaskOwner($dbh, $issueId, $taskId, $staffId, "deleted", null, $errors, $transactionSQLs, $taskEmails, $issueEmails);
1050 
1051  $sqls= array_merge($sqls, $transactionSQLs);
1052 
1053  if (count($errors) == 0)
1054  {
1055  $results= runExecStatement(implode(";", $transactionSQLs) . ";", $dbh);
1056  $errors= array_merge($errors, $results["error"]);
1057  if (count($errors) == 0)
1058  {
1059  foreach($taskEmails as $taskEmail)
1060  {
1061  createTaskEmail($taskEmail["to"], $taskEmail["subject"], $taskEmail["text"], $taskEmail["issue"], $taskEmail["title"], $taskEmail["description"], $cuName, $cu,
1062  $taskEmail["issueId"], $taskEmail["taskId"], "", $taskEmail["completeNotes"]);
1063  }
1064  foreach($issueEmails as $issueEmail)
1065  {
1066  createIssueEmail($issueEmail["to"], $issueEmail["subject"], $issueEmail["text"], $issueEmail["issue"], $issueEmail["status"], $cuName, $cu, $issueEmail["issueId"],
1067  "", $issueEmail["completeNotes"]);
1068  }
1069  }
1070  }
1071 
1072  return array("error" => $errors, "sql" => $sqls, "operation" => "delete");
1073 }
1074 
1075 /* This will change a task status from active to completed and from active to N/A. When completing a task, the next task owner (if changed) will be notified by email. */
1076 function changeTaskStatus($dbh, $staffId)
1077 {
1078  $parameters= array();
1079 
1080  dms_import_v2($parameters, "BOTTOM_LEVEL", array("taskId" => "string", "newStatus" => "string", "prevStatus" => "string", "issueId" => "string", "issueOwnerId" => "string",
1081  "parentTaskId" => "string", "cu" => "string", "cuName" => "string", "nextTaskId" => "string", "nextTaskOwnerChanged" => "string", "nextTaskOwner" => "string",
1082  "completeNotes" => "string", "forceEmail" => "string", "completeDate" => "string", "billingItemId" => "string"));
1083 
1084  $cu= strtoupper($parameters["BOTTOM_LEVEL"]["cu"]);
1085  $cuName= $parameters["BOTTOM_LEVEL"]["cuName"];
1086 
1087  $completeNotes= $parameters["BOTTOM_LEVEL"]["completeNotes"];
1088 
1089  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
1090  array("taskId" => array("required" => true, "type" => "int"),
1091  "newStatus" => array("required" => true, "type" => "int"),
1092  "prevStatus" => array("required" => true, "type" => "int"),
1093  "issueId" => array("required" => true, "type" => "int"),
1094  "issueOwnerId" => array("required" => true, "type" => "string", "maxlength" => 30),
1095  "isNew" => array("required" => false, "type" => "boolean"),
1096  "parentTaskId" => array("required" => true, "type" => "int"),
1097  "cu" => array("type" => "string", "required" => true, "maxlength" => 12),
1098  "cuName" => array("type" => "string", "required" => true, "maxlength" => 40),
1099  "nextTaskId" => array("type" => "int", "required" => false),
1100  "nextTaskOwnerChanged" => array("type" => "boolean", "required" => false),
1101  "nextTaskOwner" => array("type" => "string", "required" => false, "maxlength" => 30),
1102  "completeNotes" => array("type" => "string", "required" => false),
1103  "forceEmail" => array("type" => "boolean", "required" => false),
1104  "completeDate" => array("type" => "date", "required" => false),
1105  "billingItemId" => array("type" => "int", "required" => false)));
1106 
1107  $taskId= $parameters["BOTTOM_LEVEL"]["taskId"];
1108  $newStatus= $parameters["BOTTOM_LEVEL"]["newStatus"];
1109  $prevStatus= $parameters["BOTTOM_LEVEL"]["prevStatus"];
1110  $issueId= $parameters["BOTTOM_LEVEL"]["issueId"];
1111  $issueOwnerId= $parameters["BOTTOM_LEVEL"]["issueOwnerId"];
1112  $isNew= isset($parameters["BOTTOM_LEVEL"]["isNew"]) ? ($parameters["BOTTOM_LEVEL"]["isNew"] == "Y") : false;
1113  $parentTaskId= $parameters["BOTTOM_LEVEL"]["parentTaskId"];
1114  $nextTaskOwnerChanged= $parameters["BOTTOM_LEVEL"]["nextTaskOwnerChanged"] == "Y";
1115  $nextTaskOwner= $parameters["BOTTOM_LEVEL"]["nextTaskOwner"];
1116  $completeNotesClean= $parameters["BOTTOM_LEVEL"]["completeNotes"];
1117  $forceEmail= $parameters["BOTTOM_LEVEL"]["forceEmail"] == "Y";
1118  $nextTaskId= $parameters["BOTTOM_LEVEL"]["nextTaskId"];
1119  $cuClean= strtolower($parameters["BOTTOM_LEVEL"]["cu"]);
1120  $completeDate= $parameters["BOTTOM_LEVEL"]["completeDate"];
1121  $billingItemId= intval($parameters["BOTTOM_LEVEL"]["billingItemId"]);
1122 
1123  $taskEmails= array();
1124  $issueEmails= array();
1125  $sqls= array();
1126 
1127  $transactionSQLs= array();
1128 
1129  $completeDateSafe = "";
1130  if ($completeDate != "" && count($errors) == 0)
1131  {
1132  if (count($errors) == 0)
1133  {
1134  $completeDateTime= new DateTime($completeDate);
1135  $completeDateSafe= $completeDateTime->format("m/d/Y");
1136  }
1137  else
1138  {
1139  $completeDateSafe= "ERROR";
1140  }
1141  }
1142 
1143  $statuses= getTaskStatuses();
1144  $statusName= $statuses[$newStatus];
1145  if (!isset($statusName))
1146  $errors[]= "Status code doesn't exist";
1147 
1148  $completeDatePiece= $statusName == "Done" ? ($completeDate != "" ? "complete_date = '$completeDate', " : "complete_date = now(), ") : "complete_date = null, ";
1149  $completeDatePieceOther= $completeDate != "" ? "'$completeDate'" : "now()";
1150  $transactionSQLs[]= "update cutrackitem set $completeDatePiece task_status = $newStatus where trackitem_id = $taskId";
1151  $status= $newStatus;
1152  $noteRecord= array();
1153  $reassigned= false;
1154 
1155  if ($parentTaskId != 0) // If it is a subtask then there might be a need to complete the parent task.
1156  {
1157  if ($statusName != "TODO")
1158  {
1159  $transactionSQLs[]= "update cutrackitem set complete_date = $completeDatePieceOther, task_status = 0 where trackitem_id = $parentTaskId and not exists
1160  (select 'FOUND' from cutrackitem where parent = $parentTaskId and task_status = 1 and trackitem_id <> $taskId)";
1161  }
1162  else
1163  {
1164  $transactionSQLs[]= "update cutrackitem set complete_date = null, task_status = 1 where trackitem_id = $parentTaskId";
1165  }
1166  }
1167  else if ($statusName == "Done")
1168  {
1169 
1170  $findChildrenSQL= "select trackitem_id, billing_item_id from cutrackitem where parent = $taskId and complete_date is null";
1171  $results= runSelectStatementMap($findChildrenSQL, $dbh);
1172  $sqls[]= $findChildrenSQL;
1173  $errors= array_merge($errors, $results["error"]);
1174 
1175  if (count(array_keys($results["record"])) > 0)
1176  {
1177  foreach($results["record"] as $key => $value)
1178  {
1179  if ($value != 0)
1180  updateBillingTaskTrigger($dbh, $completeDate, $value, $transactionSQLs);
1181  }
1182 
1183  $transactionSQLs[]= "update cutrackitem set complete_date = $completeDatePieceOther, task_status = 0 where parent = $taskId";
1184  }
1185 
1186 
1187  if ($nextTaskId != "" && $nextTaskOwnerChanged)
1188  {
1189  $transactionSQLs[]= "update cutrackitem set assigned_to='$nextTaskOwner' where trackitem_id = $nextTaskId";
1190  $reassigned= true;
1191  }
1192 
1193  if ($completeNotes != "")
1194  {
1195  $noteRecord= array("author" => $staffId, "text" => $completeNotes, "trackItemId" => $taskId);
1196 
1197  $sql= "select nextval('cuinfo_notes_messageid_seq'::text::regclass) as next";
1198  $sqls[]= $sql;
1199  $results= runSelectStatement($sql, $dbh, array("next" => "next"));
1200  $errors= array_merge($errors, $results["error"]);
1201  $commentId= intval($results["record"][0]["next"]);
1202  $noteRecord["commentId"]= $commentId;
1203 
1204  $transactionSQLs[]= "insert into cuinfo_notes (messageid, trackitem_id, track_id, messagetext, cu, author, createddate, activitydate)
1205  values ($commentId, $taskId, $issueId, '$completeNotesClean', '$cuClean', '$staffId', now(), now())";
1206  }
1207 
1208  if ($billingItemId != 0)
1209  {
1210  updateBillingTaskTrigger($dbh, $completeDate, $billingItemId, $transactionSQLs);
1211  }
1212  }
1213  else if ($statusName == "N/A")
1214  {
1215  $transactionSQLs[]= "update cutrackitem set complete_date= null, task_status = 2 where parent = $taskId";
1216  }
1217 
1218  $transactionSQLs[]= "update cutrack set last_activity_date= now() where track_id= $issueId";
1219 
1220  switch($statusName)
1221  {
1222  case "Done":
1223  $mode= "finished";
1224  break;
1225  case "N/A":
1226  $mode= "inactivated";
1227  break;
1228  default:
1229  $mode= "reactivated";
1230  break;
1231  }
1232 
1233  notifyNextTaskOwner($dbh, $issueId, $taskId, $staffId, $mode, $issueOwnerId, $errors, $transactionSQLs, $taskEmails, $issueEmails, $completeNotes,
1234  $forceEmail, $reassigned);
1235 
1236  $sqls= array_merge($sqls, $transactionSQLs);
1237  if (count($errors) == 0)
1238  {
1239  $results= runExecStatement(implode(";", $transactionSQLs) . ";", $dbh);
1240  $errors= array_merge($errors, $results["error"]);
1241 
1242  if (count($errors) == 0)
1243  {
1244  foreach($taskEmails as $taskEmail)
1245  {
1246  createTaskEmail($taskEmail["to"], $taskEmail["subject"], $taskEmail["text"], $taskEmail["issue"], $taskEmail["title"], $taskEmail["description"], $cuName, $cu,
1247  $taskEmail["issueId"], $taskEmail["taskId"], "", $taskEmail["completeNotes"]);
1248  }
1249  foreach($issueEmails as $issueEmail)
1250  {
1251  createIssueEmail($issueEmail["to"], $issueEmail["subject"], $issueEmail["text"], $issueEmail["issue"], $issueEmail["status"], $cuName, $cu, $issueEmail["issueId"],
1252  "", $issueEmail["completeNotes"]);
1253  }
1254  }
1255  }
1256 
1257  if (count($errors) > 0)
1258  {
1259  $status= $prevStatus;
1260  $statusName= $statuses[$prevStatus];
1261  }
1262 
1263  $returnArray= array("error" => $errors, "sql" => $sqls, "statusId" => $status, "statusName" => $statusName, "completeDate" => trim($completeDateSafe));
1264 
1265  if ($completeNotes != "")
1266  $returnArray["noteRecord"]= $noteRecord;
1267 
1268  return $returnArray;
1269 }
1270 
1271 function updateBillingTaskTrigger($dbh, $completeDate, $billingItemId, &$transactionSQLs)
1272 {
1273  // Set it to the current month if billing isn't run yet, otherwise next month.
1274  try
1275  {
1276  $thisDate= trim($completeDate) == "" ? new DateTime() : new DateTime($completeDate);
1277  $m= $thisDate->format("m");
1278  $Y= $thisDate->format("Y");
1279  $thisDate->setDate($Y, $m, 1);
1280  $tryMonth1= $thisDate->format("Y-m-d");
1281  $thisDate->setDate($Y, $m+1, 1);
1282  $tryMonth2= $thisDate->format("Y-m-d");
1283  }
1284  catch(Exception $e)
1285  {
1286  $tryMonth1= "ERROR";
1287  $tryMonth2= "ERROR";
1288  }
1289  // Complete any hooks to billing
1290  $transactionSQLs[]= "update cubillfeaturedetail set start_date= t.date, end_date= t.date, billing_status=2, workflow_completed='Y' from
1291  (select case when not exists (select 'FOUND' from cubillmonth where billing_date='$tryMonth1') then date '$tryMonth1' else date '$tryMonth2' end) as t (date)
1292  where cubillfeaturedetail.id= $billingItemId and cubillfeaturedetail.workflow_completed= 'N'";
1293 }
1294 
1295 /* This will update the viewOrder of all tasks with the same parent. This also allows movement from one parent to another. An email will be sent to the next task owner if different. */
1296 function reorderTasks($dbh, $staffId)
1297 {
1298  $parameters= array();
1299  dms_import_v2($parameters, "BOTTOM_LEVEL", array("taskIds" => "string", "issueId" => "string", "isNew" => "string", "parentTaskId" => "string", "cu" => "string", "cuName" => "string",
1300  "taskId" => "string", "moveTaskId" => "string", "moveAfter" => "string"));
1301 
1302  $cu= $parameters["BOTTOM_LEVEL"]["cu"];
1303  $cuName= $parameters["BOTTOM_LEVEL"]["cuName"];
1304 
1305  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"], array("taskIds" => array("required" => true, "type" => "string"),
1306  "issueId" => array("required" => true, "type" => "int"),
1307  "isNew" => array("required" => false, "type" => "boolean"),
1308  "parentTaskId" => array("required" => true, "type" => "int"),
1309  "cu" => array("type" => "string", "required" => true, "maxlength" => 12),
1310  "cuName" => array("type" => "string", "required" => true, "maxlength" => 40),
1311  "taskId" => array("type" => "int", "required" => true),
1312  "moveTaskId" => array("type" => "int", "required" => true),
1313  "moveAfter" => array("type" => "boolean", "required" => false)));
1314  $taskIds= json_decode($parameters["BOTTOM_LEVEL"]["taskIds"], true);
1315  $issueId= $parameters["BOTTOM_LEVEL"]["issueId"];
1316  $isNew= $parameters["BOTTOM_LEVEL"]["isNew"] == "Y";
1317  $parentTaskId= $parameters["BOTTOM_LEVEL"]["parentTaskId"];
1318  $moveAfter= $parameters["BOTTOM_LEVEL"]["moveAfter"] == "Y";
1319  $taskId= $parameters["BOTTOM_LEVEL"]["taskId"];
1320  $moveTaskId= $parameters["BOTTOM_LEVEL"]["moveTaskId"];
1321  $sqls= array();
1322 
1323  $transactionSQLs= array();
1324  $taskEmails= array();
1325  $issueEmails= array();
1326 
1327  if (is_array($taskIds))
1328  {
1329  $sqlPartArray= array();
1330 
1331  $index= 0;
1332  $firstTaskId= -1;
1333  foreach($taskIds as $taskId)
1334  {
1335  $taskId= cleanInteger($taskId);
1336  if ($firstTaskId == -1)
1337  $firstTaskId= $taskId;
1338  if ($taskId == "ERROR")
1339  {
1340  $errors[]= "TaskIds do not have all integers";
1341  break;
1342  }
1343  else
1344  {
1345  $sqlPartArray[]= "($taskId, $index, $parentTaskId)";
1346  $index++;
1347  }
1348  }
1349 
1350  $transactionSQLs[]= "update cutrackitem as ti set view_order = t.view_order, parent= t.parent from (values " . implode(", ", $sqlPartArray) . ") as t(trackitem_id, view_order, parent)
1351  where ti.trackitem_id = t.trackitem_id";
1352 
1353  if (count($taskIds) > 0)
1354  {
1355  $transactionSQLs[]= "update cutrack set last_activity_date= now() where track_id= $issueId";
1356 
1357  if (!$isNew)
1358  {
1359  if ($parentTaskId == 0)
1360  {
1361  $sql= "with ordered_trackitem as (select ti.track_id, ti.trackitem_id, ti.assigned_to, ti.parent, ti.title, ti.item_desc, row_number() over (partition by ti.parent order by ti.parent,
1362  ti.view_order, ti.trackitem_id) as number from cutrackitem ti where ti.task_status <> 2 and ti.complete_date is null and (ti.parent = $firstTaskId or ti.trackitem_id= $firstTaskId))
1363  select t.previous_task_owner, sub.assigned_to as sassigned_to, parent.assigned_to as passigned_to, sub.title as stitle, parent.title as ptitle, sub.item_desc as sitem_desc,
1364  parent.item_desc as pitem_desc, sub.trackitem_id as strackitem_id, parent.trackitem_id as ptrackitem_id, t.assigned_to, t.issue, t.status from cutrack t
1365  left join ordered_trackitem parent on parent.parent= 0 and parent.number= 1
1366  left join ordered_trackitem sub on sub.parent= parent.trackitem_id and sub.number= 1 where t.track_id= $issueId limit 1";
1367  $sqls[]= $sql;
1368  $results= runSelectStatement($sql, $dbh, array("previous_task_owner" => "previousTaskOwner", "sassigned_to" => "subAssignedTo", "passigned_to" => "parentAssignedTo",
1369  "stitle" => "subTitle", "sitem_desc" => "subDesc", "pitem_desc" => "parentDesc", "ptitle" => "parentTitle", "strackitem_id" => "subTaskId", "ptrackitem_id" => "parentTaskId",
1370  "assigned_to" => "issueOwner", "issue" => "issue", "status" => "status"));
1371  $record= $results["record"][0];
1372  $errors= array_merge($errors, $results["error"]);
1373  }
1374  else
1375  {
1376  $sql= "select ti.track_id, ti.trackitem_id, ti.assigned_to as passigned_to, ti.parent, ti.title, ti.item_desc, t.assigned_to, t.issue, t.status, t.previous_task_owner from cutrack t
1377  inner join cutrackitem ti on t.track_id= ti.track_id and t.track_id= $issueId and ti.task_status <> 2 and ti.complete_date is null and ti.parent= 0
1378  order by ti.parent, ti.view_order, ti.trackitem_id limit 1";
1379  $sqls[]= $sql;
1380  $results= runSelectStatement($sql, $dbh, array("previous_task_owner" => "previousTaskOwner", "assigned_to" => "issueOwner", "issue" => "issue", "status" => "status",
1381  "item_desc" => "parentDesc", "title" => "parentTitle", "trackitem_id" => "parentTaskId", "passigned_to" => "parentAssignedTo"));
1382  $record= $results["record"][0];
1383  $errors= array_merge($errors, $results["error"]);
1384 
1385  if ($record["parentTaskId"] == $parentTaskId)
1386  $sql= "select ti.track_id, ti.trackitem_id, ti.assigned_to, ti.parent, ti.title, ti.item_desc from cutrackitem ti
1387  where ti.trackitem_id= $firstTaskId limit 1";
1388  else
1389  $sql= "select ti.track_id, ti.trackitem_id, ti.assigned_to, ti.parent, ti.title, ti.item_desc from cutrackitem ti
1390  where ti.parent= " . $record["parentTaskId"] . " order by ti.parent, ti.view_order, ti.trackitem_id limit 1";
1391 
1392  $sqls[]= $sql;
1393  $results= runSelectStatement($sql, $dbh, array("assigned_to" => "subAssignedTo", "title" => "subTitle", "item_desc" => "subDesc", "trackitem_id" => "subTaskId"));
1394 
1395  $record= array_merge($record, $results["record"][0]);
1396  $errors= array_merge($errors, $results["error"]);
1397  }
1398 
1399  $hasTasks= false;
1400 
1401  $previousTaskOwner= $record["previousTaskOwner"];
1402  $issueOwnerId= isset($issueOwnerId) ? $issueOwnerId : $record["issueOwner"];
1403  $issue= $record["issue"];
1404  $status= $record["status"];
1405 
1406  if ($record["subTaskId"] != "")
1407  {
1408  $nextTaskOwner= $record["subAssignedTo"];
1409  if ($nextTaskOwner == "")
1410  $nextTaskOwner= $record["parentAssignedTo"];
1411  if ($nextTaskOwner == "")
1412  $nextTaskOwner= $issueOwnerId;
1413  $title= $record["subTitle"];
1414  $description= $record["subDesc"];
1415  $taskId= $record["subTaskId"];
1416  $hasTasks= true;
1417  }
1418  else if ($record["parentTaskId"] != "")
1419  {
1420  $nextTaskOwner= $record["parentAssignedTo"];
1421  if ($nextTaskOwner == "")
1422  $nextTaskOwner= $issueOwnerId;
1423  $title= $record["parentTitle"];
1424  $description= $record["parentDesc"];
1425  $taskId= $record["parentTaskId"];
1426  $hasTasks= true;
1427  }
1428 
1429  // Now we can send an email.
1430  if ($hasTasks && (strtolower($nextTaskOwner) != strtolower($previousTaskOwner)))
1431  {
1432 
1433  $transactionSQLs[] = "update cutrack set previous_task_owner = '$nextTaskOwner' where track_id= $issueId";
1434 
1435  $text = "Tasks were rearranged. $nextTaskOwner is the next task owner, not $previousTaskOwner.";
1436  $results = CreateToLine($dbh, array(trim($nextTaskOwner), trim($previousTaskOwner)));
1437  if ($results["status"] !== "000") {
1438  $errors[] = $results["error"];
1439  }
1440  $subject = "You are the next task owner!";
1441  $taskEmails[] = array("to" => $results["data"], "subject" => $subject, "text" => $text, "issue" => $issue, "title" => $title,
1442  "description" => $description, "issueId" => $issueId, "taskId" => $taskId, "completeNotes" => $completeNotes);
1443  }
1444  }
1445  }
1446  }
1447  else
1448  {
1449  $errors[]= "TaskIds array is not valid";
1450  }
1451 
1452  $sqls= array_merge($sqls, $transactionSQLs);
1453  if (count($errors) == 0)
1454  {
1455  $results= runExecStatement(implode(";", $transactionSQLs), $dbh);
1456  $errors= array_merge($errors, $results["error"]);
1457  if (count($errors) == 0)
1458  {
1459  foreach($taskEmails as $taskEmail)
1460  {
1461  createTaskEmail($taskEmail["to"], $taskEmail["subject"], $taskEmail["text"], $taskEmail["issue"], $taskEmail["title"], $taskEmail["description"], $cuName, $cu,
1462  $taskEmail["issueId"], $taskEmail["taskId"], "", $taskEmail["completeNotes"]);
1463  }
1464  foreach($issueEmails as $issueEmail)
1465  {
1466  createIssueEmail($issueEmail["to"], $issueEmail["subject"], $issueEmail["text"], $issueEmail["issue"], $issueEmail["status"], $cuName, $cu, $issueEmail["issueId"],
1467  "", $issueEmail["completeNotes"]);
1468  }
1469  }
1470  }
1471 
1472  return array("error" => $errors, "sql" => $sqls);
1473 }
1474 
1475 /* This will add a comment to a particular task. */
1476 function modifyTaskComment($dbh, $staffId, $isAdd)
1477 {
1478  $parameters= array();
1479  $import= array("text" => "string");
1480  if ($isAdd)
1481  {
1482  $import["taskId"]= "string";
1483  $import["issueId"]= "string";
1484  $import["cu"]= "string";
1485  }
1486  else
1487  {
1488  $import["commentId"]= "string";
1489  }
1490  dms_import_v2($parameters, "BOTTOM_LEVEL", $import);
1491 
1492  $text= $parameters["BOTTOM_LEVEL"]["text"];
1493  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
1494  array("taskId" => array("required" => true, "type" => "int"),
1495  "issueId" => array("required" => true, "type" => "int"),
1496  "text" => array("required" => false, "type" => "string"),
1497  "cu" => array("required" => true, "type" => "string", "maxlength" => 10),
1498  "commentId" => array("required" => true, "type" => "int"),
1499  "isFirstTask" => array("required" => false, "type" => "boolean")));
1500 
1501  $taskId= $parameters["BOTTOM_LEVEL"]["taskId"];
1502  $issueId= $parameters["BOTTOM_LEVEL"]["issueId"];
1503  $textClean= $parameters["BOTTOM_LEVEL"]["text"];
1504  $cu= strtolower($parameters["BOTTOM_LEVEL"]["cu"]);
1505  $commentId= isset($parameters["BOTTOM_LEVEL"]["commentId"]) ? intval($parameters["BOTTOM_LEVEL"]["commentId"]) : 0;
1506  $isFirstTask= isset($parameters["BOTTOM_LEVEL"]["isFirstTask"]) ? ($parameters["BOTTOM_LEVEL"]["isFirstTask"] == "Y") : false;
1507 
1508  $record= array("commentId" => $commentId, "author" => $staffId, "text" => $text, "trackItemId" => $taskId);
1509 
1510  $textClean= $textClean == "" ? "null" : "'$textClean'";
1511  $cu= $cu == "" ? "null" : "'$cu'";
1512  $sqls= array();
1513  $transactionSQLs= array();
1514 
1515  if ($isAdd)
1516  {
1517  $sql= "select nextval('cuinfo_notes_messageid_seq'::text::regclass) as next";
1518 
1519  $sqls[]= $sql;
1520  $results= runSelectStatement($sql, $dbh, array("next" => "next"));
1521  $errors= array_merge($errors, $results["error"]);
1522  $commentId= intval($results["record"][0]["next"]);
1523  $record["commentId"]= $commentId;
1524 
1525  $transactionSQLs[]= "insert into cuinfo_notes (messageid, trackitem_id, track_id, messagetext, cu, author, createddate, activitydate) values ($commentId, $taskId, $issueId, $textClean, $cu,
1526  '$staffId', now(), now())";
1527  }
1528  else
1529  {
1530  $transactionSQLs[]= "update cuinfo_notes set messagetext=$textClean, activitydate=now() where messageid=$commentId";
1531  }
1532 
1533  $transactionSQLs[]= "update cutrack set last_activity_date= now() where track_id= $issueId";
1534 
1535  $sqls= array_merge($sqls, $transactionSQLs);
1536 
1537  if (count($errors) == 0)
1538  {
1539  $results= runExecStatement(implode(";", $transactionSQLs), $dbh);
1540  $errors= array_merge($errors, $results["error"]);
1541  }
1542 
1543  return array("error" => $errors, "sql" => $sqls, "record" => array($record));
1544 }
1545 
1546 /* This will update the billing status to Active in billing so that the feature can be billed after the workflow is done. */
1547 function updateBilling($dbh, $issueId, &$transactionSQLs)
1548 {
1549  $nextMonth= new DateTime();
1550  $m = $nextMonth->format('m');
1551  $Y = $nextMonth->format('Y');
1552  $nextMonth->setDate($Y, $m, 1);
1553  $thisMonthDate= $nextMonth->format("Y-m-d");
1554  $nextMonth->setDate($Y, $m+1, 1);
1555  $nextMonthDate= $nextMonth->format("Y-m-d");
1556 
1557  $transactionSQLs[]= "update cubillfeaturedetail set billing_status= case when billing_status = 0 then 3 else 2 end, start_date= '$nextMonthDate', end_date= '$nextMonthDate',
1558  workflow_completed= 'Y' where id in (select billing_feature_id from cutrack where track_id= $issueId) and workflow_completed= 'N'";
1559 }
1560 
1561 /* This will change the task parent for the "move underneath..." and "make parent" options. */
1562 function promoteTask($dbh, $staffId)
1563 {
1564  $parameters= array();
1565  dms_import_v2($parameters, "BOTTOM_LEVEL", array("taskId" => "string", "parentTaskId" => "string", "cu" => "string", "cuName" => "string", "trackId" => "string"));
1566 
1567  $cu= strtoupper($parameters["BOTTOM_LEVEL"]["cu"]);
1568  $cuName= $parameters["BOTTOM_LEVEL"]["cuName"];
1569 
1570  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
1571  array("taskId" => array("required" => true, "type" => "int"),
1572  "parentTaskId" => array("required" => true, "type" => "int"),
1573  "cu" => array("type" => "string", "required" => true, "maxlength" => 12),
1574  "cuName" => array("type" => "string", "required" => true, "maxlength" => 40),
1575  "trackId" => array("type" => "int", "required" => true)));
1576 
1577  $taskId= $parameters["BOTTOM_LEVEL"]["taskId"];
1578  $parentTaskId= $parameters["BOTTOM_LEVEL"]["parentTaskId"];
1579  $issueId= intval($parameters["BOTTOM_LEVEL"]["trackId"]);
1580 
1581  $transactionSQLs= array();
1582  $sqls= array();
1583  $taskEmails= array();
1584  $issueEmails= array();
1585 
1586  $transactionSQLs[]= "update cutrackitem set view_order= temp.position, parent= 0
1587  from (select i.trackitem_id, row_number() over(order by i.trackitem_id in ($parentTaskId,$taskId) desc, i.view_order, i.trackitem_id)+coalesce(iparent.view_order,0) as position
1588  from cutrackitem i inner join cutrackitem iparent on i.track_id= iparent.track_id and i.track_id= $issueId and iparent.trackitem_id= $parentTaskId
1589  where i.trackitem_id= $taskId or ( coalesce(i.view_order,0) >= coalesce(iparent.view_order,0) and i.parent= 0)) temp
1590  where cutrackitem.trackitem_id= temp.trackitem_id";
1591 
1592  $transactionSQLs[]= "update cutrack set last_activity_date= now() where track_id= $issueId";
1593 
1594  if (!$isNew)
1595  notifyNextTaskOwner($dbh, $issueId, null, $staffId, "promoted", null, $errors, $transactionSQLs, $taskEmails, $issueEmails);
1596 
1597  $sqls= array_merge($sqls, $transactionSQLs);
1598  if (count($errors) == 0)
1599  {
1600  $results= runExecStatement(implode(";", $transactionSQLs), $dbh);
1601  $errors= array_merge($errors, $results["error"]);
1602  if (count($errors) == 0)
1603  {
1604  foreach($taskEmails as $taskEmail)
1605  {
1606  createTaskEmail($taskEmail["to"], $taskEmail["subject"], $taskEmail["text"], $taskEmail["issue"], $taskEmail["title"], $taskEmail["description"], $cuName, $cu,
1607  $taskEmail["issueId"], $taskEmail["taskId"], "", $taskEmail["completeNotes"]);
1608  }
1609  foreach($issueEmails as $issueEmail)
1610  {
1611  createIssueEmail($issueEmail["to"], $issueEmail["subject"], $issueEmail["text"], $issueEmail["issue"], $issueEmail["status"], $cuName, $cu, $issueEmail["issueId"],
1612  "", $issueEmail["completeNotes"]);
1613  }
1614  }
1615  }
1616 
1617  return array("sql" => $sqls, "error" => $errors);
1618 }
1619 
1620 function addIssueFromTemplate($dbh, $staffId)
1621 {
1622  $sqls= array();
1623  $parameters= array();
1624  dms_import_v2($parameters, "BOTTOM_LEVEL", array("templateId" => "string", "cu" => "string", "cuName" => "string"));
1625 
1626  $cu= strtolower($parameters["BOTTOM_LEVEL"]["cu"]);
1627  $cuName= $parameters["BOTTOM_LEVEL"]["cuName"];
1628 
1629  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
1630  array("templateId" => array("required" => true, "type" => "int"),
1631  "cu" => array("required" => true, "type" => "string", "maxlength" => 12),
1632  "cuName" => array("type" => "string", "required" => true, "maxlength" => 40)));
1633  $templateId= $parameters["BOTTOM_LEVEL"]["templateId"];
1634 
1635  $transactionSQLs= array();
1636  $taskEmails= array();
1637 
1638  $findIssueSQL= "select i.issue_id, i.code, i.owner, ii.item_id, ii.title, ii.item_desc, ii.view_order, ii.owner as task_owner, ii.parent, ii.sales_item_id
1639  from cuissues i left join cuissuesitem ii on i.issue_id = ii.issue_id where i.issue_id = '$templateId' order by i.issue_id, ii.parent, ii.view_order, ii.item_id";
1640  $results= runSelectStatement($findIssueSQL, $dbh, array("issue_id" => "issueId", "code" => "issueTitle", "owner" => "issueOwner", "item_id" => "taskId", "title" => "taskTitle",
1641  "item_desc" => "taskDescription", "view_order" => "taskViewOrder", "task_owner" => "taskOwner", "parent" => "taskParent", "sales_item_id" => "salesItemId"));
1642 
1643  $getNextIdSQL= "select nextval('cutrackitem_trackitem_id_seq'::text::regclass) as next";
1644  $nextResults= runSelectStatement($getNextIdSQL, $dbh, array("next" => "next"));
1645  $errors= array_merge($errors, $results["error"], $nextResults["error"]);
1646  array_push($sqls, $findIssueSQL, $getNextIdSQL);
1647 
1648  $nextTaskId= intval($nextResults["record"][0]["next"]);
1649  $first= true;
1650  $insertIssueSQL= "insert into cutrack (track_id, issue, assigned_to, previous_task_owner, user_name, status, target_date_type, entry_date, last_activity_date, issue_id)";
1651  $insertTasksSQL= "insert into cutrackitem (trackitem_id, track_id, title, item_desc, view_order, parent, assigned_to, issue_item_id, billing_item_id)";
1652  $issueOwner= "";
1653  $issueId= "";
1654  $taskSQLPartArray= array();
1655  $subTaskArray= array();
1656  $parentArray= array();
1657  $hasTasks= false;
1658  $issue= "";
1659  $templateIssueId= "";
1660  $index= 0;
1661  $parentTaskId= $nextTaskId;
1662 
1663  $previousTaskOwner= "";
1664  $firstParentTask= null;
1665  $firstSubTask= null;
1666  $firstParentTaskId= "";
1667  $taskIdMap= array();
1668  foreach($results["record"] as $record)
1669  {
1670  if ($first)
1671  {
1672  $issueName= $record["issueTitle"];
1673  $issue= prepSave($record["issueTitle"], 90);
1674  $issueOwner= trim(strval($record["issueOwner"])) != "" ? prepSave($record["issueOwner"], 30) : prepSave($staffId, 30);
1675  $templateIssueId= $record["issueId"];
1676 
1677  $sql= "select nextval('cutrack_track_id_seq'::text::regclass) as next";
1678  $results= runSelectStatement($sql, $dbh, array("next" => "next"));
1679  $errors= array_merge($errors, $results["error"]);
1680  $sqls[]= $sql;
1681  $issueId= intval($results["record"][0]["next"]);
1682  $taskOwner = $record["taskOwner"];
1683 
1684  $insertIssueSQL.= " values ($issueId, '$issue', '$issueOwner', '$taskOwner', '$cu', 'Active', 'TBD', now(), now(), $templateId)";
1685  $transactionSQLs[]= $insertIssueSQL;
1686 
1687  $previousTaskOwner= $record["issueOwner"];
1688  $first= false;
1689  }
1690 
1691  $taskId= $record["taskId"];
1692 
1693  if($taskId != "" && $templateIssueId == $record["issueId"])
1694  {
1695  $title= prepSave($record["taskTitle"], 80);
1696  $description= prepSave($record["taskDescription"]);
1697  $viewOrder= $record["taskViewOrder"];
1698  $owner= prepSave($record["taskOwner"], 12);
1699  $parent= $record["taskParent"];
1700  $taskTemplateId= $record["taskId"];
1701  $salesItemId= intval($record["salesItemId"]);
1702 
1703 
1704  if ($parent == 0)
1705  {
1706  if (!isset($firstParentTask))
1707  {
1708  $firstParentTask= $record;
1709  }
1710  $taskIdMap[$taskTemplateId]= $parentTaskId;
1711  $taskSQLPartArray[]= "($parentTaskId, $issueId, '$title', '$description', $viewOrder, 0, '$owner', $taskTemplateId, $salesItemId)";
1712  $parentArray[$taskTemplateId]= $index++;
1713  $parentTaskId++;
1714  }
1715  else
1716  {
1717  if (!isset($subTaskArray[$parent]))
1718  $subTaskArray[$parent]= array();
1719  $subTaskArray[$parent][]= $record;
1720  }
1721  $hasTasks= true;
1722  }
1723  }
1724 
1725  $subTaskId= $parentTaskId;
1726  if ($hasTasks)
1727  {
1728  $transactionSQLs[]= "$insertTasksSQL values " . implode(", ", $taskSQLPartArray);
1729 
1730  $taskSQLPartArray= array();
1731 
1732  foreach($subTaskArray as $parentId => $detailRecords)
1733  {
1734  $parentTaskId= $nextTaskId + $parentArray[$parentId];
1735  foreach($detailRecords as $record)
1736  {
1737  $title= prepSave($record["taskTitle"], 80);
1738  $description= prepSave($record["taskDescription"]);
1739  $viewOrder= $record["taskViewOrder"];
1740  $owner= prepSave($record["taskOwner"], 12);
1741  $taskTemplateId= $record["taskId"];
1742  $taskIdMap[$taskTemplateId]= $subTaskId;
1743  $salesItemId= intval($record["salesItemId"]);
1744 
1745  $taskSQLPartArray[]= "($subTaskId, $issueId, '$title', '$description', $viewOrder, $parentTaskId, '$owner', $taskTemplateId, $salesItemId)";
1746  $subTaskId++;
1747  }
1748  }
1749 
1750  if (count($taskSQLPartArray) > 0) // Make sure that there are any subtasks.
1751  {
1752  $transactionSQLs[]= "$insertTasksSQL values " . implode(", ", $taskSQLPartArray);
1753  }
1754 
1755  $transactionSQLs[]= "select setval('cutrackitem_trackitem_id_seq'::text::regclass, $subTaskId, false)"; // Because inserts are done with the id, sequence is not updated.
1756  }
1757 
1758  $issueEmail= array();
1759  $taskEmail= array();
1760  if (!$first) // means that there is an issue created so now send email.
1761  {
1762  if (isset($firstParentTask))
1763  {
1764  $firstSubTask= null;
1765  if (!empty($subTaskArray) && HCU_array_key_exists($firstParentTask["taskId"], $subTaskArray)) {
1766  $firstSubTask= $subTaskArray[$firstParentTask["taskId"]][0];
1767  }
1768 
1769  if (isset($firstSubTask))
1770  {
1771  $title= $firstSubTask["taskTitle"];
1772  $description= $firstSubTask["taskDescription"];
1773  $taskId= $taskIdMap[$firstSubTask["taskId"]];
1774  $taskOwner= $firstSubTask["taskOwner"] == "" ? ($firstParentTask["taskOwner"] == "" ? $issueOwner : $firstParentTask["taskOwner"]) : $firstSubTask["taskOwner"];
1775  }
1776  else
1777  {
1778  $title= $firstParentTask["taskTitle"];
1779  $description= $firstParentTask["taskDescription"];
1780  $taskId= $taskIdMap[$firstParentTask["taskId"]];
1781  $taskOwner= $firstParentTask["taskOwner"] == "" ? $issueOwner : $firstParentTask["taskOwner"];
1782  }
1783 
1784  $text = "Job has been created. You are the first task owner.";
1785  $results = CreateToLine($dbh, array(trim($taskOwner)));
1786  if ($results["status"] !== "000") {
1787  $errors[] = $results["error"];
1788  }
1789  $subject = "You are the next task owner!";
1790  $taskEmails[] = array("to" => $results["data"], "subject" => $subject, "text" => $text, "issue" => $issue, "title" => $title,
1791  "description" => $description, "issueId" => $issueId, "taskId" => $taskId);
1792 
1793  $transactionSQLs[] = "update cutrack set previous_task_owner = '$taskOwner' where track_id = $issueId";
1794  }
1795  }
1796 
1797  $sqls= array_merge($sqls, $transactionSQLs);
1798 
1799  if (count($errors) == 0)
1800  {
1801  $results= runExecStatement(implode(";", $transactionSQLs) . ";", $dbh);
1802  $errors= array_merge($errors, $results["error"]);
1803  if (count($errors) == 0)
1804  {
1805  $results = createToLine($dbh, array(trim($issueOwner)));
1806  if ($results["status"] !== "000") {
1807  $errors[] = $results["error"];
1808  }
1809  $subject = "Job has been created from template!";
1810  $text = "Workflow is set up which is assigned to you.";
1811  $text2 = "Please mark non-relevant tasks to N/A and activate issue so that it can be worked.";
1812  CreateIssueEmail($results["data"], $subject, $text, $issueName, "Active", $cuName, $cu, $issueId, $text2);
1813 
1814  foreach($taskEmails as $taskEmail) {
1815  CreateTaskEmail($taskEmail["to"], $taskEmail["subject"], $taskEmail["text"], $issueName, $taskEmail["title"], $taskEmail["description"],
1816  $cuName, $cu, $issueId, $taskEmail["taskId"]);
1817  }
1818  }
1819  }
1820 
1821  return array("sql" => $sqls, "error" => $errors, "trackId" => $issueId);
1822 }
1823 
1824 function deleteTaskComment($dbh)
1825 {
1826  $sqls= array();
1827  $parameters= array();
1828  dms_import_v2($parameters, "BOTTOM_LEVEL", array("commentId" => "string"));
1829 
1830  $transactionSQLs= array();
1831 
1832  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
1833  array("commentId" => array("required" => true, "type" => "int")));
1834 
1835  $commentId= $parameters["BOTTOM_LEVEL"]["commentId"];
1836 
1837  $transactionSQLs[]= "delete from cuinfo_notes where messageid= $commentId";
1838  $transactionSQLs[]= "update cutrack set last_activity_date= now() where track_id= $issueId";
1839 
1840  if (count($errors) == 0)
1841  {
1842  $results= runExecStatement(implode(";", $transactionSQLs) . ";", $dbh);
1843  $errors= array_merge($errors, $results["error"]);
1844  }
1845  return array("sql" => $transactionSQLs, "error" => $errors, "operation" => "delete");
1846 }
1847 
1848 function pullInTaskTemplates($dbh)
1849 {
1850  $parameters= array();
1851  dms_import_v2($parameters, "BOTTOM_LEVEL", array("taskTemplateIds" => "string", "trackId" => "string", "taskLength" => "string", "issueId" => "string"));
1852 
1853  $errors= cleanValuesForDatabase($parameters["BOTTOM_LEVEL"],
1854  array("trackId" => array("required" => true, "type" => "int"),
1855  "taskLength" => array("required" => true, "type" => "int"),
1856  "issueId" => array("required" => true, "type" => "int")));
1857 
1858  $taskTemplateIdsEncoded= $parameters["BOTTOM_LEVEL"]["taskTemplateIds"];
1859  $trackId= $parameters["BOTTOM_LEVEL"]["trackId"];
1860  $taskLength= $parameters["BOTTOM_LEVEL"]["taskLength"];
1861  $issueId= $parameters["BOTTOM_LEVEL"]["issueId"];
1862 
1863  $transactionSQLs= array();
1864 
1865  if (trim($taskTemplateIdsEncoded) == "")
1866  $errors[]= "TaskTemplateIds are required";
1867  else
1868  {
1869  $taskTemplateIds= json_decode($taskTemplateIdsEncoded, true);
1870  if (is_array($taskTemplateIds))
1871  {
1872  foreach($taskTemplateIds as $templateId)
1873  {
1874  $templateId= cleanInteger($templateId);
1875  if ($templateId == "ERROR")
1876  {
1877  $errors[]= "One or more TaskTemplateIds is not an integer.";
1878  break;
1879  }
1880  }
1881  }
1882  else
1883  $errors[]= "TaskTemplateIds not an array";
1884  }
1885 
1886  $findSql= "select ii.item_id, ii.title, ii.item_desc, ii.view_order, ii.owner as task_owner, ii.parent, i.owner as job_owner, ii.sales_item_id from cuissuesitem ii
1887  inner join cuissues i on ii.issue_id = i.issue_id where ii.item_id in (" . implode(",", $taskTemplateIds) . ") and ii.issue_id = $issueId";
1888 
1889  $getNextIdSQL= "select nextval('cutrackitem_trackitem_id_seq'::text::regclass) as next";
1890  $nextResults= runSelectStatement($getNextIdSQL, $dbh, array("next" => "next"));
1891  $errors= array_merge($errors, $nextResults["error"]);
1892  $sqls= array($findSql, $getNextIdSQL);
1893  $nextTaskId= intval($nextResults["record"][0]["next"]);
1894  $parentTaskId= $nextTaskId;
1895  $subTaskArray= array();
1896 
1897  if (count($taskTemplateIds) > 0)
1898  {
1899  $results= runSelectStatement($findSql, $dbh, array("item_id" => "taskId", "title" => "taskTitle", "item_desc" => "taskDescription", "view_order" => "taskViewOrder",
1900  "task_owner" => "taskOwner", "parent" => "taskParent", "job_owner" => "jobOwner", "sales_item_id" => "billingItemId"));
1901  $errors= $results["error"];
1902 
1903  $insertTasksSQL= "insert into cutrackitem (trackitem_id, track_id, title, item_desc, view_order, parent, assigned_to, issue_item_id, billing_item_id)";
1904  $taskSQLPartArray= array();
1905 
1906  $taskList= array();
1907  foreach($results["record"] as $record)
1908  {
1909  if (intval($record["billingItemId"]) != 0)
1910  $taskList[]= "(" . intval($record["taskId"]) . ", " . intval($record["billingItemId"]) . ")";
1911  }
1912 
1913  if (count($taskList) != 0)
1914  {
1915  $sql= "select t.task_id, fd.id from cutrack ct inner join cubillfeaturedetail bfd on ct.billing_feature_id = bfd.id and ct.track_id= $trackId
1916  inner join cubillsalesorderdetail sod on bfd.sales_order_detail_id = sod.id inner join cubillfeaturedetail fd on sod.id = fd.sales_order_detail_id
1917  inner join (values " . implode(", ", $taskList) . ") as t (task_id, sales_item_id) on fd.sales_item_id= t.sales_item_id";
1918  $tempResults= runSelectStatementMap($sql, $dbh);
1919  $sqls[]= $sql;
1920  $errors= array_merge($errors, $tempResults["error"]);
1921  $map=$tempResults["record"];
1922 
1923  for($i=0, $count=count($results["record"]); $i != $count; $i++)
1924  {
1925  $billingItemId= $map[$results["record"][$i]["taskId"]];
1926  if (isset($billingItemId))
1927  $results["record"][$i]["billingItemId"]= $billingItemId;
1928  }
1929  }
1930 
1931  $index = 0;
1932  foreach($results["record"] as $record)
1933  {
1934  $title= prepSave($record["taskTitle"], 80);
1935  $description= prepSave($record["taskDescription"]);
1936  $viewOrder= $taskLength + intval($record["taskViewOrder"]);
1937  $owner= prepSave($record["taskOwner"], 12);
1938  $parent= $record["taskParent"];
1939  $taskTemplateId= $record["taskId"];
1940  $billingItemId= $record["billingItemId"];
1941 
1942  if ($parent == 0)
1943  {
1944  $taskSQLPartArray[]= "($parentTaskId, $trackId, '$title', '$description', $viewOrder, 0, '$owner', $taskTemplateId, $billingItemId)";
1945  $parentArray[$taskTemplateId]= $index++;
1946  $parentTaskId++;
1947  }
1948  else
1949  {
1950  if (!isset($subTaskArray[$parent]))
1951  $subTaskArray[$parent]= array();
1952  $subTaskArray[$parent][]= $record;
1953  }
1954  $hasTasks= true;
1955  }
1956 
1957  $subTaskId= $parentTaskId;
1958  if ($hasTasks)
1959  {
1960  $transactionSQLs[]= "$insertTasksSQL values " . implode(", ", $taskSQLPartArray);
1961 
1962  $taskSQLPartArray= array();
1963 
1964  foreach($subTaskArray as $parentId => $detailRecords)
1965  {
1966  $parentTaskId= $nextTaskId + $parentArray[$parentId];
1967 
1968  foreach($detailRecords as $record)
1969  {
1970  $title= prepSave($record["taskTitle"], 80);
1971  $description= prepSave($record["taskDescription"]);
1972  $viewOrder= $record["taskViewOrder"];
1973  $owner= prepSave($record["taskOwner"], 12);
1974  $taskTemplateId= $record["taskId"];
1975  $billingItemId= $record["billingItemId"];
1976 
1977  $taskSQLPartArray[]= "($subTaskId, $trackId, '$title', '$description', $viewOrder, $parentTaskId, '$owner', $taskTemplateId, $billingItemId)";
1978  $subTaskId++;
1979  }
1980  }
1981 
1982  if (count($taskSQLPartArray) > 0) // Make sure that there are any subtasks.
1983  {
1984  $transactionSQLs[]= "$insertTasksSQL values " . implode(", ", $taskSQLPartArray);
1985  }
1986 
1987  $transactionSQLs[]= "select setval('cutrackitem_trackitem_id_seq'::text::regclass, $subTaskId, false)"; // Because inserts are done with the id, sequence is not updated.
1988  }
1989 
1990  $transactionSQLs[]= "update cutrack set last_activity_date= now() where track_id= $trackId";
1991  }
1992 
1993  $sqls= array_merge($sqls, $transactionSQLs);
1994  if (count($errors) == 0)
1995  {
1996  $results= runExecStatement(implode(";", $transactionSQLs) . ";", $dbh);
1997  $errors= array_merge($errors, $results["error"]);
1998  }
1999 
2000  $records= array();
2001  $requerySQL= "select i.trackitem_id, i.title, i.item_desc, i.complete_date, i.task_status, i.parent, i.assigned_to, i.last_status_changed, i.view_order as position, i.issue_item_id
2002  from cutrackitem i where i.track_id= $trackId and i.issue_item_id in (" . implode(",", $taskTemplateIds) . ")
2003  order by i.parent, i.task_status, i.view_order, i.trackitem_id";
2004  $sqls[]= $requerySQL;
2005  if (count($taskTemplateIds) > 0)
2006  {
2007  $results= runSelectStatement($requerySQL, $dbh, array("trackitem_id" => "taskId", "title" => "title", "item_desc" => "description", "complete_date" => "completedOn", "position" => "position",
2008  "task_status" => "statusId", "parent" => "parentTaskId", "assigned_to" => "ownerId", "last_status_changed" => "statusChangedDate",
2009  "issue_item_id" => "taskTemplateId"));
2010  $errors= array_merge($errors, $results["error"]);
2011  $records= $results["record"];
2012  }
2013 
2014  return array("sql" => $sqls, "error" => $errors, "record" => $records);
2015 }
2016 
2017 function setReportCookie($pEnv)
2018 {
2019  $parameters= array();
2020  dms_import_v2($parameters, "BOTTOM_LEVEL", array("sort" => "string", "filter" => "string", "showDeleted" => "string", "showCompleted" => "string"));
2021  $sort= $parameters["BOTTOM_LEVEL"]["sort"];
2022  $filter= $parameters["BOTTOM_LEVEL"]["filter"];
2023  $showDeleted= $parameters["BOTTOM_LEVEL"]["showDeleted"] == "Y" ? "Y" : "N";
2024  $showCompleted= $parameters["BOTTOM_LEVEL"]["showCompleted"] == "Y" ? "Y" : "N";
2025 
2026  $errors= array();
2027  $sortUnpacked= json_decode($sort, true);
2028  $filterUnpacked= json_decode($filter, true);
2029  if ($sort != "" && !is_array($sortUnpacked))
2030  $errors[]= "Sort is invalid";
2031  if ($filter != "" && !is_array($filterUnpacked))
2032  $errors[]= "Filter is invalid";
2033 
2034  $expiresInDay= time()+86400;
2035  $expiresInYear= time()+86400*365;
2036 
2037  HCU_setcookie_env($pEnv, "Job_View", "sort=" . urlencode($sort) . "&filter=" . urlencode($filter) . "&showDeleted=$showDeleted&showCompleted=$showCompleted", $expiresInDay);
2038  return array("error" => $errors, "sql" => array());
2039 }
2040 
2041 function clearReportCookie($pEnv)
2042 {
2043  if (isset($_COOKIE["Job_View"]))
2044  {
2045  unset($_COOKIE["Job_View"]);
2046  HCU_setcookie_env($pEnv, "Job_View", "", time() - 3600);
2047  }
2048  return array("error" => array(), "sql" => array());
2049 }