При ведении учета в конфигурации «1С:Управление производственным предприятием 1.3» на предприятиях с большим документооборотом (десятки тысяч реализаций и счетов-фактур в квартал) пользователи часто сталкиваются с существенным замедлением регламентных операций по НДС. Документ Формирование записей книги продаж при нажатии кнопки заполнения может выполняться десятки минут или приводить к зависанию сеанса.
Разберем детально, почему возникает эта проблема, проанализируем узкие места типового алгоритма и рассмотрим проверенный программный способ ускорения обработки данных.
Проанализируем работу типовой подсистемы учета НДС. Независимо от выбранной учетной политики по моменту начисления НДС («по отгрузке» или «по оплате»), типовой механизм сопоставляет выставленные счета-фактуры и прошедшие платежи. Это необходимо, чтобы корректно выделить авансовые и частичные оплаты, а также заполнить реквизиты платежно-расчетных документов в строках книги продаж.
Основное «бутылочное горлышко» находится в модуле объекта документа ФормированиеЗаписейКнигиПродаж, а именно в процедуре РаспределитьОплатыПоДеревуСФ. В типовой реализации применен неоптимальный шаблон:
ПостроительЗапроса, обращающийся к неиндексированной таблице значений.Выполнение десятков тысяч последовательных запросов через ПостроительЗапроса к коллекции в оперативной памяти создает колоссальную нагрузку на процессор и приводит к геометрическому росту времени обработки.
Для кардинального ускорения заполнения перепишем процедуру РаспределитьОплатыПоДеревуСФ. Откажемся от использования ПостроительЗапроса внутри цикла, добавим индекс по ключевому полю СчетФактура и применим быстрый платформенный метод НайтиСтроки().
Рассмотрим по шагам модификацию процедуры в модуле объекта документа ФормированиеЗаписейКнигиПродаж:
Процедура РаспределитьОплатыПоДеревуСФ(Дерево_НДСНачисленный, ТаблицаРезультатов, СписокСчетовФактур, РаспределенныеОплаты, ОтражатьВРеестре = Истина, ОтражатьВидНачисления = Ложь)
НДСНалоговыйПериод = Неопределено;
ТаблицаОплат = Новый ТаблицаЗначений();
ТаблицаОплат.Колонки.Добавить("ДокументОплаты");
ТаблицаОплат.Колонки.Добавить("ДатаОплаты", Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя)));
ТаблицаОплат.Колонки.Добавить("СуммаОплаты", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
// Создаем копию таблицы распределенных оплат и индексируем ее для быстрого поиска
ТаблицаИсточникаПостроителя = РаспределенныеОплаты.Скопировать();
ТаблицаИсточникаПостроителя.Индексы.Добавить("СчетФактура");
ВыборкаОплат = РаспределенныеОплаты.СкопироватьКолонки();
МоментОпределения_ПоОтгрузке = Перечисления.МоментыОпределенияНалоговойБазыНДС.ПоОтгрузке;
Для Каждого СтрокаСФ Из Дерево_НДСНачисленный.Строки Цикл
ТаблицаОплат.Очистить();
Если УчетНДС.ДляСчетаФактурыНеТребуетсяОплата(СтрокаСФ.СчетФактура) Тогда
НаличиеОплатыНеТребуется = Истина;
Иначе
НаличиеОплатыНеТребуется = (СтрокаСФ.Строки[0].МоментОпределенияНалоговойБазыНДС = МоментОпределения_ПоОтгрузке) Или Дата >= '20080101';
// Быстрый поиск строк по индексу вместо ПостроителяЗапроса
МассивСтрокОплатПоСФ = ТаблицаИсточникаПостроителя.НайтиСтроки(Новый Структура("СчетФактура", СтрокаСФ.СчетФактура));
// Обработка ситуации, когда оплата отсутствует
Если МассивСтрокОплатПоСФ.Количество() = 0 И Не НаличиеОплатыНеТребуется И СтрокаСФ.СуммаСНДС >= 0 Тогда
Продолжить;
КонецЕсли;
ВыборкаОплат.Очистить();
Для Каждого СтрокаОплатыПоСФ Из МассивСтрокОплатПоСФ Цикл
Если ((СтрокаСФ.СуммаСНДС > 0) И (СтрокаОплатыПоСФ.РаспределеннаяОплата > 0)) Или
((СтрокаСФ.СуммаСНДС <= 0) И (СтрокаОплатыПоСФ.РаспределеннаяОплата < 0)) Тогда
СтрокаВыборкиОплат = ВыборкаОплат.Добавить();
Для Каждого Колонка Из ВыборкаОплат.Колонки Цикл
ИндексКолонки = ВыборкаОплат.Колонки.Индекс(Колонка);
СтрокаВыборкиОплат.Установить(ИндексКолонки, СтрокаОплатыПоСФ.Получить(ИндексКолонки));
КонецЦикла;
КонецЕсли;
КонецЦикла;
ВыборкаОплат.Сортировать("ДатаОплаты");
СуммаКПогашению = СтрокаСФ.СуммаСНДС;
Для Каждого СтрокаРаспределеннойОплаты Из ВыборкаОплат Цикл
// Далее идет типовой неизмененный код распределения сумм
Благодаря исключению накладных расходов на инициализацию парсера запросов и использованию индекса по полю СчетФактура, время сопоставления сотен и тысяч документов сокращается в десятки раз (с 30–40 минут до считанных секунд).
Если после доработки программного кода время формирования документа остается значительным, проанализируем сопутствующие факторы производительности:
НДС расчеты с покупателями, НДС начисленный и НДС с авансов раздувает размер входных таблиц. Рекомендуется закрывать зависшие копеечные остатки с помощью документа Корректировка записей регистров.Формирование записей книги продаж (разбивая их помесячно или по организациям), что снижает объем единовременно удерживаемых коллекций в оперативной памяти.UPDATE STATISTICS с полным сканированием) и дефрагментацию/реиндексацию таблиц итогов регистров накопления.rphost может исчерпать доступное адресное пространство памяти (2–3 ГБ) и аварийно завершиться. Переход на 64-битный сервер полностью решает проблему нехватки памяти при формировании объемных книг покупок и продаж.