Skip to content

Соглашения по стилю кода 1С

Notifications You must be signed in to change notification settings

kuzyara/CodeStyle1C

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Соглашения по стилю кода 1С

Дисклеймер: если какие-то моменты странные - просба написать https://t.me/KuzNikAl или отправить mr с исправлениями

0. Система стандартов и методик разработки фирмы 1С

При разработке придерживаемся стандартов и методик разработки фирмы 1С: https://its.1c.ru/db/v8std

и диагностик протокола bsl: https://1c-syntax.github.io/bsl-language-server/diagnostics/

Проверка части стандартов автоматизирована в нашем статическом анализаторе http://sonar-server.rossko.local/

1. [только для git] Обрамляющиeе комментарии

!Касается только разработки конфигураций на базе gitsync и edt! (замечание @AleksandrShumakyan)

Не рекомендуется использовать обрамляющие комментарии - со временем они усложняют чтение кода. Для отслеживания истории изменений есть git blame и плагины vscode: annotator и gitlens.

Также следует избегать комментариев, не дающих дополнительных пояснений о работе не-экспортной процедуры (функции).

  • Не нужно использовать шаблоны текста для вставки своей подписи - данный подход вынужденно используется франчайзи и фрилансерами у которых нет гита
  • Не нужно писать псевдонимы вида "КСИ" или "VVV"
  • Не нужно вставлять открывающие и закрывающие символы вида "+++" и "---" или "{" и "}"
  • Не нужно оставлять временную метку в часах минутах и секундах - всё это не несёт полезной информации
  • Правильно: комментарий начинается с пробела, с большой буквы
  • Если присутствует непреодолимое желание оставить свою подпись - это можно сделать в конце комментария - номер задачи, потом полные фамилия и имя. См. пример
Примеры

Правильно

# или комментарий без подписи

// Получает основной банковский счет даже без указания валюты.
// Если валюта не указана - выбираем первый подходящий банковский счет.
//
Функция ПолучитьОсновнойБанковскийСчет(Организация, Валюта = Неопределено) Экспорт
# или подпись в конце комментария без даты
  
    // Контрагент и сумма для выгрузки по фитам #333478 Калеников Сергей
    ДанныеСтроки.Вставить("Sum" ,              Выборка.Сумма);
    ДанныеСтроки.Вставить("ClientInetShopID" , Выборка.IDКонтрагентИМ);
    ДанныеСтроки.Вставить("ClientFrontGUID" ,  XMLСтрока(Выборка.КонтрагентГУИД));
# или вовсе без комментария
  
    ДанныеСтроки.Вставить("NDS" , Выборка.NDS);
    ДанныеСтроки.Вставить("GUIDOrder" , Строка(СтруктураШапкиДокумента.GUIDOrder));
    ДанныеСтроки.Вставить("GUIDOrganization" , Строка(СтруктураШапкиДокумента.GUIDOrganization));

Неправильно

// +++ VVV 19.10.2022: Получаем основной банковский счет даже без указания валюты
//      Если валюта не указана - выбираем первый подходящий банковский счет
Функция ПолучитьОсновнойБанковскийСчет(Организация, Валюта = Неопределено) Экспорт
//kaiten #333478 +++ 2017-06-20 КИС добавляем контрагента и сумму для выгрузки по фитам.
ДанныеСтроки.Вставить("Sum" ,              Выборка.Сумма);
ДанныеСтроки.Вставить("ClientInetShopID" , Выборка.IDКонтрагентИМ);
ДанныеСтроки.Вставить("ClientFrontGUID" ,  XMLСтрока(Выборка.КонтрагентГУИД));
//kaiten #333478 ---
// {Востриков Михаил карточка №476653 19.02.2018 17:56:23
ДанныеСтроки.Вставить("NDS" , Выборка.NDS);
ДанныеСтроки.Вставить("GUIDOrder" , Строка(СтруктураШапкиДокумента.GUIDOrder));
ДанныеСтроки.Вставить("GUIDOrganization" , Строка(СтруктураШапкиДокумента.GUIDOrganization));
//} Востриков Михаил 19.02.2018 17:56:23

Мнение эксперта:
alt text

1.1 Закомментированные блоки кода

Внимание: следует избегать закомментированных блоков старого кода. Диагностика
Если так хочется оставить старый код на всякий случай - используйте переключатель функциональности (фичатогл).
Наглядные изменения и так видны в дифе коммита гитлаба или гит-блейме гита.

Цитата из книги "Чистый код"

image

2. [только для RMQ] Сериализация ссылок для обмена через json

При обмене данными через шину данных, например RabbitMQ, встречается задача сериализации структур данных в строку методом ЗаписатьJSON:

СтруктураСообщения = Новый Структура;
// ... - заполнение структуры сообщения для передачи в другую систему

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураСообщения);
СтрокаJSON = ЗаписьJSON.Закрыть();

Справочники.ИсходящиеСообщения.Опубликовать("goods.batch.negative.balance", СтрокаJSON); // отправка сообщения

Для того чтобы передать ссылку в строковом формате - необходимо получить её GUID (идентификатор).

Не нужно приводить ссылку к строке, получая идентификатор отдельным методом, для этого встроенный метод платформы XMLСтрока()

Неправильно

СтруктураСообщения.Вставить("Partner", "" + ДанныеШапки.Партнер.УникальныйИдентификатор()); // неправильно
  
СтруктураСообщения.Вставить("УИДКонтрагента", Строка(Контрагент.УникальныйИдентификатор())); // неправильно
  
СтруктураСообщения.Вставить("GUID", XMLСтрока(ЗаказПокупателя.УникальныйИдентификатор())); // неправильно
СтруктураСообщения.Вставить("ReasonCancellation", ""+ ?(ЗначениеЗаполнено(СтрокаОтправки.ПричинаОтмены), 
    СтрокаОтправки.ПричинаОтмены.УникальныйИдентификатор(), "00000000-0000-0000-0000-000000000000")); // не надо проверять пустую ссылку

Правильно

СтруктураСообщения.Вставить("Partner", XMLСтрока(ДанныеШапки.Партнер)); // правильно
  
СтруктураСообщения.Вставить("УИДКонтрагента", XMLСтрока(Контрагент)); // правильно
СтруктураСообщения.Вставить("ReasonCancellation", XMLСтрока(СтрокаОтправки.ПричинаОтмены));
// если ссылка пустая - метод XMLСтрока() и так вернет "00000000-0000-0000-0000-000000000000"

3. Префиксы в именах переменных

Венгерская нотация противоречит стандарту https://its.1c.ru/db/v8std#content:454:hdoc , использовать ее в проектах на УФ выглядит сомнительным решением.

Неправильно

пТекущаяДата = ТекущаяДата();
 
ДанныеТЧТовары = Новый Структура("тчТоварыИтоги, тчТовары");
  
тзДанные = Новый ТаблицаЗначений;

Исключение: Конфигурации 7.7 и 8.1-8.2 (отраслевые), где не было УФ - это старые проекты которые писали клюшечники и для которых префиксы были необходимостью

3.1 Соглашение по стилю написания переменных и имен функций/методов

Неправильно:
Snake case: last_name, annual_earnings, total_snakes_found
Kebab case: "the-quick-brown-fox-jumps-over-the-lazy-dog"
lowerCamelCase: isConnected, thisIsAnExample, myXmlParser

Правильно:
UpperCamelCase (also known as PascalCase): UIInterfaceOrientationMask, SplashScreenState

В 1с общепринято использовать CamelCase нотацию, поэтому правильно: ВТДокументы или ВременнаяТаблицаДокументы, неправильно: ВТ_Документы, ВтДокументы, втДокументы или ВТ_ТЧ_НастройкиОбмена

Неправильно: Процедура ДатаОтгрузки_НачалоВыбора(Элемент, СтандартнаяОбработка),
Правильно: Процедура ДатаОтгрузкиНачалоВыбора(Элемент, СтандартнаяОбработка)

Привила именования объектов метаданных: https://its.1c.ru/db/v8std/content/550/hdoc

4. Проверка на пустой результат выполнения запроса

Если требуется выбрать (или выгрузить) результат запроса, то предварительный вызов метода Пустой не требуется

Неправильно:

// 
Результат = Запрос.Выполнить();

Если НЕ РезультатПустой() Тогда
	Выборка = Результат.Выбрать();
	Выборка.Следующий();
	Если ЗначениеЗаполнено(Выборка.Подразделение) Тогда
		ВнутенннийЗаказ.Подразделение = Выборка.Подразделение;
	Иначе
		ВнутрениийЗаказ.Подразеление = БСП.ПолучитьЗначениеРеквизитаСправочника(ВнутреннийЗаказ.Филиал, "Подразделение");
	КонецЕсли;
Иначе
	ВнутреннийЗаказ.Подразделение = БСП.ПолучитьЗначениеРеквизитаСправочника(ВнутрениийЗаказ.Филиал, "Подразделение");
КонецЕсли;

Правильно:

Результат = Запрос.Выполнить();
Выборка = Результатр.Выбрать();
Если Выборка.Следующий() Тогда
    ВнутреннийЗаказ.Подразделение = Выборка.Подразделение;
Иначе
    ВнутреннийЗаказ.Подразделение = БСП.ПолучитьЗначениеРеквизитаСправочника(ВнутреннийЗаказ.Филиал, "Подразделение");
КонецЕсли;

Неправильно:

Результат = Запрос.Выполнить();
 
Если НЕ Результат.Пустой() Тогда
    Выборка = Результат.Выбрать();
    Пока Выборка.Следующий() Цикл
        СтрокиЗаказовПривилегированный.ОпубликоватьСтатусДоставкиЗаявкиНаВозвратОтПокупателя(Выборка.Пункт);
    КонецЦикла;
КонецЕсли; 

Правильно:

Результат = Запрос.Выполнить();

Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
    СтрокиЗаказовПривилегированный.ОпубликоватьСтатусДоставкиЗаявкиНаВозвратОтПокупателя(Выборка.Пункт);
КонецЦикла;

Об этом написано и в ИТС: https://its.1c.ru/db/v8std/content/438/hdoc

Диагностика: https://docs.checkbsl.org/checks/overall/WrongVerifyEmptyQueryResult/

5. Правила перевода имен реквизитов 1с на латиницу в json

Необходимо соблюдать общепринятые латинские названия реквизитов и не придумывать свои.

Неправильно: Kontragent, Nomenklatura, Summa

Правильно: Contractor, Goods, TotalSumm

Именование основных повторяемых сущностей (формат сообщений)

Наименование реквизита Наименование в JSON
Ссылка GUID
Код Code
Дата DateTime
Наименование Name
Родитель Parent
Пометка удаления DeletionMark
Номенклатура (массив) Goods
Контрагент Contractor
Филиал Branch
Склад Warehouse
ЦФО FRC
Валюта Currency
ЗаказПокупателя CustomerOrder
Чек Receipt
Партнер Partner
Проведен Posted
ТипДокумента DocumentType
ВидОперации OperationType
Справочник Catalog
Сотрудник Employee
Поставщик Supplier

Примеры

ДанныеСтроки = Новый Структура;
ДанныеСтроки.Вставить("GUID",                       XMLСтрока(Выборка.Ссылка));
ДанныеСтроки.Вставить("Number",                     СокрЛП(Выборка.Номер));
ДанныеСтроки.Вставить("DateTime",                   XMLСтрока(Выборка.Дата));
ДанныеСтроки.Вставить("DeletionMark",               Выборка.ПометкаУдаления);
ДанныеСтроки.Вставить("Posted",                     Выборка.Проведен);
ДанныеСтроки.Вставить("Presentation",               Строка(Выборка.Ссылка));
ДанныеСтроки.Вставить("DocumentType",               Выборка.Ссылка.Метаданные().Имя);
ДанныеСтроки.Вставить("DataVersion",                Выборка.ВерсияДанных);
//
ДанныеСтроки.Вставить("TotalSumm",                  Выборка.СуммаДокумента);
ДанныеСтроки.Вставить("DeliveryAddress",            XMLСтрока(Выборка.АдресДоставки));
ДанныеСтроки.Вставить("DeliveryAddressExtraData",   XMLСтрока(Выборка.ДополнениеКАдресуДоставки));
ДанныеСтроки.Вставить("ContractorGUID",             XMLСтрока(Выборка.Контрагент));
ДанныеСтроки.Вставить("ContractGUID",               XMLСтрока(Выборка.ДоговорКонтрагента));
ДанныеСтроки.Вставить("OrganizationGUID",           XMLСтрока(Выборка.Организация));   
ДанныеСтроки.Вставить("CurrencyGUID",               XMLСтрока(Выборка.ВалютаДокумента));
ДанныеСтроки.Вставить("AdjustedDocument",           XMLСтрока(Выборка.ИсправляемыйДокументРеализации));
ДанныеСтроки.Вставить("Goods",                      Выборка.Товары);

Для сериализации гуида используйте функцию XMLСтрока()

СтруктураВСообщение.Вставить("Partner", "" + ДанныеШапки.Партнер.УникальныйИдентификатор()); // неправильно
  
СтруктураШапки.Вставить("УИДКонтрагента", Строка(Контрагент.УникальныйИдентификатор())); // неправильно
  
Структура.Вставить("GUID", XMLСтрока(ЗаказПокупателя.УникальныйИдентификатор())); // неправильно
  
Структура.Вставить("GUID", XMLСтрока(ЗаказПокупателя)); // правильно

6. ПРАВИЛА РАБОТЫ С ТРАНЗАКЦИЯМИ

Общий шаблон транзакции

