Драматическое округление до 10 копеек в продажах. УТ 11.5
Клиент захотел, чтобы цены и ручные скидки в заказах и реализациях округлялись до 10 копеек. Это способ решения проблемы с округлением НДС. Но только для рублевых документов.
Вот на этом моменте и возникла драма, которая усложнила решение задачи на добрый час.
Внутри модуля обработки действий нет информации о валюте, как и ссылки на документ. Получить ее невозможно. Это большой недостаток типовых — непродуманные события.
Поэтому я решил вставлять валюту в кэшированные значения при открытии документа:
&НаКлиенте Процедура дор_ПриОткрытииПосле(Отказ) //Обновляем кэшированные значения, добавляем туда свои параметры... дор_К.ДополнитьКэшированныеЗначения(КэшированныеЗначения, ЭтаФорма); КонецПроцедуры
Немного не точно, потому что валюту могут поменять, но это происходит редко, в принципе, для надежности я вставил вызов и в событие ДоговорПриИзменении:
&НаКлиенте Процедура дор_ДоговорПриИзмененииПеред(Элемент) дор_К.ДополнитьКэшированныеЗначения(КэшированныеЗначения, ЭтаФорма); КонецПроцедуры
Вставка в кэшированные значения выглядит так:
Процедура ДополнитьКэшированныеЗначения(КэшированныеЗначения, Форма) Экспорт Если КэшированныеЗначения = Неопределено Тогда КэшированныеЗначения = ОбработкаТабличнойЧастиКлиентСервер.ПолучитьСтруктуруКэшируемыеЗначения(); КонецЕсли; //Осипов - сохраняем валюту КэшированныеЗначения.Вставить("дор_Валюта", Форма.Объект.Валюта); КэшированныеЗначения.Вставить("дор_ТипОбъекта", ТипЗнч(Форма.Объект.Ссылка)); КонецПроцедуры
Я нашел метод ОбработкаТабличнойЧастиКлиентСервер.ПересчитатьСуммуВСтрокеТЧ, вмешательство в которое корректирует цену как при расчете по виду цены (при изменении номенклатуры, например), так и при ручной корректировке цены.
Скидку корректирую в методе после ОбработкаТабличнойЧастиКлиентСервер.ПересчитатьСуммуСУчетомРучнойСкидкиВСтрокеТЧ.
&После("ПересчитатьСуммуСУчетомРучнойСкидкиВСтрокеТЧ") Процедура дор_ПересчитатьСуммуСУчетомРучнойСкидкиВСтрокеТЧ(ТекущаяСтрока, СтруктураДействий, КэшированныеЗначения) //Округляем цену и скидку до 10 копеек Если ТребуетсяОкруглениеДо10Копеек(КэшированныеЗначения) Тогда //Проверяем скидку и округляем ее до 10 копеек СтараяСуммаРучнойСкидки = ТекущаяСтрока.СуммаРучнойСкидки; Если НЕ ЦенаОкругленаДо10Копеек(СтараяСуммаРучнойСкидки) Тогда //Округляем сумму ручной скидки НоваяСуммаРучнойСкидки = ОкруглитьЦенуДо10Копеек(СтараяСуммаРучнойСкидки); ТекущаяСтрока.СуммаРучнойСкидки = НоваяСуммаРучнойСкидки; //Если сумма скидки больше, сумма меньше... ТекущаяСтрока.Сумма = ТекущаяСтрока.Сумма - (НоваяСуммаРучнойСкидки - СтараяСуммаРучнойСкидки); КонецЕсли; КонецЕсли; КонецПроцедуры &Перед("ПересчитатьСуммуВСтрокеТЧ") Процедура дор_ПересчитатьСуммуВСтрокеТЧ(ТекущаяСтрока, СтруктураДействий, КэшированныеЗначения) //Округляем цену и скидку до 10 копеек Если ТребуетсяОкруглениеДо10Копеек(КэшированныеЗначения) Тогда //Проверяем цену и округляем ее до 10 копеек СтараяЦена = ТекущаяСтрока.Цена; Если НЕ ЦенаОкругленаДо10Копеек(ТекущаяСтрока.Цена) Тогда НоваяЦена = ОкруглитьЦенуДо10Копеек(СтараяЦена); ТекущаяСтрока.Цена = НоваяЦена; КонецЕсли; КонецЕсли; КонецПроцедуры Функция ТребуетсяОкруглениеДо10Копеек(КэшированныеЗначения) Экспорт Возврат КэшированныеЗначения.Свойство("дор_Валюта") И КэшированныеЗначения.дор_Валюта = дор_Кэш.ВалютаРегламентированногоУчета() И КэшированныеЗначения.Свойство("дор_ТипОбъекта") И ( КэшированныеЗначения.дор_ТипОбъекта = Тип("ДокументСсылка.РеализацияТоваровУслуг") ИЛИ КэшированныеЗначения.дор_ТипОбъекта = Тип("ДокументСсылка.ЗаказКлиента") ) ; КонецФункции Функция ЦенаОкругленаДо10Копеек(Цена) Экспорт Возврат Цена * 10 = Цел(Цена * 10); КонецФункции Функция ОкруглитьЦенуДо10Копеек(Цена) Экспорт Возврат Цел(Цена * 10) / 10; КонецФункции
Проверяем:
Теперь сделаем скидку 33%.
Типовой вариант делает скидку: 2 * 2 953.5 * 33% = 1 711,71 и сумму 3 475,29.
Откорректированная расширением новая сумма ручной скидки 1 711.70 и новая сумма 3 475,30:
При этом НДС считается верно 3 475,30 * 20% = 695,06
И сумма с НДС тоже корректна: 3 475,30 + 695,06 = 4170,36
UPD: пришлось переделать, т.к. функция пересчета вызывается и из сервера (например при заполнении по выбранному виду цен), и кэшированные значения передаются не с клиента, а создаются заново. Я долго ломал голову как быть, в итоге просто добавляю колонку дор_ОкруглятьЦеныВРублях в табличную часть товаров, если надо делать округление цен. Дешево и сердито, а что делать.
Вот серверная продедура, которая изменяет колонку дор_ОкруглятьЦеныВРублях в табличной части Товары.
Процедура ДополнитьТаблицуТоваровПризнаками(Форма) Экспорт Попытка //Попытка - т.к. можем добавлять реквизиты опять... ИзменяемыеРеквизиты = Новый Массив; //ИзменяемыеРеквизиты.Добавить(Новый РеквизитФормы("дор_ДокументВРублях", Новый ОписаниеТипов("Булево"), "Объект.Товары")); ИзменяемыеРеквизиты.Добавить(Новый РеквизитФормы("дор_ОкруглятьЦеныВРублях", Новый ОписаниеТипов("Булево"), "Объект.Товары")); ТипОбъекта = ТипЗнч(Форма.Объект.Ссылка); Попытка НеОкруглятьКопейки = Форма.Объект.дор_НеОкруглятьКопейки; Исключение НеОкруглятьКопейки = ложь; ОписаниеОшибки = ОписаниеОшибки(); Конецпопытки; Если Форма.Объект.Валюта = дор_Кэш.ВалютаРегламентированногоУчета() И ( ТипОбъекта = Тип("ДокументСсылка.РеализацияТоваровУслуг") ИЛИ ТипОбъекта = Тип("ДокументСсылка.ЗаказКлиента") ) И НеОкруглятьКопейки = ложь Тогда //Округлять копейки Форма.ИзменитьРеквизиты(ИзменяемыеРеквизиты,); //Добавляем реквизиты Иначе //Не округлять копейки УдаляемыеРеквизиты = Новый Массив(); УдаляемыеРеквизиты.Добавить("Объект.Товары.дор_ОкруглятьЦеныВРублях"); Форма.ИзменитьРеквизиты(,УдаляемыеРеквизиты); //Удаляем реквизиты //Форма.ИзменитьРеквизиты(,ИзменяемыеРеквизиты); //Удаляем реквизиты КонецЕсли; Исключение ОписаниеОшибки = ОписаниеОшибки(); КонецПопытки; КонецПроцедуры
UPD 2023-09-20: важно что при удалении реквизита надо указывать путь к нему строкой, а не указывать объект реквизит. Поправил это в коде. Также добавил галочку в документы «Не округлять копейки» (дор_НеОкруглятьКопейки), чтобы старые счета могли отгрузить по согласованным ценам. Это важно!
Я вызываю ее из после процедуру ПриСозданииНаСервере. А также перед процедурами РассчитатьИтоговыеПоказатели и РассчитатьИтоговыеПоказателиЗаказа, где я раньше не мох вызывать обновление кэша, потому что процедуры были серверными.
Проверяю, требуется или нет пересчет тоже несложно:
Функция ТребуетсяОкруглениеДо10Копеек(ТекущаяСтрока) Экспорт Попытка Зн = ТекущаяСтрока.дор_ОкруглятьЦеныВРублях; Возврат Истина; Исключение Возврат ложь; КонецПопытки; КонецФункции
Костыли, а что делать…
UPD: обнаружил, что не работает в новых документах, там серверный вызов конвертирует текущую строку в структуру, а мое поле не добавляет! Расширение модуля ОбработкаТабличнойЧастиКлиент:
&Вместо("ПолучитьТекущуюСтрокуСтруктурой") Функция дор_ПолучитьТекущуюСтрокуСтруктурой(ТекущаяСтрока, СтруктураДействий, ДополнительныеПараметрыЗаполнения) СтруктураПолейТЧ = ПродолжитьВызов(ТекущаяСтрока, СтруктураДействий, ДополнительныеПараметрыЗаполнения); //Если колонка есть в текущей строке, то и в структуру ее добавим Попытка СтруктураПолейТЧ.Вставить("дор_ОкруглятьЦеныВРублях", ТекущаяСтрока.дор_ОкруглятьЦеныВРублях); Исключение КонецПопытки; Возврат СтруктураПолейТЧ; КонецФункции
Также перенес событие из ПриСозданииНаСервере в ПриОткрытии (может валюта заполняется уже при открытии):
&НаКлиенте Процедура дор_ПриОткрытииПосле(Отказ) //Обновляем кэшированные значения, добавляем туда свои параметры... //дор_К.ДополнитьКэшированныеЗначения(КэшированныеЗначения, ЭтаФорма); дор_ПриОткрытииПослеНаСервере(); //Когда валюта заполнена дор_УстановитьВидимость(); КонецПроцедуры &НаСервере Процедура дор_ПриОткрытииПослеНаСервере() дор_С.ДополнитьТаблицуТоваровПризнаками(ЭтаФорма); КонецПроцедуры
UPD: Важно! Необходимо на всякий случай внедрить пересчет НДС перед записью реализации и заказа. Там же описаны и итоги внедрения.
Среда: 11.5.12.53
дор_К дор_С это реально наименования модулей?
да, Клиент и Сервер для расширения дор_ (Доработки)