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