Почему Гений 1С берет дороже, чем рядовой?
Недавно столкнулся с ужасно написанным кодом, который в итоге привел у клиента к ошибкам. Сама потребность поменять код возникла в обработке загрузке таможенной декларации из ГТД, о которой я писал в прошлой статье.
Код просто ужасал своей «логикой». Поэтому я и хочу продемонстрировать, как я сделал нормальный человеческий рефакторинг этого кода. После чего он стал наиболее логичным, правильным и стал хорошо восприниматься.
Скажу пару слов о логике работы кода — там по номеру ГТД, прочитанному из файла создавался элемент справочника «Номера ТД» с кодом, соответствующему прочитанному ГТД, но с добавленным спереди пробел. Также создавалась группа с таким же кодом, но без пробела, и элемент справочника помещался в эту группу. Зачем создавать группы, не понятно. Логичнее было, наоборот, давать пробел в названии группы, потому что элемент используется в документах и логично именно ему давать соответствие ГТД.
Но сам по себе код ужасен, хотя и небольшой. Оцените:
// Создает элемент справочника "Номера ГТД" // Процедура СоздатьНомерГТД() Если ТипЗнч(НомерГТД) = Тип("Строка") Тогда // Создаем группу ссГруппаНомерГТД = Справочники.НомераГТД.НайтиПоКоду(НомерГТД); Если ЗначениеЗаполнено(ссГруппаНомерГТД) И Не ссГруппаНомерГТД.ЭтоГруппа Тогда // Нашли без пробела, но это не группа: // Добавляем пробел в код элемента обНомерГТД = ссГруппаНомерГТД.ПолучитьОбъект(); обНомерГТД.Код = " " + НомерГТД; обНомерГТД.Записать(); // И создаем группу без пробела в коде обГруппаНомерГТД = Справочники.НомераГТД.СоздатьГруппу(); обГруппаНомерГТД.Код = НомерГТД; обГруппаНомерГТД.Записать(); // Используем созданную группу в элементе обНомерГТД.Родитель = обГруппаНомерГТД.Ссылка; обНомерГТД.Записать(); Возврат; КонецЕсли; Если Не ЗначениеЗаполнено(ссГруппаНомерГТД) Тогда обГруппаНомерГТД = Справочники.НомераГТД.СоздатьГруппу(); обГруппаНомерГТД.Код = НомерГТД; обГруппаНомерГТД.Записать(); ссГруппаНомерГТД = обГруппаНомерГТД.Ссылка; КонецЕсли; обНомерГТД = Справочники.НомераГТД.СоздатьЭлемент(); обНомерГТД.Код = " " + НомерГТД; обНомерГТД.Родитель = ссГруппаНомерГТД; обНомерГТД.Записать(); НомерГТД = обНомерГТД.Ссылка; КонецЕсли; КонецПроцедуры
Группа создается два раза. Элемент не ищется, а сразу создается. Хотя справедливости ради, надо заметить, что при загрузке ГТД делается поиск по номеру ГТД в отдельной функции, так что если код есть, создаваться не будет:
// Добавим пробел в номер ГТД при поиске номера ГТД ссНомерГТД = Справочники.НомераГТД.НайтиПоКоду(" " + НомерГТД); Если ЗначениеЗаполнено(ссНомерГТД) Тогда НомерГТД = ссНомерГТД; КонецЕсли;
Но это всё в разных местах, ужасно не прозрачно.
Я внес изменения в логику, пробел использую в группах, а элементы пишу без пробела. Это позволяет обходить обрезание пробела при записи элемента ГТД. В итоге я переписал функцию следующим образом:
&НаСервере // Создает элемент справочника "Номера ГТД" // Процедура СоздатьНомерГТД() Если ТипЗнч(НомерГТД) = Тип("Строка") Тогда // Создаем группу ссГруппаНомерГТД = ДатьНомерГТД(НомерГТД, истина); //Группа ссНомерГТД = ДатьНомерГТД(НомерГТД, ложь); //Элемент НомерГТД = ссНомерГТД.Ссылка; КонецЕсли; КонецПроцедуры &НаСервере Функция ДатьНомерГТД(НомерГТД, ЭтоГруппа) Экспорт //Осипов 2021-07-13 доработал НомерГТДСПробелом = " " + СокрЛП(НомерГТД);; НомерГТДБезПробела = СокрЛП(НомерГТД);; Если ЭтоГруппа Тогда ИскНомерГТД = НомерГТДСПробелом; КонтрИскНомерГТД = НомерГТДБезПробела; Иначе ИскНомерГТД = НомерГТДБезПробела; КонтрИскНомерГТД = НомерГТДСПробелом; КонецЕсли; ИскСсылка = ДатьНомерГТДСлуж(ИскНомерГТД, ЭтоГруппа); Если ЗначениеЗаполнено(ИскСсылка) Тогда Возврат ИскСсылка; КонецЕсли; //Поищем без пробела или с пробелом - по другому ИскСсылка = ДатьНомерГТДСлуж(КонтрИскНомерГТД, ЭтоГруппа); Если ЗначениеЗаполнено(ИскСсылка) Тогда О = ИскСсылка.ПолучитьОбъект(); О.Код = ИскНомерГТД; //О.ОбменДанными.Загрузка = истина; О.Записать(); ИскСсылка = О.Ссылка; Возврат ИскСсылка; КонецЕсли; //Не нашли, надо создавать Если ЭтоГруппа Тогда О = Справочники.НомераГТД.СоздатьГруппу(); О.Код = НомерГТДСПробелом; Иначе О = Справочники.НомераГТД.СоздатьЭлемент(); О.Код = НомерГТДБезПробела; О.Родитель = ДатьНомерГТД(НомерГТД, истина); КонецЕсли; //О.ОбменДанными.Загрузка = истина; О.Записать(); ИскСсылка = О.Ссылка; Возврат ИскСсылка; КонецФункции &НаСервере Функция ДатьНомерГТДСлуж(НомерГТД, ЭтоГруппа) Экспорт З = Новый Запрос( "ВЫБРАТЬ | НомераГТД.Ссылка КАК Ссылка |ИЗ | Справочник.НомераГТД КАК НомераГТД |ГДЕ | НомераГТД.Код = &Код | И НомераГТД.ЭтоГруппа = &ЭтоГруппа"); З.УстановитьПараметр("Код", НомерГТД); З.УстановитьПараметр("ЭтоГруппа", ЭтоГруппа); Выборка = З.Выполнить().Выбрать(); Если Выборка.Следующий() Тогда Возврат Выборка.Ссылка; КонецЕсли; КонецФункции
А код по поиску ГТД переписал так:
//Осипов 2021-07-13 ищем по номеру ГТД элемент, не группу ссНомерГТД = ДатьНомерГТД(НомерГТД, ложь); Если ЗначениеЗаполнено(ссНомерГТД) Тогда НомерГТД = ссНомерГТД; КонецЕсли;
Рефакторинг, друзья мои! Поэтому Гении 1С стоят дороже, чем подмастерья!
Объем факт: 1 час. Среда: 8.3.17.2300, БП 3.0.96.35.
Вместо префикса » Дать…» в названии функций, я бы использовал слово » Сформировать». Но, это скорее всего «вкусовщина».
Я использую Дать, потому что дать в любом случае — есть или нет, Дать или Соз-Дать.