Sugar Customize

오픈소스 비즈니스 컨설팅
둘러보기로 가기 검색하러 가기

SugarCRM Customize 방법을 정리 합니다.

목차

Sugar 분석

Home

Global Search 필드 추가

  • Global Search (Unified Search) 필드 추가 사례
  • 모듈명 : test1_vacation
  • 테이블명 : test1_vacation
  • 검색에 포함할 필드명 : subject
  • vi custom/Extension/modules/test1_vacation/Ext/Vardefs/customGlobalSearchFields.php
$dictionary['test1_vacation']['unified_search'] = true; 
$dictionary['test1_vacation']['unified_search_default_enabled'] = true;
$dictionary[‘test1_vacation']['fields']['subject']['unified_search'] = true; 
  • vi custom/modules/test1_vacation/metadata/SearchFields.php
   <?php
   if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
   require_once('modules/test1_vacation/metadata/SearchFields.php');
   
   $searchFields[$module_name]['subject'] = array( 'query_type'=>'default');
   ?>
  • "관리자 모드 -> 검색" 메뉴에서 test1_vacation을 "사용가능한 모듈"에 추가

ElasticSearch

  • vi include/SugarSearchEngine/SugarSearchEngineFactory.php
  • 검색 엔진 ($sugar_config->full_text_engine)
  • include/SugarSearchEngine/SugarSearchEngine.php
  • include/SugarSearchEngine/Elastic/SugarSearchEngineElastic.php
  • vi include/SugarSearchEngine/Elastic/SugarSearchEngineElastic.php
  • index (Database) : $_indexName($sugar_config->unique_key)으로 지정
  • type (Table) : $bean->module_dir (default. SugarBean) 로 지정

레코드의 요약 정보 표시 변경

  • 표시 위치
  • Global Search 결과
  • Recently Viewed, Favorites
class Ticket extends Issue
{
    public function get_summary_text() {
        return "Ticket #{$this->ticket_number} - {$this->name}"
    }
}

Module Menu 추가

  • vi custom/Extension/module/Opportunities/Ext/Menus/menu.customDivide.php
global $mod_strings, $app_strings, $sugar_config;
$module_menu[]=  Array(
    "index.php?module=Import&action=Step1&import_module=Opportunities&return_module=Opportunities&return_action=index", 
    $mod_strings['LNK_DIVIDE'],
    "Opportunies",
    "Opportunies"
);

Request Type

  • 목록 화면
   -   Request - Module : Cases, Action : favorites
   -   Request - Module : Cases, Action : modulelistmenu
   -   Request - Module : Cases, Action : index
  • 편집 화면 (등록)
   -   Request - Module : Cases, Action : EditView
       Request - array (   'module' => 'Cases',   'action' => 'EditView',   'return_module' => 'Cases',   'return_action' => 'DetailView', )
   -   Request - Module : ExpressionEngine, Action : getRelatedValues
       Request - array (   'module' => 'ExpressionEngine',   'action' => 'getRelatedValues',   'record_id' => ,   'tmodule' => 'Cases',   'fields' => '[{"link":"contacts","type":"related","relate":"email_c"}]',   'to_pdf' => '1', )
  • 편집 화면 (수정)
   -   Request - Module : Cases, Action : EditView, Record : 5ffc4f56-1f11-9147-3f69-515e69ad90a6
       Request - array (   
           'module' => 'Cases',   'action' => 'EditView',   'record' => '5ffc4f56-1f11-9147-3f69-515e69ad90a6',   
           'return_module' => 'Cases',   'return_action' => 'DetailView',   'return_id' => '5ffc4f56-1f11-9147-3f69-515e69ad90a6',   
           'module_tab' => ,   'isDuplicate' => 'false',   'offset' => '1',   'sugar_body_only' => , )
   -   Request - Module : ExpressionEngine, Action : getRelatedValues
  • 관련 목록에서 빠른 편집 화면 (등록) : view type - classic
  • [custom/]modules/Home/SubpanelEdits.php 파일 실행
  • custom/modules/Cases/metadata/quickcreatedefs.php
   -   Request - Module : Home, Action : SubpanelCreates, Record :
       Request - array (   
           'module' => 'Home', 'action' => 'SubpanelCreates', 'record' => , 'target_module' => 'Cases',   'target_action' => 'QuickCreate', 
           'parent_type' => 'Accounts', 'parent_id' => 'sfdc_00120000005hOWpAAM', 'parent_name' => '다우기술',   
           'return_module' => 'Accounts',   'return_action' => 'DetailView',   'return_id' => 'sfdc_00120000005hOWpAAM',   
           'return_relationship' => 'account_cases',   'return_name' => '다우기술',
           'account_id' => 'sfdc_00120000005hOWpAAM',   'account_name' => '다우기술',   'account_cases_name' => '다우기술',   
           'to_pdf' => 'true',   'tpl' => 'QuickCreate.tpl',     'account_cases_신규자료추가하기_button' => '신규자료 추가하기', )
  • 관련 목록에서 빠른 편집 화면 (수정) : view type - classic
  • [custom/]modules/Home/SubpanelEdits.php 파일 실행
  • custom/modules/Cases/metadata/quickcreatedefs.php
   -   Request - Module : Home, Action : SubpanelEdits, Record : cc180f6e-36e1-ee71-fb4a-515d0ad0af20
       Request - array (   
           'module' => 'Home',   'action' => 'SubpanelEdits',   'record' => 'cc180f6e-36e1-ee71-fb4a-515d0ad0af20', 'target_module' => 'Cases', 'target_action' => 'QuickCreate',
           'parent_type' => 'Accounts',   'parent_id' => 'sfdc_00120000005hOWpAAM', 'parent_name' => '다우기술',
           'return_module' => 'Accounts',   'return_action' => 'DetailView',   'return_id' => 'sfdc_00120000005hOWpAAM',   
           'return_relationship' => 'account_cases',        'return_name' => '다우기술',
           'account_id' => 'sfdc_00120000005hOWpAAM',   'account_name' => '다우기술',   'account_cases_name' => '다우기술',   
           'to_pdf' => 'true',   'tpl' => 'QuickCreate.tpl',            'Cases_subpanel_cancel_button' => '취소', )
   -   Request - Module : ExpressionEngine, Action : getRelatedValues
       Request - array (   'module' => 'ExpressionEngine',   'action' => 'getRelatedValues',   'record_id' => 'cc180f6e-36e1-ee71-fb4a-515d0ad0af20',   'tmodule' => 'Cases',   'fields' => '[{"link":"contacts","type":"related","relate":"email_c"}]',   'to_pdf' => '1', )
  • 관련 목록에서 빠른 편집 전체 화면 (등록)
   -   Request - Module : Cases, Action : EditView, Record :
       Request - array (   
           'module' => 'Cases',   'action' => 'EditView',    'record' => ,   
           'return_module' => 'Accounts', 'return_action' => 'DetailView',   'return_id' => 'sfdc_00120000005hOWpAAM',
           'relate_to' => 'account_cases',   'relate_id' => 'sfdc_00120000005hOWpAAM',
           'account_id' => 'sfdc_00120000005hOWpAAM', 'account_name' => '다우기술',   
           'full_form' => 'full_form',
           'assigned_user_id' => 'sfdc_00520000000shVfAAI', 'assigned_user_name' => '김계현 수석',   
                       'isDuplicate' => 'false',         'module_tab' => ,   'contact_role' => ,      'offset' => '1',   
           'user_id1_c' => ,   생략)
  • 관련 목록에서 빠른 편집 전체 화면 (수정)
   -   Request - Module : Cases, Action : EditView, Record : cc180f6e-36e1-ee71-fb4a-515d0ad0af20
       Request - array (   
           'module' => 'Cases',   'action' => 'EditView', 'record' => 'cc180f6e-36e1-ee71-fb4a-515d0ad0af20',   
           'return_module' => 'Accounts',   'return_action' => 'DetailView',   'return_id' => 'sfdc_00120000005hOWpAAM',
           'relate_to' => 'account_cases',   'relate_id' => 'sfdc_00120000005hOWpAAM', 
           'account_id' => 'sfdc_00120000005hOWpAAM', 'account_name' => '다우기술', 
           'full_form' => 'full_form',
           'assigned_user_id' => 'sfdc_00520000000slI0AAI', 'assigned_user_name' => '송솔잎 차장',   
           'isDuplicate' => 'false',         'module_tab' => ,   'offset' => '1',
           'contact_role' => ,         생략)
  • 보고서에서 빠른 편집 화면 (수정)
- Request - Module : Accounts, Action : Quickedit, Record : sfdc_00120000005hOWpAAM
  Request - array (   'to_pdf' => '1',   'module' => 'Accounts',   'action' => 'Quickedit',   'record' => 'sfdc_00120000005hOWpAAM', )
 
  • 데이터 저장 (등록)
   -   Request - Module : Accounts, Action : Save, Record :
       Request - array (   
           'module' => 'Accounts',   'action' => 'Save', 'record' => ,      
           'return_module' => 'Accounts',   'return_action' => 'index',   'return_id' => ,  
           'relate_to' => 'Accounts',   'relate_id' => ,             
           'module_tab' => ,   'isDuplicate' => 'false',   
           'contact_role' => ,     생략 )
  • 데이터 저장 (수정)
   -   Request - Module : Accounts, Action : Save, Record : 705aabbb-22d4-fd77-c66e-516226ae32f4
       Request - array (   
           'module' => 'Accounts',   'action' => 'Save', 'record' => '705aabbb-22d4-fd77-c66e-516226ae32f4',   
           'return_module' => 'Accounts',   'return_action' => 'DetailView',   'return_id' => '705aabbb-22d4-fd77-c66e-516226ae32f4',
           'relate_to' => 'Accounts',   'relate_id' => '705aabbb-22d4-fd77-c66e-516226ae32f4',
           'parent_name' => ,   'parent_id' => ,
           'isDuplicate' => 'false',         'module_tab' => ,   'contact_role' => ,      'offset' => '1',   
           'name' => 'zzaaa',   생략 )
  • 삭제 화면
   -   Request - Module : Accounts, Action : Delete, Record : 705aabbb-22d4-fd77-c66e-516226ae32f4
       Request - array (   
           'module' => 'Accounts',   'action' => 'Delete', 'record' => '705aabbb-22d4-fd77-c66e-516226ae32f4',   
           'return_module' => 'Accounts', 'return_action' => 'ListView',     'return_id' => ,   
           'module_tab' => ,   'isDuplicate' => 'false',   'offset' => '1',      'sugar_body_only' => , )
  • 조회 화면
   -   Request - Module : Accounts, Action : DetailView, Record : sfdc_00120000005hOWpAAM
       Request - array (   'module' => 'Accounts',   'action' => 'DetailView',   'record' => 'sfdc_00120000005hOWpAAM',  )
  • 목록 화면에서 고급 검색 화면만 불러 오기
Request - Module : Cases, Action : index
Request - array (   'module' => 'Cases',   'action' => 'index',   'search_form_only' => 'true',   'to_pdf' => 'true',   'search_form_view' => 'advanced_search', )

DetailView

버튼 추가 1

  • vi custom/modules/Opportunities/views/view.detail.php
class CustomOpportunitiesViewDetail extends OpportunitiesViewDetail {
	const JS_COMMON = 'custom/include/javascript/viewdefs.js';
	const JS_MODULE = 'custom/modules/Opportunities/Opportunities.js';
 	
 	public function preDisplay() {
		parent::preDisplay();
		
		//---	영업기회 분할 버튼 추가
		$tmpStr = ;
		$tmpStr = $tmpStr.'{if $bean->aclAccess("edit")}';
		$tmpStr = $tmpStr.'<input title="{$MOD.LNK_DIVIDE}" class="button" ';	//---	"button primary", "button"
		$tmpStr = $tmpStr.' onclick="funcClickButton(this.form, null, \'Divide\', \'{$fields.id.value}\', \'Opportunities\', \'DetailView\', \'{$fields.id.value}\');"';
		$tmpStr = $tmpStr.' id="divide_button" name="button" value="{$MOD.LNK_DIVIDE}" type="submit">';
		$tmpStr = $tmpStr.'{/if}';
		$this->dv->defs['templateMeta']['form']['buttons'][] = array ('customCode' => $tmpStr);

		//---	계약 생성 버튼 추가
		$tmpStr = ;
		$tmpStr = $tmpStr.'{if $bean->aclAccess("edit")}';
		$tmpStr = $tmpStr.'<input title="{$MOD.LNK_CONTRACT}" class="button" ';	//---	"button primary", "button"
		$tmpStr = $tmpStr.' onclick="funcClickButton(this.form, \'Contracts\', \'EditView\', \'\', \'Opportunities\', \'DetailView\', \'{$fields.id.value}\');"';
		$tmpStr = $tmpStr.' id="contract_button" name="button" value="{$MOD.LNK_CONTRACT}" type="submit">';
		$tmpStr = $tmpStr.'{/if}';
		$this->dv->defs['templateMeta']['form']['buttons'][] = array ('customCode' => $tmpStr);
		
		$this->dv->defs['templateMeta']['includes'][] = array ('file' => $this::JS_COMMON);
		$this->dv->defs['templateMeta']['includes'][] = array ('file' => $this::JS_MODULE); 
	}
}
  • vi custom/include/javascript/viewdefs.js
function funcClickButton(theForm, argModule, argAction, argRecord, argReturnModule, argReturnAction, argReturnId) {
	if (argModule != null) {
		theForm.module.value = argModule;
	}
	theForm.action.value = argAction;
	theForm.record.value = argRecord;
	theForm.return_module.value = argReturnModule;
	theForm.return_action.value = argReturnAction; 
	theForm.return_id.value = argReturnId;
}

버튼 추가 2

  • vi custom/modules/Opportunities/metadata/detailviewdefs.php 파일 하단에 다음을 추가 합니다.
$viewdefs['Opportunities']['DetailView']['templateMeta']['form'][buttons][] = array (
    'customCode' => '
        {if $bean->aclAccess("edit")} 
            <input title="{$MOD.LNK_DIVIDE}" class="button" 
                   onclick="this.form.action.value=\'Divide\';this.form.return_module.value=\'Opportunities\';this.form.return_action.value=\'DetailView\'; this.form.return_id.value=\'{$fields.id.value}\'" 
                   id="divide_button" 
                   name="button" 
                   value="{$MOD.LNK_DIVIDE}" 
                   type="submit">
         {/if}'
);
  • vi Extension/modules/Opportunities/Ext/Language/ko_KR.customDivide.php
$mod_strings['LNK_DIVIDE'] = 'MA/PJT 분할';

버튼의 위치 조정

$new_array = array();
$MyNewCustomCodeButton = "<input type='button' onclick='SomeAction();' value='Click me' />";
 
$new_array[] = $this->dv->defs['templateMeta']['form']['buttons'][0]; // EDIT
$new_array[] = $this->dv->defs['templateMeta']['form']['buttons'][1]; // DUPLICATE
$new_array[] = $this->dv->defs['templateMeta']['form']['buttons'][2]; // DELETE
$new_array[] = array('customCode' => $MyNewCustomCodeButton);
$new_array[] = $this->dv->defs['templateMeta']['form']['buttons'][3]; // quote2opp
$new_array[] = $this->dv->defs['templateMeta']['form']['buttons'][4]; // quote2pdf

$this->dv->defs['templateMeta']['form']['buttons'] = $new_array;
 
parent::display();

Field

Database에 없는 필드 추가

  • vi /custom/Extension/modules/$Module/Ext/Vardefs/custom_fileds.php
$dictionary[$Module]['fields'][$field]= array(
       'name' => '$field',
       'vname' => 'LBL_$FIELD',
       'type' => 'varchar',
       'len' => '255',
       'source' => 'non-db',                                   //--- Database에서 관리되지 않는 필드임을 명시함
);
  • vi /custom/Extension/modules/$Module/Ext/LogicHooks/logichooks.custom.php
if (!isset($hook_array['process_record'])) {
	$hook_array['process_record'] = Array();
}
$hook_array['process_record'][] = Array(
		count($hook_array['process_record']) + 1, 'Custom${Module}LogicHooks', 
		'custom/modules/$Module/Custom${Module}LogicHooks.php',
		'Custom${Module}LogicHooks', 'ProcessRecord');
  • vi /custom/modules/$Module/Custom${Module}LogicHooks.php
  • ProcessRecord() 함수에서 $field의 값을 계산하여 저장함
  • $field의 값으로 html tag를 사용하여 이미지 등을 표시할 수 있음
  • vi /custom/modules/$Module/Dashlets/~/~.data.php
$dashletData['MyCasesDashletWR']['columns'] = array(
    '$field' => array(
        'width' => '2',
        'label' =>,
        'default' => true,
        'sortable' => false,
        ),
);

Parent 선택 목록에 항목 추가

  • vi custom/Extension/application/Ext/Language/ko_KR.custom.php
$app_list_strings['parent_type_display']['da03_Voc'] = 'VOC';
$app_list_strings['record_type_display_notes']['da18_ProjHistory'] = '프로젝트 작업내역';

선택목록 값 변경

  • vi custom/include/language/ko_KR.lang.php
$GLOBALS['app_list_strings']['sba__snbServiceContract__c_sba__Owner_Expiration_Notice__c_list']=array (
  => ,
 '15 Days' => '15 Days',
 '30 Days' => '30일 전',
 '45 Days' => '45 Days',
 '60 Days' => '60일 전',
 '90 Days' => '90일 전',
 '120 Days' => '120 Days',
);

선택목록 값 파싱

  • 선택목록값 파싱 : $aField = unencodeMultienum($fieldValue);
  • 선택목록값 생성 : encodeMultienumValue($arr);

선택목록 키에 특수문자 사용

  • 선택목록 키에 한글 또는 공백 등을 사용하는 방법
  • vi modules/ModuleBuilder/javascript/SimpleList.js
//--- 78 line을 주석 처리
// addToValidate('dropdown_form', 'drop_name', 'error', false, SUGAR.language.get("ModuleBuilder", "LBL_JS_VALIDATE_KEY"));

//--- 102 line을 수정
// liObj.id = escape(drop_name.value);
liObj.id = drop_name.value;

ReadOnly Field

  • 1안 : 자기 자신의 값을 할당하는 수식 필드로 선언
  • 2안 : vi custom/Extension/modules/$Module/Ext/Vardefs/~.customReadOnly.php
$dictionary['$module']['fields']['$field']['readonly'] = true;

Filed의 레이블 가져오기

$displayFieldValue = $GLOBALS['app_list_strings'][$bean->field_defs[$fieldname]['options'][$bean->$fieldname]];

//--- Multi-Select
$displayFieldValues = unencodeMultienum($bean->$fieldname);
array_walk($displayFieldValues, function($val) use($bean,$fieldname) { 
    $val = $GLOBALS['app_list_strings'][$bean->field_defs[$fieldname]['options'][$bean->$fieldname]];
});

의존 관계

  • vi custom/Extension/modules/$Modules/Ext/Vardefs/sugarfield_$field.php
$dictionary['Account']['fields']['fld_industrytype2_sf_c']['visibility_grid'] = array (
    'trigger' => 'fld_industrytype1_sf_c',
    'values' => array (
        'parent_value' => array (
             0 => 'chield_value1',
             2 => 'chield_value2',
         );
    ),
);

관계(Relate) 필드명 확인

  • query 문
select distinct custom_module
  from fields_meta_data;
    
select name, type, ext2, ext3 
  from fields_meta_data 
 where custom_module = 'Opportunities' 
   and type = 'relate';
  • vi include/SugarFields/Fields/Relate/SugarFieldRelate.php
  • vi [custom/]include/SugarFields/Fields/Relate/[ko_KR.]DetailView.tpl

Expression

  • 수식 편집기
  • Module : ExpressionEngine, Action : editFormula
  • vi jssource/src_files/include/Expressions/javascript/expressions.js
  • vi jssource/src_files/modules/ExpressionEngine/javascript/formulaBuilder.js
  • vi include/Expressions/javascript/expressions.js
  • vi include/Expressions/javascript/sugar_expressions.php
  • vi modules/ExpressionEngine/javascript/formulaBuilder.js
  • 롤업 필드 삽입
  • Module : ExpressionEngine, Action : rollupWizard
  • 예) rollupSum($reports_to_link, "sp_master_sf_c")
  • 관련 필드 삽입
  • Module : ExpressionEngine, Action : selectRelatedField
  • 예) related($accounts,"field19_sf_c")
  • 수식 편집기에서 함수 정의
  • include/Expressions/Expression/~/~Expression.php 파일에서 getOperationName()가 함수의 이름을 반환함
  • include/Expressions/updatecache.php 파일이 cache/Expressions/functionmap.php 파일을 생성
  • 관련 모듈/필드 처리
  • SUGAR.forms.AssignmentHandler.getRelatedField(linkField, 'rollupSum', relField);
  • vi modules/ExpressionEngine/controller.php 파일의 action_getRelatedValues() 함수에서 처리
  • 두 날자의 일자 차이를 구하는 함수
abs(subtract(daysUntil($datefieldA),daysUntil($datefieldB)))
  • Parameter
  • vi include/Expressions/Expression/Parser/Parser.php : evaluate() 함수가 전달하는 Parameter를 생성
  • vi include/Expressions/Expression/Generic/SugarFieldExpression.php : Sugar 필드의 경우 전달되는 Parameter 객체

Custom Expression

  • custom/include/Expressions/Expression/$returnType/~Expression.php 파일 생성
  • cache/Expressions 폴더 삭제
require_once("include/Expressions/Expression/String/StringExpression.php");
/**
 * 이 Expression에 대한 도움말을 여기에 작성 합니다.
 * 
 * subStr(String s, Number from, Number length)
* Returns length characters starting at 0-based index from.
* ex: subStr("Hello", 1, 3) = "ell" */ class SubStrExpression extends StringExpression { //--- PHP로 Expression을 실행하고 그 결과를 반환 합니다. //--- $param = $this->getParameters()->evaluate(); //--- Parameter 1개 //--- $params = $this->getParameters(); //--- Parameter 여러개 //--- $param1 = $params[0]->evaluate(); //--- $paramCnt = $this->getParamCount(); //--- -1, 0, 1, ... //--- return ~ function evaluate() { } //--- JavaScript Expression을 실행하고 그 결과를 반환하는 코드를 문자열로 반환 합니다. //--- var param = this.getParameters().evaluate(); //--- Parameter 1개 //--- var params = this.getParameters(); //--- Parameter 여러개 //--- var param1 = params[0].evaluate(); //--- return ~ static function getJSEvaluate() { } //--- Expression의 이름을 반환 합니다. static function getOperationName() { } //--- Parameter의 type또는 type 배열을 반환 합니다. static function getParameterTypes() { } //--- Parameter의 갯수를 반환 합니다. static function getParamCount() { } function toString() { } }

Custom Validation

  • 방안 1 : JavaScript로 오류 처리를 합니다.
  • vi custom/modules/Opportunities/metadata/editviewdefs.php
'includes'=> array( array( 'file'=>'custom/modules/Opportunities/checkOpportunitiesDate.js'), ),
  • JavaScript file
addtovalidate(form name,field name, type, mandatory,your message); to validate your field, I guess you have one validation name should be addtovalidateDateBefore().
document.EditView.phone_work.onblur
  • 방안 2 : 테스트 필요
  • custom/include/MVC/Controller/SugarController.php 파일을 생성 합니다.
  • handle_action() 함수에서 오류가 발생하면 다음 처리를 하지 않고 넘어가도록 수정 합니다.
  • pre_save() 함수에서 validation check를 합니다.
  • 오류가 발생하면 오류메시지 저장후 view를 edit로 변경하고 pre_edit(), action_eidt(), post_edit()를 수행하고 다음으로 넘어가도록 합니다.
  • 참고 문헌

Custom Mask

Sugar Field Control 생성

require_once('modules/Import/Forms.php');
$obj= BeanFactory::getBean($Module);
echo getControl($Module, $field, $obj->getFieldDefinition($field), "");

customCode

  • vi custom/modules/$Module/metadata/detailviewdefs.php
  • 'customCode' => '<span style="color: red">{$fields.interest_c.value}</span>'
array (
    'name' => 'date_modified',
    'label' => 'LBL_DATE_MODIFIED',
    'customCode' => '{$fields.date_modified.value} {$APP.LBL_BY} {$fields.modified_by_name.value}',
),
  • vi custom/modules/$Module/metadata/editviewdefs.php
array (
    'name' => 'first_name',
    'customCode' => '{html_options name="salutation" id="salutation" options=$fields.salutation.options selected=$fields.salutation.value}'
                 .  ' <input name="first_name"  id="first_name" size="25" maxlength="25" type="text" value="{$fields.first_name.value}">',
),
  • vi custom/modules/$Module/metadata/listviewdefs.php
'PHONE_WORK' => array (
    'width' => '15%',
    'label' => 'LBL_OFFICE_PHONE',
    'default' => true,
    'related_fields' => 
        array (
        0 => 'extended_phone_c',                               //--- 추가로 사용할 필드명, 소문자로
    ),
    'customCode' => '{$PHONE_WORK} | {$EXTENDED_PHONE_C}',     //--- 필드명은 대문자로
 ),
  • custom/modules/Opportunities/views/view.detail.php
$this->dv->defs['templateMeta']['form']['buttons'] = array('EDIT', 'DUPLICATE', 'DELETE',
    array('customCode'=>'<form action="index.php" method="POST" name="Quote2Opp" id="form"><input type="hidden" name="module" value="Quotes"><input type="hidden" name="record" value="{$fields.id.value}"><input type="hidden" name="user_id" value="{$current_user->id}"><input type="hidden" name="team_id" value="{$fields.team_id.value}"><input type="hidden" name="user_name" value="{$current_user->user_name}"><input type="hidden" name="action" value="QuoteToOpportunity"><input type="hidden" name="opportunity_subject" value="{$fields.name.value}"><input type="hidden" name="opportunity_name" value="{$fields.name.value}"><input type="hidden" name="opportunity_id" value="{$fields.billing_account_id.value}"><input type="hidden" name="amount" value="{$fields.new_sub.value}"><input type="hidden" name="valid_until" value="{$fields.date_quote_expected_closed.value}"><input type="hidden" name="currency_id" value="{$fields.currency_id.value}"><input title="{$APP.LBL_QUOTE_TO_OPPORTUNITY_TITLE}" class="button" type="submit" name="opp_to_quote_button" value="{$APP.LBL_QUOTE_TO_OPPORTUNITY_LABEL}"></form>'),
    array('customCode'=>'<form action="index.php" method="{$PDFMETHOD}" name="ViewPDF" id="form" onsubmit="this.layout.value =(document.getElementById(\'layout\'))? document.getElementById(\'layout\').value: \'\';"><input type="hidden" name="layout" value=""><input type="hidden" name="module" value="Quotes"><input type="hidden" name="record" value="{$fields.id.value}"><input type="hidden" name="action" value="Layouts"><input type="hidden" name="entryPoint" value="pdf"><input type="hidden" name="email_action"><input title="{$APP.LBL_EMAIL_PDF_BUTTON_TITLE}" class="button" type="submit" name="button" value="{$APP.LBL_EMAIL_PDF_BUTTON_LABEL}" onclick="this.form.email_action.value=\'EmailLayout\';"> <input title="{$APP.LBL_VIEW_PDF_BUTTON_TITLE}"  class="button" type="submit" name="button" value="{$APP.LBL_VIEW_PDF_BUTTON_LABEL}"></form>')
);

amount_usdollar

  • amount_usdollar에는 환율이 반영된 기본 통화의 금액이 저장 됩니다.
  • 예) amount에 1$가 저장될 경우, 환율 1000원이 반영되어 amount_usdollar에는 1000 이 저장 됩니다.

금액 필드 오른쪽 정렬

  • vi [custom/]include/SugarFields/Fields/Currency/[ko_KR.]DetailView.tpl, EditView.tpl, ListView.tpl, WirelessDetailView.tpl 파일을 수정하여 변경할 수 있습니다.

Relate 관계

Standard Relate 관계 Records

  • 1:1 relate 관계 : Master record 가져오기
  • 1:1 relate 관계 : Slave record 가져오기
  • 1:n relate 관계 : Master record 가져오기
  • 1:n relate 관계 : Slave records 가져오기
  • n:m relate 관계 : Master records 가져오기
  • $relationshipName 확인 방법
  • 방안 1 : rollupSum($relationshipName, $field) 함수에서 첫번째 인수값
  • 방안 2 : vi modules/$Module/vardefs.php 파일에서 "작업실 -> $Module -> 관계" 메뉴에서 표시되는 이름을 relationship으로 가지는 것의 name
  • type이 link이고 relationship이 "작업실 -> $Module -> 관계" 메뉴에서 표시되는 이름과 같은 필드의 name
'quotes' => array (
    'name' => 'quotes',
    'type' => 'link',
    'relationship' => 'quotes_opportunities',
    'source'=>'non-db',
    'vname'=>'LBL_QUOTES',
),
  • 방안 3 : DB Query 사용, lhs_module 필드값의 소문자 사용 (추가 확인 필요)
  • load_relationship 함수에 전달하는 인수는 아래 query에서 relationship_name을 사용 합니다.
select relationship_name, relationship_type, lhs_module, lhs_key, join_key_lhs,  
          rhs_module, rhs_key, join_key_rhs
  from relationships 
 where lhs_module = '$masterModule' 
   and rhs_module = '$slaveModule'
 order by relationship_type, relationship_name;
  • $relationshipName으로 데이터 가져오기
$data = $newBean->get_linked_beans('quotes', 'Quote');
  • n:m relate 관계 : Slave records 가져오기

Custom Relate 관계 Records

  • 1:1 relate 관계 : Master record 가져오기
  • 1:1 relate 관계 : Slave record 가져오기
  • 1:n relate 관계 : Master record 가져오기
  • 1:n relate 관계 : Slave records 가져오기
  • $relationshipName 확인 방법
  • 방안 1 : "작업실 -> $Module -> 관계" 메뉴에서 표시되는 이름
  • 방안 2 : rollupSum($relationshipName, $field) 함수에서 첫번째 인수값
  • 방안 3 : vi custom/modules/$Module/Ext/Vardefs/vardefs.ext.php
  • type이 link이고 relationship이 "작업실 -> $Module -> 관계" 메뉴에서 표시되는 이름과 같은 필드의 name
  • 방안 4 : DB Query 사용, relationship_name 필드값 사용
  • load_relationship 함수에 전달하는 인수는 아래 query에서 relationship_name을 사용 합니다.
