Почему одинаковый запрос списания партий выдает разный порядок строк в рабочей базе и ее копии?

Программист 1С v8.3 (Обычные формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При сопровождении партионного учета в конфигурации 1С:Управление производственным предприятием (УПП) можно столкнуться с неприятной ситуацией: при проведении одного и того же документа списания в рабочей базе списывается номенклатура со статусом партии «Купленный», а в свежевосстановленной копии из резервной копии — «Продукция». В результате себестоимость списания и бухгалтерские проводки между двумя одинаковыми базами расходятся.

Проанализируем типовую функцию ПолучитьДеревоПартийНаСкладахУпр(), разберем причины такого поведения СУБД PostgreSQL и рассмотрим способы устранения проблемы.

Анализ проблемы: почему нарушается сортировка

Рассмотрим структуру типового запроса, используемого в механизме партионного учета конфигурации УПП. В секции сортировки присутствует следующий блок:


УПОРЯДОЧИТЬ ПО
    ЧислоСерияНоменклатуры,
    ЧислоДокументОприходования,
    ЧислоЗаказ,
    ЧислоСтатусПартии Убыв,
    ДокументОприходования,
    ПартииТоваровНаСкладах.Склад

Выясним причину, по которой строки с разными статусами партий выдаются в случайном порядке. Посмотрим, как в теле запроса формируется служебное поле ЧислоСтатусПартии:


ВЫБОР
    КОГДА ПартииТоваровНаСкладах.СтатусПартии = &НаКомиссию
        ТОГДА 1
    ИНАЧЕ 0
КОНЕЦ КАК ЧислоСтатусПартии

Обратим внимание на следующие ключевые моменты:

  1. Поле ЧислоСтатусПартии разделяет запасы только на две категории: принятые на комиссию (значение 1) и собственные (значение 0). При этом собственные статусы — «Купленный» и «Продукция» — получают абсолютно одинаковое значение 0.
  2. Если на предприятии применяется оценка МПЗ «по средней стоимости», реквизиты партий (ДокументОприходования и дата партии) в регистре не ведутся или равны пустым ссылкам.
  3. Поля ЧислоСерияНоменклатуры, ЧислоДокументОприходования, ЧислоЗаказ и Склад для обеих строк также полностью совпадают.

В результате для СУБД все критерии в конструкции ORDER BY оказываются полностью одинаковыми. Согласно стандартам SQL и реляционной теории, при равенстве ключей сортировки порядок строк в выборке является недетерминированным (неопределенным). СУБД имеет право вернуть строки в том порядке, в котором их физически прочитал оптимизатор.

Почему порядок отличается именно между рабочей базой и копией

Разберем по шагам, какие системные факторы приводят к расхождению физического чтения данных в PostgreSQL:

  1. Физическое расположение страниц (Heap / CTID): в рабочей базе постоянно выполняются операции вставки, изменения и удаления данных, а также работает фоновая очистка autovacuum. Строки разбросаны по страницам таблицы с учетом фрагментации. При восстановлении базы из дампа таблицы перезаписываются последовательно, и физический порядок страниц на диске меняется.
  2. План выполнения запроса и соединения: в запросе создается временная таблица ПартииТоваровНаСкладах, которая затем соединяется с регистром сведений СписанныеТовары. Способ соединения (например, Hash Join или Merge Join) зависит от актуальности статистики таблиц и выделенной памяти work_mem. При использовании алгоритма Hash Join строки на выходе выстраиваются в зависимости от распределения по бакетам хеш-таблицы.

Так как алгоритм списания партий в 1С обходит результат запроса последовательно сверху вниз и списывает нужное количество с первой доступной строки, любое изменение порядка строк СУБД напрямую меняет сумму проводки.

Решение 1. Доработка секции упорядочивания запроса (детерминированный порядок)

Наиболее надежный и прямой способ стабилизировать поведение типового механизма — явно прописать приоритет собственных статусов партий в запросе. Для этого скорректируем текст запроса в функции ПолучитьДеревоПартийНаСкладахУпр().

Добавим в выборку дополнительное поле с приоритетом или изменим формирование поля ЧислоСтатусПартии:


ВЫБОР
    КОГДА ПартииТоваровНаСкладах.СтатусПартии = &НаКомиссию
        ТОГДА 1
    КОГДА ПартииТоваровНаСкладах.СтатусПартии = ЗНАЧЕНИЕ(Перечисление.СтатусыПартийТоваров.Купленный)
        ТОГДА 2
    КОГДА ПартииТоваровНаСкладах.СтатусПартии = ЗНАЧЕНИЕ(Перечисление.СтатусыПартийТоваров.Продукция)
        ТОГДА 3
    ИНАЧЕ 4
КОНЕЦ КАК ЧислоСтатусПартии

Либо добавим непосредственное упорядочивание по значению перечисления в конец секции УПОРЯДОЧИТЬ ПО:


УПОРЯДОЧИТЬ ПО
    ЧислоСерияНоменклатуры,
    ЧислоДокументОприходования,
    ЧислоЗаказ,
    ЧислоСтатусПартии,
    ПартииТоваровНаСкладах.СтатусПартии,
    ДокументОприходования,
    ПартииТоваровНаСкладах.Склад

После фиксации явного порядка СУБД в любой базе (и на любых серверах) будет возвращать строки строго детерминированно.

Решение 2. Методологическое разделение номенклатуры и складов

Проанализируем учетную причину возникновения коллизии. Ситуация, когда по одной и той же карточке номенклатуры на одном складе числятся остатки одновременно со статусом «Купленный» (счет учета 41) и «Продукция» (счет учета 43) при учете по средней, методически противоречива. В классическом партионном учете списание средней рассчитывается внутри каждой партии/статуса обособленно.

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

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

Решение 3. Переход на РАУЗ (Расширенная аналитика учета затрат)

Если специфика предприятия требует совместного использования покупных позиций и продукции собственного производства, рассмотрим переход с традиционного партионного учета на Режим расширенной аналитики учета затрат (РАУЗ).

В архитектуре РАУЗ расчет себестоимости вынесен из оперативного проведения документов. Расчет средней себестоимости выполняется в конце месяца регламентным документом Расчет себестоимости выпуска путем решения системы линейных уравнений. При таком подходе полностью исключается влияние физического порядка строк в выборках СУБД на расчет себестоимости списания.

← На главную