Search code examples
phpcrmissue-trackingticket-systemvtiger

Update existing ticket in VTiger


I am a newbie to VTiger, I am using the 5.2.0 version to try, learn implementing Issue Tracking.

Intro:

A client sends an email to say [email protected], Mail Converter or Mail Scanner.. scans for the new email and if found creates a new ticket.

If admin sees a new ticket which is being raised in Trouble Tickets, makes some changes such as assigning it to someone, or making a comment etc.. VTiger CRM sends an email to a client saying that admin has made modifications to the ticket.

Lets say Client wants some changes, so he replies to [email protected], a new ticket is raised because a new mail as arrived and mail scanner creates a new ticket.

Problem:

Instead of updating an existing ticket which the client has sent before, creating a new ticket everytime duplicates the problem by making many tickets for one issue, which is a big problem.

When ever a client sends a mail to [email protected], Subject of email goes as Title of the Ticket and Body of Email as Description of Ticket.

Lets say

Title of Ticket is SubjectClientSent

Client didnot like something after admin makes some modifications and the client decides to reply to the email which VTiger has sent him, it is generally in this manner.

Re: TT17 [ Ticket Id : 22 ] Re : SubjectClientSent

I dont want Mail Scanner to create a new ticket with the title of ticket as Re: TT17 [ Ticket Id : 22 ] Re : SubjectClientSent , I want it to update the exiting ticket with title SubjectClientSent

I tried to do that with creating a new rule something like this..

alt text

But, its still creating a new ticket.

Could you help me correct this?

Is there a better way of updating the existing ticket?

Thanks for the help and support.