select relationship_name, relationship_type, lhs_module, lhs_key, join_key_lhs,  
          rhs_module, rhs_key, join_key_rhs
  from relationships 
 where lhs_module = '$masterModule' 
   and rhs_module = '$slaveModule'
 order by relationship_type, relationship_name;
  • $relationshipName으로 데이터 가져오기
if (!isset($bean->$relationshipName)) {
    $bean->load_relationship($relationshipName);
}
$data= $bean->$relationshipName->getBeans();
  • n:m relate 관계 : Master records 가져오기
  • n:m relate 관계 : Slave records 가져오기
  • 참고 문헌

Related Records 가져오기

  • Standard Relationship records 가져오기
  • $module->relationship_fields의 value를 참조하여 관계명 지정
  • $rel_~ 변수의 값으로 관계명 지정
$data = $newBean->get_linked_beans('quotes');
foreach ($data as $item) {
}
  • 모듈에서 다른 모듈의 id를 저장하고 있는 필드명은 join_key_lhs, join_key_rhs를 사용 합니다.
select relationship_name, relationship_type, lhs_module, lhs_key, join_key_lhs,  
          rhs_module, rhs_key, join_key_rhs
  from relationships 
 where (lhs_module = 'Opportunities' and rhs_module = 'Quotes')
      or (lhs_module = 'Quotes' and rhs_module = 'Opportunities')
 order by lhs_module;
  • 조건을 지정하여 Relationship records를 가져오는 방법

모듈간 Relation 관계에서 레이블 변경

  • 작업실(Studio)의 라벨 편집 화면에서 변경 가능
  • XXX vi custom/Extension/modules/$Module/Ext/Language/ko_KR.custom${othermodule}_${module}_1.php
$mod_strings['LBL_$OTHERMODULE_$MODULE_1_FROM_$OTHERMODULE_TITLE'] = '연락처';
//--- $mod_strings['LBL_CONTACTS_DA05_OPPCONTACTROLE_1_FROM_CONTACTS_TITLE'] = '연락처';

모듈간 Relation 관계에서 필수 필드 선언

  • vi custom/Extension/modules/$Module/Ext/Vardefs/${othermodule}_${module}_1_${Module}.php
  • required 속성 추가
$dictionary["da05_OppContactRole"]["fields"]["contacts_da05_oppcontactrole_1_name"] = array (
    'name' => 'contacts_da05_oppcontactrole_1_name',
    'type' => 'relate',
    'required' => true,
);

Related Module을 삭제

  • after_relationship_delete Logic Hook에서 호출
public function deleteRecord(&$bean, $event, $arguments) 
{
    if (($beanToDelete = BeanFactory::getBean($arguments['related_module'], $arguments['related_id'])) === FALSE) {
        return;
    }
    $beanToDelete->mark_deleted($beanToDelete->id);
}

Related 연결 추가 및 삭제

$opp = BeanFactory::getBean('Opportunities', $bean->opportunity_id);
$opp->load_relationship('opportunities_products_1');
$opp->opportunities_products_1->add($item->id);
$opp->opportunities_products_1->delete($bean->opportunity_id, $item->id);

Query문으로 관련 데이터 가져오기

	function get_products() 
	{
		// First, get the list of IDs.
 		$query = "SELECT product_id as id 
					from  $this->rel_products
					where bundle_id='$this->id' AND deleted=0 ORDER BY product_index";

		return $this->build_related_list($query, new Product());
	}

관계 이름 확인 방법

$linked_fields = $bean->get_linked_fields();
foreach($linked_fields as $name => $properties) {
    $GLOBALS['log']->info('Start Logic Hooks : CustomOpportunitiesLogicHooks linked_fields - '.$name);
}

Relate 필드에서 버튼 삭제

  • vi custom/modules/$Module/metadata/editviewdefs.php
0 => 
          array (
            'name' => '<YOUR_FIELD_NAME>',
            'studio' => 'visible',
            'label' => '<LBL_YOUR_FIELD_NAME>',
            'displayParams' => array('hideButtons' => true),               //--- hideButtons를 true로 설정
          ),

Subpanel

Subpanel 파일 위치

  • Subpanel의 항목 정의 사항이 저장되는 파일
  • 영업기회의 연락처 subpanel 변경시
  • custom/Extension/modules/Opportunities/Ext/Layoutdefs/_overrideOpportunity_subpanel_contacts.php
  • custom/modules/Contacts/metadata/subpanels/Opportunity_subpanel_contacts.php
$subpanel_layout['list_fields'] = array(
    'name' => array (
        'name' => 'name',
        'vname' => 'LBL_LIST_NAME',
        'widget_class' => 'SubPanelDetailViewLink',
        'module' => 'Contacts',
        'width' => '20%',
        'default' => true,
 ),
);
  • Subpanel 정의 파일
  • modules/$module/metadata/subpaneldefs.php
  • custom/modules/$module/Ext/Layoutdefs/layoutdefs.ext.php

Subpanel 필터

  • Subpanel에 필터 추가

Custom Subpanel

  • vi custom/Extension/modules/$Module/Ext/Layoutdefs/layoutdefs.custom.php
  • vi custom/modules/pn01_Organization/Ext/Layoutdefs/layoutdefs.ext.php
$layout_defs['pn01_Organization']['subpanel_setup']['teams'] = array(
	'order' => 100,
	'sort_by' => 'name',
	'sort_order' => 'asc',
	'title_key' => 'LBL_TEAMS',
		
	'module' => 'Teams',
	'subpanel_name' => 'default',       //--- modules/Teams/subpanels/ 폴더 아래에 있는 파일명
	'get_subpanel_data' => 'function:getUserTeams',
	'generate_select' => true,
	'top_buttons' => array(),
	'function_parameters' => array(
		'import_function_file' => 'custom/modules/$Module/customSubpanel.php',
		'id' => $this->_focus->id,
		'user_id' => $this->_focus->user_id_c,
		'return_as_array' => 'true',
	),
);
  • vi custom/modules/$Module/customSubpanel.php
function getUserTeams($params) {
//	$bean = $GLOBALS['app']->controller->bean;
	
	$user_id = $params['user_id'];

	$return_array['select'] = ' SELECT teams.* ';
	$return_array['from'] 	= ' FROM teams ';
	$return_array['where'] 	= ' WHERE teams.id in (select b.team_id from team_memberships b where b.user_id = \.$user_id.'\')';
 	$return_array['join'] 	= ;
 	$return_array['join_tables'] = ;
	return $return_array;
}
  • 참고 문헌
  • Subpanel을 동적으로 관리

Multi-Select checkbox 삭제

  • display()
$this->lv->multiSelect = false;

ListView

ListView에 Duration Custom Code 추가

  • customCode를 활용하여 이미지 등 html 코드를 추가할 수 있음
  • vi ~listviewdefs.php
'DURATION' => array (
    'type' => 'varchar',
    'label' => 'LBL_DURATION',
    'customCode' => '{$DURATION_HOURS}:{$DURATION_MINUTES}',
    'width' => '10% ',
    'default' => true,
    'sortable' => false,
    'related_fields' => array('duration_hours','duration_minutes'),
),

like search 대신 동일한 값 검색

$searchFields['<<moduleName>>']['<<searchFieldName>>'] = array(
    'query_type' => 'default',
    'operator'   => '='
);

Debug

