Битрикс-разработчик Вероника Малышева

Битрикс блог

Бизнес-процессы (составление ТЗ на разработку)

Параметры бизнес-процесса
на примере бургерной
  • Владелец - менеджер зала
  • Клиент - тот, кто заказывает бургер
  • Продукт - Бургер (какую потребность он удовлетворяет, какие характеристики важны клиенту)
  • Точка начала и завершения  - оформление и выдача заказа
  • Этапы, действия - касса, приготовление, выдача
  • Объект - заказ (может быть несколько)
  • Роли и зона ответственности  - кассир, повар
  • Точки передачи - между кассой и кухней, между кухней и зоной выдачи. (между разными сотрудниками)
  • Ресурсы - ингридиенты, оборудование кухни и зала
  • Метрики - Время ожидания, время приготовления, %довольных клиентов (Какие и где снимаем)
У бизнес-процесса - результат работы одинаковый. (В отличие от проектной работы, у которой результат работы разный)
Внутренний бизнес-процесс - процесс внутри компании, который Клиент (Например, посетитель бургерной) не видит. Например (поставка ингридиентов, обслуживание техники



Команда заказчика
  • Спонсор - кто платит, кому это выгодно, кто может замотивировать всю остальную команду, у кого воля и полномочия использовать новые инструменты. Например, владелец бизнеса, генеральный директор.
  • Владелец процесса - кто разбирается в процессе (цепочка, действия), кто готов делиться данными знаниями, с кем будем согласовывать автоматизацию. Например, руководитель отдела
  • Роли-исполнители - кто непосредственно участвует в процессе
Цель бизнес-процесса по SMART
  • Конкретная Specific
  • Измеримая Measurable:
  • Достижимая Achievable or Attainable:
  • Значимая Relevant:
  • Ограниченная во времени Time bound
Что можно отразить в цели: Заведение  метрик, исключение дополнительных встреч (то, что зависит только от прогр



Сбор данных
  • Первоначальные данные. Какие процессы автоматизируем? Насколько глубоко? Разработка и процессов, и подпроцессов? Итог, грубая карта процесса
  • Сбор документации.  Регламенты работы, скрипты переговоров, должностные инструкции, структура компании и тд
  • Личное интервью с участником процесса (представитель определенной роли). А что будет потом и зачем?
Итог диаграмма процесса (IDEF, eEPC, BPMN 2.0). Диаграмма нужна и для согласования с клиентом, и для тех спецов для работы. Пример диаграмм

Примечания
В хаосе процессов укрупнять процессы: уменьшать количество стадий, уменьшать количество операций.
Оптимизация VS Автоматизация. (Лучше автоматизируем существующий процесс, а потом оптимизируем. Или оптимизируем, а потом автоматизируем)
Формат работы. Какое время потребуется, регулярные встречи с какими ролями и тд. Показать схему бизнес-процесса, что примерно в итоге должны получить
Саботаж - это нормально. Перенос встречи, превращение встречи в балаган. Все боятся изменений

Бизнес-процессы в Битрикс24 (памятка)

Проверить тариф (есть ли бизнес-процессы). Можно демку включить на 30 дней
Важно, настроить оргструктуру компании правильно
Важно, при изменении шаблона БП, запущенные БП по данному шаблону не изменятся, только новые изменятся

Как включить:
Диск->общий диск -> настройка (шестеренка) -> настроить бизнес процессы -> активировать

БП по документам
Диск->общий диск ->
настройка (шестеренка) -> бизнес-процессы -> "создать стандартные шаблоны БП" или выбрать существующий шаблон для просмотра или редактирования

БП Из живой ленты
Бизнес-процессы ->процессы в ленте

БП CRM
CRM ->настройки (верхнее меню) -> роботы и бизнес-процессы -> бизнес-процессы -> выбор сущности для организации БП
Роботы - это обертка БП (упрощенная)

Скопировать БП
  • Создать новый шаблон БП
  • В редактировании шаблона БП имеющегося меню Экспортировать
  • В редактировании шаблона БП созданного меню Импортировать
Шаблон БП состоит из действийДокумент в шаблоне - это текущий объект, по которому запущен БП
Задания - действия, которые создают интерфейс для коммуникации с пользователями
Конструкции - как конструкции в языке программирования (условия, циклы)
Уведомления - возможности по коммуникации с пользователями (написать в чат, в живую ленту, поставить задачу)
Действия приложений - нестандартные, установленные (Можно добавить свое, но нужно обернуть в приложение)
CRM - для работы с CRM
Диск - Для работы с диском, с файлами


Калькулятор в БП в параметрах действий
https://helpdesk.bitrix24.ru/open/5428897/
=2+2 //выведет 4
2+2={{=2+2}} //выведет 2+2=4

ID = {=Document:ID}  //выведет ID = 234 - выведет реальный ID документа
ID+2 = {{=2+{=Document:ID}}} // выведет ID+2 = 236 
Модификаторы в БП
https://helpdesk.bitrix24.ru/open/5426411/
{=Document:CREATED_BY} > User_1{=Document:CREATED_BY > printable} > Иван Иванов [1]{=Document:CREATED_BY > friendly} > Иван Иванов
Создание процесса в живой ленте
1. создаем новый процесс (через добавить) Например, заявка на затраты
2. настраиваем поля, которые нужны (Название, сумма, статус, обоснование, счет)
3. создаем шаблон бизнес-процесса через (действия ->настроить бизнес-процесс)
4. создаем параметры (при запуске будут запрашиваться), константы (которые не изменятся за весь БП), переменные (которые будем менять-заполнять в БП)

Агенты на cron

Сron – программа-демон, предназначенная для выполнения заданий в определенное время, или через определенные промежутки времени. Для редактирования заданий используется утилита crontab.

Первоисточник, но мне этой инструкции оказалось мало, я ее дополнила.

Для начала полностью отключим выполнение агентов на хите. Для этого выполним следующую команду в php консоли.
COption::SetOptionString("main", "agents_use_crontab", "N"); 
echo COption::GetOptionString("main", "agents_use_crontab", "N"); 

COption::SetOptionString("main", "check_agents", "N"); 
echo COption::GetOptionString("main", "check_agents", "Y");
В результате выполнения должно быть "NN".

После этого убираем из файла /bitrix/php_interface/dbconn.php определение следующих констант:
define("BX_CRONTAB_SUPPORT", true);
define("BX_CRONTAB", true);
И добавляем
if(!(defined("CHK_EVENT") && CHK_EVENT===true))
   define("BX_CRONTAB_SUPPORT", true);

Создаем файл проверки агентов и рассылки системных сообщений /bitrix/php_interface/cron_events.php
<?php
//тут бывали ошибки - неправильный путь, можно прописать вручную 
$_SERVER["DOCUMENT_ROOT"] = realpath(dirname(__FILE__)."/../.."); 

$DOCUMENT_ROOT = $_SERVER["DOCUMENT_ROOT"];

define("NO_KEEP_STATISTIC", true);
define("NOT_CHECK_PERMISSIONS",true);
define('BX_NO_ACCELERATOR_RESET', true);
define('CHK_EVENT', true);
define('BX_WITH_ON_AFTER_EPILOG', true);

require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");

@set_time_limit(0);
@ignore_user_abort(true);

CAgent::CheckAgents();
define("BX_CRONTAB_SUPPORT", true);
define("BX_CRONTAB", true);
CEvent::CheckEvents();

if(CModule::IncludeModule('sender'))
{
    \Bitrix\Sender\MailingManager::checkPeriod(false);
    \Bitrix\Sender\MailingManager::checkSend();
}

require($_SERVER['DOCUMENT_ROOT']."/bitrix/modules/main/tools/backup.php");
CMain::FinalActions();
?>

И добавляем данный скрипт в cron
*/1 * * * * /usr/bin/php -f /home/bitrix/www/bitrix/php_interface/cron_events.php
где

*/1 * * * * - частота запуска, можно воспользоваться Crontab генератором

/usr/bin/php - путь до php, может быть и другой путь у php, уточнить у админа хостинга

/home/bitrix/www/ - это тоже зависит зависит от сервера, чтобы узнать,  распечатать в командной строке $_SERVER;

Можно добавить через админ-панель хостинга  (интерфейс от хостинга зависит)

Можно через ssh, например, с помощью программы putty
указываем hostName или IP адрес, порт 22, ssh, в connection data указываем autologin-username - login, нажимаем Open - вводим пароль

Работа в терминале с crontab

Текущие задания на крон (у текущего пользователя)
crontab -l 
Удалить ВСЕ задания на крон (у текущего пользователя)
crontab -r
Добавить файл с заданиями
crontab имя_файла_расписания
Этот ключ позволяет выполнять действия для конкретного пользователя:
crontab -u username

Добавить задание

Откроем редактор
crontab -e
Если эта команда выполняется в первый раз, вам предложат выбрать редактор для Cron
no crontab for sk - using an empty one
Select an editor. To change later, run 'select-editor'.
1. /bin/nano <---- простейший
 2. /usr/bin/vim.basic
 3. /usr/bin/vim.tiny
 4. /bin/ed

Choose 1-4 [1]:
Выбрать на усмотрение


У меня был vim (документация по vim)
-перейти в режим редактирования нажать i
-добавить строку, например, */1 * * * * /usr/bin/php -f /home/bitrix/www/bitrix/php_interface/cron_events.php
-вернуться в обычный режим <ESC>
-потом ввести :qw и нажать <ENTER>


После этого все агенты и отправка системных событий будут обрабатывается из под cron, раз в 5 минут. Чтобы не увеличивалась очередь отправки почтовых сообщений, советую изменить параметр отвечающий за количество почтовых событий обрабатываемых за раз. Для этого выполняем в php консоли следующую команду
COption::SetOptionString("main", "mail_event_bulk", "20"); 
echo COption::GetOptionString("main", "mail_event_bulk", "5");

Bitrix JS

битрикс документация
https://dev.1c-bitrix.ru/api_help/js_lib/index.php

фишечки
https://prominado.ru/blog/bitriks-js/

BX("elemId").value = 'value'; 
BX("elemId").innerHTML = 'texthtml';
установка атрибутов
BX("elemId").setAttribute("src",pathSrc);
BX("elemId").getAttribute("src");
работа с классами
BX.addClass(BX("elemId"), 'className');
BX.removeClass(BX("elemId"), 'className');
BX.toggleClass(BX("elemId"), ["arrowUp", "arrowDown"]);
BX.hasClass(BX("elemId"), 'className');
добавить
BX.append(BX.create('span', {text: 'someText'}),BX('elemId'));
удалить
BX.remove(BX("elemId"));

показать-скрыть
BX.show(BX("elemId"));
BX.hide(BX("elemId"));

поиск
var fields = BX.findChild(BX(elemId), {class: 'className'}, true, true);
fields.forEach(function(element){
   console.log(element);
});
Маска на телефон
//php 
CJSCore::Init(array('masked_input'));
//js
var phoneVal =  BX('orderInputPhone').value; //в value номер, из предыдущего заказа, например
var phoneMasked = new BX.MaskedInput({
        mask: '+7 999 999 9999', // устанавливаем маску
        input: BX('orderInputPhone'),
        placeholder: '_' // символ замены +7 ___ ___ __ __
}); 
if(phoneVal != ''){   
       phoneVal = phoneVal.replace(/[^\d]/g,"").trim();  //убираем все нечисла
       phoneMasked.setValue(phoneVal.substring(phoneVal.length, (phoneVal.length-10))); // устанавливаем значение без первой 7
}
ajax
//php 
CJSCore::Init(array('ajax'));
//js
var postData = {               
  'sessid': BX.bitrix_sessid(),
  'site_id': BX.message('SITE_ID'),
  'action': 'add',
  'id': offersId,
  'quantity': 1,
};
BX.ajax({
   url: '/ajax/file.php',
   method: 'POST',
   data: postData,
   dataType: 'json',
   onsuccess: function(result){      
      
   },
   onfailure: function(result){
      
   } 
});
//file.php

define("STOP_STATISTICS", true);
if (array_key_exists('site_id', $_REQUEST) && is_string($_REQUEST['site_id'])){
   $siteId = $_REQUEST['site_id'];
   if($siteId !== '' && preg_match('/^[a-z0-9_]{2}$/i', $siteId) === 1){
      define('SITE_ID', $siteId);
   }
}

require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");

//подключаем нужные модули
use Bitrix\Main\Loader,
   Bitrix\Main\Application,
   Bitrix\Currency,
   Bitrix\Sale\DiscountCouponsManager;

Loader::includeModule('iblock');
Loader::includeModule('sale');
Loader::includeModule('catalog');
CUtil::JSPostUnescape();

if (!check_bitrix_sessid() || $_SERVER["REQUEST_METHOD"] != "POST") return;

$arRes = array();

if (isset($_POST["action"]) && strlen($_POST["action"]) > 0){
   $arRes = 'hello world';
}
$APPLICATION->RestartBuffer();
header('Content-Type: application/json; charset='.LANG_CHARSET);   
echo CUtil::PhpToJSObject($arRes);
die();
чтобы пересчиталась корзина, например, после добавления товара
 BX.onCustomEvent('OnBasketChange');      

Работа с заказами битрикс api d7

if (array_key_exists('site_id', $_REQUEST) && is_string($_REQUEST['site_id'])){
   $siteId = $_REQUEST['site_id'];
   if($siteId !== '' && preg_match('/^[a-z0-9_]{2}$/i', $siteId) === 1){
      define('SITE_ID', $siteId);
   }   
}
else{
   $siteId = 's1';
}

require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");

use \Bitrix\Main,   
    \Bitrix\Main\Localization\Loc as Loc,    
    Bitrix\Main\Loader,
    Bitrix\Main\Application,
   Bitrix\Currency,    
    Bitrix\Sale\Delivery,
    Bitrix\Sale\PaySystem,
    Bitrix\Sale,
    Bitrix\Sale\Order,
    Bitrix\Sale\Affiliate,
    Bitrix\Sale\DiscountCouponsManager,
    Bitrix\Main\Context;
получаем текущую корзину и создаем заказ
$basket = Sale\Basket::loadItemsForFUser(\CSaleBasket::GetBasketUserID(), $siteId)->getOrderableItems();
$currencyCode = Option::get('sale', 'default_currency', 'RUB');
DiscountCouponsManager::init();

$order = Order::create($siteId, $arUser["USER_ID"]);
$order->setPersonTypeId($payType);
$order->setBasket($basket);

доставка "без доставки"
$shipmentCollection = $order->getShipmentCollection();
$shipment = $shipmentCollection->createItem();
$service = Delivery\Services\Manager::getById(Delivery\Services\EmptyDeliveryService::getEmptyDeliveryServiceId());
$shipment->setFields(array(
    'DELIVERY_ID' => $service['ID'],
    'DELIVERY_NAME' => $service['NAME'],                         
));
$shipmentItemCollection = $shipment->getShipmentItemCollection();
foreach ($order->getBasket() as $item)
{
    $shipmentItem = $shipmentItemCollection->createItem($item);
    $shipmentItem->setQuantity($item->getQuantity());
}
оплата
$paymentCollection = $order->getPaymentCollection();
$payment = $paymentCollection->createItem();
$paySystemService = PaySystem\Manager::getObjectById($paySystemId); //$paySystemId;  - ИД оплаты
$payment->setFields(array(
    'PAY_SYSTEM_ID' => $paySystemService->getField("PAY_SYSTEM_ID"),
    'PAY_SYSTEM_NAME' => $paySystemService->getField("NAME"),
    'SUM'=> $order->getPrice()
));

для crm - ответственный
$order->setFields(array(
   'RESPONSIBLE_ID' => MANAGER_ID_ORDER
));
Свойства заказа
Функция, которая возвращает по коду свойство
function GetPropertyByCode($propertyCollection, $code)  {
    foreach ($propertyCollection as $property){
        if($property->getField('CODE') == $code)
            return $property;
    }
}
Получаем и устанавливаем свойства заказа
$propertyCollection = $order->getPropertyCollection();

$property = $propertyCollection->getProfileName();
$property->setField('VALUE', $fio);

$property = $propertyCollection->getUserEmail();
$property->setField('VALUE', $email);

$property = $propertyCollection->getPhone();
$property->setField('VALUE', $phone);


$property = GetPropertyByCode($propertyCollection, 'CANCEL_UNPAID');
$property->setValue('Y');
Добавляем валюту, комментарии пользователя, комментарии менеджера, аффилиата и сохранаяем
$order->setField('CURRENCY', $currencyCode);
$order->setField('USER_DESCRIPTION', $userDescription);
$order->setField('COMMENTS', $managerComments);   
$order->setField('AFFILIATE_ID', $affiliateId);
$order->doFinalAction(true);
$order->save();

$orderId = $order->GetId();
Манипуляции после создания заказа
if($orderId > 0){                     
   
   //добавляем связь с контактом
   if($arUser["CONTACT_ID"]!=false){
      //\Bitrix\Crm\Binding\OrderContactCompanyTable::bindContactIDs($orderId, array($arUser["CONTACT_ID"]));
      \Bitrix\Crm\Binding\OrderContactCompanyTable::bindContacts($orderId, array(array("ENTITY_ID"=>$arUser["CONTACT_ID"],"IS_PRIMARY"=>"Y")));
   }
        if($bPayed == true){
      //устанавливаем флаг оплаты
              $payment->setPaid("Y");
            //$order->setField('PAYED', 'Y');      //для интернет-магазинов без crm
      $order->setField('STATUS_ID', ORDER_STATUS_PAYED); //для crm
           $order->save();
        }
   elseif (!empty($paySystemService)) {
      //получаем форму оплаты
                 $arPaySysAction = $paySystemService->getFieldsValues();
                if ($paySystemService->getField('NEW_WINDOW') === 'N' || $paySystemService->getField('ID') == PaySystem\Manager::getInnerPaySystemId()){
                     $initResult = $paySystemService->initiatePay($payment, null, PaySystem\BaseServiceHandler::STRING);
                     if ($initResult->isSuccess())
                        $arPaySysAction['BUFFERED_OUTPUT'] = $initResult->getTemplate(); // получаем форму оплаты из обработчика
                     else
                        $arPaySysAction["ERROR"] = $initResult->getErrorMessages();
                 }
        }
}

Разрешение оплаты после согласования менеджером в битриксе

1. Сначала создадим нужные статусы
2. В настройках (настройки -> настройки модулей -> интернет-магазин -> настройки -> Статус, начиная с которого можно оплатить заказ (указать)
3. Сделать настройки в 1С в модуле обмена (статусов)
4. Письмо клиенту, что может оплатить. Я доработала событие SALE_ORDER_ALLOW_PAY в init.php

AddEventHandler("main", "OnBeforeEventAdd", "MyOnBeforeEventAdd");
function MyOnBeforeEventAdd(&$event, &$lid, &$arFields){
    if($event == "SALE_ORDER_ALLOW_PAY" && isset($arFields["ORDER_REAL_ID"]) && intval($arFields["ORDER_REAL_ID"])>0 && 
        (!isset($arFields["ORDER_PAYMENT"]) || $arFields["ORDER_PAYMENT"]=='')
    ){
        $order = Sale\Order::load(intval($arFields["ORDER_REAL_ID"]));
        $paymentCollection = $order->getPaymentCollection();
        $paySystemService = PaySystem\Manager::getObjectById($paymentCollection->current()->getPaymentSystemId());
        //AddMessage2Log($paymentCollection->current()->getPaymentSystemId());
        //AddMessage2Log($paySystemService);
        if($paySystemService->getField("IS_CASH")=="Y"){
            //если наличные
            $arFields["ORDER_PAYMENT"] = "Вы можете оплатить в офисе по адресу: Тут адрес";
        }
        elseif($paySystemService->getField("ACTION_FILE")=="bill"){
            //если счет
            $arPdf = $paySystemService->getPdf($paymentCollection->current());

            if(isset($arPdf["SRC"]) && $arPdf["SRC"]!=''){
                $arFields["ORDER_PAYMENT"] = "<a href='https://адрес_сайта".$arPdf["SRC"]."' target='_blank'>Ссылка на счет pdf</a>";
            }
        }
        elseif ($paySystemService->getField('NEW_WINDOW') === 'N' || $paySystemService->getField('ID') == PaySystem\Manager::getInnerPaySystemId()){
            $initResult = $paySystemService->initiatePay($paymentCollection->current(), null, PaySystem\BaseServiceHandler::STRING);
            if ($initResult->isSuccess()){
                $arFields["ORDER_PAYMENT"] = $initResult->getTemplate();
            } 
        }
        else{
            $arFields["ORDER_PAYMENT"] = "Вы можете оплатить, перейдя по ссылке на заказ";
        }          

    }   
}

1C УТ11 состояния заказов

В модуле обмена с 1С-Битрикс с УТ статус заказа  можно сопоставить с состоянием заказов.

Как определяется состояние заказа в 1С
Начиная из  редакции УТ 11.2.2  список возможных состояний заказа клиента такой:
  1. Ожидается согласование  – Если в установлена функциональная опция Согласование заказов клиента, то  каждый новый заказ  нужно соглосовать.
  2. Ожидается аванс (до обеспечения) –   В заказе установится этот  статус  после того, как мы установили у заказа клиента статус К выполнению, а для строк заказа указали действие Не обеспечивать, изменилось состояние заказа. . Товар по заказу нельзя будет отгрузить (установить действие Отгрузить для строк заказа), если не зарегистрирована оплата заказа
  3. Готов к обеспечению –   В заказе установится этот статус после того, как  Клиент заплатил аванс по заказу  платежным документом. Теперь нужно подготовить товар к отгрузке и отгрузить ему товар. Для строк заказ можно изменить колонку  Действие на последующие варианты обеспечения (К обеспечению, Резервировать и т.д.). Для того чтобы можно было отгрузить товар (оформить документ Реализация товаров и услуг), необходимо для всех товаров в табличной части документа установить действие Отгрузить и провести документ;
  4. Ожидается предоплата (до отгрузки)– В заказе установится этот статус после того, как все строки заказа обеспечены и установлен вариант оплаты  Предоплата (до отгрузки)
  5. Ожидается обеспечение;
  6. Готов к отгрузке – Предоплата получена, заказ ждет выставления действия Отгрузить/Отгрузить обособленно во всех строках;
  7. В процессе отгрузки–  Означает, что получена предоплата и во  всех  строках  установлено  действие Отгрузить или Отгрузить обособленно, но складские ордера или реализации оформлены не на весь заказанный товар
  8. Ожидается оплата (после отгрузки)-  Означает, что товары отгружены и ожидается оплата потому, что в заказе выбран вариант кредит ( после отгрузки) ;
  9. Готов к закрытию – Заказы, по которым на все позиции, указанные в заказе, оформлены документы Реализация товаров и услуг и/или Акт выполненных работ.
  10. Закрыт.
А это код, который ставит состояние заказа
 ВЫБОР
        КОГДА (НЕ ДокументЗаказКлиента.Проведен)
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.ПустаяСсылка)
        КОГДА ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.НеСогласован)
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.ОжидаетсяСогласование)
        КОГДА ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.Закрыт)
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.Закрыт)
        КОГДА ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.Согласован)
                И ДокументЗаказКлиента.ПорядокРасчетов <> ЗНАЧЕНИЕ(Перечисление.ПорядокРасчетов.ПоДоговорамКонтрагентов)
                И ДокументЗаказКлиента.СуммаАвансаДоОбеспечения > 0
                И ДокументЗаказКлиента.СуммаДокумента - ЕСТЬNULL(РасчетыСКлиентамиОстатки.КОплатеОстаток, 0) < ДокументЗаказКлиента.СуммаАвансаДоОбеспечения
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.ОжидаетсяАвансДоОбеспечения)
        КОГДА ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.Согласован)
                И ДокументЗаказКлиента.СуммаДокумента > 0
                И (ДокументЗаказКлиента.СуммаДокумента - ЕСТЬNULL(РасчетыСКлиентамиОстатки.КОплатеОстаток, 0) >= ДокументЗаказКлиента.СуммаАвансаДоОбеспечения
                ИЛИ ДокументЗаказКлиента.ПорядокРасчетов = ЗНАЧЕНИЕ(Перечисление.ПорядокРасчетов.ПоДоговорамКонтрагентов))
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.ГотовКОбеспечению)
        КОГДА (ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.Согласован)
                ИЛИ ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.КОбеспечению))
                И ДокументЗаказКлиента.ПорядокРасчетов <> ЗНАЧЕНИЕ(Перечисление.ПорядокРасчетов.ПоДоговорамКонтрагентов)
                И ДокументЗаказКлиента.СуммаПредоплатыДоОтгрузки > 0
                И ДокументЗаказКлиента.СуммаДокумента - ЕСТЬNULL(РасчетыСКлиентамиОстатки.КОплатеОстаток, 0) < ДокументЗаказКлиента.СуммаПредоплатыДоОтгрузки + ДокументЗаказКлиента.СуммаАвансаДоОбеспечения
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.ОжидаетсяПредоплатаДоОтгрузки)
        КОГДА (ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.Согласован)
                ИЛИ ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.КОбеспечению))
                И ДокументЗаказКлиента.СуммаДокумента > 0
                И ((ДокументЗаказКлиента.СуммаДокумента - ЕСТЬNULL(РасчетыСКлиентамиОстатки.КОплатеОстаток, 0) >= ДокументЗаказКлиента.СуммаПредоплатыДоОтгрузки + ДокументЗаказКлиента.СуммаАвансаДоОбеспечения
                ИЛИ ДокументЗаказКлиента.ПорядокРасчетов = ЗНАЧЕНИЕ(Перечисление.ПорядокРасчетов.ПоДоговорамКонтрагентов))
                И ЕСТЬNULL(ЗаказыКлиентовОстатки.СуммаОстаток, 0) <> 0)
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.ГотовКОтгрузке)
        КОГДА ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.КОтгрузке)
                И ДокументЗаказКлиента.СуммаДокумента > 0
                И (ДокументЗаказКлиента.СуммаДокумента - ЕСТЬNULL(РасчетыСКлиентамиОстатки.КОплатеОстаток, 0) >= ДокументЗаказКлиента.СуммаПредоплатыДоОтгрузки + ДокументЗаказКлиента.СуммаАвансаДоОбеспечения
                ИЛИ ДокументЗаказКлиента.ПорядокРасчетов = ЗНАЧЕНИЕ(Перечисление.ПорядокРасчетов.ПоДоговорамКонтрагентов))
                И ЕСТЬNULL(ЗаказыКлиентовОстатки.КОформлениюОстаток, 0) <> 0
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.ОжидаетсяОтгрузка)
        КОГДА ДокументЗаказКлиента.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.КОтгрузке)
                И ДокументЗаказКлиента.ПорядокРасчетов <> ЗНАЧЕНИЕ(Перечисление.ПорядокРасчетов.ПоДоговорамКонтрагентов)
                И ДокументЗаказКлиента.СуммаДокумента - ДокументЗаказКлиента.СуммаАвансаДоОбеспечения + ДокументЗаказКлиента.СуммаПредоплатыДоОтгрузки > 0
                И ЕСТЬNULL(ЗаказыКлиентовОстатки.КОформлениюОстаток, 0) = 0
                И ЕСТЬNULL(РасчетыСКлиентамиОстатки.КОплатеОстаток, 0) > 0
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.ОжидаетсяОплатаПослеОтгрузки)
        КОГДА ДокументЗаказКлиента.Статус <> ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.Закрыт)
            ТОГДА ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.ГотовКЗакрытию)
    КОНЕЦ КАК Состояние,

