Техническая реализация расширения “Оптовые цены в Приходной накладной”
Ключевая особенность: Неинвазивная доработка
Форма НЕ была захвачена в расширении!
Использована архитектура через общие модули с подписками:
- Совместимость с обновлениями - форма остается неизменной
- Отсутствие конфликтов с другими расширениями
- Стабильная работа при изменении конфигурации
Структура расширения
РасширениеПриходная/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С.