Появилась необходимость в УНФ в заказах покупателей предоставлять ручные скидки. Например, уменьшить все суммы на 5%, или уменьшить все суммы так, чтобы итоговая сумма документа уменьшилась на 1000р., или сказать, что всё вместе продаётся за 10000р., и чтобы при этом все суммы пропорционально уменьшились/увеличились.
Во "взрослых" конфигурациях (УПП, УТ), насколько помню, есть спец.обработка таб.частей, которая позволяет решить эту задачу (а кроме неё ещё и много других) за пару кликов мыши. В УНФ такого не нашёл. Поэтому взялся за написание своего велосипеда. Большого опыта разработки для упр.приложения пока нет, поэтому буду благодарен за дельные замечания и предложения в комментариях.
Обработка добавляется в "Дополнительные отчеты и обработки" (Вид обработки = "Заполнение объекта"). После этого в документе Заказ покупателя появляется кнопка "Заполнить" с тремя командами:
- "Скидка, %" - уменьшает все суммы в таб.части Запасы на указанный процент (если ввести отрицательное число, то наоборот увеличит на указанный процент).
- "Скидка, сумма" - уменьшает все суммы в таб.части Запасы так, чтобы итоговая сумма документа уменьшилась на указанную сумму (если ввести отрицательное число, то наоборот итоговая сумма документа будет увеличена на эту сумму).
- "Скидка, установить Итого" - уменьшает/увеличивает все суммы в таб.части Запасы так, чтобы итого по таб.части Запасы стало равно введённой сумме.
Код модуля обработки:
////////////////////////////////////////////////////////////////////////////////
// ПРОГРАММНЫЙ ИНТЕРФЕЙС
// Возвращает сведения о внешней обработке.
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиЗаполнениеОбъекта();
ПараметрыРегистрации.Версия = "0.99";
ПараметрыРегистрации.Назначение.Добавить("Документ.ЗаказПокупателя");
НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
НоваяКоманда.Представление = НСтр("ru = 'Скидка, %'");
НоваяКоманда.Идентификатор = "УстановитьСкидкуПроцент";
НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовКлиентскогоМетода();
НоваяКоманда.ПоказыватьОповещение = Истина;
НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
НоваяКоманда.Представление = НСтр("ru = 'Скидка, сумма'");
НоваяКоманда.Идентификатор = "УстановитьСкидкуСумма";
НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовКлиентскогоМетода();
НоваяКоманда.ПоказыватьОповещение = Истина;
НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
НоваяКоманда.Представление = НСтр("ru = 'Скидка, установить Итого'");
НоваяКоманда.Идентификатор = "УстановитьСуммуИтого";
НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовКлиентскогоМетода();
НоваяКоманда.ПоказыватьОповещение = Истина;
Возврат ПараметрыРегистрации;
КонецФункции
Код модуля формы (сама форма не содержит никаких реквизитов и элементов):
// {{{ Код взят из модуля формы документа ЗаказПокупателя (обработка события "изменение поля Сумма таб.части Запасы").
// Изменение кода свелось по большому счету к передаче ДокОбъект во все процедуры и обращение к "ДокОбъект" (вместо "Объект") внутри процедур.
// < Начало кода, права на который принадлежат фирме 1С
// Рассчитывается сумма НДС в строке табличной части.
//
&НаСервере
Процедура РассчитатьСуммуНДС(ДокОбъект, СтрокаТабличнойЧасти)
СтавкаНДС = УправлениеНебольшойФирмойПовтИсп.ПолучитьЗначениеСтавкиНДС(СтрокаТабличнойЧасти.СтавкаНДС);
СтрокаТабличнойЧасти.СуммаНДС = ?(ДокОбъект.СуммаВключаетНДС,
СтрокаТабличнойЧасти.Сумма - (СтрокаТабличнойЧасти.Сумма) / ((СтавкаНДС + 100) / 100),
СтрокаТабличнойЧасти.Сумма * СтавкаНДС / 100);
КонецПроцедуры // ПересчитатьСуммыДокумента()
// Процедура пересчитывает суммы в платежном календаре.
//
&НаСервере
Процедура ПересчитатьПлатежныйКалендарь(ДокОбъект, ПересчитыватьПроцент = Ложь)
// +++ пересчет процента, а не суммы оплаты
// для решения моей частной задачи нужно чтобы указанная в документе сумма оплаты не менялась, а менялся процент оплаты
Если ПересчитыватьПроцент Тогда
Для каждого ТекСтрока Из ДокОбъект.ПлатежныйКалендарь Цикл
Всего = ДокОбъект.Запасы.Итог("Всего") + ДокОбъект.Работы.Итог("Всего");
ТекСтрока.ПроцентОплаты = ?(Всего = 0, 0, ТекСтрока.СуммаОплаты / Всего * 100);
ТекСтрока.СуммаНДСОплаты = Окр((ДокОбъект.Запасы.Итог("СуммаНДС") + ДокОбъект.Работы.Итог("СуммаНДС")) * ТекСтрока.ПроцентОплаты / 100, 2, 1);
КонецЦикла;
Возврат;
КонецЕсли;
// пересчет процента, а не суммы оплаты +++
Для каждого ТекСтрока Из ДокОбъект.ПлатежныйКалендарь Цикл
ТекСтрока.СуммаОплаты = Окр((ДокОбъект.Запасы.Итог("Всего") + ДокОбъект.Работы.Итог("Всего")) * ТекСтрока.ПроцентОплаты / 100, 2, 1);
ТекСтрока.СуммаНДСОплаты = Окр((ДокОбъект.Запасы.Итог("СуммаНДС") + ДокОбъект.Работы.Итог("СуммаНДС")) * ТекСтрока.ПроцентОплаты / 100, 2, 1);
КонецЦикла;
КонецПроцедуры // ПересчитатьПлатежныйКалендарь()
// Процедура - обработчик события ПриИзменении поля ввода Сумма.
//
&НаСервере
Процедура ЗапасыСуммаПриИзменении(ДокОбъект)
Для Каждого СтрокаТабличнойЧасти Из ДокОбъект.Запасы Цикл
// Цена.
Если СтрокаТабличнойЧасти.Количество <> 0 Тогда
СтрокаТабличнойЧасти.Цена = СтрокаТабличнойЧасти.Сумма / СтрокаТабличнойЧасти.Количество;
КонецЕсли;
// Скидка.
Если СтрокаТабличнойЧасти.ПроцентСкидкиНаценки = 100 Тогда
СтрокаТабличнойЧасти.Цена = 0;
ИначеЕсли СтрокаТабличнойЧасти.ПроцентСкидкиНаценки <> 0 И СтрокаТабличнойЧасти.Количество <> 0 Тогда
СтрокаТабличнойЧасти.Цена = СтрокаТабличнойЧасти.Сумма / ((1 - СтрокаТабличнойЧасти.ПроцентСкидкиНаценки / 100) * СтрокаТабличнойЧасти.Количество);
КонецЕсли;
// Сумма НДС.
РассчитатьСуммуНДС(ДокОбъект, СтрокаТабличнойЧасти);
// Всего.
СтрокаТабличнойЧасти.Всего = СтрокаТабличнойЧасти.Сумма + ?(ДокОбъект.СуммаВключаетНДС, 0, СтрокаТабличнойЧасти.СуммаНДС);
КонецЦикла;
// Платежный календарь.
ПересчитатьПлатежныйКалендарь(ДокОбъект, Истина); // для штатного пересчета платежного календаря нужно во втором параметре передать Ложь или не указывать его вообще
КонецПроцедуры // ЗапасыСуммаПриИзменении()
// Конец кода, права на который принадлежат фирме 1С / >
// Код взят из модуля формы документа ЗаказПокупателя (обработка события "изменение поля Сумма таб.части Запасы"). }}}
// отсюда и до конца файла GPLv3
&НаСервере
Процедура УстановитьСкидкуПроцент(ДокОбъект, ЗначСкидки)
Для Каждого СтрокаТЧ Из ДокОбъект.Запасы Цикл
СтрокаТЧ.Сумма = Окр(СтрокаТЧ.Сумма * (100 - ЗначСкидки) / 100, 2);
КонецЦикла;
КонецПроцедуры
&НаСервере
Процедура УстановитьСкидкуСумма(ДокОбъект, ЗначСкидки)
МассивСумм = ДокОбъект.Запасы.ВыгрузитьКолонку("Сумма");
МассивСкидок = ОбщегоНазначения.РаспределитьСуммуПропорциональноКоэффициентам(ЗначСкидки, МассивСумм);
Для Сч = 0 По МассивСумм.Количество() - 1 Цикл
МассивСумм[Сч] = МассивСумм[Сч] - МассивСкидок[Сч];
КонецЦикла;
ДокОбъект.Запасы.ЗагрузитьКолонку(МассивСумм, "Сумма");
КонецПроцедуры
&НаСервере
Процедура УстановитьСуммуИтого(ДокОбъект, ЗначСкидки)
МассивСумм = ДокОбъект.Запасы.ВыгрузитьКолонку("Сумма");
МассивРез = ОбщегоНазначения.РаспределитьСуммуПропорциональноКоэффициентам(ЗначСкидки, МассивСумм);
ДокОбъект.Запасы.ЗагрузитьКолонку(МассивРез, "Сумма");
КонецПроцедуры
&НаСервере
Процедура УстановитьСкидкуНаСервере(ИмяКоманды, ОбъектыНазначения, ЗначСкидки)
Для Каждого ДокСсылка Из ОбъектыНазначения Цикл
Если ТипЗнч(ДокСсылка) <> Тип("ДокументСсылка.ЗаказПокупателя") Тогда
Продолжить;
КонецЕсли;
ДокОбъект = ДокСсылка.ПолучитьОбъект();
Если ДокОбъект.Запасы.Количество() = 0 Тогда
Продолжить;
КонецЕсли;
Если ИмяКоманды = "УстановитьСкидкуПроцент" Тогда
УстановитьСкидкуПроцент(ДокОбъект, ЗначСкидки);
ИначеЕсли ИмяКоманды = "УстановитьСкидкуСумма" Тогда
УстановитьСкидкуСумма(ДокОбъект, ЗначСкидки);
ИначеЕсли ИмяКоманды = "УстановитьСуммуИтого" Тогда
УстановитьСуммуИтого(ДокОбъект, ЗначСкидки);
КонецЕсли;
ЗапасыСуммаПриИзменении(ДокОбъект);
ДокОбъект.Записать();
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьКоманду(ИмяКоманды, ОбъектыНазначения) Экспорт
Если ОбъектыНазначения.Количество() > 1 Тогда
// не будем давать пользователю возможность массово устанавливать скидки
ВызватьИсключение НСтр("ru = 'Установка скидок для нескольких документов не поддерживается!'");
Возврат;
КонецЕсли;
ЗначСкидки = 0;
ЧислоВведено = Ложь;
Если ИмяКоманды = "УстановитьСкидкуПроцент" Тогда
ЧислоВведено = ВвестиЧисло(ЗначСкидки, НСтр("ru = 'Уменьшить на %'"), 4, 1);
ИначеЕсли ИмяКоманды = "УстановитьСкидкуСумма" Тогда
ЧислоВведено = ВвестиЧисло(ЗначСкидки, НСтр("ru = 'Уменьшить на сумму'"), 15, 2);
ИначеЕсли ИмяКоманды = "УстановитьСуммуИтого" Тогда
ЧислоВведено = ВвестиЧисло(ЗначСкидки, НСтр("ru = 'Установить сумму Итого = '"), 15, 2);
Иначе
ВызватьИсключение "Неизвестная команда """ + ИмяКоманды + """!";
Возврат;
КонецЕсли;
Если НЕ ЧислоВведено Тогда
Возврат;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(ЗначСкидки) Тогда
Возврат;
КонецЕсли;
УстановитьСкидкуНаСервере(ИмяКоманды, ОбъектыНазначения, ЗначСкидки);
// обновим открытые списки и формы документов
Для Каждого ДокСсылка Из ОбъектыНазначения Цикл
ОповеститьОбИзменении(ДокСсылка);
ПараметрыФормы = Новый Структура("Ключ", ДокСсылка);
ТекФорма = ПолучитьФорму("Документ.ЗаказПокупателя.Форма.ФормаДокумента", ПараметрыФормы);
Если ТекФорма.Открыта() Тогда
ТекФорма.Прочитать();
КонецЕсли;
КонецЦикла;
КонецПроцедуры