Jump to content
SpravkaCRM.ru - Ваш справочник по CRM

Recommended Posts

Все мы наверное сталкивались с проектами, в которых поучавствовало несколько разработчиков. Каждый разработчик привносил что то свое в проект, свой стиль написания кода, свои любимые приемы для реализации тех или иных задач. Из некоторых приёмов мы подчёрпывали что то новое с мыслями типа:

Цитата

Вау! Так оказывается тоже можно!

Или такими:

Цитата

Блин! Ну нафига так было делать!

Но чем больше народу поучавствовало в проекте, тем больше хочется сказать:

Цитата

Ну что это за говнокод!

И не по тому, что отдельно взятые программисты плохи или хороши в своем деле (хотя и это тоже :-) ), а потому что все они пишут по разному! И когда очередной программист приходит на проект, он владеет информацией о исходном проекте SugarCRM/SuiteCRM, о том, как написан код разработчиками этой CRM. А вот стиль написания предыдущего программиста он не знает. Это делает код менее читабельным, а следовательно требует больше времени на его изучение и дальнейшую правку. Ну и в целом уменьшается комфортность сопровождения подобного проекта. По этому мы так и любим начинать проекты "с нуля": потому что там нет ЧУЖОГО НЕЧИТАБЕЛЬНОГО ГОВНОКОДА!

Таким образом суть дальнейших инструкций: выработать у нашей команды единый стиль написания кода. Чтобы получив проект от своего коллеги вы не изучали разные уровни участия в нем других программистов по подобию колец на дереве. Чтобы все было для вас читабельным и максимально приближенным к комфортному сопровождению проекта.

Дальнейшие инструкции выработаны на основании моего представления о внешнем виде кода, подкрепленного долгой практикой. Не являются конечной инстанцией. Предлагайте в комментариях свои варианты обосновав их.

Итак:

Комментарии