Битрикс24 коробка. Бизнес-процессы, php-код в условии

Есть, например, задача: Отправлять Особое письмо о создании заказа: если "онлайн-оплата на сайте", то со ссылкой на оплату, иначе без нее.

1. Создаем переменную в бизнес-процессе. (у меня pay_system_id)

2. Присваиваем переменной значение
   2.1. через блок "php код"
$this->SetVariable('pay_system_id', {=Document:PAY_SYSTEM_ID});
   2.2. через блок "изменение переменных".

3. В условии выбираем тип условия "php-код"

4. Задаем условие
//доступа к  Document и $this нет, есть $ownerActivity

$ownerActivity->GetVariable('pay_system_id') != 14 //платежная система экваринг с ID 14

4.  Если да, то блок с простой: "Отправить письмо клиенту", иначе php-код, который запускает Функцию, которая генерирует письмо со ссылкой на оплату

PageSpeed Insights зеленая зона

Что можно сделать в битрикс сайтах, чтобы увеличить скорость загрузки до зеленой зоны.
Конечно проверить и исправить ошибки сайта (php, базы),  использование кеша, убрать неоптимальные запросы, композитный сайт настроить.

1. В настройках главного модуля проставить все галки Оптимизация CSS

2. Отложить загрузку скриптов (Google, Яндекс, Bitrix24 виджет)
<?if(!$USER->IsAdmin()):?>
      
         <!-- Yandex.Metrika counter --> 
         <script type="text/javascript" > 
            $(window).on('load',function(){
               setTimeout(function(){   
                  (function(m,e,t,r,i,k,a){
                     m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; 
                                                        m[i].l=1*new Date();
                                                        k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
                                                 }) (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym"); 
                  ym(20186335, "init", { clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:true });
               },2000);
            });
         </script> 
         <noscript><div><img src="https://mc.yandex.ru/watch/20186335" style="position:absolute; left:-9999px;" alt="" /></div></noscript> <!-- /Yandex.Metrika counter -->
         
      <!-- Global site tag (gtag.js) - Google Analytics -->
      
      <script>
           $(window).on('load',function(){
            setTimeout(function(){   
               var w = window;
               var d =  document;
               var u = 'https://www.googletagmanager.com/gtag/js?id=UA-15054906-10';
                 var s=d.createElement('script');s.async=true;s.src=u;
                 var h=d.getElementsByTagName('script')[0];h.parentNode.insertBefore(s,h);                
            },1500);
         });
      </script>   
      <script>
         $(window).on('load',function(){
            setTimeout(function(){   
              window.dataLayer = window.dataLayer || [];
              function gtag(){dataLayer.push(arguments);}
              gtag('js', new Date());
              gtag('config', 'UA-15054906-10');

             },2000);
         });
      </script>
      <script>
           $(window).on('load',function(){
            setTimeout(function(){   
               var w = window;
               var d =  document;
               var u = 'https://cdn.bitrix24.ru/b12482366/crm/site_button/loader_1_i4gs5f.js';
                 var s=d.createElement('script');s.async=true;s.src=u+'?'+(Date.now()/60000|0);
                 var h=d.getElementsByTagName('script')[0];h.parentNode.insertBefore(s,h);                
            },2000);
         });
      </script>       
   <? endif;?>


3. Отложить загрузку карты
<?if(CSite::InDir("/contacts/")):?>
   <div id="Ymap"></div>   
    <script>
           $(window).on('load',function(){
            setTimeout(function(){   
               var w = window;
               var d =  document;
               var u = "https://api-maps.yandex.ru/services/constructor/1.0/js/?um=constructor%3A24aa835b98524d94399753255888480ee2960262dc0407b67e15d2ec0fd87a9d&width=100%25&height=400&lang=ru_RU&scroll=true";
                 var s=d.createElement('script');
                 s.async=true;
                 s.src=u;
                 s.type="text/javascript";
                 s.charset="utf-8";
                 d.getElementById('Ymap').appendChild(s);
                                 
            },2000);
         });
      </script>   

 <?endif;?>


4. Отложить загрузку виджета флампа, для мобильных вообще не выводить его
<div class="widget-cell" id="flamp-widget-cell" style="display:none;">
       <a class="flamp-widget" href="//krasnoyarsk.flamp.ru/firm/ХХХХХХ" data-flamp-widget-type="responsive-new" data-flamp-widget-id="985690699624139" data-flamp-widget-width="100%" data-flamp-widget-count="2">Отзывы о нас на Флампе</a>
         <script>
            $(window).on('load',function(){               
               if(document.documentElement.clientWidth >1000){
                     setTimeout(function(){          
                        var d = document;
                        var s = "script";          
                         var js,fjs=d.getElementsByTagName(s)[0];
                         js=d.createElement(s);
                         js.async=1;js.src="//widget.flamp.ru/loader.js";
                         fjs.parentNode.insertBefore(js,fjs);
                         $('#flamp-widget-cell').show();
                     },200);            
                 }      
               
            });
              
          </script>
   </div>

5. картинки lazy (микроразметка берет данные из атрибута img content)
<img src="placeholder.png" content="realimg.png" class="lazyImg" alt="altImg" title="titleImg">
if($('.lazyImg').length>0){
   setTimeout(function(){   
      $('.lazyImg').each(function(i,el){
         var src = $(el).attr("content");
         if(typeof(src) != undefined){
            $(el).attr('src',src);
         }

      });
   },1500);
}



6. отложить загрузку скрипта с иницилизацией
$(window).on('load',function(){
   setTimeout(function(){
   var w = window;
        var d =  document;      
        // добавляем скрипт
        var s=d.createElement('script');
        s.src='<?=SITE_TEMPLATE_PATH?>/js/jquery-ui.js';
        s.onload =function(){ 
            //иницилизируем после загрузки скрипта        
         var arParamsAutocomplete = <?=CUtil::PhpToJSObject($arJsParamsAutocomplete)?>;      
         for (var i = 0; i < arParamsAutocomplete.length; i++) {
            arAutocompl = arParamsAutocomplete[i];
            $( "#"+arAutocompl.id ).autocomplete({
               source: arAutocompl.source,
               select:function(event, ui) {
                  if((ui.item != null) && (typeof ui.item != undefined)){
                     if(ui.item.id != ""){
                        $("input[name='"+arAutocompl.inputName+"']").attr("checked","");
                        $('#'+ui.item.id).attr("checked","checked");               
                        smartFilter.selectDropDownItem($("label[for='"+ui.item.id+"']")[0], ui.item.id);
                     }
                  }                                             
               },
               change: function(event, ui) {                           
                  
                  if((ui.item != null) && (typeof ui.item != undefined)){
                     
                  }
                  else{
                     $("input[name='"+arAutocompl.inputName+"']").attr("checked","");         
                     $("#"+arAutocompl.all_values_input).attr("checked","checked");
                     smartFilter.selectDropDownItem($("label[for='"+arAutocompl.all_values_input+"']")[0], arAutocompl.all_values_input);                                       
                  }
               }

            });

         }
      };

        // добавляем стили
      var h=d.getElementsByTagName('script')[0];h.parentNode.insertBefore(s,h); 
        var l=d.createElement('link');
        l.type="text/css"; l.rel="stylesheet";l.href='<?=SITE_TEMPLATE_PATH?>/css/jqueryui.custom.css';
        h=d.getElementsByTagName('head')[0];h.appendChild(l);
   },2000); 
});



Убрать лишние стили и скрипты  в шаблонах. Или хотя бы отложить их загрузку.

Чтение файлов в JavaScript, FileReader

Можно импортировать на стороне сервера агентами. Но на стороне клиента нагляднее, видно сразу, что не загрузилось, какие ошибки.



html php
<?CJSCore::init("ajax");?>
<input type="file" id="fileTires" accept="text"><br>
<button id="importStart">импортировать</button>
<div id="result"></div>
<table id="tableTires" border='1'></table>
javascript
$('#importStart').click(function(){
   if (window.FileReader && window.Blob) {
      // All the File APIs are supported.          
      var files = document.getElementById('fileTires').files;         
      if(files.length != 0){
         var file = files[0];             
         if(file.name.slice(-4)=='.csv'){ //проверка по типу
            var reader = new FileReader();
            reader.onload = function(event) {
               var contents = event.target.result;
               var lines = contents.split("\n");                   
               for (var i = 2; i<lines.length;  i++) { //читаем со 2й строки
                  //что-то делаем с lines[i]

                  linestr = '<tr><td>'+data+'</td><td  id="line'+i+'"></td></tr>'; 
                  $('#tableTires').append(linestr);
                  data = { 
                        'sessid': BX.bitrix_sessid(),
                        'site_id': BX.message('SITE_ID'),
                        action: 'add',
                        line_id: i,
                        item: lines[i]                        
                  };
                  setTimeout(function(data){
                     BX.ajax({
                        url: '/tools/ajax.php',
                        method: 'POST',
                        data: data,
                        dataType: 'json',
                        async: true,
                        onsuccess: function(result){
                           //result.LINE_ID номер строки  (data.line_id)
                           if (typeof(result.STATUS) != "undefined"){
                              if(result.STATUS == 0){
                                 $('#line'+result.LINE_ID).html('Нет бренда или секции');
                              }
                           }
                        },
                        onfailure: function(error){
                                console.log(error);
                            }
                     });   
                     },
                     100*i,
                     data      
                  );   
               }
            }
            reader.onerror = function(event) {
               $('#result').html("Файл не может быть прочитан! код " + event.target.error.code);
            };                
            reader.readAsText(file,'cp1251');
         }
         else{
            $('#result').html('Ошибка! тип файла не csv');   
         }
      }
      else {          
          $('#result').html('файл не выбран');

      }
   }
   else {
      // File and Blob are not supported
      $('#result').html('браузер не поддерживает данную технологию');
   }
}


файл на сервере php
<?
define("STOP_STATISTICS", true);
if(array_key_exists('site_id',$_REQUEST) && is_string($_REQUEST['site_id']))
 if($_REQUEST['site_id'] !== '' && preg_match('/^[a-z0-9_]{2}$/i',$_REQUEST['site_id']) === 1)
      define('SITE_ID',$_REQUEST['site_id']);
   

require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
define("LOG_FILENAME", $_SERVER["DOCUMENT_ROOT"]."/bitrix/php_interface/log.txt");

use Bitrix\Main\Loader,
   Bitrix\Main\Application;

CModule::IncludeModule("iblock");
CModule::IncludeModule("catalog");
CModule::IncludeModule("sale");
CUtil::JSPostUnescape();




 if (!check_bitrix_sessid() || $_SERVER["REQUEST_METHOD"] != "POST")
    return;

$arRes = array();
if (isset($_POST["action"]) && strlen($_POST["action"]) > 0){   
   if($_POST["action"]=="add"){
      $add = 0;
      $price = floor(intval($_POST["item"]["price"]));
      if($_POST["item"]["kod"] != ""){
         //выбираем по коду поставщика
         $arSelect = Array("IBLOCK_ID", "ID");
         $arFilter = Array("IBLOCK_ID"=>$arIblocks[$_POST["item"]["category"]],"PROPERTY_KOD_POST" => $_POST["item"]["kod"]);
         $res = CIBlockElement::GetList(Array(), $arFilter, false, Array("nPageSize"=>1), $arSelect);
         if($ob = $res->GetNextElement()){
            //нашли по коду
            $arFields = $ob->GetFields();
            //обновляем   
            $el = new CIBlockElement;
            $el->Update($arFields["ID"], array("MODIFIED_BY"    => $USER->GetID(),"ACTIVE"=>"Y"));      

             CCatalogProduct::Update($arFields["ID"], array("QUANTITY"=>intval($_POST["item"]["quant"]))); //обновили доступное количество
             CPrice::SetBasePrice($arFields["ID"],$price,'RUB'); //обновили цену
             $add = 1;
             $arRes["FIND"] = $arFields;
         }      
      }
      if($add == 0){
         //ищем по параметрам, если нашли, то обновляем и код добавляем
         //.. 
         //

         if($add==0){
            //добавляем 
            

         }         
      }
      $arRes["STATUS"] = $add;
      $arRes["LINE_ID"] = $_POST["line_id"];
   }
   elseif($_POST["action"]=="deactive_old"){
      $el = new CIBlockElement;
      $i = 0;
      $arSelect = Array("IBLOCK_ID", "ID", "NAME");
      $arFilter = Array(
         "IBLOCK_ID"=>$arIblocks,         
         "ACTIVE"=>"Y", 
         "<TIMESTAMP_X"=> array(ConvertTimeStamp(time()-30000 , "FULL"))
         );
      $res = CIBlockElement::GetList(Array(), $arFilter, false, Array("nPageSize"=>10000), $arSelect);
      while($ob = $res->GetNextElement()){
         $i++;
         $arFields = $ob->GetFields();      
         $el->Update($arFields["ID"], array("ACTIVE"=>"N", 'TIMESTAMP_X' => FALSE));
      }      
      $arRes = $i.' деактивировано';
   }
}

$APPLICATION->RestartBuffer();
header('Content-Type: application/json; charset='.LANG_CHARSET);   
echo CUtil::PhpToJSObject($arRes);
die();

Методы работы с api bitrix24 коробка

добавить сделку
$arFields = array(
      "TITLE" => $_POST["name_deal"], 
        "TYPE_ID" => "SALE", 
        "STAGE_ID" => "NEW",                
        "COMPANY_ID" => $companyId,                        
        "OPENED" => "Y", 
        "ASSIGNED_BY_ID" => $managerId, 
        //"CREATED_BY_ID" =>$managerId,                         
        //UF_CRM_CONF_NAME => $_POST["configs_deal"],
        UF_CRM_CONF_NAME1 => $_POST["configsnew_deal"], //пользовательское свойство сделки   


);
$options = array('CURRENT_USER'=>1); //из под админа
$deal = new CCrmDeal(false);
$dealId = $deal->Add($arFields,true,$options);
if($dealId > 0){                    
   CModule::IncludeModule('bizproc');
   $arErrors = Array();                  
   CBPDocument::StartWorkflow(
         $bpId,
         array("crm","CCrmDocumentDeal","DEAL_".$dealId),
         array("TargetUser" => "user_1"),
         $arErrorsTmp
   );                  
}
   

проапдейтить сделку
//вариант 1
$deal = new CCrmDeal(false); //false - не учитывать права
global $DB; 
$DB->StartTransaction();              
$arUpdateData = array("STAGE_ID" => $stage);  //поля которые обновляем
$arOptions = array( 
  "CURRENT_USER"=> $arTaskCopy["CREATED_BY"] //из под кого обновляем
);              
if($deal->Update(
    $dealId,
    $arUpdateData,
    true,
    true,
    $arOptions                   
)){               
   $DB->Commit(); 
   CModule::IncludeModule('bizproc'); //запускаем бизнес-процесс или робота
   $arErrors = Array();                  
   CBPDocument::StartWorkflow(
     $bpId,
       array("crm","CCrmDocumentDeal","DEAL_". $dealId),
       array(),
       $arErrorsTmp
    );                    
}
else{                  
 $DB->Rollback();
}

//вариант 2
$deal  =  new  \CCrmDeal( false );   
$arUpdateData = array(); //поля которые обновляем
$arOptions = array("CURRENT_USER"=> $arTaskCopy["CREATED_BY"]); //из под кого обновляем
 
//сначала заполняем поля
$upRes = $deal->Update($dealId, $arUpdateData, true, true, $arOptions);                                       
//потом переходим на этап 
$arUpdateData = array("STAGE_ID" => 4);   
$upRes = $deal->Update($dealId, $arUpdateData, true, true, $arOptions);
//запускаем бизнес процесс                             
CModule::IncludeModule('bizproc');            
CBPDocument::StartWorkflow(
   $bpId,
   array("crm","CCrmDocumentDeal","DEAL_".$dealId),
   array("TargetUser" => "user_".$userId),
   $arErrorsTmp
); 
   

получить компанию
$companyId = intval($arFields["COMPANY_ID"]);
$arFilter = array("ID"=>$companyId,"CHECK_PERMISSIONS"=>"N");
$arSelect = array("TITLE","", UF_CRM_TARIFF,UF_CRM_COMPANY_TIME);
$rsCompany = CCrmCompany::GetList(Array(),$arFilter,$arSelect);
if($arComp = $rsCompany->Fetch()){
  
}       
проапдейтить компанию
$cCompany  =  new  \CCrmCompany( false );
$arUpdateData = array(UF_CRM_COMPANY_TIME => $newTime); 
$arOptions = array(
       //'DISABLE_USER_FIELD_CHECK' => true,
    "CURRENT_USER"=> 1,
    "ENABLE_SYSTEM_EVENTS" =>true
); 
$upRes = $cCompany->Update($arComp["ID"], $arUpdateData, true, true, $arOptions);

создать счет
$arInvoiceProps = array();
$entityRequisite = new \Bitrix\Crm\EntityRequisite;
$rsRequisite = $entityRequisite->getList([
   "select"=>array("*"),
   "filter"=>array("ENTITY_ID"=>$companyId,"ENTITY_TYPE_ID"=>CCrmOwnerType::Company),
   "order"=>array("SORT"=>"desc","ID"=>"desc")

]);
$arRequisite = $rsRequisite->fetchAll();

if(count($arRequisite)>0){
   $arRequisite = $arRequisite[0];
   $arInvoiceProps = array(                            
   9 => $arRequisite["RQ_INN"],                            
   10 => $arRequisite["RQ_KPP"],                           
   11 => $arRequisite["RQ_COMPANY_NAME"],                           
    14 => $arComp["CLIENT_EMAIL"],

   );
   $address = new \Bitrix\Crm\EntityAddress();
   $dbRes   = $address->getList(array(
    'filter' => array(
        'ENTITY_ID' => $arRequisite['ID'],
        'ANCHOR_ID' => $companyId
    )
));
$arAddresses = array();
while ($arAddr =  $dbRes->Fetch()) {
   if($arAddr["TYPE_ID"]==6){
      $arInvoiceProps[12] = $addr["ADDRESS_1"]." ".$addr["ADDRESS_2"];//юр адрес
      $arInvoiceProps[17] = $addr["POSTAL_CODE"];//индекс
      $arInvoiceProps[18] = $addr["CITY"];//город
      
   }
                     
}                            
}
else{
   $arInvoiceProps = array(                                                      
   11 => $arComp["TITLE"],                           
    14 => $arComp["CLIENT_EMAIL"]                        
   );
}
$arInvoiceProps[15] = ""; //телефон
$arInvoiceProps[13] = ""; //контактное лицо
   
   
   

$arFieldsInvoice = Array(
   "ORDER_TOPIC" => $arFields["TITLE"],
   "STATUS_ID" => "N",
   "DATE_INSERT" => date("d.m.Y H:i:s", strtotime("+0 hours")),
   "DATE_BILL" => date("d.m.Y", strtotime("+0 hours")),
   "PAY_VOUCHER_DATE" => "",
   "DATE_PAY_BEFORE" => "",
   "RESPONSIBLE_ID" => 1,
   "COMMENTS" => "",
   "USER_DESCRIPTION" => "",
   "UF_QUOTE_ID" => 0,
   "UF_DEAL_ID" => $arFields["ID"],
   "UF_COMPANY_ID" => $companyId,
   "UF_CONTACT_ID" => 0,
   "UF_MYCOMPANY_ID" => 9,
   "PRODUCT_ROWS" => Array(
      Array(
         "ID" => 0,
         "PRODUCT_ID" => 0,
         "PRODUCT_NAME" => "Оказание услуг бла-бла",
         "QUANTITY" =>$quantity,
         "PRICE" => $price,
         "VAT_RATE" => 0,
         "DISCOUNT_PRICE" => 0,
         "MEASURE_CODE" => 356,
         "MEASURE_NAME" => "ч",
         "CUSTOMIZED" => "Y"
      )
   ),
   "PERSON_TYPE_ID" => 1,
   "PAY_SYSTEM_ID" => 1,
   "INVOICE_PROPERTIES" => $arInvoiceProps,
   UF_CRM_1567860613 => 1 //доп свойства счета                  
);
$oCrmInvoice = new CCrmInvoice(false);
$result = $oCrmInvoice->Add($arFieldsInvoice);
получить счета
$oCrmInvoice = new CCrmInvoice(false);
$arFilter = array(   
  "CHECK_PERMISSIONS"=>"N",
  "UF_DEAL_ID" => $arDealIds,      
   "CANCELED"=>"N"            
 );            
 $arSelect = array("ID","UF_DEAL_ID");   
 $res = CCrmInvoice::GetList($arOrder = Array("ID"=>"DESC"), $arFilter ,false, false, $arSelect);   
 while($row = $res->Fetch()){            
    $oCrmInvoice->Update($row["ID"],array("CANCELED"=>"Y","STATUS_ID"=>"D")); //деактивируем счета                  
}
публичная ссылка на счет
$link = CCrmInvoice::getPublicLink($invoiceId); 


получить реквизиты по инн
if (\Bitrix\Main\Loader::includeModule('socialservices')) {
   $client = new \Bitrix\socialservices\properties\Client;
   $arRequisite = $client->getByInn($inn);
}



напоминание о задаче
//добавляем напоминание 
CModule::IncludeModule("tasks");
$arFields = Array(
    "TASK_ID" => $idTask,
    "USER_ID" => $arTask["CREATED_BY"],
    "REMIND_DATE" => date("d.m.Y H:i:s", strtotime("+5 hours")), 
    "TYPE" => CTaskReminders::REMINDER_TYPE_DEADLINE,
    "TRANSPORT" => CTaskReminders::REMINDER_TRANSPORT_JABBER
);
$obTaskReminders = new CTaskReminders;
$ID = $obTaskReminders->Add($arFields);
задачи для дела получить и обновить
$arFilter = array(                       
   'TAG' => $tag,
   "UF_CRM_TASK" => $arTask["UF_CRM_TASK"][0],   
);
$res = CTasks::GetList(
    Array("DEADLINE" => "ASC"), 
    $arFilter,
    array("ID"),
    array("USER_ID"=>1)                    
);
$obTask = new CTasks;
while($arTask1 = $res->GetNext()){   
   $obTask->Update(
      $arTask1["ID"],  
      array(
         "STATUS" => CTasks::STATE_COMPLETED               
      ), 
      array('USER_ID' => 1)
   );
}


создаем новую задачу
$newTask = array(
   "TITLE" => "заголовок",
   "DESCRIPTION" => "описание",
   "TAGS" => array("CRM","tag1"),
   "DEADLINE" => date("d.m.Y H:i:s", strtotime(("+".($time+4)." hours"))),
   "CREATED_BY" => $arTaskCopy["RESPONSIBLE_ID"],
   "RESPONSIBLE_ID" => $arTaskCopy["RESPONSIBLE_ID"],
   "AUDITORS" => $arTaskCopy["AUDITORS"],
   //"TASK_CONTROL" => "Y",
   "ADD_IN_REPORT" => "Y",
   "DEPENDS_ON"=> $arFields["ID"],
   "UF_CRM_TASK" => $arTaskCopy["UF_CRM_TASK"]
);   

$taskItem = \CTaskItem::add($newTask, 1); 
создать подзадачу для задачи из бизнес-процесса
$arFields = Array(
        "TITLE" => "Название задачи",
        "DESCRIPTION" => "Описание",
        "RESPONSIBLE_ID" => $userId, //ответственный
        "GROUP_ID" => $this->GetVariable('WORKGROUPID'),
        "PARENT_ID"=>'{=A2629_10558_53461_99205:TaskId}',
    );
 
    $obTask = new CTasks;
    $ID = $obTask->Add($arFields);


Добавим компанию, привяжем к пользователю, создадим контакт
$cCompany  =  new  \CCrmCompany( false );
$arFieldsComp = array(
    'TITLE' => (strlen($arNewUser["INN"])==10)?$arRequisite["NAME_SHORT"]:"ИП ".$arRequisite["LAST_NAME"]." ".substr($arRequisite["NAME"], 0,1).". ".substr($arRequisite["SECOND_NAME"],0,1).".",      
    "ASSIGNED_BY_ID" => $manager,                                
    //'CONTACT_ID' => array($iContactID),//здесь привязываем к компании контакт (обязательно с array())
    UF_CRM_TARIFF => 154 //почасовое обслуживание
);
$companyId = $cCompany->Add($arFieldsComp);

if($companyId >0){
   //обновим пользователя, добавим компанию
   $user = new CUser;
   if(!$user->Update($arNewUser["ID"], array("UF_CONTACT"=>$companyId))){
      //не смогли обновить пользователя
   }
   //добавим реквизиты 
   if(strlen($arNewUser["INN"])==10){ //юр лицо
      $arFields = array(
         'ENTITY_ID' => $companyId,
         'ENTITY_TYPE_ID' => CCrmOwnerType::Company,
         'PRESET_ID' => 1,//организация                              
         'NAME' => $arRequisite['NAME_SHORT'],
         'SORT' => 500,
         'ACTIVE' => 'Y',
         'RQ_COMPANY_NAME' => $arRequisite['NAME_SHORT'],
         'RQ_COMPANY_FULL_NAME' => $arRequisite['NAME'],                              
         'RQ_INN' => $arRequisite["INN"], //инн
         'RQ_KPP' => $arRequisite["KPP"],  //кпп
         'RQ_OGRN' => $arRequisite["OGRN"], //огрн
         'RQ_COMPANY_REG_DATE' => $arRequisite["CREATION_REGISTRATION_DATE"],
         'RQ_OKVED' => $arRequisite["OKVED_CODE"],                              
      );

      if(isset($arRequisite["OFFICIALS"][0]["LAST_NAME"])){
         $arFields["RQ_DIRECTOR"] = $arRequisite["OFFICIALS"][0]["LAST_NAME"]." ".$arRequisite["OFFICIALS"][0]["NAME"]." ".$arRequisite["OFFICIALS"][0]["SECOND_NAME"];
      }
   }
   else{//ИП
      $arFields = array(
         'ENTITY_ID' => $companyId,
         'ENTITY_TYPE_ID' => CCrmOwnerType::Company,
         'PRESET_ID' => 2,//ИП                              
         'NAME' => "ИП ".$arRequisite["LAST_NAME"]." ".$arRequisite["NAME"]." ".$arRequisite["SECOND_NAME"],
         'SORT' => 500,
         'ACTIVE' => 'Y',
         'RQ_LAST_NAME' => $arRequisite['LAST_NAME'],
         'RQ_FIRST_NAME' => $arRequisite["NAME"],
         'RQ_SECOND_NAME' => $arRequisite["SECOND_NAME"],                              
         'RQ_COMPANY_FULL_NAME' => $arRequisite['NAME'],                              
         'RQ_INN' => $arRequisite["INN"], //инн                              
         'RQ_OGRNIP' => $arRequisite["OGRNIP"], //огрн
         'RQ_OKVED' => $arRequisite["OKVED_CODE"],                           
      );

   }
   
   $res = $entityRequisite->add($arFields);
   
   if($res->getId() > 0){
      //адрес добавляем
      $address = new \Bitrix\Crm\EntityAddress();
      $address->register(8, $res->getId(), 6, array(                                                            
         "ADDRESS_1" =>$arRequisite["ADDRESS_STREET_NAME"]." ".$arRequisite["ADDRESS_STREET_TYPE"]." ".$arRequisite["ADDRESS_HOUSE"]." ".$arRequisite["ADDRESS_BUILDING"],
         "ADDRESS_2" => $arRequisite["ADDRESS_FLAT"],
         "CITY" => $arRequisite["ADDRESS_REGION_NAME"],
         "POSTAL_CODE" => $arRequisite["ADDRESS_INDEX"],
         "COUNTRY" => "РФ"
      ));
   }                                          
   //создаем контакт 
   $arFields = array(
       "NAME" => $arNewUser["NAME"],
       "LAST_NAME" => $arNewUser["LAST_NAME"],                                     
       "POST" => "не указано",//должность               
       "OPENED" => "N", //открыто для других пользователей                
       "EXPORT" => "Y",//участвует в экспорте                
       'FM' => array(//почта, телефон
           'EMAIL' => array(
                   'n0' => array('VALUE' => $arNewUser["EMAIL"], 'VALUE_TYPE' => 'WORK')
                 ),
                 'PHONE' => array(
                    'n0' => array('VALUE' => $arNewUser["PERSONAL_MOBILE"], 'VALUE_TYPE' => 'WORK')
                 ) 
             ),
             "COMPANY_ID" => $companyId,                
        "ASSIGNED_BY_ID" => $manager,//id ответственного менеджера 
   );
   //создаем контакт   
   $oContact = new \CCrmContact(false);
   $oContact->add($arFields);
   if($oContact->LAST_ERROR != ""){                           
      //не создался контакт
   }
   
}

получить реквизиты компании
$entityRequisite = new \Bitrix\Crm\EntityRequisite;
$rsRequisite = $entityRequisite->getList([
   "select"=>array("*"),
   "filter"=>array("ENTITY_ID"=>$companyId,"ENTITY_TYPE_ID"=>CCrmOwnerType::Company),
   "order"=>array("SORT"=>"desc","ID"=>"desc")

]);
$arRequisite = $rsRequisite->fetchAll();
$arResult["REQUISITE"] = $arRequisite[0];
if(isset($arResult["REQUISITE"]['ID']) && $arResult["REQUISITE"]['ID']>0){
      $bank   = new \Bitrix\Crm\EntityBankDetail();
      $dbRes  = $bank->getList(array(
       'filter' => array('ENTITY_ID' => $arResult["REQUISITE"]['ID'])
   ));
   $arResult["BANK"] =  $dbRes->Fetch();
   $address = new \Bitrix\Crm\EntityAddress();
   $dbRes   = $address->getList(array(
       'filter' => array(
           'ENTITY_ID' => $arResult["REQUISITE"]['ID'],
           //'ANCHOR_ID' => $companyId
       )
   ));
   $arResult["ADDRESS"] = array();
   while ($arAddr =  $dbRes->Fetch()) {
      if($arAddr["TYPE_ID"]==6){
         $arResult["ADDRESS"]["OFFICIAL"] = $arAddr;
      }
      elseif($arAddr["TYPE_ID"]==2){
         $arResult["ADDRESS"]["FACT"] =  $arAddr;   
      }                     
   }

}   

по ID пользователя получим компанию, к которой он относится, получим реквизиты компании  и их проапдейтим
global $USER;
if(is_object($USER) && $USER->IsAuthorized()){
   $rsUser = CUser::GetList($by, $order,
       array("ID" => $USER->GetID()),
       array(
           "SELECT" => array("ID","UF_CONTACT"),
       )
   );
   if($arUser = $rsUser->Fetch()){         
      $companyId = intval($arUser["UF_CONTACT"]);
       if($companyId>0 && CModule::IncludeModule('crm')){                 
          $arFilter = array("ID"=>$companyId,"CHECK_PERMISSIONS"=>"N");
          $arSelect = array("ID","TITLE");
          $rsCompany = CCrmCompany::GetList(Array(),$arFilter,$arSelect);
          if($arCompany = $rsCompany->Fetch()){   
             $entityRequisite = new \Bitrix\Crm\EntityRequisite;
             $rsRequisite = $entityRequisite->getList([
                "select"=>array("*"),
                "filter"=>array(
                   "ENTITY_ID"=>$companyId,
                   "ID"=> intval($_REQUEST["req_id"]),
                   "ENTITY_TYPE_ID"=>CCrmOwnerType::Company
                ),
                "order"=>array("SORT"=>"desc","ID"=>"desc")

             ]);
             $arRequisiteOld = $rsRequisite->fetchAll();
             $arRequisiteTemp = $arRequisiteOld[0];
             if(isset($arRequisiteTemp['ID']) && (intval($arRequisiteTemp['ID'])>0)){
                $arRequisite = $_REQUEST['fields'];
                if($_REQUEST["type_company"] == "ip"){
                   //ИП
                   $arFields = array(                                                      
                     'NAME' => $arRequisite['RQ_COMPANY_NAME'],                           
                     'ACTIVE' => 'Y',
                     'RQ_LAST_NAME' => $arRequisite['RQ_LAST_NAME'],
                     'RQ_FIRST_NAME' => $arRequisite["RQ_FIRST_NAME"],
                     'RQ_SECOND_NAME' => $arRequisite["RQ_SECOND_NAME"],                              
                     'RQ_COMPANY_FULL_NAME' => $arRequisite['RQ_COMPANY_FULL_NAME'],                              
                     'RQ_INN' => $arRequisite["RQ_INN"], //инн                              
                     'RQ_OGRNIP' => $arRequisite["RQ_OGRNIP"], //огрн
                     'RQ_OKVED' => "",                           
                  );
                }
                else{
                   //юр лицо
                   $arFields = array(                                                   
                     'NAME' => $arRequisite['RQ_COMPANY_NAME'],
                     'SORT' => 500,
                     'ACTIVE' => 'Y',
                     'RQ_COMPANY_NAME' => $arRequisite['RQ_COMPANY_NAME'],
                     'RQ_COMPANY_FULL_NAME' => $arRequisite['RQ_COMPANY_FULL_NAME'],                              
                     'RQ_INN' => $arRequisite["RQ_INN"], //инн
                     'RQ_KPP' => $arRequisite["RQ_KPP"],  //кпп
                     'RQ_OGRN' => $arRequisite["RQ_OGRN"], //огрн
                     'RQ_COMPANY_REG_DATE' => "",
                     'RQ_OKVED' => "",   
                     'RQ_DIRECTOR' => $arRequisite["RQ_DIRECTOR"],
                     'RQ_ACCOUNTANT' => $arRequisite["RQ_ACCOUNTANT"]                           
                  );
                }
                $entityRequisite->Update(intval($arRequisiteTemp['ID']),$arFields);                   

                $bank   = new \Bitrix\Crm\EntityBankDetail();
               $dbRes  = $bank->getList(array(
                   'filter' => array('ENTITY_ID' => $arRequisiteTemp['ID'])
               ));
               $arBankOld =  $dbRes->Fetch();
               
               $arFields = array(
                  "RQ_BANK_NAME" => $arRequisite["RQ_BANK_NAME"],
                  "RQ_BIK" => $arRequisite["RQ_BIK"],
                  "RQ_ACC_NUM" => $arRequisite["RQ_ACC_NUM"],
                  "RQ_COR_ACC_NUM" => $arRequisite["RQ_COR_ACC_NUM"],
                  "RQ_BANK_ADDR" => $arRequisite["RQ_BANK_ADDR"],
               );
               if($arBankOld["ID"] >0){
                  $bank->Update(intval($arBankOld["ID"]),$arFields);
               }

               $address = new \Bitrix\Crm\EntityAddress();
               $address->deleteByEntity(2, $arRequisiteTemp['ID']);
               $address->deleteByEntity(6, $arRequisiteTemp['ID']);

               //юр адрес
               $arFields = array(
                  "ADDRESS_1" => $arRequisite["company_addr_address_1"],
                  "ADDRESS_2" => $arRequisite["company_addr_address_2"],
                  "CITY" => $arRequisite["company_addr_city"],
                  "POSTAL_CODE" => $arRequisite["company_addr_postal_code"],
                  "COUNTRY" => $arRequisite["company_addr_country"],
                  "REGION" => $arRequisite["company_addr_region"],
                  "PROVINCE" => $arRequisite["company_addr_province"],
               );
               $res = $address->register(8, $arRequisiteTemp['ID'], 6, $arFields);
               
               //факт адрес
               $arFields = array(
                  "ADDRESS_1" => $arRequisite["company_realaddr_address_1"],
                  "ADDRESS_2" => $arRequisite["company_realaddr_address_2"],
                  "CITY" => $arRequisite["company_realaddr_city"],
                  "POSTAL_CODE" => $arRequisite["company_realaddr_postal_code"],
                  "COUNTRY" => $arRequisite["company_realaddr_country"],
                  "REGION" => $arRequisite["company_realaddr_region"],
                  "PROVINCE" => $arRequisite["company_realaddr_province"],
               );
               $res = $address->register(8, $arRequisiteTemp['ID'], 2, $arFields);
               
               echo Cutil::PhpToJSObject(true);
               die();

             }   
             
          }
       }
   }   
}
получить контакта по компании
 $arDelete = array();
$res = CCrmContact::GetContactByCompanyId($arFields['ID']);
while ($ar = $res->Fetch()) {
     $arDelete[] = $ar['ID'];
}
$CCrmContact = new CCrmContact();
$CCrmContact->UpdateCompanyId($arDelete, 0);

отправим уведомление в мессенджер
$arFields = array(
   "MESSAGE_TYPE" => "S", # P - private chat, G - group chat, S - notification
   "TO_USER_ID" => $manager,
   "FROM_USER_ID" => $arNewUser["ID"],
   "MESSAGE" => "Новый клиент",
   "AUTHOR_ID" => $arNewUser["ID"],
   "EMAIL_TEMPLATE" => "some",

   "NOTIFY_TYPE" => 2,  # 1 - confirm, 2 - notify single from, 4 - notify single
   "NOTIFY_MODULE" => "main", # module id sender (ex: xmpp, main, etc)
   "NOTIFY_EVENT" => "IM_GROUP_INVITE", # module event id for search (ex, IM_GROUP_INVITE)
   "NOTIFY_TITLE" => "Новый клиент", # notify title to send email
);
if(CModule::IncludeModule('im')){
   CIMMessenger::Add($arFields);
}


перезвоните мне от клиента
$rsUser = CUser::GetList($by, $order,
    array("ID" => $USER->GetID()),
    array(
        "SELECT" => array("ID","NAME","UF_CONTACT","PERSONAL_MOBILE"),
    )
);
if($arUser = $rsUser->Fetch()){                
    $companyId = $arUser["UF_CONTACT"];        
    if(intval($companyId)>0 && CModule::IncludeModule('crm') && CModule::IncludeModule('im')){                                 
       $arComp = CCrmCompany::GetByID(intval($companyId),false);
       $managerId = $arComp["ASSIGNED_BY_ID"];
       if($managerId >0 && ($USER->GetID() != $managerId)){                        
          $result = CIMMessage::Add(array(  
              'FROM_USER_ID' => $USER->GetID(),  
              'TO_USER_ID' => $managerId, 
              'MESSAGE' => 'Перезвоните мне, пожалуйста! Тел: '.$arUser["PERSONAL_MOBILE"], 
          ));                      
       }
    }
}

создать рабочую группу и ее диск из бизнес-процесса
CModule::IncludeModule('socialnetwork');
CModule::IncludeModule('disk');
CModule::IncludeModule('webdav');
 
$SocGroup=new CSocNetGroup;
global $USER;
 
$arFieldsSG=array(
   "NAME"=>"{=Document:TITLE}",
   "SITE_ID" => "s1",
   "DESCRIPTION"=>"{=Document:TITLE} Workgroup",
   "ACTIVE"=>"Y",
   "VISIBLE"=>"Y",
   "OPENED"=>"Y",
   "CLOSED"=>"N",
   "SUBJECT_ID"=>4,
   "OWNER_ID"=>1,
   "INITIATE_PERMS"=>A,
   "SPAM_PERMS"=>"N",
   "SUBJECT_NAME"=>"Management Board"
 
);
 
$GroupID=CSocNetGroup::CreateGroup($USER->GetID(),$arFieldsSG);
 
if (CModule::IncludeModule("disk")){
   BitrixDiskDriver::getInstance()->addGroupStorage($GroupID);
}
 
$this->SetVariable('WORKGROUPID',$GroupID);



добавить событие в календарь пользователя
$fromTs = MakeTimeStamp($_POST['from'], "MM/DD/YYYYS");
$toTs = MakeTimeStamp($_POST['to'], "MM/DD/YYYYS");
$arFields = array(
    "OWNER_ID" => 1,
     "CAL_TYPE" =>'user',
   "NAME" => $_POST['eventName'],
   "DESCRIPTION" => $_POST['description'],
   "SKIP_TIME" => date('H:i', $fromTs) == '00:00' && date('H:i', $toTs) == '00:00',
   "IS_MEETING" => false,
   "RRULE" => false,
   "DT_FROM_TS" => $fromTs,
   "DT_TO_TS" => $toTs
);

$CalEventId = CCalendar::SaveEvent(
   array(
      'arFields' => $arFields,
      'autoDetectSection' => true
   )
);
запустить бизнес-процесс, не привязанный к CRM
$documentId = CBPVirtualDocument::CreateDocument(
    0,
    array(
     "IBLOCK_ID" => 27,
     "NAME" => "Create Notification",
     "CREATED_BY" => "user_".$GLOBALS["USER"]->GetID(),
    )
);
$arErrorsTmp = array();
$wfId = CBPDocument::StartWorkflow(
   $bpId,
    array("bizproc", "CBPVirtualDocument", $documentId),
    array_merge(array(), array("TargetUser" => "user_".intval($GLOBALS["USER"]->GetID()))),
    $arErrorsTmp
);
добавить лид
CModule::IncludeModule('crm');
 
$lead = new CCrmLead;
 
$arFields = Array(
    "TITLE" => "Заголовок",
    "COMPANY_TITLE" => "Название компании",
   "NAME" => "Имя",
   "LAST_NAME" => "Фамилия",
   SECOND_NAME" => "Отчество",
   "POST" => "Должность",
   "ADDRESS" => "Адрес",
   "COMMENTS" => "Комментарий",
    "SOURCE_DESCRIPTION" => "",
   "STATUS_DESCRIPTION" => "",
   //"OPPORTUNITY" => 123456,
   "CURRENCY_ID" => "RUB",
   //"PRODUCT_ID" => "PRODUCT_1",
   "SOURCE_ID" => "SELF",
       "STATUS_ID" => "NEW",
       "ASSIGNED_BY_ID" => $userId,  
       "UF_CRM_143192342342" => "",   
   );
 
$lidId = $lead->Add($arFields);