Как правильно изменить статус или контролировать остатки книг при проведении документа в 1С?

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

При проектировании систем учета в среде «1С:Предприятие» начинающие разработчики часто совершают классическую архитектурную ошибку: пытаются записывать изменяющиеся состояния объектов непосредственно в реквизиты справочников. Например, при проведении документа выдачи книги возникает соблазн напрямую изменить реквизит статуса в справочнике Книги на значение «Выдана». В этой статье мы подробно разберем, почему такой подход неэффективен, рассмотрим эволюцию проектных решений и реализуем современный надежный механизм контроля остатков с использованием регистров накопления по новой методике проведения.

В чем опасность изменения реквизитов справочника при проведении документов?

Давайте проанализируем, почему запись данных в справочник из модуля проведения документа является плохой практикой:

  1. Нарушение объектной модели и транзакционности. Документы в 1С предназначены для регистрации хозяйственных операций во времени. Проведение документа должно влиять на регистры, но не должно изменять сами условно-постоянные данные (справочники). Если вы программно перезаписываете элемент справочника в транзакции проведения, это вызывает избыточные блокировки базы данных и существенно снижает производительность системы.
  2. Потеря истории изменений. Если вы просто меняете статус в карточке книги, вы видите только текущее состояние. Вы теряете информацию о том, когда книга была выдана, кому, сколько раз она возвращалась и какова хронология ее перемещений.
  3. Проблема группового перепроведения. Представьте, что вы перепроводите документы за прошлый месяц. Если логика завязана на перезапись реквизитов справочника, система начнет хаотично менять текущие статусы книг в зависимости от порядка документов, что приведет к полной рассинхронизации данных.

Чтобы избежать этих проблем, в платформе 1С используются регистры. Для уникальных единичных объектов (например, если у нас каждая книга уникальна и имеет свой инвентарный номер) идеально подходит Регистр сведений. Если же одна и та же книга может храниться в библиотеке в количестве нескольких экземпляров, необходимо использовать Регистр накопления.

Рассмотрим по шагам, как уйти от ошибочной логики изменения справочника и построить надежную систему учета остатков книг.

Шаг 1. Проектирование структуры метаданных в конфигураторе

Для реализации корректного количественного учета нам понадобятся три основных объекта метаданных:

  1. Справочник Книги. Хранит общую информацию о книгах (наименование, автор, год издания). Никаких реквизитов вроде «Статус» или «Остаток» внутри справочника создавать не нужно.
  2. Документ ВыдачаКниг. Этот документ фиксирует факт выдачи книг читателям. В нем должна быть табличная часть (например, СписокКниг) с реквизитами Книга (тип СправочникСсылка.Книги) и Количество (тип Число).
  3. Регистр накопления ОстаткиКниг. Это ключевой элемент системы. Создадим его со следующими свойствами:
    • Вид регистра: Остатки.
    • Измерения: Книга (тип СправочникСсылка.Книги).
    • Ресурсы: Количество (тип Число, длина 10, точность 0, неотрицательное).

После создания структуры необходимо настроить документ ВыдачаКниг так, чтобы он являлся регистратором для нашего регистра ОстаткиКниг. Это делается на вкладке «Движения» в свойствах документа в конфигураторе.

Шаг 2. Разбор методик проведения документов (Теория)

Перед тем как писать код контроля остатков, важно определиться с методикой проведения. В платформе «1С:Предприятие 8» существуют две основные методики: «старая» и «новая».

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

«Новая» методика (Сначала списываем, потом проверяем):
Это современный и рекомендуемый стандарт разработки 1С. Алгоритм работает следующим образом: документ без предварительных проверок записывает свои движения по расходу в регистр накопления. Сразу после записи движений выполняется один-единственный запрос, который анализирует остатки с учетом только что сделанного списания. Если запрос обнаруживает отрицательный остаток (дефицит) по тем позициям, которые фигурируют в проводимом документе, транзакция отменяется, пользователю выводится сообщение об ошибке, а все записанные движения автоматически аннулируются платформой. Этот подход работает гораздо быстрее и безопаснее в условиях параллельной работы.

Рассмотрим, как реализовать «новую» методику проведения программно.

Шаг 3. Программная реализация контроля остатков

Откроем модуль документа ВыдачаКниг и перейдем к предопределенной процедуре ОбработкаПроведения. Мы напишем эффективный и безопасный алгоритм списания книг с проверкой отрицательных остатков.

