Здравствуйте! Часто начинающие разработчики 1С сталкиваются с проблемами при выполнении тестовых задач или реальных кейсов, связанных с программным созданием документов и настройкой пользовательского интерфейса. Рассмотрим подробнее основные ошибки, допущенные в представленном решении, и выясним, как их исправить, придерживаясь стандартов разработки 1С.
Исходная задача заключалась в следующем: на форме обработки со списком заказов клиентов нужно было добавить кнопку для создания документов «Реализация товаров и услуг» по выбранным заказам и настроить условное оформление, чтобы уже реализованные заказы подсвечивались. Мы проанализируем каждую часть решения и предложим оптимальные подходы.
Одной из наиболее распространенных ошибок новичков является попытка вручную заполнить множество реквизитов нового документа, создаваемого на основании существующего. Это не только увеличивает объем кода и делает его сложным для чтения, но и приводит к потере типового функционала и потенциальным ошибкам.
В представленном коде для создания документа «Реализация товаров и услуг» автор вручную присваивает множество свойств, таких как валюта, даты, банковские счета, статус и другие, а также вызывает несколько вспомогательных функций для заполнения дополнительных данных и табличных частей:
Для Каждого ДокументЗаказ Из МассивЗаказов Цикл
// ... проверка на существование реализации ...
ДокРеализация = Документы.РеализацияТоваровУслуг.СоздатьДокумент;
ЗаполнитьЗначенияСвойств(ДокРеализация, ДокументЗаказ); // Заполняет только общие реквизиты, не табличные части
ДокРеализация.ВалютаВзаиморасчетов = ДокументЗаказ.Валюта;
ДокРеализация.ДатаВозвратаМногооборотнойТары = МногооборотнаяТараСервер.РассчитатьДатуВозвратаМногооборотнойТары(...);
ПродажиСервер.ЗаполнитьБанковскиеСчетаПоДоговору(...);
// ... и множество других строк кода для заполнения ...
Документы.РеализацияТоваровУслуг.ЗаполнитьПоОстаткамЗаказов(
ДокРеализация, ДокРеализация.Товары, ДокРеализация.СкидкиНаценки,
?(СкладОтгрузки=Неопределено, Справочники.Склады.ПустаяСсылка, СкладОтгрузки),
МассивЗаказовПараметр, ПараметрыЗаполнения);
// ...
КонецЦикла;
Такой подход игнорирует встроенные механизмы платформы и конфигурации. Конфигурация «Управление торговлей» (УТ) и другие типовые решения 1С имеют мощный функционал для создания документов на основании.
Вместо того чтобы вручную переносить данные, мы должны использовать типовой механизм 1С. Для программного создания документа на основании другого достаточно создать новый объект документа и вызвать у него метод Заполнить(), передав в него ссылку на документ-основание.
Когда вы вызываете ДокРеализация.Заполнить(ДокументЗаказ), происходит следующее:
Процедура ОбработкаЗаполнения(Основание)
// Здесь разработчики конфигурации УТ11 уже реализовали всю логику
// по переносу данных из ЗаказаКлиента в РеализациюТоваровУслуг.
// Это включает заполнение шапки документа, табличных частей (товары, скидки и т.д.),
// расчеты по срокам возврата тары, заполнение банковских счетов и многое другое.
КонецПроцедуры
Пример исправленного кода для создания документа:
&НаСервере
Процедура РеализацияТоваровУслугНаСервере(МассивЗаказов)
Если МассивЗаказов.Количество > 0 Тогда
СписокРеализацийПоЗаказам = ПолучичтьРеализацииПоЗаказам(МассивЗаказов); // Функция ниже
Для Каждого ДокументЗаказ Из МассивЗаказов Цикл
// Проверяем, есть ли уже реализация по данному заказу
ЕстьРеализация = ?(СписокРеализацийПоЗаказам.Найти(ДокументЗаказ) = Неопределено, Ложь, Истина);
Если ЕстьРеализация Тогда
Продолжить; // Пропускаем заказ, если реализация уже создана
КонецЕсли;
ДокРеализация = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
ДокРеализация.Заполнить(ДокументЗаказ); // Используем стандартный метод заполнения
// Теперь можно добавить или изменить только те реквизиты,
// которые не заполняются типовым образом или требуют специфической логики.
// Например, установка статуса или других параметров, не зависящих от заполнения.
ДокРеализация.ЗаказКлиента = ДокументЗаказ; // Может уже заполниться методом Заполнить()
ДокРеализация.РеализацияПоЗаказам = Истина;
ДокРеализация.Статус = Перечисления.СтатусыРеализацийТоваровУслуг.Отгружено;
// ... любые дополнительные действия, если они действительно необходимы после типового заполнения ...
ДокРеализация.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;
КонецЕсли;
УстановитьУсловноеОформление(); // Вызов функции для обновления УО
КонецПроцедуры
Таким образом, практически весь объем кода по заполнению реквизитов, включая табличные части, заменяется одной строкой ДокРеализация.Заполнить(ДокументЗаказ).
Проблема с некорректной нумерацией документов, когда первый созданный документ получает номер "00-000000", а последующие вызывают ошибку уникальности, является частым следствием попытки ручного присвоения номера.
В исходном коде автор пытается присвоить номер документа вручную, вызывая функцию ПолучитьПосленийНомерРеализаций:
ДокРеализация.Номер = ПолучитьПосленийНомерРеализаций;
И сама функция ПолучитьПосленийНомерРеализаций является проблемной:
&НаСервере
Функция ПолучитьПосленийНомерРеализаций
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ПараметрГод", Год(ТекущаяДатаСеанса));
Запрос.Текст = "Выбрать Максимум(Номер) Из Документ.РеализацияТоваровУслуг Где ГОД(Дата) = &ПараметрГод И Номер Подобно(""00%"")";
ТаблицаЗапроса = Запрос.Выполнить().Выгрузить(); // Ошибка: функция ничего не возвращает, а ТаблицаЗапроса - это таблица, а не строка.
// При этом ищется максимальный номер, но не генерируется следующий.
КонецФункции
Во-первых, эта функция не возвращает никакого значения. Во-вторых, даже если бы она возвращала максимальный номер, простое присвоение уже существующего максимального номера приведет к ошибке уникальности при попытке записать второй документ. В-третьих, запрос находит номера, которые начинаются на "00%", что, вероятно, не соответствует стандартному формату нумерации.
В 1С номера документов генерируются автоматически при их записи, если в конфигурации для данного типа документа настроена автонумерация. Это делается исходя из установленной периодичности (например, в пределах года) и максимального существующего номера в соответствующем "пространстве имён".
Чтобы платформа сама корректно присваивала номера, вам нужно полностью удалить строку, где вы пытаетесь присвоить номер вручную:
// Удаляем эту строку: ДокРеализация.Номер = ПолучитьПосленийНомерРеализаций;
Проблема с номером "00-000000" и последующими ошибками уникальности возникает, когда вы пытаетесь управлять нумерацией программно, но делаете это некорректно. Как только вы уберете ручное присвоение, метод Записать(РежимЗаписиДокумента.Проведение) сам позаботится о присвоении следующего уникального номера.
Важный момент: использование метода ДокРеализация.Заполнить(ДокументЗаказ) также помогает, так как он подготавливает документ к правильной записи, которая включает автоматическую нумерацию.
Условное оформление в динамических списках — мощный инструмент для визуального выделения данных. Однако его некорректное использование может привести к снижению производительности, особенно когда логика оформления включает запросы к базе данных или сложную работу с компоновщиком данных при каждом обновлении.
В представленном коде функция УстановитьУсловноеОформление содержит запрос к базе данных, а также выполняет полную перекомпоновку и вывод схемы компоновки данных, чтобы получить список заказов:
&НаСервере
Процедура УстановитьУсловноеОформление
// ... получение схемы и настроек компоновки данных ...
// ... выполнение компоновки и выгрузка результата ...
МассивЗаказов = Результат.ВыгрузитьКолонку("Ссылка"); // Это список всех заказов в списке, а не только реализованных
// ...
Запрос.УстановитьПараметр("МассивЗаказов", МассивЗаказов); // И тут же этот список используется для отбора реализованных
ТабРезультат = Запрос.Выполнить().Выгрузить();
СписокДокументовУО = Новый СписокЗначений;
СписокДокументовУО.ЗагрузитьЗначения(ТабРезультат.ВыгрузитьКолонку("ЗаказКлиента"));
УсловноеОформление.Элементы.Очистить;
// ... добавление нового элемента УО с отбором по СписокДокументовУО ...
КонецПроцедуры
Основные недостатки этого подхода:
Неоптимальное получение списка заказов для УО: Запрос к базе данных для получения списка реализованных заказов должен выполняться с учетом только тех заказов, которые *могут быть* в текущем динамическом списке. Более того, первоначальная попытка получить все ссылки из динамического списка через компоновщик, а затем передать их в запрос для *поиска реализованных* заказов, является избыточной и ресурсоемкой. Список МассивЗаказов в запросе УО должен содержать ссылки на заказы из *текущего* списка, а не всех существующих.
Частые вызовы: Эта процедура вызывается после каждой успешной записи реализации. Если пользователь создаст несколько реализаций подряд, процедура будет вызываться многократно, каждый раз выполняя ресурсоемкие операции.
Полная очистка и пересоздание УО: УсловноеОформление.Элементы.Очистить удаляет все элементы условного оформления, включая те, что могли быть настроены в конфигураторе. Это может привести к непредсказуемому поведению и потере части настроек.
Для оптимизации условного оформления динамических списков мы рекомендуем следующий подход:
Добавим реквизит формы для хранения списка реализованных заказов. Создайте на форме новый реквизит, например, с типом СписокЗначений или Массив, который будет хранить ссылки на заказы клиентов, по которым уже есть реализации. Назовем его РеализованныеЗаказыДляУО.
Настроим условное оформление на основе этого реквизита формы. В конфигураторе или программно настройте элемент условного оформления так, чтобы его отбор сравнивал текущий заказ в динамическом списке с элементами вашего нового реквизита формы.
Пример настройки отбора для условного оформления:
ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ОтборЭлемента.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка"); // Поле 'Ссылка' из динамического списка
ОтборЭлемента.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
ОтборЭлемента.ПравоеЗначение = ЭтаФорма.РеализованныеЗаказыДляУО; // Сравниваем со списком на форме
ОтборЭлемента.Использование = Истина;
Обратите внимание: для условного оформления динамического списка поле, по которому ведется отбор (в данном случае Ссылка), должно быть доступно в запросе динамического списка.
Оптимизируем процедуру обновления списка реализованных заказов. Вместо полной перенастройки УО при каждом вызове, будем обновлять только наш реквизит формы.
Предположим, что в процедуре РеализацияТоваровУслугНаСервере у нас есть список успешно созданных реализаций (или измененных заказов). Мы можем передать его в процедуру обновления УО:
&НаСервере
Процедура ОбновитьСписокРеализованныхЗаказовДляУО(МассивТолькоЧтоРеализованныхЗаказов)
// Инициализация реквизита формы при первом открытии или обновлении
Если ЭтаФорма.РеализованныеЗаказыДляУО = Неопределено Тогда
ЭтаФорма.РеализованныеЗаказыДляУО = Новый СписокЗначений;
КонецЕсли;
// Добавляем новые реализованные заказы в список.
// Можно также запросить все реализованные заказы, если список УО нужно обновлять полностью.
Для Каждого Заказ Из МассивТолькоЧтоРеализованныхЗаказов Цикл
Если Не ЭтаФорма.РеализованныеЗаказыДляУО.Содержит(Заказ) Тогда
ЭтаФорма.РеализованныеЗаказыДляУО.Добавить(Заказ);
КонецЕсли;
КонецЦикла;
// Важно: после изменения реквизита формы, который используется в УО,
// необходимо обновить динамический список, чтобы изменения вступили в силу.
Элементы.СписокЗаказов.ОбновитьУсловноеОформление();
// Или если нужно обновить сами данные списка:
// Элементы.СписокЗаказов.Обновить();
КонецПроцедуры
Эту процедуру ОбновитьСписокРеализованныхЗаказовДляУО нужно будет вызывать после успешного создания реализаций, передавая ей только те заказы, по которым только что были созданы реализации.
Инициализация списка при открытии формы. При первом открытии формы (в обработчике ПриСозданииНаСервере) необходимо будет заполнить РеализованныеЗаказыДляУО всеми уже существующими реализованными заказами, чтобы корректно отобразить состояние при загрузке.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Инициализация списка реализованных заказов при открытии формы
ЭтаФорма.РеализованныеЗаказыДляУО = ПолучитьВсеРеализованныеЗаказы();
// Убедитесь, что эта функция возвращает СписокЗначений или Массив
КонецПроцедуры
Функция ПолучитьВсеРеализованныеЗаказы() будет выполнять запрос, аналогичный вашему текущему, но один раз при открытии формы.
Этот подход значительно снижает нагрузку на сервер, так как запросы к базе данных выполняются только при необходимости (например, при инициализации формы или если нужно полностью перечитать статусы) и обновляется только список значений, а не вся схема компоновки данных.
Работодатель упомянул, что ваше решение "не является решением по стандартам разработки 1с специалист". Это очень важный аспект, особенно при работе в команде и на крупных проектах. Фирма "1С" выпускает "Систему стандартов и методик разработки конфигураций для платформы 1С:Предприятие 8", которая является основополагающим документом для всех разработчиков.
Мы настоятельно рекомендуем изучить эти стандарты, они доступны на сайте ИТС (Информационно-Технологическое Сопровождение) 1С. В них вы найдете подробное описание лучших практик, но мы выделим ключевые моменты, которые были затронуты в вашем решении:
Сохранение типовой конфигурации. При доработке существующих типовых конфигураций (как УТ11) крайне важно использовать механизмы, позволяющие сохранять возможность легкого обновления. Это достигается использованием расширений конфигурации или минимальным внесением изменений в типовые объекты. Ваше решение, скорее всего, было реализовано в модифицированном модуле типового объекта, что усложняет обновление.
Использование типового функционала. Как мы рассмотрели выше, ручное заполнение документов вместо использования метода Заполнить() является ярким примером игнорирования типового функционала. Стандарты призывают максимально использовать встроенные возможности платформы и конфигурации, а не "изобретать велосипед".
Клиент-серверное взаимодействие. Важно правильно разделять логику на клиентскую и серверную части. Тяжелые операции (запросы к базе данных, объемные вычисления) должны выполняться на сервере. Общие модули с флагом "Сервер" выполняются на сервере, а для доступа к ним с клиента используется флажок "Вызов сервера". В вашем коде это учтено (используются директивы &НаСервере и &НаКлиенте), но общая структура и количество серверных вызовов должны быть оптимизированы.
Именование объектов и переменных. Стандарты регламентируют правила именования (например, использование префиксов для новых объектов, соответствие синтаксис-помощнику для ключевых слов). Это делает код более читаемым и понятным для других разработчиков.
Оформление кода. Использование табуляции для автоматического форматирования кода (сочетание клавиш Alt+Shift+F), избегание неиспользуемых методов и комментариев отладки — все это улучшает качество кода.
Комментарии в коде. Любые изменения в типовом коде должны сопровождаться комментариями специального формата (например, //++, //-- с указанием автора и задачи). Это необходимо для отслеживания изменений и упрощения сопровождения.
Понимание и применение этих стандартов значительно повысит качество вашего кода и вашу ценность как специалиста 1С.
Ваше тестовое задание было хорошей возможностью для обучения и приобретения опыта. Хотя в решении были допущены ошибки с точки зрения стандартов разработки и использования типового функционала, вы проделали большую работу, самостоятельно разобрались во многих аспектах и успешно реализовали поставленную задачу. Это свидетельствует о пытливом уме и способности к обучению.
Мы рассмотрели, как:
Заполнить(), значительно сократив код и повысив его надежность.Продолжайте изучать стандарты, экспериментировать с типовым функционалом и стремиться к созданию чистого и эффективного кода. Это поможет вам стать востребованным специалистом в области 1С.