Все мы знаем, что комментировать код надо. Его приятнее читать, когда ты можешь понять при помощи простого языка что же хотел тут сделать предыдущий программист. При должном уровне знания языка программирования конечно можно читать код, и сходу понимать что он делает. Но не всегда код является явным, названия переменных описывают их суть, а внезапные require_once вообще вносят некоторую сумятицу. Конечно не надо при этом делать из кода произведение и писать сочения для каждой строки кода. Но основные позиции по комментированию я укажу:

  • Старайтесь комментировать любой логический блок! Описывайте в целом суть внутри дальнейших инструкций:
  • /**
     * Получить PDF-версию протокола
     * @return mPDF
     */
    public function getProtocolPDF() {
    
       // Проверяем версию Комитета
       if((int)date("Ymd", strtotime($this->fact_date)) < 20160601) {
          // Версия протокола до 1 июня 2016 года
          $pdf = $this->getProtocolPDFBefore20160601();
       }
    
       return $pdf;
    }
  • Всегда вносите описание для функций: должно быть в целом описание функции, перечисление и описание всех входных параметров и описание типа возвращаемых функцией данных:
    /**
     * Тут краткое описание функции, для чего она нужна
     * @param $var1
     * @param $var2
     * @param string $var3
     * @return array
     */
    public function testFunction($var1, $var2, $var3 = '') {
       // Тут тело функции
       $return_array = array();
    
       // ....
    
       return $return_array;
    }
  • Тоже самое касается в обязательном порядке и JavaScript-кода:
  • /**
     * Тут краткое описание функции, для чего она нужна
     * @param var1
     * @returns {*}
     */
    function testFunction(var1) {
        // Тут тело функции
        var i;
    
        // ....
    
        return i;
    }
  • В редакторе, в котором я пользуюсь для написания проектов (PHPStorm, возможно в других редакторах типа NetBeans тоже есть), есть удобная вещь для упрощения внесения описания для функций: если прям над функцией набрать /** и нажать на энтер, то весь код описания переменных и возвращаемого функцией значения сгенерится автоматически, и вам останется лишь дописать словами описание что же делает данная функция! Вот такой удобный лайфхак!
  • Комментарии в SQL-запросах.  Да да. Встречаются редко. Почти не встречаются. Но все мы знаем как выглядят названия таблиц и полей в них для связей. Особенно связей созданных новых модулей, где не какой-нибудь `account_id`, а куча полей типа `am_tasktemplates_am_projecttemplatesam_projecttemplates_ida`. А если еще модули и с длинными названиями ... Как правило комментарии в SQL-запросах применяются, когда весь SQL-запрос формируется в одном блоке кода, а не "размазан" по коду и собирается из кирпичиков. Удобно пользоваться комментариями именно на этапе написания кода. В итоговом сгенерированном SQL-запросе комментарии уже особо не нужны. Комментировать необходимо логические блоки подключения разных таблиц через INNER/LEFT JOIN, а также не явные условия в WHERE:
    // Получаем список активных Проектов с активными Сделками + их Контрагент
    // В проектах должны быть Проектные задачи, назначенные на текущего пользователя
    // И ряд условий
    $sql = "
            SELECT DISTINCT
                `accounts`.`id` AS `account_id`,
                `accounts`.`name` AS `account_name`,
                `opportunities`.`id` AS `opportunity_id`,
                `opportunities`.`name` AS `opportunity_name`,
                `opportunities`.`name` AS `opportunity_name`,
                `opportunities`.`conditions` AS `opportunity_conditions`,
                `opportunities`.`working_sheme`,
                `opportunities`.`jobprice_scope_of_work`,
                `opportunities`.`jobprice_monthly_requirement_type`,
                `opportunities`.`jobprice_monthly_requirement_hours`,
                `opportunities`.`jobprice_single_max_hours`,
                `opportunities`.`hourly_money_in_month`,
                `opportunities`.`hourly_scope_of_work`,
                `opportunities`.`hourly_hours_in_day`,
                `opportunities`.`hourly_cost`,
                `opportunities`.`hourly_single_max_hours`,
                `project`.`id` AS `project_id`,
                `project`.`name` AS `project_name`,
                `project`.`estimated_start_date` AS `project_start`,
                `project`.`estimated_end_date` AS `project_end`,
                `project`.`assigned_user_id` AS `project_assigned_user_id`
            FROM
                `project`
            # Подключаем Сделку
            INNER JOIN
                `projects_opportunities`
                ON `projects_opportunities`.`project_id` = `project`.`id` AND `projects_opportunities`.`deleted` = 0
            INNER JOIN
                `opportunities`
                ON `opportunities`.`id` = `projects_opportunities`.`opportunity_id`
                AND `opportunities`.`status` = 'Active'
                AND `opportunities`.`deleted` = 0
            # Подключаем Контрагента
            INNER JOIN
                `projects_accounts`
                ON `projects_accounts`.`project_id` = `project`.`id` AND `projects_accounts`.`deleted` = 0
            INNER JOIN
                `accounts`
                ON `accounts`.`id` = `projects_accounts`.`account_id` AND `accounts`.`deleted` = 0
            # Подключаем задачу    
            INNER JOIN
                `project_task`
                ON `project_task`.`project_id` = `project`.`id`
                AND `project_task`.`deleted` = 0
                AND `project_task`.`assigned_user_id` = '{$row_user['id']}'
            WHERE
                `project`.`deleted` = 0
                # Нужны только Проекты с активным статусом
                AND `project`.`status` = 'Active'
            ORDER BY
                `opportunities`.`priority_number`, `project`.`estimated_start_date`
    ";
  • Комментарии в Smarty-шаблонах считаю не обязательными, хотя и будут приятным дополнением:
  • <span style="position: relative; top: 3px;">
        {if $user.app.curentAction}
    
            {* Значек статуса задачи *}
            {if $user.app.curentAction.action == 'startTask'}
    
                {* В работе *}
                <span style="position: relative; top: -3px;">
                    <span class="label label-sm arrowed-right label-success" style="top: 1px;">
                        <span style="position:relative;top: 1px;">В процессе</span></span>
                </span>
    
            {elseif $user.app.curentAction.action == 'pauseTask'}
    
                {* На паузе *}
                <span style="position: relative; top: -3px;">
                    <span class="label label-sm arrowed-right" style="top: 1px;">
                        <span style="position:relative;top: 1px;">Приостановлено</span></span>
                </span>
    
            {/if}
    
            {if $user.app.curentAction.trelloURL}
                {* Ссылка на карту Trello *}
                <a href="{$user.app.curentAction.trelloURL}" target="_blank"><i class="fa fa-trello"></i></a>
            {/if}
    
            &quot;<A href="index.php?module=ProjectTask&action=DetailView&record={$user.app.curentAction.id}" target="_blank">{$user.app.curentAction.name}</A>&quot;
            <small>для</small>
                 &quot;{$user.app.curentAction.accountName}&quot;
            <small>делает</small>
                 {$user.app.curentAction.taskDuration}
            {if $user.app.curentAction.estimated_effort}
                <small>при расчетной длительности</small>
                {$user.app.curentAction.estimated_effort}
            {/if}
        {else}
    
            <span class="red">Задача не найдена</span>
        {/if}
    </span>

 

SQL

Запросы к базе данных так же необходимо оформлять таким образом, чтобы можно было их легко читать. Структура и перечень таблиц стандартных модулей как правило разработчикам более менее знакома. А вот привнесенные модули с их таблицами и полями как правило не известны другому программисту. И увеличение читабельности SQL-запросов позволит уменьшить барьер непонимания логики запроса. Да и читать подобные запросы намного приятнее:

  • Самое первое и ОБЯЗАТЕЛЬНОЕ правило: обрамляйте ВСЕГДА названия таблиц и полей в обратные апострафы "`"! Это позволяет не только увеличить читабельность запроса, но и избежать ошибок SQL-синтаксиса да и в целом повышает отказоустойчивость кода.
  • Используйте отступы для описания структуры SQL-запроса. При помощи кнопки Tab это легко делается, а IDE сама подстроится под нужный уровень вложенности когда после очередной строки вы нажмете Enter. Любая строка, относящаяся к той или иной SQL-конструкции, должна быть на один уровень глубже этой конструкции. Тоже самое касается сложных логических кострукций в WHERE, где при помощи скобок и AND или OR формируются сложные запросы: выделяйте внутренние скобки углубляя их:
    SELECT
        `nra_certificates`.`rate_lang_type`,
        `nra_certificates`.`count`,
        `nra_certificates`.`date_due`,
        `nra_certificates`.`date_deadline`,
        `nra_certificates`.`description` AS `certificate_description`,
        `ract_ratingactions_cstm`.`data_publ_reliza_c` AS `date_publ`
    FROM
        `nra_certificates`
    LEFT JOIN
        `opportunities_cstm`
        ON `opportunities_cstm`.`id_c` = `opportunities`.`id`
    WHERE
        (`nra_certificates`.`status` IN ('','pr','print','send_drb') OR `nra_certificates`.`status` IS NULL)
        AND
        (
          `ract_ratingactions_cstm`.`rating_type_c` = `opportunities_cstm`.`last_rating_type_1_c` AND `opportunities_cstm`.`last_rating_status_1_c` NOT IN ('otozvan','priostanovlen')
          OR
          `ract_ratingactions_cstm`.`rating_type_c` = `opportunities_cstm`.`last_rating_type_2_c` AND `opportunities_cstm`.`last_rating_status_2_c` NOT IN ('otozvan','priostanovlen')
        )
        AND `ract_ratingactions_cstm`.`data_publ_reliza_c` IS NOT NULL
        AND `nra_certificates`.`deleted` = 0
  • Используйте верхний регистр для написания SQL-команд. это позволит визуально легко отличать команды от названия таблиц и полей, что тоже способствует повышению читабельности SQL-запроса.

Share this post


Link to post
Share on other sites

Далее давайте посмотрим на приёмы, используемые при разработке функционала. Одну и ту же функциональность можно сделать кучей разных способов. Но не все они являются "правильными". Здесь я укажу на что я буду обращать внимание при анализе кода, что будет приемлемым исполнением задачи, а что нет.

Итак:

  1. Не использовать в SQL-запросах функцию NOW(): Все вы наверное уже знаете, что SuiteCRM/SugarCRM хранит все даты в базе данных в UTC. То есть там не текущая ВАША дата и время, а дата и время по нулевому меридиану. А уже при отображении этой даты в самой CRM она автоматически преобразует дату в ваше время используя ваши настройки, касаемые вашего часового пояса. Так вот: NOW() - функция, получающая текущее время сервера! Не UTC! И сравнивать NOW() с значениями полей с датой - НЕПРАВИЛЬНО! Возможно для полей типа Date это еще не критично, так как там нет времени, но для Datetime - уже критично. По этому везде, где надо в SQL-запросах учесть текущее время необходимо использовать функцию 
    UTC_TIMESTAMP()
  2. Не пользоваться Студией для создания полей: При создании поля в Студии есть ряд минусов, которые в итоге заставили отказаться от этого метода. Для создания новых полей необходимо добавлять их описание в специально для этого существующую папку: custom/Extension/modules/Модуль/Ext/Vardefs/название_поля.php CRM-система при быстром восстановлении сама проанализирует все подобные файлы и добавит необходимые поля в объектную модуль того или иного модуля и поля в таблицы базы данных для того или иного модуля. Если вы затрудняетесь грамотно сформировать meta-данные поля, то можно поступить следующим образом: в конструкторе модулей сделать новый пакет и в нем новый модуль (например Test). и в этом модуле при помощи веб-интерфейса добавляете поле, которое вы хотели бы добавить в свой модуль. Указываете его тип, названия, все прочие атрибуты, характерные выбранному типу. После того, как вы добавили новое поле в этот тестовый модуль, вы можете найти meta-данные этого поля поискав их по названию этого поля в папке custom/modulebuilder. А вот описание минусов использования Студии для создания новых полей:
    1. Основное: описание нового поля, созданного в студии, полностью хранится в базе данных (в таблице `fields_meta_data`). Как мы знаем база данных не учавствует в работе с GIT, и по этому добавив поле у себя на локальной машине о нем ничего не будет знать ни основной сервер с проектом, ни другие разработчики этого проекта. И даже если вы вручную перенесете поля на основной сервер, то все равно к другим разработчикам эти данные не попадут, пока они сами так же не перенесут к себе вручную записи о этих новых полях. Это очень не удобно.
    2. Из Студии новые поля добавляются в дополнительную таблицу `название_таблицы_модуля_cstm`. Использование лишней таблицы в запросах предполагаю что подспудно увеличивает нагрузку на базу данных.
  3. ...

Share this post


Link to post
Share on other sites

Тут поднакопилось условных "ошибок" в написании кода. Это не синтаксические или логические ошибки, а именно внешний вид, читабельность и вообще ...

Итак, продолжим!

  1. Всегда добавлять локализацию для добавляемых полей в модуле! Речь идет про ситуации, когда вы для реализации своих задач добавляете поле в тот или иной модуль и в параметре vname указываете лейбл добавляемого поля. Например так:
    $dictionary['Account']['fields']['custom_all_link'] = array (
        'name' => 'custom_all_link',
        'vname' => 'LBL_CUSTOM_ALL_LINK',
        'type' => 'varchar',
        'inline_edit' => '',
        'source' => 'non-db',
        'studio' => 'visible',
    );
    В данном примере мы в модуль Accounts добавляем некое поле custom_all_link, которое не будет хранить значение в базе данных, а скорее всего будет неким калькулируемым "на лету" ('source' => 'non-db'). Так вот если нигде в языковых файлах не описать связь 
    $mod_strings['LBL_CUSTOM_ALL_LINK'] = 'Все ссылки';
      то мало того, что мы увидим LBL_CUSTOM_ALL_LINK в месте отображения этого поля, но мы также увидим этот лейбл и в технических местах типа в списке полей в Отчетах или в Студии: 2018-11-21_21-16-49.thumb.png.df0c02648f9dd45520a846d97bcbc6fb.png
    И да! Это касается не только полей, но и связей:
    $dictionary['Account']['fields']['accounts_audit_link'] = array (
    	'name' => 'accounts_audit_link',
    	'type' => 'link',
    	'relationship' => 'accounts_audit',
    	'vname' => 'LBL_ACCOUNTS_AUDIT',
    	'link_type' => 'one',
    	'module' => 'AccountsAudit',
    	'bean_name' => 'AccountsAudit',
    	'source' => 'non-db',
    );

    Нам обязательно надо в файле локализации определить ключ LBL_ACCOUNTS_AUDIT

  2. Использование функции strtotime(): Допустим вам надо получить дату меньше текущей на 3 дня. Вот такой подход "кривой":
    $old_date = time() - 259200;

    Сходу не понятно сколько это в днях/часах/минутах. А если еще и комментариев нет - считай совсем фигня фигней. Не удобно и не понятно. Вот так чуть лучше: 

    $old_date = time() - 60*60*24*3;

    Ну хотя бы наглядно видно что тут происходит. А моя рекомендация: функция strtotime(): 

    $old_date = strtotime("-3 day");

    Или, если надо относительно какой то даты, а не текущего времени: 

    $old_time = strtotime("-3 day", strtotime($date));

     

  3. Любые переменные, перед их использованием, необходимо инициализировать! Если в коде где то планируется использовать переменные, которые будут дополняться данными в процессе работы текущего скрипта (наполнение списков, конкатенация строк), то в обязательном порядке необходимо вначале работы скрипта инициализировать эти переменные. Например: 
            if(!empty($massiv_report[$i]['our_offer_percent_total'])) {
                $predlogenie_ot_totala[] = $massiv_report[$i]['our_offer_percent_total'];
            }

    Перед тем, как выполнить этот блок, в скрипте обязательно необходимо как то создать или получить переменную $predlogenie_ot_totala: или она в параметре была передана в функцию, или что то типа: 

    $predlogenie_ot_totala = [];

    и ровным счетом наоборот должно происходить с объявлением новых LBL_xxx в модулях (в следующем пункте)

  4. Объявление LBL_xxx: 
    $mod_strings = array (
      'LBL_SITE_ID' => 'id на сайте',
      'LBL_DESCRIPTION' => 'Текущие договоренности',
      'LBL_TYPE_OF_OWNERSHIP' => 'Форма собственности',
      'LBL_URIST_NAME' => 'Полное наименование',
      'LBL_INN' => 'ИНН',
      'LBL_KPP' => 'КПП',
      'LBL_BANK' => 'Банк',
      'LBL_BIK' => 'БИК',
      'LBL_KS' => 'Кор. Счет',
      'LBL_RS' => 'Р. Счет',
      'LBL_POSITION_DIR' => 'Должность руководителя',
      'LBL_DIR' => 'ФИО Руководителя',
      'LBL_CONTAKT_FIO_REAL' => 'ФИО контактного лица',
      'LBL_POSITION' => 'Должность',
      'LBL_PHONE_OFFICE' => 'Телефон',
      'LBL_EMAIL' => 'E-mail:',
    );

    Так нельзя делать в кастомных разделах добавления новых лейблов! Оно собой затрет все что там остальное будет! Надо добавлять примерно так: 

    $mod_strings['LBL_SITE_ID'] = 'id на сайте';
    $mod_strings['LBL_DESCRIPTION'] = 'Текущие договоренности';
    $mod_strings['LBL_TYPE_OF_OWNERSHIP'] = 'Форма собственности';
    $mod_strings['LBL_URIST_NAME'] = 'Полное наименование';
    $mod_strings['LBL_INN'] = 'ИНН';
    $mod_strings['LBL_KPP'] = 'КПП';
    $mod_strings['LBL_BANK'] = 'Банк';
    $mod_strings['LBL_BIK'] = 'БИК';
    $mod_strings['LBL_KS'] = 'Кор. Счет';
    $mod_strings['LBL_RS'] = 'Р. Счет';
    $mod_strings['LBL_POSITION_DIR'] = 'Должность руководителя';
    $mod_strings['LBL_DIR'] = 'ФИО Руководителя';
    $mod_strings['LBL_CONTAKT_FIO_REAL'] = 'ФИО контактного лица';
    $mod_strings['LBL_POSITION'] = 'Должность';
    $mod_strings['LBL_PHONE_OFFICE'] = 'Телефон';
    $mod_strings['LBL_EMAIL'] = 'E-mail';

     

  5. Переменные внутри SQL-запросов: SQL-запрос изначально просто строка. И тут действуют все те правила, что можно отнести к программированию строковых переменных. Я про ситуации типа такой: 
    $sql="SELECT `id`, `user_id7_c` FROM `accounts` WHERE `avance_date` = '$day_control' AND `deleted`='0' ";

    Здесь переменная $day_control вставлена не корректно. Да, возможно это будет работать. Возможно даже чаше будет работать, чем не работать. Но следует избегать подобных вставок переменных! Вместо подобного необходимо пользоваться конструкциями типа такой: 

    $sql="SELECT `id`, `user_id7_c` FROM `accounts` WHERE `avance_date` = '".$day_control."' AND `deleted`='0' ";
    

    Или такой: 

    $sql="SELECT `id`, `user_id7_c` FROM `accounts` WHERE `avance_date` = '{$day_control}' AND `deleted`='0' ";
    Подобный подход позволяет однозначно отделить код пхп от текста, чтобы интерпретатор абсолютно четко понимал где заканчивается название переменной и начинается текст. В случае с '$var' может это еще не так критично, но например привет$username_маленькаяжирнаяжопа уже будет не понятно где текст а где переменная.
  6. Передавать второй параметр true в запросах к БД через ->query: 
    $result = $db->query($sql);

    Так НЕ ПРАВИЛЬНО

    $result = $db->query($sql, true);

    А так ПРАВИЛЬНО! Второй параметр указывает на то, что если во время выполнения SQL-запроса произойдет синтаксическая ошибка, то сценарий остановится (сработает функция die()). По-умолчанию второй параметр равен false, то есть если не указать второй параметр, то в случае кривого SQL-запроса сценарий продолжит свою работу. Это плохо тем, что усложняет отладку подобного кода: то вы сразу увидели проблему (страница не прогрузилась). А то не увидили, не все ситуации предусмотрели или в принципе SQL-запрос получился "кривым" - все это ушло на боевую систему и там будет работать криво. В общем о возможных проблемах надо знать сразу на этапе отладки, и чем ярче будет уведомление, тем лучше. Просто добавляйте везде , true

  7. "return true;" в любых задачах планировщика: Если вы пишите новую задачу для планировщика, то в конце функции необходимо обязательно поставить 

    return true;

    Если функция планировщика не вернула true, то планировщик считает, что задача не была выполнена, и если стоит в настройках задачи галка "Выполнить, если пропущено":2018-11-21_21-55-24.thumb.png.1c4e4ae036463c6e61d86b9a815bd7eb.pngто планировщик будет запускать задачу не в 5 утра каждый день, а каждую минут, потому что предыдущий запуск задачи не был корректно выполнен (true то не вернула функция).

  8.  Проверять наличие индексов в массиве перед их использованием: Если наличие индекса не однозначно и массив набивается из SQL-запроса или еще из каких пришедших данных, и структура массива не является жестко заданной константой - необходимо проверять наличие используемых индексов перед тем, как их использовать. Иначе будут вот такие ошибки валиться в лог PHP:

    Nov-2018 13:26:30 Europe/Moscow] PHP Notice:  Undefined index: description in /home/euspenskiy/suitecrm/custom/modules/Accounts/auto_update_site_sunch.php on line 27
    [08-Nov-2018 13:26:30 Europe/Moscow] PHP Notice:  Undefined index: deleted in /home/euspenskiy/suitecrm/custom/modules/Accounts/auto_update_site_sunch.php on line 27
    [08-Nov-2018 13:26:30 Europe/Moscow] PHP Notice:  Undefined index: assigned_user_id in /home/euspenskiy/suitecrm/custom/modules/Accounts/auto_update_site_sunch.php on line 27
    [08-Nov-2018 13:26:30 Europe/Moscow] PHP Notice:  Undefined index: account_type in /home/euspenskiy/suitecrm/custom/modules/Accounts/auto_update_site_sunch.php on line 27
    [08-Nov-2018 13:26:30 Europe/Moscow] PHP Notice:  Undefined index: industry in /home/euspenskiy/suitecrm/custom/modules/Accounts/auto_update_site_sunch.php on line 27
    [08-Nov-2018 13:26:30 Europe/Moscow] PHP Notice:  Undefined index: annual_revenue in /home/euspenskiy/suitecrm/custom/modules/Accounts/auto_update_site_sunch.php on line 27

    А иногда таких логов на пару мегабайт в минуту прилетает ...

  9. Наименование переменных. Даже не так: НАИМЕНОВАНИЕ ПЕРЕМЕННЫХ: Необходимо внимательно относится к тому, как вы называете свои переменные. Они должны быть понятными. Это самое главное! Чтобы одно название переменной в себе уже содержало информацию что находится внутри этой переменной: какой тип и примерно какое значение. И второе: старайтесь называть переменные всеже английскими словами без ошибок. Очень часто не обязательно лезть в словарь, а достаточно просто внимательнее приглядеться к коду. Ну например: 

    $seedParticpaint= new s7_event_participants();

    То, что тут используется "seed" - отлично! сразу понятно что речь пойдет про экземпляр класса. Но вот "Particpaint" - видно, что есть модуль "s7_event_participants" и в нем есть слово "participants", не "particpaint". Или: 

    $dell_volue['module_parrent'] == 'Agend_Break'

    Нет с SuiteCRM слова "parrent", есть "parent". Да, согласен, это мелкие придирки, и в целом то код рабочий. Но мы ведь писали выше, что код должен быть не только рабочим )))

наверное "to be continue ..."

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×