Sugar Logger 확장

  • vi include/SugarLogger/LoggerManager.php
  • Request로 showlog=true 가 요청이된 세션에서 모든 로그 표시
 	public function __call($method, $message)
 	{
 	 	if (isset($_REQUEST['showlog'])) {
 			$_SESSION['showlog'] = ($_REQUEST['showlog'] == 'true');
 		}
 		
 		if ( !isset(self::$_levelMapping[$method]) )
            $method = $this->_level;

 		//if the method is a direct match to our level let's let it through this allows for custom levels
 		if (($method == $this->_level) ||
 		    (isset($_SESSION['showlog']) && ($_SESSION['showlog'] == true)) ||
            (!empty(self::$_levelMapping[$method]) && self::$_levelMapping[$this->_level] >= self::$_levelMapping[$method]) ) {
  • Trace Log 표시
  • vi config_override.php
$sugar_config['stack_trace_errors'] = true;
  • vi include/utils.php
  • set_error_handler('StackTraceErrorHandler'); 참조
  • 참고 문헌

로그 메시지

$GLOBALS['log']->info('~');
$GLOBALS['log']->error('~');
$GLOBALS['log']->debug('~');

Error message 표시

SugarApplication::appendErrorMessage('Enter your message string');

krumo를 사용한 디버깅

  • vi custom/net/sourceforge/krumo/krumo.ini
url = "http://ServerUrl/InstanceName/custom/net/sourceforge/krumo/"
  • 사용법
require_once('custom/net/sourceforge/krumo/class.krumo.php');
krumo($this->bean);

다른 Sugar Page로 분기

$queryParams = array(
    'module' => 'Accounts',
    'action' => 'DetailView',
    'record' => $recordId,
);
SugarApplication::redirect('index.php?' . http_build_query($queryParams));

LogicHooks

  • LogicHooks의 종류
  • after_ui_frame, after_ui_footer
  • after_save, before_save
  • before_retrieve, after_retrieve
  • process_record
  • before_delete, after_delete
  • before_restore, after_restore
  • server_roundtrip
  • before_logout, after_logout
  • before_login, after_login, login_failed
  • after_session_start
  • after_entry_point
  • Logic Hooks 선언 (Module별 실행 후 Application별 실행)
  • Module별 실행
  • custom/modules/$module/logic_hooks.php
  • custom/module/$module/Ext/LogicHooks/logichooks.ext.php
  • custom/Extenstion/module/$module/Ext/LogicHooks/logichooks.custom.php
  • Application별 실행
  • custom/modules/logic_hooks.php
  • custom/application/Ext/LogicHooks/logichooks.ext.php
  • custom/Extenstion/application/Ext/LogicHooks/logichooks.ext.php
  • $hook_array
$hook_array[$event][] = array($order_idx, 'fts', $file, $class, $function);
    $class = new $hook_class([$this->bean, ]$event, $arguments);
    $class->$hook_function([$this->bean, ]$event, $arguments)
  • vi custom/Extenstion/module/$module/Ext/LogicHooks/logichooks.custom.php
if (!isset($hook_array['before_save'])) {
	$hook_array['before_save'] = Array();
}
$hook_array['before_save'][] = Array(
		count($hook_array['before_save']) + 1, 'CustomAccountsLogicHooks', 
		'custom/modules/Accounts/CustomAccountsLogicHooks.php',
		'CustomAccountsLogicHooks', 'BeforeSave');
  • vi custom/modules/$Module/Custom${Module}LogicHooks.php
   /*
    * $bean					: Event가 발생한 모듈
    * 		Old value			: $bean->fetched_row['$field']
    * 		New value			: $bean->$field
    * $event					: Event의 종류
    * 		before_delete, after_delete / before_save, after_save / after_retrieve, process_record
    * $arguments				: Event에 따른 인수값
    * 		array ( 'check_notify' => false, )
    * 
    * 조건 체크
    * 		after_save && $bean->fetched_row == false		: 등록
    * 		after_save && $bean->fetched_row == true		: 수정
    */
  • 참고 문헌

Administration에 메뉴 추가

  • vi custom/Extension/modules/Administration/Ext/Administration/administration.custom.php
global $current_user,$admin_group_header;
$admin_group_header[4][3]['Administration']['ShowModule'] = array(
		'ConfigureTabs','LBL_SHOW_MODULE','LBL_SHOW_MODULE_DESC','./index.php?module=Administration&action=ShowModule'
		);

SugarBean

SugarBean 가져오기

  • moduleName : include/modules.php 파일의 $moduleList 참조
  • data/BeanFactory.php
$bean = BeanFactory::newBean($module);
$bean = BeanFactory::getBean($module);
  • include/MVC/SugarModule.php
$sugarModule = SugarModule::get($module);
$bean = $sugarModule ->loadBean();

SugarBean item 가져오기

  • data/BeanFactory.php
  • 모듈별 10개의 cache된 bean 관리
$bean = BeanFactory::getBean($module, $id);
  • data/SugarBean.php
$bean = $bean->getRelatedFields($module, $id, null, true);

SugarBean data 가져오기

$bean->load_relationship('opportunities_products_1');
$products = $bean->opportunities_products_1->getBeans();

SchedulersJob

   //---	SchedulersJobs을 실행 합니다.
   //---	$this->target = $method::$option
   //---	$method
   //---		function				: modules/Schedulers/_AddJobsHere.php에 등록된 함수($option) 실행
   //---				[custom/]modules/Schedulers/_AddJobsHere.php
   //---				custom/modules/Schedulers/Ext/ScheduledTasks/scheduledtasks.ext.php
   //---		link					: 등록된 URL($option)을 호출하고 그 결과를 반환
   //---		class					: RunnableSchedulerJob를 구현한 class($option)를 실행
   //---			custom/include/SugarQueue/SugarCrontJobs.php 에 추가 class를 정의할 것

Scheduler 추가

  • vi custom/Extension/modules/Schedulers/Ext/Language/ko_KR.custom.php
$mod_strings['LBL_CUSTOMSCHEDULER001'] = '수식 필드 일배치 갱신';
  • vi custom/Extension/modules/Schedulers/Ext/ScheduledTasks/scheduledtasks.custom.php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

require_once('data/BeanFactory.php');
require_once('include/SugarDateTime.php');
require_once('include/TimeDate.php');

$job_strings[] = 'CustomScheduler001';                     //---	새로 추가하는 스케쥴러 Task
function CustomScheduler001($data) {
 	$today = new DateTime();
 	$today->setDate(date("Y"), date("m"), date("d"));
 	$today->setTime(0, 0, 0);
 	
 	$bean = BeanFactory::getBean('Contracts');
 	$data = $bean->get_full_list(, "'".$today->format('Y-m-d')."' < end_date or 0 < contract_days_sf_c");
 	foreach ($data as $item) {
 		CustomScheduler001_setDays($item, $today);
 	}
       return true;                                        //---   반드시 true를 반환할 것
}
  • "관리자모드 -> 스케쥴러" 메뉴에서 위에서 추가한 기능을 스케쥴러로 등록

Scheduler를 사용하여 수식을 동적으로 갱신

  • 수식을 동적으로 갱신
$module = "Quotes"; // Set the module where you calculated field is.
$order_by = ""; //
$where = ""; // If you want to filter the records to update, specify it here.  See SugarBean->getFullList for docs on how to
require_once('include/utils.php');
require_once('include/export_utils.php');

$cnt = 0;
$moduleBean = BeanFactory::getBean($module);
$beanList = $moduleBean->get_full_list($order_by,$where);
if( $beanList != null ) {
    foreach($beanList as $b) {
        // These lines prevent the modified date and user from being changed.
        $b->update_date_modified = false;
        $b->update_modified_by = false;
        $b->tracker_visibility = false;
        $b->save();
        $cnt++;
    }
}

run_job

  • vi run_job.php
  • CLI 방식으로 호출되어 실행
  • run_job.php $jobId $clientId
  • $jobId : SchedulersJob의 ID
  • SchedulersJob에 등록된 해당 Job을 실행할 수 있는 Client ID

User에 Default Role 추가

SugarCharts

  • include/SugarCharts/
  • saved_reports 모듈
  • modules/Charts/
  • include/Dashlets/DashletGenericChart.php

SugarCharts Customize

  • vi include/SugarCharts/SugarChartFactory.php
  • $sugar_config['chartEngine'] = 'Jit';
  • [custom/]include/SugarCharts/$chartEngine/${chartEngine}$Module.php, ${chartEngine}$Module class
  • vi [custom/]include/SugarCharts/Jit/JitReports.php
  • 상속 관계
  • vi include/SugarCharts/Jit/Jit.php
  • vi include/SugarCharts/JsChart.php
  • vi include/SugarCharts/SugarCharts.php
  • 함수
  • setData() : $this->data_set 에 데이터 저장
  • setProperties() : $this->chart_properties의 값 지정
  • generateXML() : XML 데이터 생성
  • saveXMLFile() : 생성된 XML 데이터를 파일로 저장 (cache/xml/~_saved_chart.xml)
  • display() : Chart를 화면에 표시
  • getChartResources() : Chart에서 사용할 JavaScript를 반환
include/SugarCharts/Jit/FlashCanvas/flashcanvas.js
include/SugarCharts/Jit/js/Jit/jit.js
include/SugarCharts/Jit/js/sugarCharts.js
  • vi include/SugarCharts/Jit/js/sugarCharts.js
  • loadSugarChart() 함수에 각 chart별 처리 로직이 구현되어 있음
  • SugarCharts의 종류
  • "bar chart", "group by chart", "stacked group by chart"
  • "horizontal"/"horizontal bar chart", , "horizontal group by chart"
  • "pie chart", "funnel chart 3D", "line chart", "gauge chart"
  • ChartConfigParams
chartType    : barChart(막대), pieChart(원형), funnelChart(깔때기), lineChart(줄), gaugeChart(게이지)
orientation  : virtical, horizontal
barType      : basic, grouped, stacked
  • cache/xml/~_saved_chart.xml (~는 report의 uid임) 파일의 구조
  • 420b0a67-4227-7ab2-170a-51675cd154dc_saved_chart.xml
<?xml version="1.0" encoding="UTF-8"?>
<sugarcharts version="1.0">
    <properties>
        //--- Chart의 Properties
    </properties>
    
        //--- Chart를 위해 생성된 데이터
        <group>
            <title>곽재용 과장</title>
            <value>9</value>
            <subgroups>
                <group>
                    <title>나인소프트</title>
                    <value>1</value>
                    <label>1</label>
                    <link></link>
                </group>
            </subgroups>
        </group>
    
    <yAxis>
        <yMin>0</yMin>
        <yMax>21</yMax>
        <yStep>5</yStep>
        <yLog>1</yLog>
    </yAxis>

</sugarcharts>

  • cache/xml/~_saved_chart.js (~는 report의 uid임) 파일의 구조
  • 420b0a67-4227-7ab2-170a-51675cd154dc_saved_chart.js
{
    "properties": [{
        //--- Chart의 Properties
    }],
    "label": [ ~ ],
    "color": [ ~ ],
    "values": [{
        "label": "곽재용 과장",
         "values": [ ~ ],
         "valuelabels": [ ~ ],
         "links": [ ~],
    }],
}
  • cache/images/420b0a67-4227-7ab2-170a-51675cd154dc_saved_chart.png 파일
  • SugarCharts에 새로운 chartType 추가
  • JitReports.getChartResources() 함수에서 아래 JavaScript를 추가하도록 수정
  • vi custom/include/SugarCharts/Jit/js/sugarCharts.js 생성

보고서에서 Chart 사용

  • 보고서 마법사
  • vi [custom/]modules/Reports/ReportsWizard.php
$sugarChart = SugarChartFactory::getInstance();
$resources = $sugarChart->getChartResources();
$sugar_smarty->assign('chartResources', $resources);
  • vi modules/Reports/templates/templates_reports.php
$sugarChart = SugarChartFactory::getInstance();
$resources = $sugarChart->getChartResources();
$smarty->assign('chartResources', $resources);
  • 보고서
  • vi modules/Reports/templates/templates_chart.php
$sugarChart = SugarChartFactory::getInstance(,'Reports');
$sugarChart->setData($chart_rows);
$sugarChart->setProperties($chartTitle, , $chartType, 'on', 'value', 'on', $do_thousands);
$xmlFile = get_cache_file_name($reporter);
$sugarChart->saveXMLFile($xmlFile, $sugarChart->generateXML());
echo $sugarChart->display($guid, $xmlFile, $width, $height, $reportChartDivStyle);
  • SugarCharts에 새로운 chartType 추가
  • vi [custom/]modules/Reports/ReportsWizard.php
  • $chart_types 에 새로운 chartType 추가
  • vi modules/Reports/templates/templates_reports.php
  • ReportsWizard.php 파일에서 template_reports_chart_options() 함수를 override 하는 방법 검토 필요
  • $chart_types 에 새로운 chartType 추가
  • vi modules/Reports/templates/templates_chart.php
  • ReportsWizard.php 파일에서 draw_chart() 함수를 override 하는 방법 검토 필요
  • draw_chart() 함수에서 새로운 chartType 처리 기능 추가
  • 보고서에서 Chart 호출
var css = new Array();
css["gridLineColor"] = '#cccccc';
css["font-family"] = 'Arial';
css["color"] = '#000000';
 
var chartConfig = new Array();
chartConfig["orientation"] = 'horizontal';
chartConfig["barType"] = 'stacked';
chartConfig["tip"] = 'name';
chartConfig["chartType"] = 'barChart';
chartConfig["imageExportType"] = 'png';

loadSugarChart('420b0a67-4227-7ab2-170a-51675cd154dc','cache/xml/420b0a67-4227-7ab2-170a-51675cd154dc_saved_chart.js',css,chartConfig);

SugarPDF

  • index.php?module=$Module&action=sugarpdf&sugarpdf=$pdfAction
  • include/MVC/View/views/view.sugarpdf.php
  • [custom/]modules/$Module/views/view.sugarpdf.php
  • $pdfAction : smarty (Defalut), 이 값을 사용하여 다양한 양식의 PDF 문서를 작성할 수 있습니다.
  • include/Sugarpdf/sugarpdf/sugarpdf.$pdfAction.php
  • [custom/]modules/<module>/sugarpdf/sugarpdf.$pdfAction.php
  • Sugarpdf
  • include/Sugarpdf/Sugarpdf.php
  • include/tcpdf/tcpdf.php
  • Header(), Setfont(), Cell(), getNumLines()
  • predisplay(), display(), process(), writeCellTable(), writeHTMLTable()

Chart

  • vi [custom/]Charts/chartDefs.ext.php

Global 설정

  • Global Utility 정보
  • $GLOBALS['sugar_config']
  • SugarConfig::getInstance()->get('logger.level', $this->_level);
  • $GLOBALS['app_strings']
  • $GLOBALS['mod_strings']
  • $GLOBALS['log']
  • $GLOBALS['db']
  • Global 정보
  • $GLOBALS['current_user']
  • $GLOBALS['app']
  • $GLOBALS['app']->controller
  • $GLOBALS['module'], $GLOBALS['action']
  • $GLOBALS['current_view'], $GLOBALS['view']
  • $GLOBALS['FOCUS'] : 현재 읽은 레코드
  • 기타 Global 정보
  • $GLOBALS['current_language']
  • $GLOBALS['beanList'], $GLOBALS['beanFiles']
  • $GLOBALS['moduleList']
  • $GLOBALS['app_list_strings']
  • $GLOBALS['system_config']
  • $GLOBALS['logic_hook']
  • $GLOBALS['current_sugarfeed']
  • $GLOBALS['request_string']
  • $GLOBALS['adminOnlyList']
  • $GLOBALS['modListHeader']
  • $GLOBALS['modInvisList'], $GLOBALS['modInvisListActivities']
  • $GLOBALS['sugar_version'], $GLOBALS['sugar_db_version'], $GLOBALS['sugar_flavor'], $GLOBALS['server_unique_key']
  • $GLOBALS['image_path']
  • $GLOBALS['gridline']
  • $GLOBALS['updateSilent']
  • global $theme;, global $max_tabs;

Sugar Limit

  • vi config.php
$sugar_config['resource_management']['default_limit'] = 1000;
$sugar_config['resource_management']['special_query_limit'] = 50000;
$sugar_config['resource_management']['special_query_modules'][] = 'Opportunities';

성능 개선

튜닝

  • vi config_override.php
$sugar_config['disable_count_query'] = true;
$sugar_config['verify_client_ip'] = false;
$sugar_config['disable_vcr'] = true;
  • 참고 문헌

권한 설정

  • vi data/SugarBean.php
  • add_team_security_where_clause() 함수에서 user id에 해당하는 데이터만 가져오도록 query문에 where 조건을 설정 합니다.
  • 활용 방안
  • 이슈 1 : 모듈별 데이터에 대한 공유 설정을 지원하지 않음
  • 이슈 2 : 관리자가 데이터에 대한 공유 설정을 할 수 있는 기능이 없음
  • 활용
  • team_memberships 테이블에 모듈 필드를 추가함, 이 필드에 값이 있을 경우 관리자가 설정한 공유 설정임
  • 조직도를 추가하고 관리자가 설정할 수 있는 공유 설정 화면을 추가함

Java에서 RESTful 함수 사용

.htaccess를 사용한 custom 설정

  • 세션 이름 변경
php_value session.name TEST
  • 참고 문헌

SugarCache

sugar_cache_put($key,$mod_strings);
$return_result = sugar_cache_retrieve($key);

SugarTheme

  • 테마에서 이미지 또는 JavaScript 파일을 찾는 순서
[custom/]themes/$imagePath/images/$imageName
[custom/]themes/$parentTheme_imagePath/images/$imageName
[custom/]themes/default/images/$imageName
include/images/$imageName

MultiLanguage

  • $app_strings
$lang : en_us, $sugar_config->default_language, $language
include/language/$lang.lang.php, $lang.lang.override.php, $lang.lang.php.override
custom/application/Ext/Language/$lang.lang.ext.php
custom/include/language/$language/$lang.lang.php
  • $mod_strings
$lang : en_us, $sugar_config->default_language, $language
include/SugarObjects/templates/$template/language/$lang.lang.php
include/SugarObjects/implements/$template/language/$lang.lang.php
modules/$module/language/$lang.lang[.override].php
custom/modules/$module/Ext/Language/$lang.lang.ext.php
custom/modules/$module/language/$lang.lang.php
  • $mod_list_strings
modules/$module/language/en_us.lang.php
modules/$module/language/$language.lang[.override].php, $language.lang.php.override
  • $theme_strings
$lang : $language, $sugar_config->default_language
themes/$theme/language/$lang.lang[.override].php, $lang.lang.php.override
  • notify template file
[custom/]include/language/$language.notify_template.html
[custom/]include/language/en_us.notify_template.html

TimeDate

PHP#DateTime

  • DB 포맷으로 저장된 Datetime을 datetime 형식으로 변환
$tmpDateStart = SugarDateTime::createFromFormat(TimeDate::DB_DATETIME_FORMAT, $bean->date_start);
$tmpDateStart->add(new DateInterval("PT9H"));   //--- 9시간 더하기
  • vi include/TimeDate.php
  • datetime과 datetime간의 일수 계산
$dateTo = SugarDateTime::createFromFormat($timedate->get_date_format(), $timedate->asUserType($timedate->fromDbType($bean->date_closed_sf_c, 'datetime'), 'date'));
$dateFr = SugarDateTime::createFromFormat($timedate->get_date_format(), $timedate->asUserType($timedate->fromDbType($bean->date_opened_sf_c, 'datetime'), 'date'));
$tmpDateInterval = date_diff($dateTo, $dateFr);
$bean->time_to_close_sf_c = $tmpDateInterval ->days;
  • date와 datetime간의 일수 계산
$dateTo = SugarDateTime::createFromFormat($timedate->get_date_format(), $timedate->asUserType(SugarDateTime::createFromFormat(TimeDate::DB_DATETIME_FORMAT, $bean->initial_touch_sf_c.' 00:00:00'), 'date'));
$dateFr = SugarDateTime::createFromFormat($timedate->get_date_format(), $timedate->asUserType($timedate->fromDbType($bean->date_opened_sf_c, 'datetime'), 'date'));
$tmpDateInterval = date_diff($dateTo, $dateFr);
$duration = $tmpDateInterval->days;
  • date와 date간의 일수 계산
$dateTo = SugarDateTime::createFromFormat($timedate->get_date_format(), $timedate->asUserType(SugarDateTime::createFromFormat(TimeDate::DB_DATETIME_FORMAT, $bean->initial_touch_sf_c.' 00:00:00'), 'date'));
$dateFr = SugarDateTime::createFromFormat($timedate->get_date_format(), $timedate->asUserType(SugarDateTime::createFromFormat(TimeDate::DB_DATETIME_FORMAT, $bean->date_opened_sf_c.' 00:00:00'), 'date'));
$tmpDateInterval = date_diff($dateTo, $dateFr);
$duration = $tmpDateInterval->days;
  • date와 date간의 일수 계산
$dateTo = SugarDateTime::createFromFormat($timedate->get_date_format(), $timedate->asUserType(SugarDateTime::createFromFormat(TimeDate::DB_DATETIME_FORMAT, $bean->date_closed.' 00:00:00'), 'date'));
$dateFr = SugarDateTime::createFromFormat($timedate->get_date_format(), $timedate->asUserType(SugarDateTime::createFromFormat(TimeDate::DB_DATETIME_FORMAT, $bean->sfdc_paymentdate_sf_c.' 00:00:00'), 'date'));
$tmpDateInterval = date_diff($dateTo, $dateFr);
$duration = $tmpDateInterval->days;

Email Archiving

  • vi config.php
'host_name' => 'demo.smartprocess.co.kr:88',
'site_url' => 'http://demo.smartprocess.co.kr:88/testEnt',

Inbound Email

  • vi run_job.php
  • vi modules/Schedulers/_AddJobsHere.php
  • pollMonitoredInboxes()
  • vi modules/InboundEmail/InboundEmail.php 파일의 handleCreateCase() 함수가 호출됨
  • Case 생성시 description에 한줄로 표시되는 현상
  • Email에 description과 description_html이 저장되는데 cases에서는 Email의 description 필드가 저장됨
  • description 필드의 값이 줄바꿈이 있을 경우 화면에서는 줄이 변경되어 표시됨

team_sets의 team_md5

$team_md5 = ;
sort($team_ids, SORT_STRING);
$team_md5 .= $team_id;
$team_md5 = md5($team_md5);
  • Team Set의 아이디와 Team 아이디가 같고 Team Set에 하나의 팀만 등록되어 있을 경우
select id, name, team_md5, md5(id)
  from team_sets
 where team_md5 != md5(id);
   
update team_sets
   set name = md5(id),
       team_md5 = md5(id)
 where id like 'update_%'
   and team_md5 != md5(id);
  • Team Set에 속한 팀 조회
select id, team_id, team_set_id 
  from team_sets_teams 
 where team_set_id = '3ac43ff5-3886-36e4-db35-5140567d78f7';

  • 팀 구성원 조회
select id, user_id, explicit_assign, implicit_assign, deleted 
  from team_memberships 
 where team_id = 'update_team_16' 
   and explicit_assign = 1
   and deleted = 0;

Authentication 구조

  • 로그인 화면
  • Request - array ( 'module' => 'Users', 'action' => 'Authenticate', 'login_language' => 'ko_KR', 'user_name' => '사용자명', 'user_password' => '비밀번호', )
  • 로그인 실행 순서
  • $sugar_config['authenticationClass'] : SugarAuthenticate 클래스명
SugarApplication.execute()
    loadUser()
        global $authController = new AuthenticationController((!empty($GLOBALS['sugar_config']['authenticationClass'])? $GLOBALS['sugar_config']['authenticationClass'] : 'SugarAuthenticate'));
            //---   $this->authController = custom/modules/Users/authentication/SugarAuthenticate/SugarAuthenticate.php
            //---       $this->userAuthenticate = custom/modules/Users/authentication/SugarAuthenticate/SugarAuthenticateUser.php
    SugarController.execute().process().callLegacyCode()
        modules/Users/Authenticate.php
            $authController.login($user_name, $password);
                SugarAuthenticate.loginAuthenticate($username, $password, false, $PARAMS);
                    SugarAuthenticateUser.loadUserOnLogin($username, $password, $fallback, $PARAMS).authenticateUser($name, $input_hash, $fallback)
                        User::findUserPassword($name, $password, ~).checkPasswordMD5($password, $row['user_hash'])

검색에서 담당자명 정렬

  • vi include/utils.php 파일의 758 라인을 수정
  • get_user_array 함수
// $query = $query.' ORDER BY user_name ASC';
$query = $query.' ORDER BY last_name ASC';

Custom Files

  • QuickCreateModules : custom/modules/Emails/metadata/qcmodulesdefs.php
  • View
  • custom/modules/$Module/metadata/editviewdefs.php
  • vi custom/Charts/chartDefs.ext.php
  • vi modules/Charts/chartdefs.php
  • vi custom/Charts/$chart.php
  • custom/$dashletDirectory$dashletClassname/$dashletClassname$current_language.lang.php
  • custom/Extension/application/Ext/TableDictionary/, custom/application/Ext/TableDictionary/
  • custom/backup/

초대자 추가하기

  • "회의일정"의 초대자 추가하기 관련 파일
  • vi modules/Meetings/jsclass_scheduler.js

지원하는 Cache

  • APC
  • Wincache
  • Zend Cache
  • Memcache (using either the memcache or memcached extension)
  • Redis (using the PHP Redis extension)
  • 참고 문헌

참고 자료

Sugar Metadata

dictionary

  • $dictionary 정의 파일
  • vi modules/$Module/vardefs.php
  • vi custom/Extension/modules/$Module/Ext/Vardefs/vardefs.custom.php
  • $dictionary[$Module]
  • table : 테이블명
  • audited : true. audit 대상 테이블
  • unified_search, unified_search_default_enabled : true. Global Search 대상 모듈
  • full_text_search
  • duplicate_merge
  • comment
  • fields[$field]
  • name : 필드명
  • vname : 레이블명
  • type : id, name, enum, currency, datetime, date, varchar, int, link, relate
  • 'function' : call_user_func_array($execute_function, $execute_params);
  • function_name, function_require : include할 PHP 파일명, function_class, function_params, function_params_source = 'this/parent', source = 'function'
  • dbType : id, varchar, char, double
  • source
  • 'non-db'. Database에서 관리되지 않는 필드
  • 'function'
  • len
  • options : 선택목록일 경우 사용할 list 이름
  • comment
  • function : call_user_func($function, $this->focus, $name, $value, $this->view);
  • include : include할 PHP 파일명
  • name : 함수명
  • returns
  • 'html'. html 문자열을 값으로 반환
  • returns가 선언되지 않았을 경우, function의 결과값이 필드의 options에 저장됨
  • params
  • onListView : true, false
  • readonly : Read Only 필드
  • required : true. 필수 필드
  • validation
  • type, min, max : array('type' => 'range', 'min' => 0, 'max' => 100)
  • dependency : 의존 관계
  • visibility_grid : 의존 관계
  • trigger : 상위 필드명
  • values[$parent_value] : array ($chield_value1, $chield_value2)
  • unified_search : true. Global Search 대상 필드
  • full_text_search
  • group
  • id_name : type이 relate일 경우 사용할 key 필드명
  • table, module, join_name, link, rname
  • relationship, link_type, rel_fields
  • acl
  • merge_filter, isnull, audited, importable, reportable, massupdate, duplicate_merge, disable_num_format, enable_range_search
  • studio
  • wirelesseditview, wirelessdetailview, editview, detailview, quickcreate
  • indices
  • name
  • type
  • fields : array('id','deleted')
  • relationships[$relationshipName]
  • lhs_module, lhs_table, lhs_key
  • rhs_module, rhs_table, rhs_key
  • relationship_type, relationship_role_column, relationship_role_column_value

Sugar 검토

활동 내역과 활동 기록에서 관련 자료 선택 목록에 표시되는 모듈 추가

  • vi custom/Extension/application/Ext/Language/ko_KR.custom.php
<?php
//--- 관련 자료 선택 목록에 표시되는 모듈 추가
$app_list_strings['parent_type_display']['da03_Voc'] = 'VOC';

//--- 메모(Notes) 모듈의 관련 자료 선택 목록에 표시되는 모듈 추가
$app_list_strings['record_type_display_notes']['da03_Voc'] = 'VOC';
?>

Cases에서 거래처(account_name)를 필수 필드에서 삭제

  • vi config_override.php
$sugar_config['require_accounts'] = false;

Sugar 버그

화면이 깨어지고 Ajax 오류 창이 표시됨

  • 원인 : CentOS에 설치된 PHP 라이브러리의 버전이 달라 문제가 발생 합니다.
  • vi jssource/Minifier.php 파일의 158 라인
// $this->input = preg_replace('/\h/u', ' ', $this->input);
$this->input = preg_replace('/[ \t]/u', ' ', $this->input); 
  • "Admin -> Repair -> Repair JS Files" 실행
  • cache/ 폴더의 파일을 모두 삭제

Home 화면에서 Dashlet의 탭이 계속 실행중으로 표시됨

  • cache/xml/ 폴더를 apache:apache 권한으로 생성을 합니다.

한글로 모듈 목록 검색시 빈화면 표시

  • 현상 : AJAX 목록 화면에서 한글로 검색시 화면이 표시되지 않고 또한 한글이 깨어짐
  • vi include/ListView/ListViewData.php 파일의 529 라인
// $queryString = htmlentities($_REQUEST[$field_name]);
$queryString = htmlentities($_REQUEST[$field_name], null, "UTF-8");

메일 발송시 보낸 사람 이름

  • vi modules/Emails/Email.php 파일에서 851 라인
  • 근본적으로 해결을 하려면 $mail->FromName 값이 MIME으로 인코딩 되었을 경우에만 decode를 하도록 수정할 것
// $this->from_addr_name = $this->from_addr;
$this->from_addr_name = "{$mail->FromName} <{$mail->From}>";

의존 관계에서 Drag-Drop 오류

  • 현상 : 상위 선택목록에 따른 하위 선택목록 구성시 오류 발생
  • 원인 : 상위 선택목록의 값으로 JavaScript에서 index를 사용함으로 인한 오류
  • 해결 방안 : 값과는 별개로 index를 관리하여야 함
  • vi custom/modules/ModuleBuilder/views/view.depdropdown.php
  • vi custom/modules/ModuleBuilder/tpls/depdropdown.tpl

ACLAction 오류

  • 오류 메시지 : [FATAL] Error evaluating expression: Non-static method ACLAction::getUserAccessLevel() should not be called statically, assuming $this from incompatible context
  • 조치 방법
  • vi modules/ACLActions/ACLAction.php 파일의 365라인
// function getUserAccessLevel($user_id, $category, $action,$type='module'){
public static function getUserAccessLevel($user_id, $category, $action,$type='module'){

PDF 생성시 암호를 물어볼 경우

  • vi /etc/php.ini
[mbstring]
mbstring.language = UTF-8
mbstring.internal_encoding = UTF-8
mbstring.http_input = auto
;mbstring.http_output = UTF-8                           //--- 이 라인을 지우고
mbstring.http_output = pass                             //--- 이 라인을 추가 하세요
mbstring.encoding_translation = On
mbstring.detect_order = auto
mbstring.substitute_character = none

Sugar 개선

Relate 필드에서 필드명 표시

  • vi modules/DynamicFields/templates/Fields/Forms/relate.tpl

기능 추가

자동 번호 필드

  • vi custom/modules/DynamicFields/templates/Fields/TemplateAutoincrement.php
  • vi custom/modules/DynamicFields/templates/Fields/Forms/autoincrement.php
  • vi custom/modules/DynamicFields/templates/Fields/Forms/autoincrement.tpl
  • vi custom/include/SugarFields/Fields/Autoincrement/SugarFieldAutoincrement.php
  • vi custom/include/SugarFields/Fields/Autoincrement/EditView.tpl
  • vi custom/Extension/modules/DynamicFields/Ext/Language/ko_KR.Autoincrement.lang.php
  • vi custom/Extension/modules/ModuleBuilder/Ext/Language/ko_KR.Autoincrement.lang.php
  • Core 소스 수정 사항
  • vi modules/ExpressEngine/formulaHelper.php
  • vi data/SugarBean.php
updateCalculatedFields() 함수 최상단에
SugarFieldAutoincrement::saveBean($this); 추가
  • LogicHooks 등에서 자동번호 필드값을 설정하고 싶은 경우
  • 위에서 SugarBean.php를 수정하였을 경우에는 사용할 필요가 없습니다.
require_once('include/SugarFields/SugarFieldHandler.php');

$quote = BeanFactory::getBean('Quotes');
$field = 'no_c';
$sugarField = SugarFieldHandler::getSugarField($quote->field_defs[$field]['type']);
$sugarField->save($quote, array($field => ), $field, $quote->field_defs[$field]);

Multi-Layout 지원

  • 구현 방안
  • Record별로 recordType 저장
  • 사용자별로 모듈별 사용할 수 있는 레코드 타입을 저장
  • 초기 레코드 생성시 사용자가 사용할 수 있는 레코드 타입이 2개 이상이면 레코드 타입을 선택하는 화면을 표시
  • 일반 모듈에서 Layout (metadata/~viewdefs.php) 지정 방법
  • include/MVC/View/views/view.detail.php 파일에서 getMetaDataFile() 함수 호출
  • 결론 : include/MVC/View/SugarView.php, getMetaDataFile() 함수에서 metadata 파일명을 반환
  • $this->module, $this->type 정보와 $this->bean 정보를 사용하여 metadataFile명을 지정
  • 대안 : custom/modules/$Module/metadata/metafiles.php 파일을 수정하여 구현 가능
  • Studio에서 Layout 지정 방법
  • modules/ModulerBuilder/controller.php, action_editLayout() 함수에서 $this->view에 작업할 view 이름 지정
  • Parameter : 'view_module' => 'Accounts', 'view' => 'editview'
  • modules/ModulerBuilder/views/view.layoutview.php
  • $this->type = $this->sm->getViewType($this->editLayout);
  • modules/ModuleBuilder/Module/StudioModule.php, getViewType() 함수가 type을 반환
  • $this->editModule, $this->editLayout 를 사용하여 type 지정
  • 결론 : 아래 두 파일의 getFileName()에서 반환하는 Layout (metadata/~viewdefs.php) 파일을 사용
  • modules/ModuleBuilder/parsers/views/UndeployedMetaDataImplementation
  • modules/ModuleBuilder/parsers/views/DeployedMetaDataImplementation
  • $view , $moduleName , $packageName를 사용하여 파일명 확인 가능

이번 주 검색 지원

  • 보고서와 화면에서 "지난 주", "이번 주", "다음 주" 검색 조건을 추가 합니다.

보고서

  • vi custom/Extension/modules/Reports/Ext/Language/ko_KR.custom.php
  • vi modules/Reports/language/ko_KR.lang.php
$mod_strings['LBL_LAST_WEEK'] = '지난 주';
$mod_strings['LBL_THIS_WEEK'] = '이번 주';
$mod_strings['LBL_NEXT_WEEK'] = '다음 주';
  • vi modules/Reports/templates/templates_modules_def_js.php
   qualifiers[qualifiers.length] = {name:'tp_last_week',value:'<?php echo $mod_strings['LBL_LAST_WEEK']; ?>'};
   qualifiers[qualifiers.length] = {name:'tp_this_week',value:'<?php echo $mod_strings['LBL_THIS_WEEK']; ?>'};
   qualifiers[qualifiers.length] = {name:'tp_next_week',value:'<?php echo $mod_strings['LBL_NEXT_WEEK']; ?>'};
   
  • vi custom/include/generic/SugarWidgets/SugarWidgetFielddatetime.php
  • function displayInput(&$layout_def) 수정
       'TP_last_week'     => $home_mod_strings['LBL_LAST_WEEK'],
       'TP_this_week'     => $home_mod_strings['LBL_THIS_WEEK'],
       'TP_next_week'     => $home_mod_strings['LBL_NEXT_WEEK'],
  • function queryFilterTP_last_week($layout_def) 생성
       function queryFilterTP_last_week($layout_def)
       {
               global $timedate;
               $end = new SugarDateTime();
               switch ($end->format('w')) {                            //--- 0. 일요일, 1. 월요일, ...
                       case '1' : $end->add(new DateInterval("P6D"));  break;
                       case '2' : $end->add(new DateInterval("P5D"));  break;
                       case '3' : $end->add(new DateInterval("P4D"));  break;
                       case '4' : $end->add(new DateInterval("P3D"));  break;
                       case '5' : $end->add(new DateInterval("P2D"));  break;
                       case '6' : $end->add(new DateInterval("P1D"));  break;
                       case '0' :
                       default :
                               break;
               }
               $begin = clone $end;
               $begin = $begin->sub(new DateInterval("P13D"))->get_day_begin();
               $end = $end->sub(new DateInterval("P7D"))->get_day_end();
               return $this->get_start_end_date_filter($layout_def,$begin->asDb(),$end->asDb());
       }
  • function queryFilterTP_this_week($layout_def) 생성
       function queryFilterTP_this_week($layout_def)
       {
               global $timedate;
               $end = new SugarDateTime();
               switch ($end->format('w')) {                            //--- 0. 일요일, 1. 월요일, ...
                       case '1' : $end->add(new DateInterval("P6D"));  break;
                       case '2' : $end->add(new DateInterval("P5D"));  break;
                       case '3' : $end->add(new DateInterval("P4D"));  break;
                       case '4' : $end->add(new DateInterval("P3D"));  break;
                       case '5' : $end->add(new DateInterval("P2D"));  break;
                       case '6' : $end->add(new DateInterval("P1D"));  break;
                       case '0' :
                       default :
                               break;
               }
               $begin = clone $end;
               $begin = $begin->sub(new DateInterval("P6D"))->get_day_begin();
               $end = $end->get_day_end();
               return $this->get_start_end_date_filter($layout_def,$begin->asDb(),$end->asDb());
       }
  • function queryFilterTP_next_week($layout_def) 생성
       function queryFilterTP_next_week($layout_def)
       {
               global $timedate;
               $end = new SugarDateTime();
               switch ($end->format('w')) {                            //--- 0. 일요일, 1. 월요일, ...
                       case '1' : $end->add(new DateInterval("P6D"));  break;
                       case '2' : $end->add(new DateInterval("P5D"));  break;
                       case '3' : $end->add(new DateInterval("P4D"));  break;
                       case '4' : $end->add(new DateInterval("P3D"));  break;
                       case '5' : $end->add(new DateInterval("P2D"));  break;
                       case '6' : $end->add(new DateInterval("P1D"));  break;
                       case '0' :
                       default :
                               break;
               }
               $begin = clone $end;
               $begin = $begin->add(new DateInterval("P1D"))->get_day_begin();
               $end = $end->add(new DateInterval("P7D"))->get_day_end();
               return $this->get_start_end_date_filter($layout_def,$begin->asDb(),$end->asDb());
       }

고급검색

  • vi custom/Extension/application/Ext/Language/ko_KR.custom.php
  • vi include/language/ko_KR.lang.php
$app_list_strings['date_range_search_dom']['last_week'] = '지난 주';
$app_list_strings['date_range_search_dom']['this_week'] = '이번 주';
$app_list_strings['date_range_search_dom']['next_week'] = '다음 주';
$app_list_strings['kbdocument_date_filter_options']['last_week'] = '지난 주';
$app_list_strings['kbdocument_date_filter_options']['this_week'] = '이번 주';
$app_list_strings['kbdocument_date_filter_options']['next_week'] = '다음 주';
  • vi include/SearchForm/SearchForm2.php
  • generateSearchWhere()
       case 'last_week':
       case 'this_week':
       case 'next_week':
  • vi include/TimeDate.php
  • parseDateRange()
       case 'next_week':
           return $this->diffWeek(1,  $user, $adjustForTimezone);
       case 'last_week':
           return $this->diffWeek(-1,  $user, $adjustForTimezone);
       case 'this_week':
           return $this->diffWeek(0,  $user, $adjustForTimezone);
  • diffWeek() 생성
       protected function diffWeek($mdiff, User $user = null, $adjustForTimezone = true)
       {
           global $timedate;

               $end = new SugarDateTime();
               switch ($end->format('w')) {                            //--- 0. 일요일, 1. 월요일, ...
                       case '1' : $end->add(new DateInterval("P6D"));  break;
                       case '2' : $end->add(new DateInterval("P5D"));  break;
                       case '3' : $end->add(new DateInterval("P4D"));  break;
                       case '4' : $end->add(new DateInterval("P3D"));  break;
                       case '5' : $end->add(new DateInterval("P2D"));  break;
                       case '6' : $end->add(new DateInterval("P1D"));  break;
                       case '0' :
                       default :
                               break;
               }
               $begin = clone $end;
               $begin->sub(new DateInterval("P6D"));
           if ($mdiff == 1) {
               $begin->add(new DateInterval("P7D"));
               $end->add(new DateInterval("P7D"));
           }
           if ($mdiff == -1) {
               $begin->sub(new DateInterval("P7D"));
               $end->sub(new DateInterval("P7D"));
           }
           $begin = $begin->get_day_begin();
           $end = $end->get_day_end();
           return array($begin, $end);
       }

미확인 항목

  • vi custom/Extension/modules/Home/Ext/Language/ko_KR.custom.php
  • vi modules/Home/language/ko_KR.lang.php
$mod_strings['LBL_LAST_WEEK'] = '지난 주';
$mod_strings['LBL_THIS_WEEK'] = '이번 주';
$mod_strings['LBL_NEXT_WEEK'] = '다음 주';

Sugar Tip

자신만의 로그 보기

  • vi include/SugarLogger/LoggerManager.php
  • 214 라인 아래에 다음을 추가 합니다.
} else {
   if (isset($_REQUEST['showlog'])) {
       if ($_REQUEST['showlog'] == 'true') {
           $_SESSION['showlog'] = true;
       } else {
           $_SESSION['showlog'] = false;
       }
   }

   if (isset($_SESSION['showlog'])) {
       if ($_SESSION['showlog'] == true) {
           $logger = (!empty(self::$_logMapping[$method])) ? self::$_logMapping[$method] : self::$_logMapping['default'];
           if (!isset(self::$_loggers[$logger])) {
               self::$_loggers[$logger] = new $logger();
           }
           self::$_loggers[$logger]->log($method, $message);
       }
   }

회의일정에 사용자 초대 화면이 보이지 않을 경우

  • 화면에 "기간" 필드를 추가 하세요.

백업 파일 확장자

  • Upgrade 등을 할 경우, layout은 ~.suback.php 확장자를 가진 파일로 백업 됩니다.
  • 예)custom/modules/Contacts/metadata/detailviewdefs.php.suback.php

지원 업체

SuiteCRM을 사용한 영업관리, 고객관리는 아래 담당자에게 연락하여 주시면, 빠르고 친절하게 전문적인 답변을 드리겠습니다.

영업 문의 sales@obcon.biz 010-4667-1106 영업 대표
기술 문의 tech@obcon.biz 구축/컨설팅 담당
고객 지원 support@obcon.biz 고객 지원 담당

OBCon 홈페이지 바로가기