При работе со старыми, но до сих пор активно используемыми конфигурациями на платформе 1С 7.7 (такими как «Производство + Услуги + Бухгалтерия», или ПУБ) специалисты часто сталкиваются с техническими последствиями некорректно выполненной свертки информационной базы. Одна из самых распространенных и неприятных проблем — расхождение остатков между партионным учетом (регистр Партии) и складским учетом (регистр МестаХранения).
В этой статье мы подробно разберем, почему возникают такие расхождения, проанализируем внутреннее устройство «пустых» документов свертки и предложим проверенное на практике программное решение для полного выравнивания учета.
Проанализируем ситуацию, которая часто ставит в тупик начинающих разработчиков. База данных была «обрезана», в системе присутствует документ Свертка ИБ, который сделал движения по регистрам, но при попытке открыть его модуль проведения или форму выясняется, что они абсолютно пусты. Как такое возможно?
В платформе 1С 7.7 это стандартное следствие одного из двух подходов к свертке базы данных:
RA* и RG*) и связала их с идентификатором (ID) созданного документа-пустышки, минуя стандартный механизм проведения 1С.Свертка ИБ, запустил свертку, документы успешно сформировали движения. После этого код из модуля был удален (или конфигурация была обновлена на MD-файл без этого кода), чтобы пользователи случайно не перепровели документ и не уничтожили зафиксированные остатки. Физически движения в 1С 7.7 остаются в базе данных до тех пор, пока документ не будет принудительно распроведен или перепроведен.Проблема заключается в том, что при расчете сальдо на дату свертки разработчики часто фиксируют остатки «как есть», не обращая внимания на то, что партионный и складской учет уже были разбалансированы. В результате складские остатки показывают одно количество товаров, а партионный учет — совершенно другое (либо партии вовсе отсутствуют).
Рассмотрим подробнее, какие пути решения существуют в данной ситуации:
Выполним выравнивание партий по складским остаткам. За основу мы возьмем регистр МестаХранения (складские остатки) и приведем регистр Партии в полное соответствие с ним.
Важный нюанс логики партионного учета: В складском учете нет аналитики «Партия». Нам придется «выдумать» эту аналитику. Лучший способ — в качестве партии (которая в 1С 7.7 обычно является ссылкой на документ поступления) указать сам наш корректирующий документ. При этом стоимость партий мы рассчитаем как средневзвешенную цену из существующих (пусть и кривых) партионных остатков на момент корректировки.
Ниже представлен оптимизированный и безопасный код для модуля проведения корректирующего документа. В отличие от стандартных медленных запросов 1С 7.7, здесь используется высокоскоростной метод выгрузки итогов напрямую в таблицы значений, а также добавлена защита от деления на ноль при расчете себестоимости.
Процедура ОбработкаПроведения()
Перем ТабПартий, ТабСклада, ТЗ_Цены;
РегПартии = СоздатьОбъект("Регистр.Партии");
РегСклада = СоздатьОбъект("Регистр.МестаХранения");
ТабПартий = СоздатьОбъект("ТаблицаЗначений");
ТабСклада = СоздатьОбъект("ТаблицаЗначений");
ТЗ_Цены = СоздатьОбъект("ТаблицаЗначений");
// Включаем временный расчет для получения итогов на нужную дату
РегПартии.ВременныйРасчет(1);
РегСклада.ВременныйРасчет(1);
РассчитатьРегистрыНа(ДатаДок);
// Выгружаем текущие итоги по обоим регистрам
РегПартии.ВыгрузитьИтоги(ТабПартий);
РегСклада.ВыгрузитьИтоги(TabСклада);
// Шаг 1. Сторнируем (обнуляем) все текущие остатки по регистру "Партии"
ТабПартий.ВыбратьСтроки();
Пока ТабПартий.ПолучитьСтроку() = 1 Цикл
// Проверяем, есть ли что сторнировать
Если (ТабПартий.Количество = 0) И (ТабПартий.Сумма = 0) Тогда
Продолжить;
КонецЕсли;
Регистр.Партии.Партия = ТабПартий.Партия;
Регистр.Партии.ВидМПЗ = ТабПартий.ВидМПЗ;
Регистр.Партии.МПЗ = ТабПартий.МПЗ;
Регистр.Партии.МестоХранения = ТабПартий.МестоХранения;
Регистр.Партии.Качество = ТабПартий.Качество;
Регистр.Партии.СрокЭксплуатации = ТабПартий.СрокЭксплуатации;
Регистр.Партии.Заявка = ТабПартий.Заявка;
Регистр.Партии.Владелец = ТабПартий.Владелец;
// Расход с тем же знаком обнуляет остаток
Регистр.Партии.Количество = ТабПартий.Количество;
Регистр.Партии.Сумма = ТабПартий.Сумма;
Регистр.Партии.СуммаНУ = ТабПартий.СуммаНУ;
Регистр.Партии.ДвижениеРасходВыполнить();
КонецЦикла;
// Шаг 2. Расчет средних цен номенклатуры (МПЗ) для последующего распределения
ТЗ_Цены.Загрузить(ТабПартий);
ТЗ_Цены.Свернуть("МПЗ", "Количество, Сумма");
ТЗ_Цены.НоваяКолонка("Цена", "Число", 15, 5);
ТЗ_Цены.ВыбратьСтроки();
Пока ТЗ_Цены.ПолучитьСтроку() = 1 Цикл
// Защита от деления на ноль и зависших суммовых остатков без количества
Если ТЗ_Цены.Количество > 0 Тогда
ТЗ_Цены.Цена = ТЗ_Цены.Сумма / ТЗ_Цены.Количество;
Иначе
ТЗ_Цены.Цена = 0;
КонецЕсли;
КонецЦикла;
// Шаг 3. Заполнение партий по актуальным складским остаткам
ТабСклада.ВыбратьСтроки();
Пока ТабСклада.ПолучитьСтроку() = 1 Цикл
Если ТабСклада.Количество <= 0 Тогда
Продолжить; // Пропускаем нулевые или отрицательные складские остатки
КонецЕсли;
// Ищем среднюю цену номенклатуры
СтрокаЦены = 0;
СредняяЦена = 0;
Если ТЗ_Цены.НайтиЗначение(ТабСклада.МПЗ, СтрокаЦены, "МПЗ") = 1 Тогда
СредняяЦена = ТЗ_Цены.ПолучитьЗначение(СтрокаЦены, "Цена");
КонецЕсли;
Регистр.Партии.Партия = ТекущийДокумент(); // Наш документ становится новой партией
Регистр.Партии.ВидМПЗ = ТабСклада.ВидМПЗ;
Регистр.Партии.МПЗ = ТабСклада.МПЗ;
Регистр.Партии.МестоХранения = ТабСклада.МестоХранения;
Регистр.Партии.Качество = ТабСклада.Качество;
Регистр.Партии.Владелец = ТабСклада.Владелец;
// Приравниваем количество к складскому
Регистр.Партии.Количество = ТабСклада.Количество;
// Рассчитываем новую партионную стоимость
НоваяСумма = Окр(СредняяЦена * ТабСклада.Количество, 2);
Регистр.Партии.Сумма = НоваяСумма;
Регистр.Партии.СуммаНУ = НоваяСумма;
Регистр.Партии.ДвижениеПриходВыполнить();
КонецЦикла;
КонецПроцедуры
Проанализируем ключевые моменты, о которых нельзя забывать при использовании данного метода:
Партии ссылаются на наш созданный документ-пустышку в качестве измерения Партия, этот документ становится системно важным. Если в будущем кто-то случайно распроведет или удалит его, последующие списания потеряют связь с партией, и в отчетах появится битая ссылка вида "<Документ не найден>".
Рекомендация: После проведения выравнивания заблокируйте интерактивное изменение этого документа для пользователей (через настройку прав доступа или программный запрет в модуле формы).
Посмотрим на ситуацию с точки зрения производительности. Если ваша база данных 1С 7.7 работает под управлением MS SQL Server и имеет гигантский объем номенклатуры, проведение корректирующего документа стандартными средствами 1С может занять долгие часы.
В этом случае эффективнее прибегнуть к возможностям внешней компоненты 1С++ и выполнить операцию выравнивания через прямой SQL-запрос. С помощью SQL-инструкций можно за несколько секунд очистить таблицы итогов и движений регистра Партии на дату свертки и заполнить их скопированными записями из таблиц регистра МестаХранения, рассчитав рублевую стоимость по требуемому алгоритму прямо на стороне сервера. Это позволит выровнять учет мгновенно и без изменения метаданных конфигурации.