Как программно установить новую цену для номенклатуры в 1С:Управление торговлей 11?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

В мире автоматизации бизнеса часто возникает потребность в программной установке цен на номенклатуру. Это может быть актуально при интеграции с внешними системами, массовом обновлении цен по определенному алгоритму, или при работе с большим объемом данных, когда ручное заполнение документов становится неэффективным. В 1С:Управление торговлей (УТ) 11, как и в других современных конфигурациях 1С, для этих целей предусмотрен специальный документ — «Установка цен номенклатуры». Мы с вами подробно разберем, как программно создать, заполнить и провести этот документ, используя встроенные средства платформы 1С:Предприятие 8.3.

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

Создание и заполнение документа "Установка цен номенклатуры"

Первым шагом в программной установке цен является создание экземпляра документа «Установка цен номенклатуры». Для этого мы будем использовать менеджер документов, который предоставляет платформу 1С.

  1. Получение менеджера документов и создание нового экземпляра.

    Для начала нам необходимо обратиться к коллекции документов через глобальный объект Документы и вызвать метод СоздатьДокумент() для конкретного типа документа.

    
    // Получаем менеджер документов "Установка цен номенклатуры"
    МенеджерДокумента = Документы.УстановкаЦенНоменклатуры;
    
    // Создаем новый экземпляр документа
    ДокУстановкаЦен = МенеджерДокумента.СоздатьДокумент();
    

    Здесь Документы — это предопределенная коллекция, содержащая ссылки на менеджеры всех документов в конфигурации. УстановкаЦенНоменклатуры — это имя метаданных нашего документа. Метод СоздатьДокумент() возвращает новый, еще не записанный в базу данных объект документа, который мы можем заполнять.

  2. Заполнение реквизитов шапки документа.

    Каждый документ имеет обязательные и опциональные реквизиты, которые необходимо заполнить. К таким реквизитам относятся Дата, Ответственный, Организация и Комментарий.

    
    // Заполняем основные реквизиты шапки документа
    ДокУстановкаЦен.Дата           = ТекущаяДата(); // Дата документа - текущая дата
    ДокУстановкаЦен.Ответственный  = Пользователи.ТекущийПользователь(); // Ответственный - текущий пользователь
    // Для поля "Организация" необходимо найти соответствующий элемент справочника "Организации"
    // Мы можем найти его по коду или по наименованию. Например, найдем основную организацию.
    ОрганизацияСсылка = Справочники.Организации.НайтиПоКоду("00001");
    Если ОрганизацияСсылка <> Неопределено Тогда
        ДокУстановкаЦен.Организация    = ОрганизацияСсылка;
    Иначе
        Сообщить("Ошибка: Организация с кодом '00001' не найдена!");
        Возврат; // Прерываем выполнение, если организация не найдена
    КонецЕсли;
    
    // Добавляем произвольный комментарий для удобства
    ДокУстановкаЦен.Комментарий    = "Цены установлены программно по расписанию";
    

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

  3. Заполнение табличной части "Цены номенклатуры".

    Самая важная часть документа – это его табличная часть ЦеныНоменклатуры, где мы указываем, для какой номенклатуры, какого типа цены и какую цену нужно установить. Каждая строка табличной части представляет собой одну ценовую позицию.

    Для каждой новой строки табличной части нам понадобятся следующие данные:

    • Номенклатура: Ссылка на элемент справочника «Номенклатура».
    • ТипЦены: Ссылка на элемент справочника «Типы цен номенклатуры».
    • Цена: Числовое значение новой цены.
    • Валюта: Ссылка на элемент справочника «Валюты».
    • ЕдиницаИзмерения: Ссылка на элемент справочника «Единицы измерения». Обычно это базовая единица измерения номенклатуры.
    • ХарактеристикаНоменклатуры: Ссылка на элемент справочника «Характеристики номенклатуры», если номенклатура имеет характеристики. Если нет, это поле остается незаполненным.

    Давайте посмотрим, как добавить одну такую строку:

    
    // Получим необходимые справочные данные для заполнения строки
    // 1. Номенклатура: Найдем по наименованию или коду
    НоменклатураСсылка1 = Справочники.Номенклатура.НайтиПоНаименованию("Монитор 27\"");
    // Или, например, по коду:
    // НоменклатураСсылка2 = Справочники.Номенклатура.НайтиПоКоду("ТВ0000001");
    
    // 2. Тип цены: Найдем по наименованию
    ТипЦеныРозничная = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Розничная");
    
    // 3. Валюта: Найдем по коду
    ВалютаРубли = Справочники.Валюты.НайтиПоКоду("RUB");
    
    // Обязательно проверяем, что все справочные объекты найдены
    Если НоменклатураСсылка1 = Неопределено Тогда
        Сообщить("Ошибка: Номенклатура 'Монитор 27\"' не найдена!");
        Возврат;
    КонецЕсли;
    Если ТипЦеныРозничная = Неопределено Тогда
        Сообщить("Ошибка: Тип цены 'Розничная' не найден!");
        Возврат;
    КонецЕсли;
    Если ВалютаРубли = Неопределено Тогда
        Сообщить("Ошибка: Валюта 'RUB' не найдена!");
        Возврат;
    КонецЕсли;
    
    // Добавляем новую строку в табличную часть "ЦеныНоменклатуры"
    НоваяСтрока = ДокУстановкаЦен.ЦеныНоменклатуры.Добавить();
    НоваяСтрока.Номенклатура           = НоменклатураСсылка1;
    НоваяСтрока.ТипЦены                = ТипЦеныРозничная;
    НоваяСтрока.Цена                   = 15000; // Устанавливаемая цена
    НоваяСтрока.Валюта                 = ВалютаРубли;
    // Единица измерения обычно берется из самой номенклатуры (единица хранения остатков)
    НоваяСтрока.ЕдиницаИзмерения       = НоменклатураСсылка1.ЕдиницаХраненияОстатков;
    
    // Если номенклатура использует характеристики, необходимо указать ссылку на характеристику.
    // Пример:
    // ХарактеристикаСсылка = Справочники.ХарактеристикиНоменклатуры.НайтиПоНаименованию("Цвет: Черный");
    // Если ХарактеристикаСсылка <> Неопределено Тогда
    //     НоваяСтрока.ХарактеристикаНоменклатуры = ХарактеристикаСсылка;
    // КонецЕсли;
    

    В этом примере мы используем методы НайтиПоНаименованию() и НайтиПоКоду() для получения ссылок на элементы справочников. Это удобный способ, но в реальных системах, особенно при большом объеме данных, может быть эффективнее использовать запросы к базе данных или заранее подготовленные таблицы значений со ссылками. Поле ЕдиницаИзмерения рекомендуется заполнять значением ЕдиницаХраненияОстатков из объекта номенклатуры, так как это ее базовая единица.

  4. Запись и проведение документа.

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

    
    Попытка
        // Записываем и проводим документ. РежимЗаписиДокумента.Проведение означает,
        // что документ будет не только записан, но и проведен, т.е. сделает движения по регистрам.
        ДокУстановкаЦен.Записать(РежимЗаписиДокумента.Проведение);
        Сообщить("Документ 'Установка цен номенклатуры' успешно проведен.");
    Исключение
        // В случае ошибки при записи или проведении, выводим сообщение об ошибке
        Сообщить("При проведении документа возникла ошибка: " + ОписаниеОшибки());
    КонецПопытки;
    

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