Solution

  • Found the solution!

    The entire answer was written stealing information from the VTiger PDF document, VTiger Forum, VTiger Bug Link

    The below pictures shows the basic process involved in automating ticketing with MailScanner or MailConverter

    ![Mail Scanner Basic Process][4]

    1: Customer (having a Contact/Account record) sends email to [email protected], with subject “Test Trouble Ticket”

    2: Mail Scanner creates ticket, links it to matching contact/account record filtered by emailid lookup. HelpDeskHandler will send a acknowledgment email with more information on how to respond further to Customer. The email subject looks like “TT15 [Ticket Id: 1483] Test Trouble Ticket”

    3: Customer replies to the acknowledgment email keeping part of the subject intact to [email protected]. As mail scanner is configured with Regex rule on subject and finds a matching Trouble Ticket linked to Customer, it updates the comments with the email body.

    4: When support team update their comment, an email is sent to Customer again.

    The following steps will help us achieve this functionality

    Step 1: Setup Outgoing Mail Server

    If your Outgoing Mail Server is Gmail, the following settings should work for you

    Mail Server Settings (SMTP)     
    Server Name ssl://smtp.gmail.com:465 
    User Name   [email protected] 
    Password    ******  
    From Email  [email protected] 
    Requires Authentication?    Yes 
    

    Step 2: Setup MailScanner or MailConverter

    DEFAULT Information 
    
    Scanner Name    DEFAULT
    Server Name     imap.gmail.com
    Protocol       imap4
    User Name     [email protected]
    SSL Type       ssl
    SSL Method      novalidate-cert
    Connect URL     {imap.gmail.com:993/imap4/ssl/novalidate-cert}
    Status       Enabled
    
    Scanning Information 
    
    Look for    All Messages from lastscan
    After scan   Mark message as Read
    

    Step 3: Setup Rules to create & update tickets in MailScanner or MailConverter

    Rules For Mail Converter [DEFAULT] 
    
        Priority    
        From    
        To  
        Subject     Regex   Ticket Id[^:]?: ([0-9]+)
        Body        
        Match   All Condition
        Action  Update Ticket
    
        Priority    
        From    
        To  
        Subject         
        Body        
        Match   Any Condition
        Action  Create Ticket 
    

    Step 4: Configure config.inc.php

    update the following variables in your config.inc.php

    $HELPDESK_SUPPORT_EMAIL_ID

    FROM address information to be used when sending mails Example: [email protected]

    $HELPDESK_SUPPORT_EMAIL_ID = '[email protected]';
    

    $HELPDESK_SUPPORT_NAME

    FROM name to be used for purpose of display for emails sentout. Example: Automated Reply

    $HELPDESK_SUPPORT_NAME = 'Company Support';
    

    $HELPDESK_SUPPORT_EMAIL_REPLY_ID

    REPLY-TO address to be set in the email sent. Example: [email protected]

    $HELPDESK_SUPPORT_EMAIL_REPLY_ID = '[email protected]';
    

    Setting this information is one of the important step for autoticketing. When user tries to Reply for the automated emails the TO address will be set by the mail-client and reaches the MailBox for which we have setup the scanning.

    Step 5: Create HelpDeskHandler.php in VTigerCRM/modules/HelpDesk/

    <?php
    /*+**********************************************************************************
     * The contents of this file are subject to the vtiger CRM Public License Version 1.0
     * ("License"); You may not use this file except in compliance with the License
     * The Original Code is:  vtiger CRM Open Source
     * The Initial Developer of the Original Code is vtiger.
     * Portions created by vtiger are Copyright (C) vtiger.
     * All Rights Reserved.
     ************************************************************************************/
    
    class HelpDeskHandler extends VTEventHandler {
    
      function __getSendToEmail($crmid) {
        if(empty($crmid)) return false;
    
        $sendtoemail = false;
    
        global $adb;
    
        $metaresult = $adb->pquery("SELECT setype FROM vtiger_crmentity WHERE crmid=? AND deleted = 0", array($crmid));
        if($metaresult && $adb->num_rows($metaresult)) {
          $metaresultrow = $adb->fetch_array($metaresult);
    
          $emailres = false;
    
          if($metaresultrow['setype'] == 'Contacts') {
            $emailres = $adb->pquery("SELECT email,yahooid FROM vtiger_contactdetails WHERE contactid = ?", array($crmid));
          } else if($metaresultrow['setype'] == 'Accounts') {
            $emailres = $adb->pquery("SELECT email1,email2 FROM vtiger_account WHERE accountid = ?", array($crmid));
          }
    
          if($emailres && $adb->num_rows($emailres)) {
            $emailresrow = $adb->fetch_array($emailres);
    
            if(!empty($emailresrow[0])) $sendtoemail = $emailresrow[0];
            if(!empty($emailresrow[1])) $sendtoemail = $emailresrow[1];
    
          }
    
        }
        return $sendtoemail;
    
      }
    
    
      function handleEvent($eventName, $entityData) {
        global $log, $adb;
    
        if($eventName == 'vtiger.entity.aftersave') {
          $moduleName = $entityData->getModuleName();
    
          // Event not related to HelpDesk - IGNORE
          if($moduleName != 'HelpDesk') {
            return;
          }
    
          // Take action if the service running is MailScanner (either via Cron/Scan Now)
          if(isset($_REQUEST) && $_REQUEST['service'] == 'MailScanner' ) {
    
            $focus = $entityData->focus;
    
            $sendToEmail = $this->__getSendToEmail($focus->column_fields['parent_id']); 
    
            // If the entity is create new and we know whom to send the mail proceed.
            if($entityData->isNew() && $sendToEmail) {
              global $HELPDESK_SUPPORT_EMAIL_ID, $HELPDESK_SUPPORT_NAME, $HELPDESK_SUPPORT_EMAIL_REPLY_ID;
    
              include_once 'vtlib/Vtiger/Mailer.php';
              $mailer = new Vtiger_Mailer();
              $mailer->ConfigSenderInfo($HELPDESK_SUPPORT_EMAIL_ID, $HELPDESK_SUPPORT_NAME);
              $mailer->AddReplyTo($HELPDESK_SUPPORT_EMAIL_REPLY_ID);
    
              $mailer->initFromTemplate('Auto Ticket First Response Template');
    
              // Update the email subject
              $mailer->Subject = sprintf("%s [ Ticket Id : %s ] Re : %s",
                $focus->column_fields['ticket_no'],
                $focus->id,
                $focus->column_fields['ticket_title']
              );
    
              $mailer->SendTo( $sendToEmail, '', false, false, true );
            }
          }        
        }
      }
    }
    
    ?>
    

    Step 6: Create email template named as "Auto Ticket First Response"

    This is an acknowledgement email Company Support automatically sends after client sends email to [email protected]

    To create Email Template go to Settings/E-Mail Templates; Select New Template and name it as "Auto Ticket First Response"

    Step 7: Create a new PHP file and name it as RegisterHelpDeskHandler.php

    Place the following code & execute the file

    <?php
    /*+**********************************************************************************
     * The contents of this file are subject to the vtiger CRM Public License Version 1.0
     * ("License"); You may not use this file except in compliance with the License
     * The Original Code is:  vtiger CRM Open Source
     * The Initial Developer of the Original Code is vtiger.
     * Portions created by vtiger are Copyright (C) vtiger.
     * All Rights Reserved.
     ************************************************************************************/
    
    $Vtiger_Utils_Log = true;
    
    include_once 'vtlib/Vtiger/Module.php';
    include_once 'vtlib/Vtiger/Event.php';
    
    $moduleInstance = Vtiger_Module::getInstance('HelpDesk');
    Vtiger_Event::register($moduleInstance, 'vtiger.entity.aftersave', 'HelpDeskHandler', 'modules/HelpDesk/HelpDeskHandler.php');
    ?>
    

    To execute, simply type the following URL

    http://localhost:8888/registerHelpDeskHandler.php
    

    You should see the following output in the browser

    Registering Event vtiger.entity.aftersave with [modules/HelpDesk/HelpDeskHandler.php] HelpDeskHandler ... DONE
    

    Step 9: Check for the bug!

    If you are on VTiger 5.2.0, the bug is already fixed!

    If not, go to modules/Settings/MailScanner/core/MailScannerAction.php and replace the entire code with the following code

    <?php
    /*********************************************************************************
     ** The contents of this file are subject to the vtiger CRM Public License Version 1.0
     * ("License"); You may not use this file except in compliance with the License
     * The Original Code is:  vtiger CRM Open Source
     * The Initial Developer of the Original Code is vtiger.
     * Portions created by vtiger are Copyright (C) vtiger.
     * All Rights Reserved.
     *
     ********************************************************************************/
    
    require_once('modules/Emails/Emails.php');
    require_once('modules/HelpDesk/HelpDesk.php');
    require_once('modules/Users/Users.php');
    require_once('modules/Documents/Documents.php');
    
    /**
     * Mail Scanner Action
     */
    class Vtiger_MailScannerAction {
        // actionid for this instance
        var $actionid  = false; 
        // scanner to which this action is associated
        var $scannerid = false;
        // type of mailscanner action
        var $actiontype= false;
        // text representation of action
        var $actiontext= false;
        // target module for action
        var $module    = false;
        // lookup information while taking action
        var $lookup    = false;
    
        // Storage folder to use
        var $STORAGE_FOLDER = 'storage/mailscanner/';
    
        /** DEBUG functionality */
        var $debug     = false;
        function log($message) {
            global $log;
            if($log && $this->debug) { $log->debug($message); }
            else if($this->debug) echo "$message\n";
        }
    
        /**
         * Constructor.
         */
        function __construct($foractionid) {
            $this->initialize($foractionid);        
        }
    
        /**
         * Initialize this instance.
         */
        function initialize($foractionid) {
            global $adb;
            $result = $adb->pquery("SELECT * FROM vtiger_mailscanner_actions WHERE actionid=? ORDER BY sequence", Array($foractionid));
    
            if($adb->num_rows($result)) {
                $this->actionid   = $adb->query_result($result, 0, 'actionid');
                $this->scannerid  = $adb->query_result($result, 0, 'scannerid');
                $this->actiontype = $adb->query_result($result, 0, 'actiontype');
                $this->module     = $adb->query_result($result, 0, 'module');
                $this->lookup     = $adb->query_result($result, 0, 'lookup');
                $this->actiontext = "$this->actiontype,$this->module,$this->lookup";
            }
        }
    
        /**
         * Create/Update the information of Action into database.
         */
        function update($ruleid, $actiontext) {
            global $adb;
    
            $inputparts = explode(',', $actiontext);
            $this->actiontype = $inputparts[0]; // LINK, CREATE
            $this->module     = $inputparts[1]; // Module name
            $this->lookup     = $inputparts[2]; // FROM, TO
    
            $this->actiontext = $actiontext;
    
            if($this->actionid) {
                $adb->pquery("UPDATE vtiger_mailscanner_actions SET scannerid=?, actiontype=?, module=?, lookup=? WHERE actionid=?",
                    Array($this->scannerid, $this->actiontype, $this->module, $this->lookup, $this->actionid));
            } else {
                $this->sequence = $this->__nextsequence();
                $adb->pquery("INSERT INTO vtiger_mailscanner_actions(scannerid, actiontype, module, lookup, sequence) VALUES(?,?,?,?,?)",
                    Array($this->scannerid, $this->actiontype, $this->module, $this->lookup, $this->sequence));
                $this->actionid = $adb->database->Insert_ID();
            }
            $checkmapping = $adb->pquery("SELECT COUNT(*) AS ruleaction_count FROM vtiger_mailscanner_ruleactions 
                WHERE ruleid=? AND actionid=?", Array($ruleid, $this->actionid));
            if($adb->num_rows($checkmapping) && !$adb->query_result($checkmapping, 0, 'ruleaction_count')) {
                $adb->pquery("INSERT INTO vtiger_mailscanner_ruleactions(ruleid, actionid) VALUES(?,?)", 
                    Array($ruleid, $this->actionid));
            }
        }
    
        /**
         * Delete the actions from tables.
         */
        function delete() {
            global $adb;
            if($this->actionid) {
                $adb->pquery("DELETE FROM vtiger_mailscanner_actions WHERE actionid=?", Array($this->actionid));
                $adb->pquery("DELETE FROM vtiger_mailscanner_ruleactions WHERE actionid=?", Array($this->actionid));
            }
        }
    
        /**
         * Get next sequence of Action to use.
         */
        function __nextsequence() {
            global $adb;
            $seqres = $adb->pquery("SELECT max(sequence) AS max_sequence FROM vtiger_mailscanner_actions", Array());
            $maxsequence = 0;
            if($adb->num_rows($seqres)) {
                $maxsequence = $adb->query_result($seqres, 0, 'max_sequence');
            }
            ++$maxsequence;
            return $maxsequence;
        }
    
        /**
         * Apply the action on the mail record.
         */
        function apply($mailscanner, $mailrecord, $mailscannerrule, $matchresult) {
            $returnid = false;
            if($this->actiontype == 'CREATE') {
                if($this->module == 'HelpDesk') {
                    $returnid = $this->__CreateTicket($mailscanner, $mailrecord); 
                }
            } else if($this->actiontype == 'LINK') {
                $returnid = $this->__LinkToRecord($mailscanner, $mailrecord);
            } else if($this->actiontype == 'UPDATE') {
                if($this->module == 'HelpDesk') {
                    $returnid = $this->__UpdateTicket($mailscanner, $mailrecord, 
                        $mailscannerrule->hasRegexMatch($matchresult));
                }
            }
            return $returnid;
        }
    
        /**
         * Update ticket action.
         */
        function __UpdateTicket($mailscanner, $mailrecord, $regexMatchInfo) {
            global $adb;
            $returnid = false;
    
            $usesubject = false;
            if($this->lookup == 'SUBJECT') {
                // If regex match was performed on subject use the matched group
                // to lookup the ticket record
                if($regexMatchInfo) $usesubject = $regexMatchInfo['matches'];
                else $usesubject = $mailrecord->_subject;
    
                // Get the ticket record that was created by SENDER earlier
                $fromemail = $mailrecord->_from[0];
    
                $linkfocus = $mailscanner->GetTicketRecord($usesubject, $fromemail);
                $relatedid = $linkfocus->column_fields[parent_id];
    
                // If matching ticket is found, update comment, attach email
                if($linkfocus) {
                    $timestamp = $adb->formatDate(date('YmdHis'), true);
                    $adb->pquery("INSERT INTO vtiger_ticketcomments(ticketid, comments, ownerid, ownertype, createdtime) VALUES(?,?,?,?,?)",
                        Array($linkfocus->id, $mailrecord->getBodyText(), $relatedid, 'customer', $timestamp));
                    // Set the ticket status to Open if its Closed
                    $adb->pquery("UPDATE vtiger_troubletickets set status=? WHERE ticketid=? AND status='Closed'", Array('Open', $linkfocus->id));
    
                    $returnid = $this->__CreateNewEmail($mailrecord, $this->module, $linkfocus);
    
                } else {
                    // TODO If matching ticket was not found, create ticket?
                    // $returnid = $this->__CreateTicket($mailscanner, $mailrecord);
                }
            }
            return $returnid;
        }
    
        /**
         * Create ticket action.
         */
        function __CreateTicket($mailscanner, $mailrecord) {
            // Prepare data to create trouble ticket
            $usetitle = $mailrecord->_subject;
            $description = $mailrecord->getBodyText();
    
            // There will be only on FROM address to email, so pick the first one
            $fromemail = $mailrecord->_from[0]; 
            $linktoid = $mailscanner->LookupContact($fromemail);
            if(!$linktoid) $linktoid = $mailscanner->LookupAccount($fromemail);
    
            /** Now Create Ticket **/
            global $current_user;
            if(!$current_user) $current_user = new Users();
            $current_user->id = 1;
    
            // Create trouble ticket record
            $ticket = new HelpDesk();
            $ticket->column_fields['ticket_title'] = $usetitle;
            $ticket->column_fields['description'] = $description;
            $ticket->column_fields['ticketstatus'] = 'Open';
            $ticket->column_fields['assigned_user_id'] = $current_user->id;
            if($linktoid) $ticket->column_fields['parent_id'] = $linktoid;
            $ticket->save('HelpDesk');
    
            // Associate any attachement of the email to ticket
            $this->__SaveAttachements($mailrecord, 'HelpDesk', $ticket);
    
            return $ticket->id;
        }
    
        /**
         * Add email to CRM record like Contacts/Accounts
         */
        function __LinkToRecord($mailscanner, $mailrecord) {
            $linkfocus = false;
    
            $useemail  = false;
            if($this->lookup == 'FROM') $useemail = $mailrecord->_from;
            else if($this->lookup == 'TO') $useemail = $mailrecord->_to;
    
            if($this->module == 'Contacts') {
                foreach($useemail as $email) {
                    $linkfocus = $mailscanner->GetContactRecord($email);
                    if($linkfocus) break;
                }
            } else if($this->module == 'Accounts') {
                foreach($useemail as $email) {          
                    $linkfocus = $mailscanner->GetAccountRecord($email);
                    if($linkfocus) break;
                }
            }
    
            $returnid = false;
            if($linkfocus) {
                $returnid = $this->__CreateNewEmail($mailrecord, $this->module, $linkfocus);
            }
            return $returnid;
        }
    
        /**
         * Create new Email record (and link to given record) including attachements
         */
        function __CreateNewEmail($mailrecord, $module, $linkfocus) {   
            global $current_user, $adb;
            if(!$current_user) $current_user = new Users();
            $current_user->id = 1;
    
            $focus = new Emails();
            $focus->column_fields['parent_type'] = $module;
            $focus->column_fields['activitytype'] = 'Emails';
            $focus->column_fields['parent_id'] = "$linkfocus->id@-1|";
            $focus->column_fields['subject'] = $mailrecord->_subject;
    
            $focus->column_fields['description'] = $mailrecord->getBodyHTML();
            $focus->column_fields['assigned_user_id'] = $linkfocus->column_fields['assigned_user_id'];
            $focus->column_fields["date_start"]= date('Y-m-d', $mailrecord->_date);
    
            $from=$mailrecord->_from[0];
            $to = $mailrecord->_to[0];
            $cc = (!empty($mailrecord->_cc))? implode(',', $mailrecord->_cc) : '';
            $bcc= (!empty($mailrecord->_bcc))? implode(',', $mailrecord->_bcc) : '';
            $flag=''; // 'SENT'/'SAVED'
            //emails field were restructured and to,bcc and cc field are JSON arrays
            $focus->column_fields['from_email'] = $from;
            $focus->column_fields['saved_toid'] = $to;
            $focus->column_fields['ccmail'] = $cc;
            $focus->column_fields['bccmail'] = $bcc;  
            $focus->save('Emails');
    
            $emailid = $focus->id;
    
            $this->log("Created [$focus->id]: $mailrecord->_subject linked it to " . $linkfocus->id);
    
            // TODO: Handle attachments of the mail (inline/file)
            $this->__SaveAttachements($mailrecord, 'Emails', $focus);
    
            return $emailid;
        }
    
        /**
         * Save attachments from the email and add it to the module record.
         */
        function __SaveAttachements($mailrecord, $basemodule, $basefocus) {
            global $adb;
    
            // If there is no attachments return
            if(!$mailrecord->_attachments) return;
    
            $userid = $basefocus->column_fields['assigned_user_id'];
            $setype = "$basemodule Attachment";
    
            $date_var = $adb->formatDate(date('YmdHis'), true);
    
            foreach($mailrecord->_attachments as $filename=>$filecontent) {
                $attachid = $adb->getUniqueId('vtiger_crmentity');
                $description = $filename;
                $usetime = $adb->formatDate($date_var, true);
    
                $adb->pquery("INSERT INTO vtiger_crmentity(crmid, smcreatorid, smownerid, 
                    modifiedby, setype, description, createdtime, modifiedtime, presence, deleted)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 
                    Array($attachid, $userid, $userid, $userid, $setype, $description, $usetime, $usetime, 1, 0));
    
                $issaved = $this->__SaveAttachmentFile($attachid, $filename, $filecontent);
                if($issaved) {
                    // Create document record
                    $document = new Documents();
                    $document->column_fields['notes_title']      = $filename;
                    $document->column_fields['filename']         = $filename;
                    $document->column_fields['filestatus']       = 1;
                    $document->column_fields['filelocationtype'] = 'I';
                    $document->column_fields['folderid']         = 1; // Default Folder 
                    $document->column_fields['assigned_user_id'] = $userid;
                    $document->save('Documents');
    
                    // Link file attached to document
                    $adb->pquery("INSERT INTO vtiger_seattachmentsrel(crmid, attachmentsid) VALUES(?,?)", 
                        Array($document->id, $attachid));
    
                    // Link document to base record
                    $adb->pquery("INSERT INTO vtiger_senotesrel(crmid, notesid) VALUES(?,?)", 
                        Array($basefocus->id, $document->id));              
                }
            }   
        }
    
        /**
         * Save the attachment to the file
         */
        function __SaveAttachmentFile($attachid, $filename, $filecontent) {
            global $adb;
    
            $dirname = $this->STORAGE_FOLDER;
            if(!is_dir($dirname)) mkdir($dirname);
    
            $description = $filename;
            $filename = str_replace(' ', '-', $filename);
            $saveasfile = "$dirname$attachid" . "_$filename";
            if(!file_exists($saveasfile)) {
    
                $this->log("Saved attachement as $saveasfile\n");
    
                $fh = fopen($saveasfile, 'wb');
                fwrite($fh, $filecontent);
                fclose($fh);
            }
    
            $mimetype = MailAttachmentMIME::detect($saveasfile);
    
            $adb->pquery("INSERT INTO vtiger_attachments SET attachmentsid=?, name=?, description=?, type=?, path=?",
                Array($attachid, $filename, $description, $mimetype, $dirname));
    
            return true;
        }
    }
    ?>
    

    Step10: If you still face problems, and still cant get the update ticket functionality; Check out the VTiger Forums.

    [4]: https://i.sstatic.net/5ZU7Q.jpgemphasized text*emphasized text*