Загрузка...

обратиться в техподдержку

Техническая реализация расширения "Оптовые цены в Приходной накладной"

Служба заботы
27 августа 2025

Техническая реализация расширения “Оптовые цены в Приходной накладной”

Ключевая особенность: Неинвазивная доработка

Форма НЕ была захвачена в расширении!

Использована архитектура через общие модули с подписками:

  • Совместимость с обновлениями - форма остается неизменной
  • Отсутствие конфликтов с другими расширениями
  • Стабильная работа при изменении конфигурации

Структура расширения

РасширениеПриходная/CommonModules/
├── митОбщегоСервер/              # Серверная логика работы с ценами
├── ПодключаемыеКоманды/          # Серверная инициализация формы
├── ПодключаемыеКомандыКлиент/    # Клиентские обработчики команд
└── ТабличныеЧастиУНФКлиент/      # Автозаполнение цен при вводе

🚀 Реализация по шагам

Шаг 1: Инициализация через подписку

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

Шаг 2: Динамическое добавление колонки

Процедура ДобавитьКолонку(Форма) Экспорт
    
    Если Форма.Элементы.Найти("ЗапасымитЦенаОпт") <> Неопределено Тогда
        Возврат; // Защита от дублирования
    КонецЕсли;
    
    // Создаём реквизит формы
    ДобавляемыеРеквизиты = Новый Массив;
    НовыйРеквизит = Новый РеквизитФормы("митЦенаОпт",
        Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)),
        "Объект.Запасы", "Цена опт");
    ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
    
    Форма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
    
    // Создаём элемент интерфейса
    ТабличнаяЧасть = Форма.Элементы.Найти("Запасы");
    НоваяКолонка = Форма.Элементы.Добавить("ЗапасымитЦенаОпт", 
        Тип("ПолеФормы"), ТабличнаяЧасть);
    НоваяКолонка.ПутьКДанным = "Объект.Запасы.митЦенаОпт";
    НоваяКолонка.Заголовок = "Цена опт";
    
КонецПроцедуры

Шаг 3: Оптимизированное заполнение через SQL

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

Шаг 4: Автозаполнение при вводе

&После("РассчитатьСуммыВСтрокеТЧ")
Процедура мит_Приходная_РассчитатьСуммыВСтрокеТЧ(СтрокаТабличнойЧасти, ПараметрыРасчета)

	Если ЗначениеЗаполнено(СтрокаТабличнойЧасти.Номенклатура) Тогда
		Попытка
			СтрокаТабличнойЧасти.митЦенаОпт = 
				митОбщегоСервер.ПолучитьОптовуюЦену(СтрокаТабличнойЧасти.Номенклатура);
		Исключение
			СтрокаТабличнойЧасти.митЦенаОпт = 0;
		КонецПопытки;
	КонецЕсли;

КонецПроцедуры

Шаг 5: Серверная функция получения цены

Функция ПолучитьОптовуюЦену(Номенклатура) Экспорт

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

Шаг 6: Оптимизированная запись цен

Функция ЗаписатьОптовыеЦены(Масс) Экспорт
	
	// Создаем типизированную таблицу значений
	ТЗ_Цены = Новый ТаблицаЗначений;
	ТЗ_Цены.Колонки.Добавить("Номенклатура", 
		Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
	ТЗ_Цены.Колонки.Добавить("митЦенаОпт", 
		Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
	
	Для Каждого Стр Из Масс Цикл
		НовСтр = ТЗ_Цены.Добавить();
		НовСтр.Номенклатура = Стр.Номенклатура;
		НовСтр.митЦенаОпт = Стр.Цена;
	КонецЦикла;
	
	// Поиск только измененных цен
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ ТЗ_Цены.Номенклатура, ТЗ_Цены.митЦенаОпт КАК НоваяЦена
		|ПОМЕСТИТЬ втНовыеЦены ИЗ &ТЗ_Цены КАК ТЗ_Цены ГДЕ ТЗ_Цены.митЦенаОпт > 0;
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ втНовыеЦены.Номенклатура, втНовыеЦены.НоваяЦена
		|ИЗ втНовыеЦены ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(,
		|	ВидЦен = &ВидЦенОптовый) КАК Цены
		|ПО втНовыеЦены.Номенклатура = Цены.Номенклатура
		|ГДЕ втНовыеЦены.НоваяЦена <> ЕСТЬNULL(Цены.Цена, 0)";
	
	Запрос.УстановитьПараметр("ТЗ_Цены", ТЗ_Цены);
	Запрос.УстановитьПараметр("ВидЦенОптовый", Справочники.ВидыЦен.Оптовая);
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	// Пакетная запись всех изменений
	НаборЗаписей = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.ВидЦен.Установить(Справочники.ВидыЦен.Оптовая);
	
	КоличествоЗаписанных = 0;
	Пока Выборка.Следующий() Цикл
		НоваяЗапись = НаборЗаписей.Добавить();
		НоваяЗапись.Период = ТекущаяДатаСеанса();
		НоваяЗапись.ВидЦен = Справочники.ВидыЦен.Оптовая;
		НоваяЗапись.Номенклатура = Выборка.Номенклатура;
		НоваяЗапись.Цена = Выборка.НоваяЦена;
		НоваяЗапись.Актуальность = Истина;
		НоваяЗапись.ЕдиницаИзмерения = Выборка.Номенклатура.ЕдиницаИзмерения;
		КоличествоЗаписанных = КоличествоЗаписанных + 1;
	КонецЦикла;
	
	Если КоличествоЗаписанных > 0 Тогда
		НаборЗаписей.Записать(); // Одна транзакция
	КонецЕсли;
	
	Возврат КоличествоЗаписанных;
	
КонецФункции

Шаг 7: Клиентская команда записи

&ИзменениеИКонтроль("НачатьВыполнениеКоманды")
Процедура мит_Приходная_НачатьВыполнениеКоманды(Форма, Команда, Знач Источник)

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

		КоличествоЗаписанных = митОбщегоСервер.ЗаписатьОптовыеЦены(Масс);
		
		Если ТипЗнч(КоличествоЗаписанных) = Тип("Число") Тогда
			ПоказатьОповещениеПользователя("Запись цен",, 
				"Записано изменений цен: " + КоличествоЗаписанных);
		КонецЕсли;
		
		Возврат;
	КонецЕсли;
	
КонецПроцедуры

🔧 Ключевые технические решения

Неинвазивная интеграция:

  • Подписки &После() и &ИзменениеИКонтроль()
  • Динамическое изменение формы через API
  • Сохранение исходной структуры конфигурации

Производительность:

  • Пакетная запись в одной транзакции
  • Индексированные временные таблицы
  • Фильтрация только измененных цен

Типобезопасность:

  • Явное указание типов данных
  • Контроль типов для предотвращения SQL-ошибок

🏆 Преимущества подхода

  • Совместимость с обновлениями - форма остается неизменной
  • Производительность - оптимизированные SQL и пакетные операции
  • Надежность - всесторонняя обработка ошибок
  • Масштабируемость - модульная архитектура

Расширение готово к production и демонстрирует современные подходы разработки для 1С.


Нужна помощь?

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

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

заполните, пожалуйста
укажите Ваш e-mail
укажите Ваш номер телефона для связи