Установка цен для нескольких позиций в цикле

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

Предположим, у нас есть таблица значений или другой источник данных (например, результат запроса, данные из файла Excel), содержащий список номенклатуры, типов цен и новых значений цен. Мы можем пройтись по этому источнику и программно заполнить табличную часть ЦеныНоменклатуры.


// Пример: Создадим таблицу значений с данными для установки цен.
// В реальной задаче эти данные могут прийти откуда угодно: из файла, запроса, API.
СписокЦенДляУстановки = Новый ТаблицаЗначений();
СписокЦенДляУстановки.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
СписокЦенДляУстановки.Колонки.Добавить("ТипЦены", Новый ОписаниеТипов("СправочникСсылка.ТипыЦенНоменклатуры"));
СписокЦенДляУстановки.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
СписокЦенДляУстановки.Колонки.Добавить("Характеристика", Новый ОписаниеТипов("СправочникСсылка.ХарактеристикиНоменклатуры")); // Опционально

// Заполняем тестовыми данными
// Первая позиция
НоваяСтрокаТЗ = СписокЦенДляУстановки.Добавить();
НоваяСтрокаТЗ.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Монитор 27\"");
НоваяСтрокаТЗ.ТипЦены = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Розничная");
НоваяСтрокаТЗ.Цена = 15500;