Проанализируем следующий программный код:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
	
	// Шаг 3.1. Подготовка и очистка движений
	// Устанавливаем признак необходимости записи движений по нашему регистру
	Движения.ОстаткиКниг.Записывать = Истина;
	
	// Очищаем старые движения на случай, если документ перепроводится.
	// Это критически важно, чтобы старые данные документа не влияли на актуальный расчет остатков.
	Движения.ОстаткиКниг.Очистить();
	
	// Шаг 3.2. Формирование предварительных движений расхода
	// Обходим строки табличной части документа и записываем расход в регистр
	Для Каждого ТекСтрока Из СписокКниг Цикл
		Движение = Движения.ОстаткиКниг.ДобавитьРасход();
		Движение.Период = Дата;
		Движение.Книга = ТекСтрока.Книга;
		Движение.Количество = ТекСтрока.Количество;
	КонецЦикла;
	
	// Принудительно записываем движения документа в базу данных.
	// Без этого вызова виртуальная таблица остатков не увидит наши изменения в текущей транзакции!
	Движения.Записать();
	
	// Шаг 3.3. Контроль отрицательных остатков с помощью запроса
	// Мы проверяем, не ушли ли остатки в минус после нашей записи.
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ОстаткиКнигОстатки.Книга КАК Книга,
		|	ОстаткиКнигОстатки.Книга.Наименование КАК КнигаНаименование,
		|	-ОстаткиКнигОстатки.КоличествоОстаток КАК Дефицит
		|ИЗ
		|	РегистрНакопления.ОстаткиКниг.Остатки(&МоментВремени, Книга В 
		|		(ВЫБРАТЬ РАЗЛИЧНЫЕ Книга ИЗ Документ.ВыдачаКниг.СписокКниг ГДЕ Ссылка = &Ссылка)) КАК ОстаткиКнигОстатки
		|ГДЕ
		|	ОстаткиКнигОстатки.КоличествоОстаток < 0";
	
	// Важно передавать именно МоментВремени(), чтобы остатки анализировались на точную секунду проведения документа
	Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
	Запрос.УстановитьПараметр("Ссылка", Ссылка);
	
	РезультатЗапроса = Запрос.Выполнить();
	
	// Шаг 3.4. Анализ результатов запроса и отмена транзакции при дефиците
	Если Не РезультатЗапроса.Пустой() Тогда
		Выборка = РезультатЗапроса.Выбрать();
		Пока Выборка.Следующий() Цикл
			// Формируем понятное сообщение для пользователя
			Сообщение = Новый СообщениеПользователю;
			Сообщение.Текст = "Недостаточно книги '" + Выборка.КнигаНаименование + "' в наличии! Не хватает: " + Выборка.Дефицит + " шт.";
			Сообщение.Сообщить();
		КонецЦикла;
		
		// Устанавливаем флаг Отказ в Истина. Платформа прервет транзакцию проведения
		// и откатит все сделанные движения назад.
		Отказ = Истина;
	КонецЕсли;
	
КонецПроцедуры

Давайте подробно разберем ключевые особенности этого кода:

Шаг 4. Обратная операция (Поступление и Возврат книг)

Чтобы в нашей системе велся корректный баланс, книги должны не только списываться, но и приходоваться. Для этого создадим документ поступления или возврата книг (например, документ ВозвратКниг).

В модуле документа ВозвратКниг логика проведения будет гораздо проще, так как приходные операции не требуют контроля отрицательных остатков. Процедура проведения будет выглядеть следующим образом:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
	
	Движения.ОстаткиКниг.Записывать = Истина;
	Движения.ОстаткиКниг.Очистить();
	
	Для Каждого ТекСтрока Из СписокКниг Цикл
		// Обратите внимание на использование метода ДобавитьПриход()
		Движение = Движения.ОстаткиКниг.ДобавитьПриход();
		Движение.Period = Дата;
		Движение.Книга = ТекСтрока.Книга;
		Движение.Количество = ТекСтрока.Количество;
	КонецЦикла;
	
	Движения.Записать();
	
КонецПроцедуры

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

Резюме и рекомендации

Мы прошли путь от неверной идеи перезаписи реквизитов справочника к профессиональной реализации транзакционного учета остатков. Подведем итоги нашей работы:

Этот подход является стандартным и обязательным для прохождения аттестации «1С:Специалист по платформе», а его освоение — важный шаг в становлении квалифицированного разработчика 1С.

← На главную