Как правильно рассчитывать итоги в Таблице Значений в 1С 7.7 и обрабатывать НДС 18% и 20% в переходный период?

Программист 1С v7.7 1C:Бухгалтерия Налоговый учет IT и автоматизация бизнеса
← На главную

Приветствуем вас, уважаемые коллеги! Сегодня мы детально разберем одну из важнейших тем в разработке на платформе 1С:Предприятие 7.7 — эффективную работу с универсальным объектом ТаблицаЗначений. Несмотря на почтенный возраст этой технологической платформы, тысячи предприятий продолжают успешно эксплуатировать решения на ее базе, а перед программистами регулярно встают нетривиальные задачи поддержки и доработки легаси-кода.

В этой статье мы подробно проанализируем механизм расчета сумм с использованием метода Итог(), выясним, почему обращение к колонкам по их порядковым номерам представляет огромную опасность для стабильности системы, и выработаем архитектурно верное решение для сложной методологической задачи — раздельного учета и вывода в отчеты сумм НДС по ставкам 18% и 20% в переходный период.

Базовые принципы работы метода Итог() в 1С 7.7

Начнем с основ. Объект ТаблицаЗначений (сокращенно ТЗ) в 1С 7.7 является двумерным динамическим массивом данных, который активно применяется для промежуточного хранения информации, группировки показателей и подготовки данных к выводу в печатные формы. Для быстрого вычисления сумм по числовым столбцам у этого объекта предусмотрен встроенный метод Итог().

Синтаксис метода предельно прост:

Итог(<Колонка>)

Параметр Колонка может принимать значения двух типов:

  1. Строковый идентификатор — текстовое название колонки, заданное при ее создании (например, "СуммаНДС").
  2. Числовой индекс (номер) — порядковый номер колонки в таблице значений, начиная с 1 (например, 9 или 13).

Рассмотрим простейший практический пример инициализации таблицы, ее заполнения и расчета итогов:


// Создание объекта и добавление типизированных колонок
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("Товар", "Справочник.Номенклатура");
ТЗ.НоваяКолонка("Количество", "Число", 10, 3);
ТЗ.НоваяКолонка("Сумма", "Число", 15, 2);

// Наполнение таблицы тестовыми данными
ТЗ.НоваяСтрока();
ТЗ.Товар = КонкретныйТовар1;
ТЗ.Количество = 5;
ТЗ.Сумма = 1500.50;

ТЗ.НоваяСтрока();
ТЗ.Товар = КонкретныйТовар2;
ТЗ.Количество = 10;
ТЗ.Сумма = 3000.00;

// Расчет итогов по колонкам
ВсегоКоличество = ТЗ.Итог("Количество");
ВсегоСумма = ТЗ.Итог("Сумма");

Опасность обращения к колонкам по номерам (индексам)

Давайте выясним причину, по которой использование числовых индексов (например, конструкция ТЗ.Итог(9)) считается крайне плохой практикой разработки, или так называемым «хрупким кодом».

Представим реальную ситуацию: вы написали сложный аналитический отчет, где расчет итоговых показателей жестко привязан к номерам колонок. Программа запущена и успешно работает. Спустя несколько месяцев бизнес-требования меняются, и другой программист (или вы сами) добавляет новую информационную колонку (например, «Артикул» или «Склад») в самое начало или середину таблицы значений. При этом все последующие колонки автоматически сдвигаются вправо на один шаг.

Что произойдет в этом случае?

Именно поэтому для обеспечения отказоустойчивости и долговечности вашего программного кода всегда обращайтесь к колонкам по их строковым именам: ТЗ.Итог("Сумма"). Исключением из этого правила могут служить только универсальные алгоритмы обхода колонок в циклах, когда требуется собрать итоги по абсолютно всем имеющимся числовым полям динамической таблицы:


// Безопасный универсальный сбор итогов по всем колонкам в цикле
ОбщийИтог = 0;
Для НомерКолонки = 1 По ТЗ.КоличествоКолонок() Цикл
    // Проверяем тип колонки, чтобы избежать ошибок суммирования нечисловых данных
    Если ТЗ.ПолучитьПараметрыКолонки(НомерКолонки) = "Число" Тогда
        ОбщийИтог = ОбщийИтог + ТЗ.Итог(НомерКолонки);
    КонецЕсли;
КонецЦикла;

Проблема расчета НДС в переходный период (18% и 20%)

Рассмотрим практическую запару, с которой столкнулся автор темы на форуме. При переходе со ставки НДС 18% на 20% (произошедшем 1 января 2019 года) возникла необходимость формировать отчеты за периоды, захватывающие оба этих интервала времени (например, за четвертый квартал 2018 и первый квартал 2019 года).

Автор попытался написать в ячейке итоговой строки макета следующее условное выражение:

?(Дата1 <= '31.12.2018', ТЗ.Итог(13), ТЗ.Итог(18))

Где Дата1 — дата начала периода отчета. Давайте проанализируем, почему такой подход в корне неверен и приводит к некорректным результатам:

  1. Глобальность условия. Переменная Дата1 определяет начало всего периода отчета. Если отчет формируется за весь первый квартал 2019 года, дата начала будет равна 01.01.2019. Условие отработает по ветке «Иначе», и система выведет итог только по ставке 20% (колонка 18), полностью проигнорировав возможные документы конца 2018 года, если они попали в выборку.
  2. Нарушение методологии учета. Налоговое законодательство требует строгого разделения базы и сумм НДС по разным ставкам. В официальных декларациях, Книге продаж и Книге покупок для ставок 18% и 20% выделены абсолютно разные графы. Сваливать их в один общий итог на основе даты начала периода — грубейшая методологическая ошибка.

Разработка архитектурно верного решения

