При сопровождении партионного учета в конфигурации 1С:Управление производственным предприятием (УПП) можно столкнуться с неприятной ситуацией: при проведении одного и того же документа списания в рабочей базе списывается номенклатура со статусом партии «Купленный», а в свежевосстановленной копии из резервной копии — «Продукция». В результате себестоимость списания и бухгалтерские проводки между двумя одинаковыми базами расходятся.
Проанализируем типовую функцию ПолучитьДеревоПартийНаСкладахУпр(), разберем причины такого поведения СУБД PostgreSQL и рассмотрим способы устранения проблемы.
Рассмотрим структуру типового запроса, используемого в механизме партионного учета конфигурации УПП. В секции сортировки присутствует следующий блок:
УПОРЯДОЧИТЬ ПО
ЧислоСерияНоменклатуры,
ЧислоДокументОприходования,
ЧислоЗаказ,
ЧислоСтатусПартии Убыв,
ДокументОприходования,
ПартииТоваровНаСкладах.Склад
Выясним причину, по которой строки с разными статусами партий выдаются в случайном порядке. Посмотрим, как в теле запроса формируется служебное поле ЧислоСтатусПартии:
ВЫБОР
КОГДА ПартииТоваровНаСкладах.СтатусПартии = &НаКомиссию
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ КАК ЧислоСтатусПартии
Обратим внимание на следующие ключевые моменты:
ЧислоСтатусПартии разделяет запасы только на две категории: принятые на комиссию (значение 1) и собственные (значение 0). При этом собственные статусы — «Купленный» и «Продукция» — получают абсолютно одинаковое значение 0.ДокументОприходования и дата партии) в регистре не ведутся или равны пустым ссылкам.ЧислоСерияНоменклатуры, ЧислоДокументОприходования, ЧислоЗаказ и Склад для обеих строк также полностью совпадают.В результате для СУБД все критерии в конструкции ORDER BY оказываются полностью одинаковыми. Согласно стандартам SQL и реляционной теории, при равенстве ключей сортировки порядок строк в выборке является недетерминированным (неопределенным). СУБД имеет право вернуть строки в том порядке, в котором их физически прочитал оптимизатор.
Разберем по шагам, какие системные факторы приводят к расхождению физического чтения данных в PostgreSQL:
autovacuum. Строки разбросаны по страницам таблицы с учетом фрагментации. При восстановлении базы из дампа таблицы перезаписываются последовательно, и физический порядок страниц на диске меняется.ПартииТоваровНаСкладах, которая затем соединяется с регистром сведений СписанныеТовары. Способ соединения (например, Hash Join или Merge Join) зависит от актуальности статистики таблиц и выделенной памяти work_mem. При использовании алгоритма Hash Join строки на выходе выстраиваются в зависимости от распределения по бакетам хеш-таблицы.Так как алгоритм списания партий в 1С обходит результат запроса последовательно сверху вниз и списывает нужное количество с первой доступной строки, любое изменение порядка строк СУБД напрямую меняет сумму проводки.
Наиболее надежный и прямой способ стабилизировать поведение типового механизма — явно прописать приоритет собственных статусов партий в запросе. Для этого скорректируем текст запроса в функции ПолучитьДеревоПартийНаСкладахУпр().
Добавим в выборку дополнительное поле с приоритетом или изменим формирование поля ЧислоСтатусПартии:
ВЫБОР
КОГДА ПартииТоваровНаСкладах.СтатусПартии = &НаКомиссию
ТОГДА 1
КОГДА ПартииТоваровНаСкладах.СтатусПартии = ЗНАЧЕНИЕ(Перечисление.СтатусыПартийТоваров.Купленный)
ТОГДА 2
КОГДА ПартииТоваровНаСкладах.СтатусПартии = ЗНАЧЕНИЕ(Перечисление.СтатусыПартийТоваров.Продукция)
ТОГДА 3
ИНАЧЕ 4
КОНЕЦ КАК ЧислоСтатусПартии
Либо добавим непосредственное упорядочивание по значению перечисления в конец секции УПОРЯДОЧИТЬ ПО:
УПОРЯДОЧИТЬ ПО
ЧислоСерияНоменклатуры,
ЧислоДокументОприходования,
ЧислоЗаказ,
ЧислоСтатусПартии,
ПартииТоваровНаСкладах.СтатусПартии,
ДокументОприходования,
ПартииТоваровНаСкладах.Склад
После фиксации явного порядка СУБД в любой базе (и на любых серверах) будет возвращать строки строго детерминированно.
Проанализируем учетную причину возникновения коллизии. Ситуация, когда по одной и той же карточке номенклатуры на одном складе числятся остатки одновременно со статусом «Купленный» (счет учета 41) и «Продукция» (счет учета 43) при учете по средней, методически противоречива. В классическом партионном учете списание средней рассчитывается внутри каждой партии/статуса обособленно.
Чтобы избежать подобных конфликтов без изменения конфигурации, применим следующие методические решения:
Если специфика предприятия требует совместного использования покупных позиций и продукции собственного производства, рассмотрим переход с традиционного партионного учета на Режим расширенной аналитики учета затрат (РАУЗ).
В архитектуре РАУЗ расчет себестоимости вынесен из оперативного проведения документов. Расчет средней себестоимости выполняется в конце месяца регламентным документом Расчет себестоимости выпуска путем решения системы линейных уравнений. При таком подходе полностью исключается влияние физического порядка строк в выборках СУБД на расчет себестоимости списания.