// Вторая позиция
НоваяСтрокаТЗ = СписокЦенДляУстановки.Добавить();
НоваяСтрокаТЗ.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Клавиатура игровая");
НоваяСтрокаТЗ.ТипЦены = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Розничная");
НоваяСтрокаТЗ.Цена = 3500;

// Третья позиция с другой ценой и другим типом
НоваяСтрокаТЗ = СписокЦенДляУстановки.Добавить();
НоваяСтрокаТЗ.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Мышь беспроводная");
НоваяСтрокаТЗ.ТипЦены = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Оптовая"); // Другой тип цены
НоваяСтрокаТЗ.Цена = 1200;

// Создаем документ установки цен
ДокУстановкаЦен = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
ДокУстановкаЦен.Дата           = ТекущаяДата();
ДокУстановкаЦен.Ответственный  = Пользователи.ТекущийПользователь();
ОрганизацияСсылка = Справочники.Организации.НайтиПоКоду("00001");
Если ОрганизацияСсылка <> Неопределено Тогда
    ДокУстановкаЦен.Организация    = ОрганизацияСсылка;
Иначе
    Сообщить("Ошибка: Организация с кодом '00001' не найдена при пакетной установке!");
    Возврат;
КонецЕсли;
ДокУстановкаЦен.Комментарий    = "Пакетное обновление цен из внешнего источника";

// Получим валюту, так как предполагаем, что все цены в одной валюте для этого примера
ВалютаРубли = Справочники.Валюты.НайтиПоКоду("RUB");
Если ВалютаРубли = Неопределено Тогда
    Сообщить("Ошибка: Валюта 'RUB' не найдена!");
    Возврат;
КонецЕсли;

// Перебираем данные и добавляем строки в табличную часть документа
Для Каждого СтрокаЦены Из СписокЦенДляУстановки Цикл
    // Важно проверять, что ссылки на номенклатуру и тип цены существуют
    Если СтрокаЦены.Номенклатура = Неопределено Тогда
        Сообщить("Предупреждение: Номенклатура не найдена для строки, пропускаем. Цена: " + СтрокаЦены.Цена);
        Продолжить;
    КонецЕсли;
    Если СтрокаЦены.ТипЦены = Неопределено Тогда
        Сообщить("Предупреждение: Тип цены не найден для номенклатуры '" + СтрокаЦены.Номенклатура.Наименование + "', пропускаем.");
        Продолжить;
    КонецЕсли;

    НоваяСтрокаДокумента = ДокУстановкаЦен.ЦеныНоменклатуры.Добавить();
    НоваяСтрокаДокумента.Номенклатура           = СтрокаЦены.Номенклатура;
    НоваяСтрокаДокумента.ТипЦены                = СтрокаЦены.ТипЦены;
    НоваяСтрокаДокумента.Цена                   = СтрокаЦены.Цена;
    НоваяСтрокаДокумента.Валюта                 = ВалютаРубли; // Если валюта может быть разной, брать из СтрокаЦены
    НоваяСтрокаДокумента.ЕдиницаИзмерения       = СтрокаЦены.Номенклатура.ЕдиницаХраненияОстатков;

    // Если в исходных данных есть характеристики, заполняем их
    Если СписокЦенДляУстановки.Колонки.Найти("Характеристика") <> Неопределено
        И СтрокаЦены.Характеристика <> Неопределено Тогда
        НоваяСтрокаДокумента.ХарактеристикаНоменклатуры = СтрокаЦены.Характеристика;
    КонецЕсли;
КонецЦикла;

// Записываем и проводим документ с множеством строк
Попытка
    ДокУстановкаЦен.Записать(РежимЗаписиДокумента.Проведение);
    Сообщить("Документ 'Установка цен номенклатуры' с множеством цен успешно проведен.");
Исключение
    Сообщить("Ошибка при проведении пакетного документа 'Установка цен номенклатуры': " + ОписаниеОшибки());
КонецПопытки;

