В практике работы с конфигурацией 1С:Управление торговлей 10.3 программисты часто сталкиваются с ситуацией, когда данные в регистре ТоварыНаСкладах (складской учет) не совпадают с данными в регистре ПартииТоваровНаСкладах (партийный учет). Чаще всего это происходит из-за разрешения «списания в минус» по складам: товар продается до того, как документ поступления фактически проведен в базе, или из-за ошибок в последовательности проведения документов.
Разберем ситуацию, когда нам необходимо «дозаполнить» партийный учет, ориентируясь на последние реальные поступления товара. Мы будем использовать логику LIFO (Last In, First Out) — то есть будем искать самые свежие документы прихода, чтобы по ним восстановить недостающее количество в партиях. Рассмотрим подробно, как это реализовать программно.
Представим сценарий: на складе числится 10 единиц товара «Клеящий карандаш», а по регистру партий — всего 4 единицы. Нам нужно оприходовать недостающие 6 единиц. Чтобы себестоимость остатков была максимально приближена к актуальной, логично взять данные из последних приходных накладных. Если последняя накладная была на 2 штуки, а предпоследняя — на 10, то мы должны взять 2 штуки из последней и еще 4 из предпоследней.
Для решения задачи нам не подходят остатки, так как по партиям у нас может быть пусто или данные некорректны. Нам нужны именно обороты прихода. Проанализируем структуру запроса. Нам важно получить список документов оприходования, отсортированных по дате в обратном порядке.
Рассмотрим пример запроса, который собирает данные о поступлениях:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПартииОбороты.ПериодСекунда КАК Период,
| ПартииОбороты.ДокументОприходования КАК Партия,
| ПартииОбороты.Номенклатура КАК Номенклатура,
| ПартииОбороты.ХарактеристикаНоменклатуры КАК Характеристика,
| ПартииОбороты.СерияНоменклатуры КАК Серия,
| СУММА(ПартииОбороты.КоличествоПриход) КАК Количество
|ИЗ
| РегистрНакопления.ПартииТоваровНаСкладах.Обороты(&НачалоПериода, &КонецПериода, Авто, Номенклатура = &Номенклатура) КАК ПартииОбороты
|ГДЕ
| ПартииОбороты.КоличествоПриход > 0
|
|СГРУППИРОВАТЬ ПО
| ПартииОбороты.ДокументОприходования,
| ПартииОбороты.Номенклатура,
| ПартииОбороты.ХарактеристикаНоменклатуры,
| ПартииОбороты.СерияНоменклатуры,
| ПартииОбороты.ПериодСекунда
|
|УПОРЯДОЧИТЬ ПО
| Период УБЫВ";
Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);
// Период лучше брать достаточно большой, чтобы найти старые поступления, если свежих мало
Запрос.УстановитьПараметр("НачалоПериода", ДобавитьМесяц(ТекущаяДата(), -12));
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
Важный нюанс: В запросе мы обязательно используем УПОРЯДОЧИТЬ ПО Период УБЫВ. Это и есть техническая реализация принципа LIFO в контексте выборки: самые новые документы окажутся в начале списка.
После получения списка партий нам нужно «разложить» требуемое количество по этим строкам. Рассмотрим, как правильно организовать цикл обработки, чтобы не взять лишнего и корректно обработать ситуацию, когда поступлений было меньше, чем нам требуется оприходовать.
Проанализируем программный код обработки выборки:
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
НужноОприходовать = 10; // Например, нам не хватает 10 штук
ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Партия");
ТаблицаРезультат.Колонки.Добавить("Количество");
Пока Выборка.Следующий() И НужноОприходовать > 0 Цикл
// Вычисляем, сколько мы можем взять из текущей партии
КоличествоИзПартии = Мин(Выборка.Количество, НужноОприходовать);
// Добавляем строку в результат
НоваяСтрока = ТаблицаРезультат.Добавить();
НоваяСтрока.Партия = Выборка.Партия;
НоваяСтрока.Количество = КоличествоИзПартии;
// Уменьшаем счетчик остатка
НужноОприходовать = НужноОприходовать - КоличествоИзПартии;
КонецЦикла;
// Если после цикла НужноОприходовать > 0, значит истории приходов не хватило
Если НужноОприходовать > 0 Тогда
// Здесь можно либо выдать предупреждение, либо создать пустую партию
// (например, текущий документ оприходования)
Сообщить("Внимание: истории поступлений не хватило на " + НужноОприходовать + " шт.");
КонецЕсли;
При реализации данного алгоритма в реальной базе 1С:УТ 10.3, следует обратить внимание на следующие моменты:
Номенклатура, но и по ХарактеристикаНоменклатуры и СерияНоменклатуры. Если вы оприходуете товар на партию, где была другая серия, вы создадите еще больший беспорядок в регистрах.КорректировкаЗаписейРегистров, чтобы сделать движения только по регистру ПартииТоваровНаСкладах.Если записей о приходах очень много, перебор выборки в цикле может быть не самым быстрым решением. Можно переложить часть логики на СУБД, вычислив нарастающий итог прямо в запросе. Рассмотрим, как это выглядит концептуально:
Мы соединяем таблицу приходов саму с собой по условию «Дата прихода из таблицы А <= Даты прихода из таблицы Б». Это позволяет для каждой строки получить сумму всех предыдущих (или последующих, если сортируем по убыванию) поступлений. Таким образом, мы можем сразу в запросе отсечь те строки, где накопленная сумма уже превысила наш лимит в 10 штук.
Однако для разовых задач по выравниванию остатков вариант с простым циклом (как в Шаге 2) является более читаемым, простым в отладке и вполне производительным.
Метод LIFO при оприходовании — это эффективный инструмент «реанимации» партийного учета. Помните, что перед выполнением таких корректировок крайне рекомендуется запустить стандартную обработку «Проведение по партиям». Возможно, система сама распределит остатки, если документы просто были проведены в неправильном порядке. Если же это не помогло, описанный выше программный метод позволит вам восстановить учет, опираясь на актуальные цены последних поставок.