Author: Pavel Chumachenko, Software Engineer
We continue to reveal the secrets of SugarCRM customization, and our today tutorial contains a step-by-step guide how to generate user’s activity schedule with the help of a module.
To be precise, we will cover the advanced SugarCRM framework, namely, the possibility to create a module for SugarCRM 6, which does not require any storage in a SugarCRM database, but, at the same time, provides the additional upgraded features for CRM system users, i.e. the generation of a user’s activity schedule.
It should be noted that the module under discussion can also be applied in the SugarCRM 7 (either through backward-capability mode, or with some customizations of the existing module). By the way, you can find more examples of SugarCRM customization in our video tutorials library. Various useful enhancements, solutions to the most important tasks are available here: https://integroscrm.com/sugarcrm-video-tutorials-and-webinars/.
What do we need for our SugarCRM customization?
So, the issue is to customize SugarCRM with such a functionality that will generate the working schedule of a company’s employee. To solve it, we need to create a module that does not require any storage in a SugarCRM database, by this, to determine its own module view and to connect the generation of the Excel-file to the PHP Excel library.
Here are the 4 steps of a new module creation:
- Declaring a new module, its connection to a CRM system.
- Defining of a controller, the navigation across the actions, building-up of the controller’s internal logic.
- Creation of module views with the internal logic.
- Adding the logic to Excel-file generation
Step 1. Declaring a new module, its connection to a CRM system
First of all, you need to create a module, and then add it to the list of SugarCRM modules. Let’s name the module, for example, PX_FlexibleSchedule.
In order to create a standard module, you should create a folder PX_FlexibleSchedule in the “modules” directory.
The contents of this folder:
At this step it is necessary to download the PHP Excel library, the language file en_us.lang.php from the language folder and to define PX_FlexibleSchedule_sugar.php file, PX_FlexibleSchedule.php file, Menu.php, vardefs.php file. Here and force, we will not refer to the content of the language file en_us.lang.php, because it is a standard language file, there are no bottlenecks there.
The files PX_FlexibleSchedule.php and PX_FlexibleSchedule_sugar.php are also standard ones, except that the following values are set to the PX_FlexibleSchedule_sugar.php file:
1 2 3 4 5 6 | public $new_schema = true; public $module_dir = 'PX_FlexibleSchedule'; public $object_name = 'PX_FlexibleSchedule'; public $name = ''; public $id = ''; public $table_name = ''; |
Here it should be noted that the field $table_name remains to be empty, because the database table is not created.
We should leave only action index in Menu.php file:
1 2 3 4 5 | if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point'); global $mod_strings; if(ACLController::checkAccess('PX_FlexibleSchedule', 'edit', true))$module_menu[]= Array("index.php?module=PX_FlexibleSchedule&action=index", $mod_strings['LNK_NEW_RECORD'],"Create Schedule") |
Please, mention that initially the data array should be the following in vardefs.php file:
1 2 3 4 5 6 7 8 9 10 11 12 13 | $dictionary['PX_FlexibleSchedule'] = array( 'audited' => false, 'activity_enabled' => false, 'duplicate_merge' => false, 'fields' => array( 'name' => array(), 'id' =>array(), ), 'relationships' => array( ), 'optimistic_locking' => true, 'unified_search' => false, ); |
While declaring the module you need not add anything, particularly, the attribute ‘table’ and the inheritance from VardefManager.
What is more, the key moment is that any field, added into $dictionary, should certainly have the parameter ‘source’ => ‘non-db‘.
The last step of declaring a module is to register in inside SugarCRM. To do it is necessary to set the following data into the file custom/Extension/application/Ext/Include/Scheduler.php:
1 2 3 | $beanList['PX_FlexibleSchedule'] = 'PX_FlexibleSchedule'; $beanFiles['PX_FlexibleSchedule'] = 'modules/PX_FlexibleSchedule/PX_FlexibleSchedule.php'; $moduleList[] = 'PX_FlexibleSchedule'; |
And also you should define the module name in the appropriate language file.
As soon as you perform QuickRepair and Rebuild of the system, the just registered module will appear in a CRM system. By this, the system should not request to perform any SQL queries, because at it was mentioned earlier the special module table is not created inside CRM system.
Step 2. Defining of a controller, the navigation across the actions, building-up of the controller’s internal logic
The next step is to define the controller.php file.
To do it you need to connect the standard controller and to inherit the module controller from it.
1 2 | require_once('include/MVC/Controller/SugarController.php'); class PX_FlexibleScheduleController extends SugarController |
Here it is important to override the standard actions, so that while switching to them, the customized module views should be replaced, but not the standard ones.
1 2 3 4 | function action_index() { $this->view = ‘confirm_schedule'; } |
Similarly, in such a way the other actions should be overridden with the only difference that the different actions should be replaced by the different views.
The correspondence of Action and View:
Action | View |
index | ‘confirm_schedule’ |
listview | ‘confirm_schedule’ |
detail | ‘confirm_schedule’ |
confirm_schedule | ‘confirm_schedule’ |
generate_schedule | ‘generate_schedule’ |
save | ‘generate_schedule’ |
The action confirm_schedule is charged with entering the Employee’s Name, the selection of working days, defining Start Time and End Time of the activity schedule, etc., and generate_schedule – for entering directly the working hours by weekdays. The action ‘save’ is charged with switching from ‘confirm_schedule‘ view to ‘generate_schedule‘. It is necessary to override the function ‘post_save()’ so that the switching between views is performed correctly:
1 2 3 4 5 6 7 8 9 10 11 | protected function post_save() { $module = (!empty($this->return_module) ? $this->return_module : $this->module); $action = (!empty($this->view) ? $this->view : 'index'); $full_name = (!empty($_REQUEST['full_name']) ? '&full_name=' . $_REQUEST['full_name'] : ''); $work_days = (!empty($_REQUEST['work_days']) ? '&work_days=' . json_encode($_REQUEST['work_days']) : ''); $up_down_week = '&up_down_week=' . (int)$_REQUEST['up_down_week']; $day_start = (!empty($_REQUEST['day_start']) ? '&day_start=' . $_REQUEST['day_start'] : ''); $day_end = (!empty($_REQUEST['day_end']) ? '&day_end=' . $_REQUEST['day_end'] : ''); $url = "index.php?module=" . $module . "&action=" . $action . $full_name . $work_days . $up_down_week . $day_start . $day_end; $this->set_redirect($url); |
It is also necessary to create the action, that’ll be charged with the generation of the Excel-file. We define it as follows:
1 2 3 4 | function action_run_schedule() { $this->excellGenerate(); } |
We’ll define the function ‘$this->excelGenerate()’ a bit further.
Step 3. Creation of module views with the internal logic
It’s high time to define the custom module view. To do this, you should create the following files under views:
As well as the metadata for them:
You should declare the buttons in confirm_scheduleviewdefs.php file:
1 2 3 4 5 6 7 8 9 10 | 'buttons' => array( array( 'customCode' => '<input type="button" name="save" id="save" onclick="var _form = document.getElementById(\'confirm_schedule\'); _form.action.value=\'Save\'; if(check_form(\'confirm_schedule\'))SUGAR.ajaxUI.submitForm(_form);return false;" value="Save and Continue">', ), array( 'customCode' => '<input title="{$APP.LBL_CANCEL_BUTTON_TITLE}" class="button" onclick="window.location=\'index.php?module=PX_FlexibleSchedule&action=index\'" type="button" value="{$APP.LBL_CANCEL_BUTTON_LABEL}">' ), ), |
And also to determine the initial field, for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 'panels' => array( 'LBL_PX_SCHEDULE_SETTINGS' => array( array( 'full_name',//a company employee’s name 'work_days',//the selection of working days ), array( 'up_down_week',//whether the activity schedule depends on up or down weeks ), array( 'day_start',//Start time of activity schedule 'day_end'//End time of activity schedule ), ), ), |
At this stage, all the added fields should also be placed to vardefs.php file.
Create the hidden fields in generate_scheduleviewdefs.php file that will store the value of the fields from the previous module view:
1 2 3 4 5 6 7 | 'hidden' => array( '<input type="hidden" name="full_name" value="">', '<input type="hidden" name="work_days" value="">', '<input type="hidden" name="up_down_week" value="">', '<input type="hidden" name="day_start" value="">', '<input type="hidden" name="day_end" value="">', ), |
As well as the custom buttons:
1 2 3 4 5 6 7 8 9 10 | 'buttons' => array( array( 'customCode' => '<input type="button" name="save" id="save" onclick="var _form = document.getElementById(\'generate_schedule\'); _form.action.value=\'Run_schedule\'; if(check_form(\'generate_schedule\'))SUGAR.ajaxUI.submitForm(_form);return false;" value="Generate">', ), array( 'customCode' => '<input title="{$APP.LBL_CANCEL_BUTTON_TITLE}" class="button" onclick="window.location=\'index.php?module=PX_FlexibleSchedule&action=index\'" type="button" value="{$APP.LBL_CANCEL_BUTTON_LABEL}">' ), ), |
‘Panels‘ data array is remained to be empty, as the fields will be added automatically there, depending on the selected days.
The view view.confirm_schedule.php and view.generate_schedule.php are inherited from module EditView. You should replace $this-> type with the name of a particular view right within the Module Builder.
It is necessary to override the function ‘preDisplay()’ in view.confirm_schedule.php file for correct Smarty template:
1 2 3 4 5 6 7 8 9 10 11 12 | public function preDisplay() { $this->metadataFile = $this->getMetaDataFile(); if (!empty($this->metadataFile)) { include($this->metadataFile); $this->viewdefs = $viewdefs; } $this->ev = new EditView(); $this->ev->view = 'confirm_schedule'; $this->ev->ss = &$this->ss; $this->ev->setup($this->module, null, $this->metadataFile, 'include/EditView/EditView.tpl'); } |
Also, you should add the fields for validation onto the view.confirm_schedule.php file.
This can be done as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public function display() { parent::display(); echo $this->get_validate_js(); } private function get_validate_js() { global $mod_strings; return <<<EOQ <script type="text/javascript" language="Javascript"> function add_checks() { addToValidate('confirm_schedule', 'full_name', 'varchar', 'true', '{$mod_strings['LBL_FULL_NAME']}'); addToValidate('confirm_schedule', 'work_days', 'multienum', 'true', '{$mod_strings['LBL_WORK_DAYS']}'); addToValidate('confirm_schedule', 'day_start', 'date', 'true', '{$mod_strings['LBL_DAY_START']}'); addToValidate('confirm_schedule', 'day_end', 'date', 'true', '{$mod_strings['LBL_DAY_END']}'); return true; } $(document).ready(function(){ add_checks(); }); </script> EOQ; } |
The similar actions should be performed in view.generate_schedule.php except that 2 methods of schedule filling and values overriding in hidden fields are added:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public function preDisplay() { $this->metadataFile = $this->getMetaDataFile(); if (!empty($this->metadataFile)) { include($this->metadataFile); $this->viewdefs = $viewdefs; } $this->ev = new EditView(); $this->ev->view = 'generate_schedule'; $this->ev->ss = &$this->ss; $this->ev->setup($this->module, null, $this->metadataFile, 'include/EditView/EditView.tpl'); $this->fill_form(); } |
1 2 3 4 5 6 | public function display() { parent::display(); echo $this->set_hidden(); echo $this->get_validate_js(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private function set_hidden() { return <<<EOQ <script type="text/javascript" language="Javascript"> function set_hidden() { $('input[name="full_name"]').val("{$_REQUEST['full_name']}"); $('input[name="work_days"]').val("{$_REQUEST['work_days']}"); $('input[name="up_down_week"]').val("{$_REQUEST['up_down_week']}"); $('input[name="day_start"]').val("{$_REQUEST['day_start']}"); $('input[name="day_end"]').val("{$_REQUEST['day_end']}"); return true; } $(document).ready(function(){ set_hidden(); }); </script> EOQ; } |
The needed fields are added in the method fill_form in the following way:
1 2 3 4 | $this->ev->defs['panels']['default'][] = array( 'field1', 'field2', ); |
Step 4. Adding the logic to Excel-file generation
In order to generate Excel-file, you need to connect PHP Excel library files to the controller.php file:
1 2 | require_once('modules/PX_FlexibleSchedule/PHPExcel/PHPExcel.php'); require_once('modules/PX_FlexibleSchedule/PHPExcel/PHPExcel/Writer/Excel5.php'); |
And also determine ‘excellGenerate()’ method.
Firstly, we need to create an Excel object, to define its basic properties, and choose the active sheet:
1 2 3 4 5 6 7 8 9 | $phpExelObject = new PHPExcel(); $phpExelObject->getProperties()->setCreator($_REQUEST['full_name']); $phpExelObject->getProperties()->setLastModifiedBy($_REQUEST['full_name']); $phpExelObject->getProperties()->setTitle($_REQUEST['full_name']); $phpExelObject->getProperties()->setSubject('Combination graph'); $phpExelObject->getProperties()->setDescription('Combination graph'); $phpExelObject->setActiveSheetIndex(0); $sheet = $phpExelObject->getActiveSheet(); |
Then we should difference between the Start Date of schedule and End Date of schedule and set the value for each day:
1 2 3 4 | $sheet->setCellValueByColumnAndRow( $j, $i, $value); |
It is necessary to set the needed format:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <pre class="theme:github lang:default decode:true " >$sheet->getStyle( 'A1:' . $sheet->getHighestColumn() . $sheet->getHighestRow() )->getBorders()->getAllBorders()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN); </pre> Finally, we display the file with a user’s activity schedule: // Display HTTP-headers header("Expires: Mon, 1 Apr 1974 05:00:00 GMT"); header("Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT"); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); header("Content-type: application/vnd.ms-excel"); header("Content-Disposition: attachment; filename=matrix.xls"); // Display the file contents $objWriter = new PHPExcel_Writer_Excel5($phpExelObject); $objWriter->save('php://output'); |
The generated Excel-file displaying a user’s activity schedule will be saved on a user PC.
The Results of Our SugarCRM Customization
That’s what we have at the result:
To draw the conclusion we want to highlight the obvious pros of such SugarCRM customization . Firstly, from the system perspective, it is not needed to store the generated schedules inside CRM system. Secondly, from a user’s perspective, it is very convenient to get the ready activity schedule right from his PC.
As always, feel free to contact us if you need advice on how to customize SugarCRM to make it more convenient and efficient for your needs.
Other SugarCRM customization tutorials: