Вводные данные: 1С "Управление торговлей", редакция 10.3 (10.3.8.9), 8 организаций (4 с НДС).
Задача: Убрать разницу в копейках. Сумма НДС от суммы документа не равна сумме всех строк сумма НДС в документе.
Решаем: Так как программа дорабатывалась много раз, кое-что используется нестандартно. Ставки НДС по организациям находятся в регистре сведений "СтавкаНДСОрганизации".
Был создан общий модуль (серверный) - "ПересчетНДСРавенство" с процедурой "ВыровнятьНДСПоДокументуТоварыУслуги" в качестве параметра будем передавать "ДокОбъект".
// Выравнивание НДС по документу с двумя ТЧ: "Товары" и "Услуги".
// Единая ставка НДС (5% или 20%) на документ, расчет от брутто.
// Округляем только СуммаНДС в строках до 2 знаков.
// Распределение разницы методом дробных частей, шаг ±0,01, минимум по строке 0,00.
Процедура ВыровнятьНДСПоДокументуТоварыУслуги(ДокОбъект, ПорогМалойСтроки = 0.01) Экспорт
// Получаем ставку НДС как число 5 или 20
НовыйНабор = РегистрыСведений.СтавкаНДСОрганизации.СоздатьНаборЗаписей();
НовыйНабор.Отбор.Организация.Установить(ДокОбъект.Организация);
НовыйНабор.Прочитать();
Ставка = УчетНДС.ПолучитьСтавкуНДС(НовыйНабор[0].СтавкаНДС);
Если (Ставка <> 5) И (Ставка <> 20) Тогда
ВызватьИсключение "Поддерживаются ставки НДС 5% и 20%. Текущая: " + Строка(Ставка);
КонецЕсли;
Коэф = Ставка / (100 + Ставка);
ТочностьКопеек = 2;
// Сбор строк из двух табличных частей
Т = Новый ТаблицаЗначений;
Т.Колонки.Добавить("Источник"); // "Товары" или "Услуги"
Т.Колонки.Добавить("Индекс"); // порядок ввода
Т.Колонки.Добавить("Строка"); // объект строки ТЧ
Т.Колонки.Добавить("СуммаСтроки"); // с НДС
Т.Колонки.Добавить("НДСНеОкр");
Т.Колонки.Добавить("НДСОкр");
Т.Колонки.Добавить("Остаток"); // НДСНеОкр - НДСОкр
СуммаДок = 0;
Инд = 0;
// Товары
Если ДокОбъект.Метаданные().ТабличныеЧасти.Найти("Товары") <> Неопределено Тогда
Для Каждого СтрТ Из ДокОбъект.Товары Цикл
СуммаДок = СуммаДок + СтрТ.Сумма;
НДСНеОкрЛок = СтрТ.Сумма * Коэф;
НДСОкрЛок = Окр(НДСНеОкрЛок, ТочностьКопеек);
Н = Т.Добавить();
Н.Источник = "Товары";
Н.Индекс = Инд;
Н.Строка = СтрТ;
Н.СуммаСтроки = СтрТ.Сумма;
Н.НДСНеОкр = НДСНеОкрЛок;
Н.НДСОкр = НДСОкрЛок;
Н.Остаток = НДСНеОкрЛок - НДСОкрЛок;
Инд = Инд + 1;
КонецЦикла;
КонецЕсли;
// Услуги
Если ДокОбъект.Метаданные().ТабличныеЧасти.Найти("Услуги") <> Неопределено Тогда
Для Каждого СтрУ Из ДокОбъект.Услуги Цикл
СуммаДок = СуммаДок + СтрУ.Сумма;
НДСНеОкрЛок = СтрУ.Сумма * Коэф;
НДСОкрЛок = Окр(НДСНеОкрЛок, ТочностьКопеек);
Н = Т.Добавить();
Н.Источник = "Услуги";
Н.Индекс = Инд;
Н.Строка = СтрУ;
Н.СуммаСтроки = СтрУ.Сумма;
Н.НДСНеОкр = НДСНеОкрЛок;
Н.НДСОкр = НДСОкрЛок;
Н.Остаток = НДСНеОкрЛок - НДСОкрЛок;
Инд = Инд + 1;
КонецЦикла;
КонецЕсли;
Если Т.Количество() = 0 Тогда
Возврат; // нечего выравнивать
КонецЕсли;
// НДС по документу
НДСДокНеОкр = СуммаДок * Коэф;
НДСДок = Окр(НДСДокНеОкр, ТочностьКопеек);
// Сумма округленного НДС по строкам
СумОкрНДС = 0;
Для Каждого Р Из Т Цикл
СумОкрНДС = СумОкрНДС + Р.НДСОкр;
КонецЦикла;
Разница = НДСДок - СумОкрНДС;
// Если разницы нет — записываем значения в строки
Если Макс(Разница, -Разница) < 0.005 Тогда
Для Каждого Р Из Т Цикл
Р.Строка.СуммаНДС = Окр(Р.НДСОкр, ТочностьКопеек);
КонецЦикла;
Возврат;
КонецЕсли;
// Количество копеек для распределения
Копеек = Цел(Окр(Макс(Разница, -Разница) * 100, 0));
// Таблица кандидатов
Кандидаты = Новый ТаблицаЗначений;
Кандидаты.Колонки.Добавить("Остаток");
Кандидаты.Колонки.Добавить("СуммаСтроки");
Кандидаты.Колонки.Добавить("Индекс");
Кандидаты.Колонки.Добавить("Ссылка"); // ссылка на строку Т
Если Разница > 0 Тогда
// Нужно добавить копейки
Для Каждого Р Из Т Цикл
Если Р.НДСОкр >= ПорогМалойСтроки Тогда
НК = Кандидаты.Добавить();
НК.Остаток = Р.Остаток;
НК.СуммаСтроки = Р.СуммаСтроки;
НК.Индекс = Р.Индекс;
НК.Ссылка = Р;
КонецЕсли;
КонецЦикла;
// Если после фильтра никого — берём все строки
Если Кандидаты.Количество() = 0 Тогда
Для Каждого Р Из Т Цикл
НК = Кандидаты.Добавить();
НК.Остаток = Р.Остаток;
НК.СуммаСтроки = Р.СуммаСтроки;
НК.Индекс = Р.Индекс;
НК.Ссылка = Р;
КонецЦикла;
КонецЕсли;
Кандидаты.Сортировать("Остаток Убыв, СуммаСтроки Убыв, Индекс Возр");
Пока Копеек > 0 Цикл
Изменено = Ложь;
Для Каждого К Из Кандидаты Цикл
К.Ссылка.НДСОкр = К.Ссылка.НДСОкр + 0.01;
Копеек = Копеек - 1;
Изменено = Истина;
Если Копеек = 0 Тогда Прервать; КонецЕсли;
КонецЦикла;
Если НЕ Изменено Тогда Прервать; КонецЕсли;
КонецЦикла;
Иначе // Разница < 0 — нужно отнять копейки
Для Каждого Р Из Т Цикл
Если Р.НДСОкр >= 0.01 И Р.НДСОкр >= ПорогМалойСтроки Тогда
НК = Кандидаты.Добавить();
НК.Остаток = Р.Остаток;
НК.СуммаСтроки = Р.СуммаСтроки;
НК.Индекс = Р.Индекс;
НК.Ссылка = Р;
КонецЕсли;
КонецЦикла;
// Если никого — ослабим фильтр (все, где НДСОкр >= 0.01)
Если Кандидаты.Количество() = 0 Тогда
Для Каждого Р Из Т Цикл
Если Р.НДСОкр >= 0.01 Тогда
НК = Кандидаты.Добавить();
НК.Остаток = Р.Остаток;
НК.СуммаСтроки = Р.СуммаСтроки;
НК.Индекс = Р.Индекс;
НК.Ссылка = Р;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Кандидаты.Сортировать("Остаток Возр, СуммаСтроки Убыв, Индекс Возр");
Пока Копеек > 0 Цикл
Изменено = Ложь;
Для Каждого К Из Кандидаты Цикл
Если К.Ссылка.НДСОкр >= 0.01 Тогда
К.Ссылка.НДСОкр = К.Ссылка.НДСОкр - 0.01;
Копеек = Копеек - 1;
Изменено = Истина;
Если Копеек = 0 Тогда Прервать; КонецЕсли;
КонецЕсли;
КонецЦикла;
Если НЕ Изменено Тогда Прервать; КонецЕсли;
КонецЦикла;
КонецЕсли;
// Записываем НДС в строки обеих ТЧ
Для Каждого Р Из Т Цикл
Р.Строка.СуммаНДС = Окр(Р.НДСОкр, ТочностьКопеек);
КонецЦикла;
КонецПроцедуры
Вызывать будем из модуля документа из процедуры "ПередЗаписью", проверку на ставку "БезНДС", оформим там же, так как нам нужно обсчитывать только 5% и 20%
НовыйНабор = РегистрыСведений.СтавкаНДСОрганизации.СоздатьНаборЗаписей();
НовыйНабор.Отбор.Организация.Установить(Организация);
НовыйНабор.Прочитать();
Если НовыйНабор[0].СтавкаНДС <> Перечисления.СтавкиНДС.БезНДС Тогда
ПересчетНДСРавенство.ВыровнятьНДСПоДокументуТоварыУслуги(ЭтотОбъект);
КонецЕсли;
Проверено на релизе 10.3.8.9.
PS - может, кому будет полезно.
Вступайте в нашу телеграмм-группу Инфостарт