gifts2017

Программный контроль границы последовательности партионного учета при проведении документов задним числом

Опубликовал Даниил Матвеев (cargobird) в раздел Программирование - Практика программирования

Редактирование и проведение документов задним числом несмотря на обилие корректировочных документов - суровая необходимость, особенно на больших предприятиях. Естественно, что если документ двигает границу последовательности партионного учета, граница устанавливается на этот документ, и для получения актуальных данных требуется восстанавливать последовательность, как правило в монопольном режиме. Вернем границу последовательности программно на её законное место, если движения по партиям документа, проведенного задним числом не изменились.

Приступим...

1. Создадим подписку на событие ПередЗаписью. Подписка нужна для запоминания таблицы движений документа по партиям до проведения. Включим в подписку все документы, входящие в список последовательности партионного учета.

Последовательность ПартионныйУчет

2. Создадим в общем модуле процедуру для подписки. Для хранения таблицы движений документа используем встроенный реквизит документа ДополнительныеСвойства. Информация, помещаемая в эту структуру, хранится всё время "путешествия" объекта по подпискам. Запомним движения по партиям до проведения, а также границу последовательности, которая была на момент проведения документа.

Процедура ПередЗаписьюСписанияТоваров(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
	Если НЕ Источник.ЭтоНовый() Тогда
		Если Источник.Движения.Найти("ПартииТоваровНаСкладах") <> Неопределено Тогда
			ТаблицаДвиженийПоПартиям = ВернутьТаблицуДвиженийПоПартиям(Источник.Ссылка);
			Источник.ДополнительныеСвойства.Вставить("ПартииДоПроведения", ТаблицаДвиженийПоПартиям);			
		КонецЕсли;
		ГраницаПартионногоУчета = Последовательности.ПартионныйУчет.ПолучитьГраницу();		
		Источник.ДополнительныеСвойства.Вставить("ГраницаПоследовательности", ГраницаПартионногоУчета);
	КонецЕсли;
КонецПроцедуры

Функция ВернутьТаблицуДвиженийПоПартиям(СсылкаНаОбъект) Экспорт
	Запрос = Новый Запрос("ВЫБРАТЬ РАЗРЕШЕННЫЕ
	                      |	ПартииТоваровНаСкладах.Номенклатура,
	                      |	ПартииТоваровНаСкладах.Склад,
	                      |	ПартииТоваровНаСкладах.ХарактеристикаНоменклатуры,
	                      |	ПартииТоваровНаСкладах.СерияНоменклатуры,
	                      |	ПартииТоваровНаСкладах.ДокументОприходования,
	                      |	ПартииТоваровНаСкладах.СтатусПартии,
	                      |	ПартииТоваровНаСкладах.Заказ,
	                      |	ПартииТоваровНаСкладах.Качество,
	                      |	ПартииТоваровНаСкладах.Количество,
	                      |	ПартииТоваровНаСкладах.Стоимость
	                      |ИЗ
	                      |	РегистрНакопления.ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах
	                      |ГДЕ
	                      |	ПартииТоваровНаСкладах.Регистратор = &Регистратор");
	Запрос.УстановитьПараметр("Регистратор", СсылкаНаОбъект);
	РезультатЗапроса = Запрос.Выполнить();
	Если РезультатЗапроса.Пустой() Тогда
		Возврат Новый ТаблицаЗначений;
	Иначе
		Возврат РезультатЗапроса.Выгрузить();
	КонецЕсли;
КонецФункции

Таблицу движений по партиям получили и запомнили. Теперь надо сравнить с тем, что получилось после проведения. Создаем еще одну подписку на событие ОбработкаПроведения, также включаем туда документы из последовательности партионного учета. Таблицу движения по партиям после проведения также поместим в ДополнительныеСвойства.

Процедура ОбработкаПроведенияДляКонтроляГП(Источник, Отказ, РежимПроведения) Экспорт
    ВыполнитьКонтрольГП(Источник);
КонецПроцедуры

Процедура ВыполнитьКонтрольГП(Источник) Экспорт
    Если Источник.Метаданные().Реквизиты.Найти("ОтражатьВУправленческомУчете") <> Неопределено Тогда
        Если НЕ Источник.ОтражатьВУправленческомУчете Тогда
            Возврат;
        КонецЕсли;
    КонецЕсли;
    Если НЕ Источник.ЭтоНовый() Тогда
        Если Источник.Движения.Найти("ПартииТоваровНаСкладах") <> Неопределено Тогда
            ПартииПослеПроведения = Источник.Движения.ПартииТоваровНаСкладах.Выгрузить();
            Если ПартииПослеПроведения.Количество() = 0 Тогда
                // для части документов партии появляются после записи объекта в базу
                ПартииПослеПроведения = ВернутьТаблицуДвиженийПоПартиям(Источник.Ссылка);
            КонецЕсли;
            Источник.ДополнительныеСвойства.Вставить("ПартииПослеПроведения", ПартииПослеПроведения);
            ОпределитьНеобходимостьВозвращенияГП(Источник);
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Ну и, наконец, анализ необходимости возвращения последовательности.

Функция ОпределитьНеобходимостьВозвращенияГП(Источник) Экспорт
    ДополнительныеСвойства = Источник.ДополнительныеСвойства;
    ГраницаПоследовательностиДокумента = Неопределено;
    ДополнительныеСвойства.Свойство("ГраницаПоследовательности", ГраницаПоследовательностиДокумента);
    Идентичность = УстановитьИдентичностьТаблицДвиженийПоПартиям(ДополнительныеСвойства);
    ГраницаПоследовательностиТекущая = Последовательности.ПартионныйУчет.ПолучитьГраницу();
    Если ГраницаПоследовательностиДокумента <> Неопределено Тогда
        Если Идентичность И ГраницаПоследовательностиТекущая.Дата < ГраницаПоследовательностиДокумента.Дата Тогда
            // если таблицы идентичны, но текущая граница сдвинулась назад - вернем обратно
            Последовательности.ПартионныйУчет.УстановитьГраницу(ГраницаПоследовательностиДокумента);
            ЗаписьЖурналаРегистрации("Контроль ГП", УровеньЖурналаРегистрации.Информация, Источник.Метаданные(), Источник.Ссылка, "Граница последовательности возвращена на " + Строка(ГраницаПоследовательностиДокумента.Дата) + ", документ: " + Строка(ГраницаПоследовательностиДокумента.Ссылка));
        КонецЕсли;
    КонецЕсли;
КонецФункции
 Функция УстановитьИдентичностьТаблицДвиженийПоПартиям(ДополнительныеСвойства) Экспорт
    Идентичность = Ложь;
    ПартииДоПроведения = Неопределено;
    ДополнительныеСвойства.Свойство("ПартииДоПроведения", ПартииДоПроведения);
    ПартииПослеПроведения = Неопределено;
    ДополнительныеСвойства.Свойство("ПартииПослеПроведения", ПартииПослеПроведения);
    Если СравнитьТаблицыЗначений(ПартииДоПроведения, ПартииПослеПроведения) Тогда
        Идентичность = Истина;
    Иначе
        Идентичность = Ложь;
    КонецЕсли;        
    Возврат Идентичность;
КонецФункции

Извлекли из ДополнительныеСвойства таблицы движений до и после, сравнили, определили необходимость возвращения последовательности и вернули при необходимости на место.

Механизм также полезен для ситуаций, когда пользователь открывает документ, проведенный задним числом, и вместо закрытия нажимает ОК. Граница улетает на "закрытый" таким образом документ.

Если еще углубляться в детали, в стандартной конфигурации граница последовательности сбрасывается программно кодом:

	Если мУдалятьДвижения Тогда
		ОбщегоНазначения.УдалитьДвиженияРегистратора(ЭтотОбъект, Отказ);
	КонецЕсли;

То есть при перепроведении независимо ни от чего движения документа очищаются и записываются заново. В более современных продвинутых конфигурациях вроде это уже не так, но на родной переписанной до почти неузнаваемости УТ 10.3 вопрос восстановления границы последовательности потребовал программного решения.

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Андрей Карпов (karpik666) 05.11.15 05:08
Код функции СравнитьТаблицыЗначений
// Функция сравнивает две таблицы значений на идентичность структуры и данных
//
// Параметры
//  ТаблицаЗначений1 - ТаблицаЗначений для сравнения
//  ТаблицаЗначений2 - ТаблицаЗначений для сравнения
//
// Возвращаемое значение:
//   Булево, идентичны или нет две таблицы
//
Функция СравнитьТаблицыЗначений(ТаблицаЗначений1, ТаблицаЗначений2)

	Если ТипЗнч(ТаблицаЗначений1) <> Тип("ТаблицаЗначений") ИЛИ ТипЗнч(ТаблицаЗначений2) <> Тип("ТаблицаЗначений") Тогда
		Возврат Ложь;
	КонецЕсли; 
	
	Если ТаблицаЗначений1.Количество() <> ТаблицаЗначений2.Количество() Тогда
		Возврат Ложь;
	КонецЕсли; 

	Если ТаблицаЗначений1.Колонки.Количество() <> ТаблицаЗначений2.Колонки.Количество() Тогда
		Возврат Ложь;
	КонецЕсли; 

	Для каждого Колонка Из ТаблицаЗначений1.Колонки Цикл
		
		Если ТаблицаЗначений2.Колонки.Найти(Колонка.Имя) = Неопределено Тогда
			Возврат Ложь;
		КонецЕсли;
		
		Для каждого СтрокаТаблицы Из ТаблицаЗначений1 Цикл
		
			Попытка
			
				Если СтрокаТаблицы[Колонка.Имя] <> ТаблицаЗначений2[ТаблицаЗначений1.Индекс(СтрокаТаблицы)][Колонка.Имя] Тогда
				
					Возврат Ложь;
				
				КонецЕсли;
			
			Исключение
				
				Возврат Ложь;
				
			КонецПопытки;
		
		КонецЦикла; 
	
	КонецЦикла; 
	
	Возврат Истина;
	
КонецФункции // СравнитьТаблицыЗначений()
...Показать Скрыть
2. Даниил Матвеев (cargobird) 05.11.15 07:04
(1) karpik666, спасибо, добавлю в статью.
3. Анянов Михаил (insurgut) 05.11.15 07:41
Увеличивая нагрузку на проведение документа уменьшаем (возможно даже кратно) нагрузку на перепроведение по партиям. Неплохой вариант! На какой версии конфигурации отлажен механизм? Работает ли он с корректировками записей регистров?
4. Даниил Матвеев (cargobird) 05.11.15 08:07
(3) insurgut, Управление Торговлей, ред. 10.3. Версия конфигурации более чем двухгодовалой давности, даже затрудняюсь определиться с номером. При этом добавлялись необходимые новые документы, вроде корректировок поступления и реализации.
Корректировка записей регистров при перезаписи без изменений движений в партионном учете границу не роняет (и вроде это штатное поведение, не помню чтобы что-то там трогал), поэтому по нему дорабатывать не понадобилось.
5. Светлана Юрусова (svilsa) 13.11.15 20:14
Супер! Спасибо, это то что нужно. Особенно, когда настроен обмен УТ-БП, и все документы. которые "провелись по партиям" зря хотят выгрузиться в бухгалтерию. Очень-очень пригодилась статья! С помощью метода статьи надеюсь снизить в разы количество документов обмена из-за того, что в "Отчетах о розничных продажах" месячной давности бухгалтер меняет вид кредита банка.
6. Даниил Матвеев (cargobird) 14.11.15 08:12
(5) svilsa, могу предложить еще одну оптимизацию работы, раз дорабатываете конфигурацию: если ввести дополнительный параметр сеанса типа Булево (назвать его, например, "ВосстановлениеПоследовательности"), перед восстановлением последовательности взвести его в Истина, и по Истина отключать работу этого механизма - последовательность будет восстанавливаться немного быстрее...
7. Светлана Юрусова (svilsa) 14.11.15 13:10
(6) cargobird, Спасибо, попробую.
8. Alexander F (lx@) 20.11.15 10:16
Мне показалось, что не хватает блокировок. Может получиться, что движения у документа остались, однако граница была сдвинута другим документом и посему возращать ее не надо.
9. Даниил Матвеев (cargobird) 20.11.15 12:13
(8) lx@, в УТ 10.3 механизм блокировок еще жесткий - при проведении блокируется вся таблица регистра целиком, поэтому пока проводится один документ, второй не проведется. Или дождется, или выпадет в транзакцию. То есть двигать границу может только один документ, который проводит и тот и двигает. Вроде так.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа