При разработке систем учета в 1С новички часто сталкиваются с ситуацией, когда данные в запросах отображаются некорректно или расчеты не сходятся с реальностью. Одной из самых распространенных ошибок является неправильный выбор типа регистра накопления и попытка получить из него данные, которые он физически не может предоставить. Рассмотрим подробно, как решить задачу учета упаковочных материалов, основываясь на практическом опыте и разборе типичных архитектурных ошибок.
Разберем ситуацию, описанную автором: для учета остатков используется регистр накопления с видом Обороты. В системе 1С существует два принципиально разных типа регистров накопления:
Проанализируем код автора. Попытка получить КоличествоКонечныйОстаток из виртуальной таблицы ОстаткиИОбороты оборотного регистра не даст желаемого результата, так как для оборотного регистра это поле будет означать лишь оборот за период, а не реальный складской остаток. Первое правило: если вам нужно знать, «сколько сейчас на складе», всегда используйте регистр накопления вида Остатки.
Для решения задачи упаковки деталей (кейсы, палеты, решетки) нам необходимо создать регистр накопления «ОстаткиМатериалов» (вид — Остатки). Выясним, какие измерения и ресурсы нам понадобятся:
Важный момент в коде автора: ЗапросОстаток.УстановитьПараметр("МодельДетали", Объект.Ссылка);. Это критическая ошибка. В измерениях регистра должна храниться ссылка на элемент справочника (например, «Диск сцепления»), а не ссылка на сам документ. Документ в регистре выступает в роли Регистратора, но не должен быть фильтром для получения остатка по конкретной номенклатуре.
Рассмотрим алгоритм расчета, который требуется внедрить. Допустим, на линию приходит 550 деталей, а в палету помещается 60. Нам нужно вычислить количество полных паллет и остаток деталей, который перейдет на следующую партию. Посмотрим, как реализовать это программно, избегая сложных циклов в запросах.
Для получения целого количества упаковок и остатка в 1С эффективно использовать функции работы с числами:
// Количество деталей на линии
ОбщееКоличество = 550;
ВместимостьПалеты = 60;
// Вычисляем количество полных палет
КоличествоПалет = Цел(ОбщееКоличество / ВместимостьПалеты);
// Вычисляем остаток, который не влез в целую палету
ОстатокДеталей = ОбщееКоличество % ВместимостьПалеты;
Если ваша задача требует суммирования остатка с «прошлых линий», то этот остаток должен храниться именно в регистре накопления (вид Остатки). При проведении нового документа мы сначала считываем текущий остаток из регистра и прибавляем его к новому поступлению.
Проанализируем, как должен выглядеть правильный запрос к виртуальной таблице остатков. Обратите внимание на использование объекта Граница. Это необходимо для того, чтобы запрос видел (или не видел) движения текущего документа в зависимости от того, в какой момент он вызывается.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОстаткиМатериаловОстатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ОстаткиМатериалов.Остатки(&МоментВремени, Номенклатура = &Номенклатура) КАК ОстаткиМатериаловОстатки";
// Используем Границу, чтобы получить остатки строго ДО текущего документа
ГраницаОстатков = Новый Граница(Объект.Дата, ВидГраницы.Исключая);
Запрос.УстановитьПараметр("МоментВремени", ГраницаОстатков);
Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяДеталь);
Результат = Запрос.Выполнить();
В тексте обсуждения упоминается, что на каждую коробку нужно 2 крышки, 5 решеток и т.д. Программировать такие коэффициенты жестко в коде (Количество * 5) — плохая практика. Правильнее использовать механизм Спецификаций.
Создадим справочник «Спецификации», подчиненный справочнику «Номенклатура». В табличной части спецификации укажем нормативы расхода. Тогда при изменении количества деталей на форме документа, мы выполним следующий алгоритм:
&НаСервере.Рассмотрим пример кода для такого заполнения:
&НаСервере
Процедура ЗаполнитьМатериалыПоСпецификации()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СпецификацииСостав.Материал,
| СпецификацииСостав.Количество * &КоличествоУпаковок КАК НужноМатериалов
|ИЗ
| Справочник.Спецификации.Состав КАК СпецификацииСостав
|ГДЕ
| СпецификацииСостав.Ссылка = &ВыбраннаяСпецификация";
Запрос.УстановитьПараметр("КоличествоУпаковок", Объект.КоличествоПалетов);
Запрос.УстановитьПараметр("ВыбраннаяСпецификация", Объект.Спецификация);
// Очищаем и заполняем ТЧ
Объект.Материалы.Очистить();
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = Объект.Материалы.Добавить();
НоваяСтрока.НаименованиеМатериала = Выборка.Материал;
НоваяСтрока.Количество = Выборка.НужноМатериалов;
КонецЦикла;
КонецПроцедуры
Выясним причину путаницы, возникшей в обсуждении на форуме. Один из участников предложил использовать СрезПоследних. Важно помнить: метод СрезПоследних существует только у Регистров сведений. Для регистров накопления получение актуального значения — это всегда обращение к таблице Остатки.
Если вы хотите реализовать логику «последней записи», как в регистре сведений, но при этом иметь возможность суммировать остатки, вам все равно нужен регистр накопления. Но доступ к данным в нем осуществляется через запросы к виртуальным таблицам, а не через менеджер регистра с методами среза.
Проанализируем взаимодействие клиента и сервера. Автор вызывает серверную процедуру при каждом изменении количества. Если расчет простой (например, деление на 60), это можно и нужно делать &НаКлиенте. Это избавит пользователя от микро-задержек при вводе данных. На сервер стоит обращаться только тогда, когда нужен запрос к базе данных (получение остатков или чтение спецификации).
Для успешной реализации задачи упаковки деталей необходимо:
Соблюдение этих архитектурных принципов 1С позволит создать надежную систему, которая будет корректно считать остатки независимо от количества документов и сложности производственных цепочек.