В этом примере мы создали фиктивную таблицу значений СписокЦенДляУстановки, которую можно представить как данные, полученные из какого-либо источника. Затем мы в цикле Для Каждого перебираем эти данные, создаем строки в табличной части документа ДокУстановкаЦен.ЦеныНоменклатуры и заполняем их. Критически важно проверять наличие ссылок на справочники (Номенклатура, ТипЦены) перед их использованием, чтобы избежать ошибок "Поле объекта не обнаружено".

Дополнительные рекомендации и нюансы

  1. Повышение производительности для больших объемов данных (ОбменДанными.Загрузка = Истина).

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

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

    Важно: Использование ОбменДанными.Загрузка = Истина должно быть осознанным. Оно может отключить важные проверки целостности данных, подписки на события и некоторые расчеты, что в случае некорректных входных данных может привести к порче информационной базы. Применяйте этот механизм только тогда, когда вы полностью уверены в качестве загружаемых данных и понимаете его последствия. И всегда сбрасывайте флаг в Ложь после завершения операций.

  2. Получение текущих цен номенклатуры.

    Иногда перед установкой новой цены требуется получить текущую действующую цену для сравнения или для проведения расчетов (например, установить цену на 10% выше текущей). В УТ 11 для этого существуют специальные функции.

    Для конфигураций УТ 11.4 и выше можно использовать функцию ПолучитьЦенуНоменклатуры из общего модуля УправлениеЗапасамиСервер (или аналогичного, в зависимости от точной версии УТ):

    
    // Предполагаем, что НоменклатураСсылка1 и ТипЦеныРозничная уже определены
    // (как в предыдущих примерах)
    
    // Получение текущей цены для номенклатуры и типа цены на определенную дату
    ТекущаяЦена = УправлениеЗапасамиСервер.ПолучитьЦенуНоменклатуры(
        НоменклатураСсылка1, // Номенклатура
        ТипЦеныРозничная,    // Тип цены
        ТекущаяДата()        // Дата, на которую запрашивается цена
    );
    
    Если ТекущаяЦена <> Неопределено Тогда
        Сообщить("Текущая розничная цена для '" + НоменклатураСсылка1.Наименование + "': " + ТекущаяЦена);
        // Например, увеличим цену на 10%
        НоваяЦена = ТекущаяЦена * 1.10;
        Сообщить("Новая розничная цена (плюс 10%): " + НоваяЦена);
    Иначе
        Сообщить("Текущая розничная цена для '" + НоменклатураСсылка1.Наименование + "' не найдена.");
    КонецЕсли;
    

    Если же вы работаете со старыми версиями УТ или предпочитаете использовать запросы, можно обратиться к регистру сведений ЦеныНоменклатуры, который хранит историю цен:

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

    Использование среза последних РегистрСведений.ЦеныНоменклатуры.СрезПоследних позволяет получить актуальную цену на заданную дату с учетом всех измерений регистра (номенклатура, тип цены, характеристика).

  3. Работа с единицами измерения.

    Очень важно правильно указывать ЕдиницаИзмерения для каждой строки цен. Если номенклатура продается в разных единицах (например, штуки и упаковки), и для каждой единицы установлены свои цены, то это нужно явно отразить. Однако в большинстве случаев цена устанавливается для базовой единицы хранения остатков, которую можно получить из объекта Номенклатура через свойство ЕдиницаХраненияОстатков.

  4. Учет характеристик номенклатуры.

    Если в вашей системе используется номенклатура с характеристиками (например, "Рубашка" с характеристиками "Размер: S", "Размер: M", "Цвет: Синий", "Цвет: Красный"), то поле ХарактеристикаНоменклатуры в табличной части документа "Установка цен номенклатуры" становится обязательным для заполнения. В этом случае цена будет устанавливаться не просто для "Рубашки", а для "Рубашки" определенного "Размера" и "Цвета". Необходимо будет найти соответствующий элемент справочника ХарактеристикиНоменклатуры и присвоить его в поле.

Мы рассмотрели основные аспекты программной установки цен на номенклатуру в 1С:Управление торговлей 11. Следуя этим шагам и рекомендациям, вы сможете автоматизировать процесс управления ценами, интегрировать его с другими системами и значительно упростить работу с большими объемами данных. Всегда тщательно тестируйте разработанный код на тестовой базе перед внедрением в рабочую систему.

← На главную