Давайте разберем по шагам, как решить эту задачу правильно, надежно и красиво. Для этого мы применим классический паттерн подготовки данных: перенесем всю сложную логику распределения из шаблона печатной формы непосредственно в программный код модуля отчета.

Вместо того чтобы пытаться производить условные расчеты в момент вывода итоговой строки макета, мы создадим в нашей ТаблицеЗначений две независимые числовые колонки — НДС18 и НДС20. При обходе документов мы будем анализировать дату каждой операции и записывать сумму налога строго в соответствующую колонку.

Рассмотрим пример реализации этого алгоритма в коде:


// Инициализация Таблицы Значений с жесткой типизацией колонок
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("ДокументСФ", "Документ.СчетФактура");
ТЗ.НоваяКолонка("ДатаДок", "Дата");
ТЗ.НоваяКолонка("СуммаНДС18", "Число", 15, 2);
ТЗ.НоваяКолонка("СуммаНДС20", "Число", 15, 2);

// Определяем граничную дату перехода на новую ставку НДС
ГраничнаяДата = Дата(2018, 12, 31);

// Выборка документов за период отчета
ВыборкаСФ = СоздатьОбъект("Документ.СчетФактура");
ВыборкаСФ.ВыбратьДокументы(ДатаНачала, ДатаКонца);

Пока ВыборкаСФ.ПолучитьДокумент() = 1 Цикл
    // Пропускаем непроведенные документы
    Если ВыборкаСФ.Проведен() = 0 Тогда
        Продолжить;
    КонецЕсли;
    
    ТЗ.НоваяСтрока();
    ТЗ.ДокументСФ = ВыборкаСФ.ТекущийДокумент();
    ТЗ.ДатаДок  = ВыборкаСФ.ДатаДок;
    
    // Распределяем суммы НДС в зависимости от даты документа
    Если ВыборкаСФ.ДатаДок <= ГраничнаяДата Тогда
        ТЗ.СуммаНДС18 = ВыборкаСФ.СуммаНДС;
        ТЗ.СуммаНДС20 = 0;
    Иначе
        ТЗ.СуммаНДС18 = 0;
        ТЗ.СуммаНДС20 = ВыборкаСФ.СуммаНДС;
    КонецЕсли;
КонецЦикла;

Благодаря такому подходу в нашей таблице значений всегда будут содержаться абсолютно достоверные, разделенные по ставкам суммы. В строке итогов отчета нам останется лишь вывести простые выражения:

Критически важные технические нюансы платформы 1С 7.7

При реализации данного алгоритма на практике начинающие разработчики часто сталкиваются с неочевидными системными сбоями. Давайте подробно проанализируем эти нюансы, чтобы застраховать наш код от ошибок.

1. Обязательная типизация колонок

В отличие от платформы 1С v8, где типы данных в коллекциях более гибкие, в 1С 7.7 метод ТЗ.Итог() вернет 0, если колонка не была предварительно жестко типизирована при создании. Если вы напишете просто ТЗ.НоваяКолонка("НДС18"), платформа создаст колонку неопределенного типа. При записи чисел туда они могут быть неявно преобразованы в строки, и суммирование не сработает. Всегда явно указывайте тип «Число» и его разрядность!

2. Безопасное сравнение дат

В коде автора темы присутствует конструкция ТЗ.ДатаСФ <= '31.12.2018'. Использование строковых констант в одинарных кавычках для сравнения дат — источник повышенной опасности. Интерпретация такой строки зависит от региональных настроек Windows на компьютере пользователя или сервере приложений. Если формат даты в системе настроен как MM/DD/YYYY, сравнение отработает неверно.

Рекомендуется использовать безопасные варианты записи:

3. Оптимизация производительности формирования отчетов

Если ваша таблица значений содержит десятки тысяч строк, размещение прямых вызовов методов ТЗ.Итог() в формулах ячеек макета печатной формы (mxl) приведет к катастрофическому падению производительности. Дело в том, что движок построения отчетов 1С 7.7 будет вычислять итог по всей таблице заново при выводе каждой секции, где упомянута эта формула.

Для ускорения работы отчета в разы примените следующий прием:

  1. В коде модуля перед выводом секции подвала рассчитайте все итоги в локальные переменные.
  2. В ячейках макета сошлитесь на эти готовые переменные вместо вызова методов ТЗ.

Рассмотрим пример оптимизированного вывода секции подвала отчета:


// Предварительный расчет итогов в переменные модуля
ИтогНДС18 = ТЗ.Итог("СуммаНДС18");
ИтогНДС20 = ТЗ.Итог("СуммаНДС20");
ОбщийИтог = ИтогНДС18 + ИтогНДС20;

// Печать подвала отчета (в макете в ячейках прописаны переменные ИтогНДС18, ИтогНДС20, ОбщийИтог)
Таб.ВывестиСекцию("Подвал");

4. Динамическое обновление итогов на экранных формах

Если вы вывели ТаблицуЗначений на экранную форму (диалог) для интерактивного ввода или просмотра данных пользователем, вы должны помнить, что итоги по колонкам не пересчитываются автоматически при изменении чисел в строках. Чтобы реализовать «живой» пересчет сумм на форме, используйте формулы колонок ТЗ на форме, вызывающие принудительное обновление интерфейса с помощью метода Форма.Обновить(0) или напрямую пересчитывающие текстовые реквизиты итогов на форме диалога.

Заключение

Мы подробно разобрали особенности работы с итогами таблиц значений в 1С 7.7. Написание стабильного кода требует от разработчика понимания внутренних механизмов платформы. Избегайте использования индексов колонок, всегда строго типизируйте данные, аккуратно работайте с датами и оптимизируйте вывод отчетов. Эти простые правила позволят вашим решениям работать быстро и безотказно в любых условиях!

← На главную