НачатьТранзакцию();
Попытка
    БлокировкаДанных = Новый БлокировкаДанных;
    ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("Документ.ПриходнаяНакладная");
    ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", СсылкаДляОбработки);
    ЭлементБлокировкиДанных.Режим = РежимБлокировкиДанных.Исключительный;
    БлокировкаДанных.Заблокировать();
 
    // ... // чтение или запись данных
 
    ДокументОбъект.Записать();
 
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
 
    ЗаписьЖурналаРегистрации(НСтр("ru = 'Выполнение операции'"),
        УровеньЖурналаРегистрации.Ошибка,
        ,
        ,
        ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
 
    ВызватьИсключение; // если есть внешняя транзакция
 
КонецПопытки;

Основные правила:

  1. Метод "НачатьТранзакцию" должен располагаться непосредственно перед оператором "Попытка"
  2. Метод "ЗафиксироватьТранзакцию" должен идти последним в блоке "Попытка"
  3. Метод "ОтменитьТранзакцию" должен идти первым в блоке "Исключение"
  4. Необоснованное использование метода ТранзакцияАктивна()
  5. При обработке исключений необходимо использовать метод ЗаписьЖурналаРегистрации()
  6. Необходимо обязательно указывать 1, 2 и 5 параметр метода ЗаписьЖурналаРегистрации()
  7. При получении текста ошибки в блоке исключения необходимо использовать ПодробноеПредставлениеОшибки()
  8. Обращение к внешним ресурсам внутри транзакции вызывает проблемы производительности
  9. Внутри транзакции недопустимо подавлять ошибки, вызывающие событие SDBL Func='setRollbackOnly'
  10. При использовании вложенных транзакций в конце блока Исключение рекомендуется добавить оператор ВызватьИсключение
  11. При использовании оператора ВызватьИсключение необходимо сохранять стек ошибок
  12. Чтение данных в транзакции с их последующим изменением, необходимо производить после установки исключительной управляемой блокировки

Подробнее описано в статье: Правила работы с транзакциями 1С

6.2 Почему не рекомендуется передавать текст ошибки вместо непосредственной записи в ЖР

Не рекомендуется

Исключение
    ОтменитьТранзакцию();
         
    СтруктураРезультат.Успех = Ложь;
    СтруктураРезультат.ОписаниеОшибки = ОписаниеОшибки();
 
    Возврат СтруктураРезультат;
         
КонецПопытки;

При переиспользовании такой функции другими разработчиками на запись в ЖР обычно забивают - неумышленно или по незнанию как правильно использовать - поэтому рекомендую писать текст ошибки в ЖР сразу.

6.3.1 Почему нужно использовать оператор ВызватьИсключение в блоке Исключение...КонецПопытки без параметра

Не следует передавать параметр при вызове исключения в блоке Исключение...КонецПопытки.

Неправильно

Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение ОписаниеОшибки(); // <- неправильно, потерян стек
КонецПопытки;

Для этого следует вызывать оператор ВызватьИсключение; без параметров. Это позволяет сохранить стек ошибок и передать изначальное исключение в неизменённом виде.

Правильно

Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

Подробнее смотри статью https://infostart.ru/1c/articles/1513676/

Справка по оператору ВызватьИсключение (отрывок из встренной контекстной справки Ctrl+F1)
ВызватьИсключение;

Оператор позволяет вызвать исключение в тех случаях, когда несмотря на отработку исключительной ситуации 
операторами исключения необходимо прервать выполнение модуля с ошибкой времени выполнения. Оператор 
допустим только внутри операторных скобок `Исключение – КонецПопытки`.

Выполнение данного оператора прекращает выполнение последовательности операторов исключения и производит 
поиск более "внешнего" обработчика исключения (при вложенных попытках). Если таковой есть, то управление 
передается на его первый оператор. Если нет, то исключительная ситуация обрабатывается системно, выдается 
сообщение о первоначально возникшей ошибке, а выполнение модуля прекращается

6.3.2 Когда перед вызовом оператора ВызватьИсключение; не следует делать запись в журнал регистрации

Если во внешней попытке делается запись в ЖР, во внутренней делать её повторно уже не нужно

Неправильно

Исключение
    ОтменитьТранзакцию();
    ИнформацияОбОшибке = ИнформацияОбОшибке();
    ЗаписьЖурналаРегистрации("Удаление файла", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); // дублирует запись!
    ВызватьИсключение;
КонецПопытки;

так как это вызовет дублирование записей и в журнале появится 2 одинаковых записи. На неперехваченное исключение платформа делает запись в ЖР сама.

Правильно

Процедура ЗагрузитьДанные() Экспорт
    Попытка
        ВыполнитьЗаписьДанных();
    Исключение
        ЗаписьЖурналаРегистрации(); // <- исключение подавляется с записью в ЖР
    КонецПопытки;
КонецПроцедуры
 
Процедура ВыполнитьЗаписьДанных()
    НачатьТранзакцию();
    Попытка
        // ...
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение; // <- вложенная попытка, запись в ЖР не требуется
    КонецПопытки;
КонецПроцедуры

Исключение составляют случаи:

  • когда исключение перехватывается на клиенте, где метод ЗаписьЖурналаРегистрации недоступен,
  • или когда необходимо уточнить текст ошибки, дополнив его прикладной информацией
  • или когда логируется составное имя события для подсистемы, типа "РаботаСФайлами.Удаление файла в MinIO".

Подробнее смотри: IS: Шаблоны для применения cтандартов и методик разработки конфигураций 1С и ИТС: Перехват исключений в коде

Обсуждене в группе https://t.me/bsl_language_server

Мнение эксперта:
alt text

6.3.3 Почему не рекомендуется использовать ОписаниеОшибки()

Не следует использовать функцию ОписаниеОшибки, т.к. она неинформативна для разработчика, потому что не возвращает стек в тексте ошибки.

Неправильно

Исключение
    ЗаписьЖурналаРегистрации("Ошибка <ИмяАлгоритма>", УровеньЖурналаРегистрации.Ошибка, , ,
        "Не удалось выполнить <ОписаниеОсобоВажнойПроцедуры> " + ОписаниеОшибки());
КонецПопытки;

Нельзя передавать ОписаниеОшибки() или КраткоеПредставлениеОшибки, теряется стек вызовов, нужно обязательно передавать ПодробноеПредставлениеОшибки. В стандарте 1С https://its.1c.ru/db/v8std/content/499/hdoc об этом написано.

Правильно записывать в журнал регистрации подробное представление исключения:

Правильно

Исключение
    // Запись события в журнал регистрации для системного администратора.
    ЗаписьЖурналаРегистрации(НСтр("ru = 'Выполнение операции'"),
       УровеньЖурналаРегистрации.Ошибка,,,
       ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
  КонецПопытки;

6.4 Почему нужно использовать блокировки при многопоточной загрузке данных

При наличии нескольких обработчиков(потоков) на загрузку объектов из очереди следует учитывать ситуацию когда два сообщения с одним и тем же объектом но с разной версией данных может начать обрабатываться двумя потоками одновременно. Это вызовет состояние гонки и второй, более медленный поток не сможет обработать сообщение из-за изменившихся данных объекта (см. Оптимистическая блокировка).

Потоконебезопасный код

Процедура Записать(Документ, Значение) Экспорт
     
    ДокументОбъект = Документ.ПолучитьОбъект(); // <- ошибка "нарушение целостности чтения объекта базы данных из-за параллельного изменения объекта другим сеансом"
    ДокументОбъект.Реквизит = Значение;
    ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
     
КонецПроцедуры

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

Потокобезопасный код

Процедура ЗаписатьСБлокировкой(Документ, Значение) Экспорт
     
    НачатьТранзакцию();
     
    Попытка
         
        Блокировка = Новый БлокировкаДанных;
        ЭлементБлокировки = Блокировка.Добавить();
        ЭлементБлокировки.Область = "Документ.РеализацияТоваровУслуг";
        ЭлементБлокировки.УстановитьЗначение("Ссылка", Документ);
        Блокировка.Заблокировать();
         
        ДокументОбъект = Документ.ПолучитьОбъект();
        ДокументОбъект.Реквизит = Значение;
        ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
         
        ЗафиксироватьТранзакцию();
         
    Исключение
         
        ОтменитьТранзакцию();
        ВызватьИсключение;
         
    КонецПопытки;
     
КонецПроцедуры

При успешном наложении блокировки и 2 поток будет ожидать, пока не отработает 1

Подробнее в статье: Разбор причины ошибки "Нарушение целостности чтения объекта базы данных из-за параллельного изменения объекта другим сеансом"

7. [только для ADD] Необоснованное использование таймаута в тесте

Для покрытия фонового задания тестом и ожидания его запуска многие используют шаг Пауза

Неправильно

И ВызываюМетодРегламентногоЗадания "МодульАТН.АТН_Загрузка"
И Пауза 5

или самостоятельно реализуют паузу в своем шаге

Неправильно

И Вызываю метод регламентного задания "РегламентныеЗадания.ОбработкаОчередиПоПотокам"
И я ожидаю появления записей в таблице "Справочник.ИсходящиеСообщения" "10" секунд

В случае если сервер bdd перегружен, таймаут ожидания запуска фоновых может быть превышен и это превращается в плавающий баг, фича становится мигающей.

Более того, эти паузы искусственно увеличивают время сборки.

Правильно Для избегания таймаутов рекомендуется использовать режим тестирования:
  • вызывать методы фоновых напрямую

    Для Каждого Набор Из НаборыДанных Цикл
        Если Тестирование.ЭтоРежимАвтоматическогоТестирования() Тогда // под пользователем Jenkins выполняем методы интерактивно
            Выполнить(СтрШаблон("РегламентныеЗаданияВызовСервера.%1(Набор.Значение)", ИмяПроцедурыПотока));
            Продолжить;
        КонецЕсли;
    КонецЦикла;
  • прокидывать дополнительный параметр АвтоТест

    Функция ПроверитьНаСуществованиеВФНС(ДанныеКонтрагента, ЭтоТестБДД = Ложь) Экспорт
    Функция ВыполнитьБыструюПродажуПоСценарию(Сценарий=Неопределено,Автотест=Ложь) Экспорт
  • использовать ДополнительныеПараметры объекта

    Если ДополнительныеСвойства.Свойство("ЭтоАвтотест") Тогда
        Отказ = Истина;
        Возврат;
    КонецЕсли;
  • использовать переключатель функциональности

    Для Каждого Набор Из НаборыДанных Цикл
        
        // для атвотеста - не используем фоновое выполнение, а последовательный вызов процедуры
        Если ПереключателиФункциональности.Автотест() Тогда
            Выполнить("РегламентныеЗаданияВызовСервера." + ИмяПроцедурыПотока + "(Набор,Значение)");
        Иначе
            
            // запуск фонового
            // ...

8. Приведение к числу в попытке

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

Неправильно

Попытка
    ТекЧисло = Число(ТекстовоеПоле);
Исключение
    ТекЧисло = 0;
КонецПопытки;

Это является нарушением стандарта разработки ИТС: https://its.1c.ru/db/v8std/content/499/hdoc п.3.8

Правильно

ОписаниеТипа = Новый ОписаниеТипов("Число");
КоличествоДнейРазрешения = ОписаниеТипа.ПривестиЗначение(Значение);

8.2 Приведение числа к строке.

Правильно: Формат(Число, "ЧГ=0"). Неправильно: СтрЗаменить(Строка(Число), Символы.НПП, "")

9. Замена вложенных условных операторов граничным оператором

Выделите все проверки специальных или граничных случаев выполнения в отдельные условия и поместите их перед основными проверками. В идеале, вы должны получить «плоский» список операторов, идущих один за другим. Видео: https://www.youtube.com/shorts/xtmdJoYhGhI

Общий принцип: чем меньше кода с двойным и более отступом - тем проще его читать.

Неправильно

Процедура ДополнитьСтруктуруДаннымиГруппыСклада(Структура, Склад)
     
    Если ЗначениеЗаполнено(Склад) Тогда
         
        Запрос = Новый Запрос;
        Запрос.Текст =
        "ВЫБРАТЬ
        |   Склады.Родитель.Код КАК Код
        |ИЗ
        |   Справочник.Склады КАК Склады
        |ГДЕ
        |   Склады.Ссылка = &Склад";
         
        Запрос.УстановитьПараметр("Склад", Склад);
         
        РезультатЗапроса = Запрос.Выполнить();
         
        ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
        ВыборкаДетальныеЗаписи.Следующий();
        Структура.Вставить("WarehouseGroupCode", СокрЛП(ВыборкаДетальныеЗаписи.Код));
    Иначе
        Структура.Вставить("WarehouseGroupCode", "");
    КонецЕсли;
     
КонецПроцедуры

Правильно - использовать приём "Ранний возврат"

Процедура ДополнитьСтруктуруДаннымиГруппыСклада(Структура, Склад)
     
    Если НЕ ЗначениеЗаполнено(Склад) Тогда
        Структура.Вставить("WarehouseGroupCode", "");
        Возврат;
    КонецЕсли;
     
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |   Склады.Родитель.Код КАК Код
    |ИЗ
    |   Справочник.Склады КАК Склады
    |ГДЕ
    |   Склады.Ссылка = &Склад";
     
    Запрос.УстановитьПараметр("Склад", Склад);
     
    РезультатЗапроса = Запрос.Выполнить();
     
    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
    ВыборкаДетальныеЗаписи.Следующий();
    Структура.Вставить("WarehouseGroupCode", СокрЛП(ВыборкаДетальныеЗаписи.Код));
     
КонецПроцедуры

Неправильно

&НаКлиенте
Процедура ОбработкаВыбораТипаДанных(ВыбранныйЭлемент, СписокПараметров) Экспорт
    
    Если ВыбранныйЭлемент <> Неопределено Тогда
        
        ИмяОткрываемойФормы = "";
        
        Если ВыбранныйЭлемент.Значение = Тип("СправочникСсылка.Контрагенты") Тогда
            Если НЕ ЗначениеЗаполнено(Объект.VID_Контрагент) Тогда
                Сообщить("Заполните контрагента!");
            Иначе
                                
                ЗначениеОтбора = Новый Структура;
                ЗначениеОтбора.Вставить("Ссылка", Объект.VID_Контрагент);
                ПараметрыФормы = Новый Структура;
                ПараметрыФормы.Вставить("Отбор",ЗначениеОтбора);
                ПараметрыФормы.Вставить("РежимВыбора", Истина);
                ПараметрыФормы.Вставить("МножественныйВыбор",Ложь);
                ПараметрыФормы.Вставить("ЗакрыватьПриВыборе",Истина);
                
                ОбработкаВыбора = Новый ОписаниеОповещения("ПриЗакрытииФормыВыбораКонтрагентРемонты", ЭтаФорма, СписокПараметров);
                
                ОткрытьФорму("Справочник.Контрагенты.Форма.ФормаВыбораИспользуютсяТолькоПартнерыБезПолнотекстовогоПоиска",ПараметрыФормы,ЭтаФОрма,,,,ОбработкаВыбора);
            КонецЕсли;
        ИначеЕсли ВыбранныйЭлемент.Значение = Тип("СправочникСсылка.ФизическиеЛица") Тогда
            ПараметрыФормы = Новый Структура("РежимВыбора, НачальноеЗначениеВыбора", Истина, СписокПараметров.Исполнитель);
            ПараметрыФормы.Вставить("ПараметрыОтбора", Новый Структура("Организация", Объект.Организация));
            ОбработкаВыбора = Новый ОписаниеОповещения("ПриЗакрытииФормыВыбораФЛРемонты", ЭтаФорма, СписокПараметров);

            ОткрытьФорму("РегистрСведений.уатВодителиОрганизаций.ФормаСписка", ПараметрыФормы, ЭтаФорма,,,,ОбработкаВыбора);
            
        КонецЕсли;        

    КонецЕсли;

Правильно

&НаКлиенте
Процедура ОбработкаВыбораТипаДанных(ВыбранныйЭлемент, СписокПараметров) Экспорт
    
    Если ВыбранныйЭлемент = Неопределено Тогда
        Возврат;
    КонецЕсли;
    ИмяОткрываемойФормы = "";
        
    Если ВыбранныйЭлемент.Значение = Тип("СправочникСсылка.Контрагенты") Тогда
        Если НЕ ЗначениеЗаполнено(Объект.VID_Контрагент) Тогда
            Сообщить("Заполните контрагента!");
            Возврат;
        КонецЕсли;
                                
        ЗначениеОтбора = Новый Структура;
        ЗначениеОтбора.Вставить("Ссылка", Объект.VID_Контрагент);
        ПараметрыФормы = Новый Структура;
        ПараметрыФормы.Вставить("Отбор",ЗначениеОтбора);
        ПараметрыФормы.Вставить("РежимВыбора", Истина);
        ПараметрыФормы.Вставить("МножественныйВыбор",Ложь);
        ПараметрыФормы.Вставить("ЗакрыватьПриВыборе",Истина);
                
        ОбработкаВыбора = Новый ОписаниеОповещения("ПриЗакрытииФормыВыбораКонтрагентРемонты", ЭтаФорма, СписокПараметров);
        ОткрытьФорму("Справочник.Контрагенты.Форма.ФормаВыбораИспользуютсяТолькоПартнерыБезПолнотекстовогоПоиска",ПараметрыФормы,ЭтаФОрма,,,,ОбработкаВыбора);

    ИначеЕсли ВыбранныйЭлемент.Значение = Тип("СправочникСсылка.ФизическиеЛица") Тогда
        ПараметрыФормы = Новый Структура("РежимВыбора, НачальноеЗначениеВыбора", Истина, СписокПараметров.Исполнитель);
        ПараметрыФормы.Вставить("ПараметрыОтбора", Новый Структура("Организация", Объект.Организация));
        ОбработкаВыбора = Новый ОписаниеОповещения("ПриЗакрытииФормыВыбораФЛРемонты", ЭтаФорма, СписокПараметров);

        ОткрытьФорму("РегистрСведений.уатВодителиОрганизаций.ФормаСписка", ПараметрыФормы, ЭтаФорма,,,,ОбработкаВыбора);       

    КонецЕсли;

Этот прием позволит избежать flattering arrow

if
  if
    if
      if
        do something // <- code smell
      endif
    endif
  endif
endif

10. Чрезмерный уровень вложенности конструкций

Количество вложенных конструкций "Если", "Для", "Пока" и "Попытка" не должно превышать 4

Диагностика: http://sonar-server.rossko.local/coding_rules?open=bsl%3ANestedControlFlowDepth&rule_key=bsl%3ANestedControlFlowDepth

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

Также известен как: Flattening Arrow Code

Неправильно

Если Чтото Тогда                     // Допустимо - уровень = 1
    /* ... */
    Если ЧтоТоЕще Тогда              // Допустимо - уровень = 2
        /* ... */
        Для Ном = 0 По 10 Цикл       // Допустимо - уровень = 3
            /* ... */
            Если ОпятьУсловие Тогда  // Уровень = 4, лимит достигнут, но не превышен
                Попытка              // Уровень = 5, Превышен лимит
                    /* ... */
                Исключение
                КонецПопытки;
                Возврат;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
КонецЕсли;

Источник: https://docs.checkbsl.org/checks/overall/NestedControlFlowDepth/

Чтобы избавиться от "гребенки" советуют следующие приёмы рефакторинга:

  1. Place Guard Conditions at the Start, так же этот приём известер под названием "Ранний возврат" Все возможные проверки следует выносить в начало метода:

    Если (!Check#1)
        Возврат;
    CodeBlock#1
    
    Если (!Check#2)
        Возврат;
    CodeBlock#2

    todo: добавить пример на языке 1С

    Подробнее смотри: https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html

  2. Decompose Conditional blocks into seperate functions или Extract into a function, так же известный как "Извлечение метода"

    Когда код состоит из сложного условного оператора (если-то-иначе), обычно следует применить рефакторинг. Одним из решений является разложение сложных частей условия на отдельные методы: if, then и else. Извлеките условный код в методы с говорящими именами, чтобы облегчаете жизнь человеку который будет поддерживать код позже.

    todo: перевести и вставить статью https://qafoo.com/blog/098_extract_method.html

  3. Remove Logic for Controlling the Iteration, или "Вынос контролирующей логики из цикла" Отфильтруйте ненужные элементы коллекции (в запросе или методе) заранее, используюя функциональный подход вместо императивного ("map/filter/reduce", LINQ)

    ФильтрованнаяКоллекция = ФильтроватьЭлементыПоУсловиюБизнесЛогики(Коллекция);
    МодифицированнаяКоллекция = ВыполнитьПредварительныеПреобразования(ФильтрованнаяКоллекция);
    // далее наш цикл без вложенных условий
    // ...

    todo: добавить более наглядный пример до и после

  4. Evaluate complex conditions into a single boolean, или "Объединение условий в логические переменные"

    См. пункт "11. Сложные условия"

    alt text

  5. Избегать обратных условий

    Считаю, всеми силами надо избегать обратных условий. При невозможности обойтись - искать синонимы.

    Неправильно, имя переменной не должно начинаться с отрицания

    Если НетОшибок Тогда
    

    Например надо добавить параметр учетной политики "НеИспользоватьФункционал". Кажется, что его удобнее задать именно так, т.к. по умолчанию будет Ложь, и не надо перезаполнять документы. Но потом начинается кошмар в коде... Если НЕ НеИспользоватьФукнционал() Тогда

    Лучше либо описать прямо: ИспользоватьФункционал, и добавить обработчик обновления, который взведет флаг в Истина в существующих документах (учетных политик не так много, на самом деле). Либо найти синоним, например "ИсключитьИспользованиеФункционала".

    Иначе при дальнейшем развитии и усложнении кода начинаются игры разума с "НЕ НетОшибок". И вместо того чтобы работать приходится маяться такой фигней, соображать что будет при расчете.

    Источник: https://infostart.ru/1c/articles/1670466/

11. Сложные условия

"Тяжелые" многострочные условия затрудняют понимание кода.

Подозрительный код (todo: пример найти получше, условие отлично читается)

// Плохо:
Если ИдентификаторОбъекта = "АнализСубконто"
    ИЛИ ИдентификаторОбъекта = "АнализСчета"
    ИЛИ ИдентификаторОбъекта = "ОборотноСальдоваяВедомость"
    ИЛИ ИдентификаторОбъекта = "ОборотноСальдоваяВедомостьПоСчету"
    ИЛИ ИдентификаторОбъекта = "ОборотыМеждуСубконто"
    ИЛИ ИдентификаторОбъекта = "ОборотыСчета"
    ИЛИ ИдентификаторОбъекта = "СводныеПроводки" 
    ИЛИ ИдентификаторОбъекта = "ГлавнаяКнига"
    ИЛИ ИдентификаторОбъекта = "ШахматнаяВедомость" Тогда
    ПараметрыРасшифровки.Вставить("ОткрытьОбъект", Ложь);
		
    ЕстьПоказатель  = Ложь;
    ЕстьСчет        = Истина;
    ПервыйЭлемент   = Неопределено;
КонецЕсли;

Плохо читаемые или сложные условия нужно выделять в переменные или методы.

Исправленный код

// Хорошо:
Если ОткрыватьОбъектПриИдентификаторе(ИдентификаторОбъекта) Тогда // <- спрятали все проверки в функцию

    ПараметрыРасшифровки.Вставить("ОткрытьОбъект", Ложь);
		
    ЕстьПоказатель  = Ложь;
    ЕстьСчет        = Истина;
    ПервыйЭлемент   = Неопределено;
КонецЕсли;

Источник: https://docs.checkbsl.org/checks/overall/BulkyConditions/

alt text

Подробнее смотри: https://refactoring.com/catalog/decomposeConditional.html

12. Смешивание контекстов условий фильтрации и объединения в запросах

Не нужно писать условия фильтрации в секции ON и не нужно писать условия соединения в секции WHERE

Сравните читаемость двух вариантов

Правильно

-- вариант 1
SELECT *
FROM facebook
LEFT JOIN linkedin
ON facebook.name = linkedin.name
WHERE facebook.city = 'SF'

Неправильно

-- вариант 2
SELECT *
FROM facebook
LEFT JOIN linkedin
ON facebook.name = linkedin.name AND facebook.city = 'SF'

Keep the context separate between joining the tables and filtering the joined table. It is the most readable, least likely to be inaccurate, and should not be less performant.

  • JOIN data in ON
  • Filter data in WHERE
  • Write explicit JOINs to make your Query more readable
  • Filter data in the WHERE clause instead of the JOIN to ensure it is correct and readable
  • Different SQL languages may have different query plans based on filtering in the ON clause vs the WHERE clause, so test the performance on your database

Разделяйте контекст между объединением и фильтрацией. Это более читабельно, уменьшаеат вероятность внести ошибку и одинаково по производительности.

  • Объединяйте данные в секции ПО
  • Фильтруйте данные в секции ГДЕ
  • Пишите явные соединения, чтобы сделать запрос более читабельным
  • Фильтруйте данные в предложении WHERE вместо JOIN, чтобы гарантировать их корректность и читаемость.
  • Различные языки SQL могут иметь разные планы запросов, основанные на фильтрации в предложении ON и предложении WHERE, поэтому проверьте производительность в своей базе данных.

Подробнее смотри:

13. Экспортные процедуры/функции должны содержать описание

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

ИТС: Описание процедур и функций

Для вставки шаблона комментария в конфигураторе используем контекстное меню Текст → Рефакторинг → Создать описание процедуры

Внимание! Если это обработчик регламентного задания - то первой строчкой в комментарии должно идти описание к какому регзаданию процедура принадлежит.

В описании спользуйте имя метаданного, а не синоним. Пример:

Правильный комментарий метода регзадания

// Обработчик регламентного задания ПривязкаПлатежейИРеализаций
//
// Формирует задания на привязку платежей и реализаций за последний месяц,
// разбивает контрагентов по порциям,
// сохраняет в регистр истории и отправляет в очередь RMQ для последующей обработки
//
Процедура ПривязкаПлатежейИРеализаций(НачПериода, КонПериода, Очистка = Ложь) Экспорт

Как правильно писать комментарии описано в диагностике: https://docs.checkbsl.org/checks/overall/UndocumentedPublicApi/

Документация по типизации таблиц значений, списков, массивов и структур в комментариях: v8-code-style: Типизация кода

Документирующие типизирующие комментарии, примеры для основных типов

Объект

// Описание метода
// 
// Параметры:
//  Объект - СправочникОбъект.Товары, ДокументОбъект.РеализацияТоваров - Здесь описание для списка типов
Процедура ОбработкаОбъекта(Объект)

Ссылка

// Возвращаемое значение:
// Структура:
// * Ссылка - СправочникСсылка.Номенклатура - ссылка на текущую номенклутру
// ...
Функция НовыйСложныйОбъектДанных()
    Данные = Новый Структура;
    Данные.Вставить("Ссылка", ...);
    ...
    Возврат Данные;
КонецФункции

Массив

// Параметры:
//  Объект - Массив из СправочникОбъект.Товары - Здесь единственный тип элементов
Процедура ОбработкаОбъекта(Объект)

Структура

// Возвращаемое значение:
// Структура:
// * Ссылка - СправочникСсылка.Номенклатура - ссылка на текущую номенклутру
// ...
Функция НовыйСложныйОбъектДанных()
    Данные = Новый Структура;
    Данные.Вставить("Ссылка", ...);
    ...
    Возврат Данные;
КонецФункции

Структура сложная

// Параметры:
//  Объект - Структура - Здесь нельзя многострочное описание:
//  * ПолеСтруктруы - ТаблицаЗначений - Описание поля структуры, после опять раширение уже для таблицы:
//    ** ИмяКолонки - Число - описание колонки таблицы
Процедура ОбработкаОбъекта(Объект)

Таблица значений

// Возвращаемое значение:
// ТаблицаЗначений:
//  * Номенклатура - СправочникСсылка.Номенклатура
//  * Характеристика - СправочникСсылка.ХарактеристикиНоменклатуры
//  * Склад - СправочникСсылка.Склады
//  * Количество - Число
// ...
Функция ТоварыДляСписания()
....
    Возврат Таблица;
КонецФункции

Строка таблицы или дерева значений

// Параметры:
// СтрокаТаблицы - СтрокаТаблицыЗначений:
//  * Номенклатура - СправочникСсылка.Номенклатура
Процедура ОбработкаТаблицы(СтрокаТаблицы)
    СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();

Соответствие

// Возвращаемое значение:
// Соответствие из КлючИЗначение:
// * Ключ - Строка
// * Значение - Массив из ДокументОбъект
Функция НовоеСоответствие()
   ...

Список значений

// Функция-конструктор нового пустого объекта даннных
//
// Возвращаемое значение:
// СписокЗначений из ДокументОбъект:
Функция НовыйСписокДокументов()
   Возврат Новый СписокЗначений;
КонецФункции

Дерево значений

// Параметры:
// ...
//
// Возвращаемое значение:
// ДеревоЗначений:
//  * Наименование - Строка
//  * ВидЭлемента - ...
//  * ...
Функция ДеревоНовыхЭлементов(Форма, ИмяЭлементаДерева)
    Возврат Форма.РеквизитФормыВЗначение(ИмяЭлементаДерева);
КонецФункции

Выборка из результат запроса

// Возвращаемое значение:
// ВыборкаИзРезультатаЗапроса:
//  * Номенклатура - СправочникСсылка.Номенклатура
Функция ОстаткиДляОбработки()
    Запрос = Новый Запрос;
    Запрос.Текст = 
        ТексЗапросаОстатков()
        + ТекстЗапросаРезервов();
    РезультаЗапроса = Запрос.Выполнить();
    Возврат РезультатЗапроса.Выбрать();
КонецФункции;

13.1. Скопированные из интернета куски кода должны содержать описание

В случае если из интернета копируется процедура/функция - копировать не только метод, но и так же описание метода и, желательно, ссылку на источник.

Если код универсальный - желательно помещать его в общий модуль для переиспользования.

14. Бестолковые сообщения об ошибках: внести ясность

Зачастую Разработчики оставляют в коде технические сообщения, а иногда и просто ленятся написать хорошее информативное описание ошибки.

Пример неинформативного сообщения об ошибке

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // ...
    АдресаИмИзТочкиДоставки = ПолучитьАдресаИмИзТочкиДоставки();
         
    Если АдресаИмИзТочкиДоставки.Количество() > 0 Тогда
             
        ТекстОшибки = "Запрещено создавать в ручную новую заявку на возврат с видом доставки отличного
            |от Самовывоз и не заполненным реквизитом <Адрес доставки ИМ>.";
        ДопМодуль.СообщитьПользователю(ТекстОшибки);
        Отказ = Истина;

При написании текста ошибки оцените его по шкале информативности, 5 основных правил:

alt text

Используйте активный, а не пассивный залог. Побуждайте к действию, а не запрещайте.

Идеальное сообщение об ошибке - это его отсутствие, поэтому если есть возможность - исправляйте ошибки автоматически.

В данном случае в ходе код-ревью была выявлена возможность подставлять <Адрес доставки ИМ> из документа-основания.

Ещё один пример:

В сообщение требуется внести какой сейчас статус, и какой нужен

Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    Если НЕ СхемаОптимизацииЛогистикиПартеррыСервер.ЗаказИмеетКорректныйСтатусПоСхемеОЛП(Ссылка) Тогда
        ВызватьИсключение("Заказ клиента имеет некорректный статус для движения по схеме ОЛП");
    КонецЕсли;

В данном случае используется вызов исключения вместо установки параметра Отказ. При коридорном тестировании неосведомленный человек будет поставлен в тупик "некорректным" статусом.

todo: Отказ или ВызватьИсключение? - расписать

Источник: https://habr.com/ru/post/645551/

15. Программное изменение текста запроса

15.1 Не следует разбивать текст запроса на отдельные строки, ставящие крест на использовании конструктора.

Неправильно

ТекстЗапроса =
"ВЫБРАТЬ
| Номенклатура.Наименование  КАК Наименование ,
| Номенклатура. " + ИмяПоляКод + " КАК КодАртикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";

Лучше использовать для этого параметр:

Правильно

ТекстЗапроса =
"ВЫБРАТЬ
| Номенклатура.Наименование  КАК Наименование,
| &ИмяПоляКод  КАК КодАртикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИмяПоляКод", "Номенклатура." + ИмяПоляКод);

15.2 аналогично для имени таблицы

Правильно

ТекстЗапроса =
"ВЫБРАТЬ
| ТаблицаСправочника.Наименование КАК Наименование,
| ТаблицаСправочника.Код КАК Код
|ИЗ
| #ТаблицаСправочника КАК ТаблицаСправочника";
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТаблицаСправочника", "Справочник." + ИмяСправочника); // или "&ТаблицаСправочника"

15.3 В случае если необходимо программно изменить условия запроса, лучше использовать замену выражения вида (1 = 1).

правильно

|ИЗ
|   ВТ_ПредварительныеДанные КАК ВТ_ПредварительныеДанные
|       ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
|       ПО ВТ_ПредварительныеДанные.ТипЦен = ЦеныНоменклатуры.ТипЦен
|           И ВТ_ПредварительныеДанные.Номенклатура = ЦеныНоменклатуры.Номенклатура
|           И ВТ_ПредварительныеДанные.Период = ЦеныНоменклатуры.Период
|           И (1 = 1)
|ГДЕ
|   ЦеныНоменклатуры.Регистратор ЕСТЬ NULL";
Запрос.УстановитьПараметр("Таблица",Данные.Таблица);
Если ЗначениеЗаполнено(пДокСсылка) Тогда
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "1 = 1","ЦеныНоменклатуры.Регистратор <> &ТекущаяСсылка");
    Запрос.УстановитьПараметр("ТекущаяСсылка",пДокСсылка);
КонецЕсли;

или вариант 2

Запрос.Текст =
    "ВЫБРАТЬ
    |   ПотребностьВНоменклатуреОстатки.ПредЗаказПоставщику КАК ПредЗаказПоставщику,
    |   ПотребностьВНоменклатуреОстатки.ПредЗаказПоставщику.Получатель КАК Получатель,
    |   ПотребностьВНоменклатуреОстатки.ПредЗаказПоставщику.Ответственный КАК Менеджер,
    |   ПотребностьВНоменклатуреОстатки.ПредЗаказПоставщику.Получатель.ОсновнойЦФО КАК ПолучательЦФО
    |ПОМЕСТИТЬ ВТДокументы
    |ИЗ
    |   РегистрНакопления.утз_ПотребностьВНоменклатуре.Остатки(
    |           &ТекущаяДата,
    |           ИСТИНА
    |               И &УсловиеПолучатель) КАК ПотребностьВНоменклатуреОстатки";
     
Если ЗначениеЗаполнено(ПолучательОтбор) Тогда
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеПолучатель", "ПредЗаказПоставщику.Получатель = &Получатель");
Иначе
    Запрос.УстановитьПараметр("УсловиеПолучатель", Истина);
КонецЕсли;

15.4 В случае если необходимо собирать запрос по частям, допускается разделение на блоки, не вызывающие ошибки конструктора.

Пример

Разделитель =
    "
    |;
    |/////////////////////////////////////////////////////////////
    |";
 
ТекстыЗапросовПакета = Новый Массив;
 
ТекстЗапроса =
    "ВЫБРАТЬ
    | Упаковки.Ссылка КАК Ссылка
    |ИЗ
    | Справочник.Упаковки КАК Упаковки";
 
ТекстыЗапросовПакета.Добавить(ТекстЗапроса);
 
ТекстЗапроса =
    "ВЫБРАТЬ
    | Номенклатура.Ссылка КАК Ссылка
    |ИЗ
    | Справочник.Номенклатура КАК Номенклатура";
ТекстыЗапросовПакета.Добавить(ТекстЗапроса);
ТекстЗапроса = СтрСоединить(ТекстыЗапросовПакета, Разделитель);

или так:

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|   ТаблицаДанных.Ссылка КАК Ссылка
|ПОМЕСТИТЬ ВТДляОбработки
|ИЗ
|   &ОбновляемыеДанные КАК ТаблицаДанных
|
|ИНДЕКСИРОВАТЬ ПО
|   Ссылка
|
|"
+ ОбъектыРасчетовСервер.ТекстПроверкаИспользованияВРасчетныхРегистрах(Истина, Истина)
+ ОбъектыРасчетовСервер.ТекстПроверкаИспользованияВТабличныхЧастях(Истина, Истина)
+ ТекстЗапросаДоговораСДвижениямиПоДочернимПартнерам(Истина)
+ "
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|   ИспользованиеВРасчетныхРегистрах.ОбъектРасчетов КАК ОбъектРасчетов
|ПОМЕСТИТЬ ВТТребуетсяГеренация
|ИЗ ВТИспользованиеВРасчетныхРегистрах КАК ИспользованиеВРасчетныхРегистрах

Источник: https://its.1c.ru/db/v8std/content/437/hdoc п. 6.2

16. Как правильно размещать текст запроса в коде программы

Неправильно

Запрос.Текст  = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
                |  ЕСТЬNULL(Пункты.Маршрут.НомерМаршрута, """") КАК НомерМаршрутногоЛиста,
                |  ЕСТЬNULL(Пункты.Маршрут.Дата, """") КАК ДатаМаршрута,

Текст запроса нужно размещать на следующей строке после оператора "=", кавычка должна быть расположена на том же уровне, что и начало оператора на прошлой строке.

Только в таком случае не портится конструктором запроса размещение запроса в коде и форматирование кода Alt+Shift+F так же не изменяет положение запроса. Т.е. два этих редактора только в таком случае согласованы между собой.

Правильно

Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
|  ЕСТЬNULL(Пункты.Маршрут.НомерМаршрута, """") КАК НомерМаршрутногоЛиста,
|  ЕСТЬNULL(Пункты.Маршрут.Дата, """") КАК ДатаМаршрута,

Так же, для выразительности, короткий текст запроса можно выделить табуляцией, как это делает "Конструктор запроса с обработкой результата...":

Правильно

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Банки.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.Банки КАК Банки
    |ГДЕ
    |	Банки.Код = &Код";

РезультатЗапроса = Запрос.Выполнить();

Источник: https://programmist1c.ru/st_programming/st_kak_pravilno_razmest_text_v_zapros.html

17. Ограничение на количество параметров метода

Максимальное количество параметров - 7: https://1c-syntax.github.io/bsl-language-server/diagnostics/NumberOfParams/

Максимальное количество необязательных параметров - 3: https://1c-syntax.github.io/bsl-language-server/diagnostics/NumberOfOptionalParams/

Рекомендуется группировать однотипные параметры в один или несколько составных параметров типа Структура: ИТС: Параметры процедур и функций, п. 5

18. Кто делает Code Review

По-хорошему нужно делать так, что каждый из команды ревьюит разработчика который сделал Merge Request. Но иногда встречается, что над любым разработчиком есть ментор/наставник и именно он ревьюит разработчика. Первый подход считаю более правильным, так как чем больше людей ревьюит код, тем лучше для проекта и для команды. Здесь сразу затрагиваются такие аспекты, как командное взаимодействие, коллективная ответственность, увеличение bus-фактора.

19. Повторное использование переменных

Использование одной переменной для разных целей - это неправильно.

todo: привести пример

Во-первых, вы, я вас уверяю, скорее всего не столкнетесь с такой ситуацией, когда лишние 4 байта (или сколько ваш компилятор будет задействовать для хранения int'a) могут сыграть большую роль. (более того, я могу вам гарантировать, что будет миллион случаев, где вы успешно потратите впустую куда большие объемы памяти и даже не обратите на это внимание)

Во-вторых, (и в главных) такая экономия ни к чему хорошему не приведет - переменная должна использоваться только для одной цели, использовать ее в нескольких целях чревато серьезными проблемами. Скорее всего, если вы будете применять эту порочную практику в сколько-нибудь больших проектах, вы однажды просто используете "старое" значение переменной в "новых" целях просто из-за невнимательности, а это может привести к самым непредсказуемым последствиям.

Источники:

todo:

Для ознакомления ещё правила: https://nullarity.com/code/codestyle/

Смотри так же: https://github.com/skyksandr/1c-styleguide

"Чистый код в 1С" или как прокачать свой код? https://infostart.ru/1c/articles/2188438/#message3099561

Черновики

"не могу выйти из функции"

Не усложняйте отладку:

  • не возвращайте функцию Возврат ОписаниеПлагинаНаСервере(ядро, ВозможныеТипыПлагинов); - такая конструкция при построчной отладке вместо выхода наружу загоняет нас ещё глубже в стек, запутывая ещё больше.
  • Не делайте вызов фунции от функции ЭтотОбъектНаСервере().УдалитьДокументыПоОрганизации(Организация); - вместо перемещения к определению функции по f11 нас перекидывает в однострочник
&НаСервере
Функция ЭтотОбъектНаСервере()
Возврат РеквизитФормыВЗначение("Объект");
КонецФункции

Кто-нибудь мне подскажет откуда пошло оборачивать примитивы в однострочники??

  • Ещё пример двойной вложенности: Возврат ЭтотОбъектНаСервере().ОписаниеПлагина(ядро, ВозможныеТипыПлагинов); - чтобы выйти из такой функции без команды shift+f11 (Шагнуть из) и вовсе не обойтись.

About

Соглашения по стилю кода 1С

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published