v8: Универсальный контроль остатков при проведении/отмены проведения документовПри разработке собственных документов товародвижения (или при доработке существующих), как правило, приходиться писать код для проверки остатков в регистрах накопления. Предлагается использовать три универсальных глобальных процедуры, которые гарантировано проверяют перерасход по регистрам. ПРИ ЭТОМ - ПРОВЕРКА НЕ ЗАВЯЗАНА НА СТРУКТУРУ И СОДЕРЖАНИЕ ТАБЛИЧНОЙ ЧАСТИ ДОКУМЕНТА!
Что надо сделать:
- Добавить прилогаемый ниже код в модуль приложения;
- Добавить немного кода в модуль набора записей регистров накопления, по которым неоходимо контролировать остатки.
Методика использования процедур очень проста и описана в комментарии текста программы | | Автор статьи: Nafanail | Редакторы: Михей, aa_214, Гений 1С Последняя редакция №9 от 30.01.08 | История URL: http://kb.mista.ru/article.php?id=609 | |
Ключевые слова: Документы, Контроль остатков
================ ЭТО НАДО ВСТАВИТЬ В МОДУЛЬ ПРИЛОЖЕНИЯ ===============================
Перем ПроводитьБезПроверки;
Функция глПроводитьБезПроверки(ИстинаЛожь = Неопределено) Экспорт
Если ИстинаЛожь <> Неопределено Тогда
ПроводитьБезПроверки = ИстинаЛожь;
КонецЕсли;
Возврат ПроводитьБезПроверки;
КонецФункции
Функция глПустаяИлиНеопределено(Ссылка) Экспорт
Если Ссылка = Неопределено ИЛИ Ссылка.Пустая() Тогда
Возврат Истина;
КонецЕсли;
Возврат Ложь;
КонецФункции
////////////////////////////////////////////////////////////////////////////////////////////////
// Процедуры проверки движений документов //
////////////////////////////////////////////////////////////////////////////////////////////////
// Суть проверки: Проведение, перепроведение и отмена проведения не должны приводить
// к возникновению отрицательного актуального остатка (ресурс Количество).
// Порядок использования процедур достаточно прост - в модуле объекта документа:
//
// Процедура ПередЗаписью(...)
// глЗапомнитьСтарыеДвижения(ЭтотОбъект [, Движения.<ИмяРегистраНакопления>]);
// КонецПроцедуры;
//
// Процедура ОбработкаПроведения(...)
// Смело делаем все необходимые движения
// и перед выходом из процедуры:
// глПроверкаПроведения(ЭтотОбъект, Отказ [, Движения.<ИмяРегистраНакопления>]);
// КонецПроцедуры;
//
// Процедура ОбработкаУдаленияПроведения(...)
// глПроверкаУдаленияПроведения(ЭтотОбъект, Отказ [, Движения.<ИмяРегистраНакопления>]);
// КонецПроцедуры;
//
// Если указан последний необязательный параметр - проверка будет производится только по указанному
// регистру движений. Иначе проверка будет производиться по всем регистрам движений.
// В модуле набора записей регистра накопления должны быть определены экспортируемые переменные
// и процедуры (см. далее). Если регистр не удовлетворяет указанным
// требования - проверка по этому регистру не будет производиться.
// ВНИМАНИЕ: Для коректной работы процедур не следует в ОбработкаПроведения() использовать
// Движения.<Имярегистра>.Записать() - система это сделет автоматически.
//
// Если все же пользователю крайне необходимо выполнить проведение документа:
// 1. в табло: глПроводитьБезПроверки(Истина)
// 2. проводим документ(ы)
// 3. в табло: глПроводитьБезПроверки(Ложь)
//
// Диагностика. Во время проверки при обнаружении перерасхода будет выдано в окно сообщений стандартная
// диагностика. Однако эту диагностику можно переопределить. Для этого необходимо в модуле объекта документа
// определить процедуру:
// Сообщить(НабЗаписей, стрТЗ, Остаток) Экспорт
// НабЗаписей - набор записей регистра нгакопления по которому обнаружен перерасход
// стрТЗ - строка таблицы значений со значениями измерений
// Остаток - прогнозируемый актуальный остаток в случае проведения/перепрведения/удаленияПроведения
Процедура глЗапомнитьСтарыеДвижения(Регистратор, НабЗаписей = Неопределено) Экспорт
Перем рег;
Если НабЗаписей <> Неопределено Тогда
НабЗаписей.ЗапомнитьСтарыеДвижения();
Иначе
Для Каждого рег Из Регистратор.Движения Цикл
Попытка
рег.ЗапомнитьСтарыеДвижения();
Исключение
Продолжить;
КонецПопытки;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Процедура глПроверкаПроведения(Регистратор, Отказ, НабЗаписей = Неопределено) Экспорт
Перем рег, стрСвертки;
Если глПроводитьБезПроверки() Тогда
Возврат;
КонецЕсли;
Если НабЗаписей <> Неопределено Тогда
стрСвертки = НабЗаписей.стрИзмерения + ",НомерСтроки";
ПроверкаПроведения(НабЗаписей, Отказ, стрСвертки, Регистратор);
Иначе
Для Каждого рег Из Регистратор.Движения Цикл
Попытка
стрСвертки = рег.стрИзмерения + ",НомерСтроки";
Исключение
Продолжить;
КонецПопытки;
ПроверкаПроведения(рег, Отказ, стрСвертки, Регистратор);
КонецЦикла;
КонецЕсли;
Конецпроцедуры
Процедура глПроверкаУдаленияПроведения(Регистратор, Отказ, НабЗаписей = Неопределено) Экспорт
Перем рег, стрСвертки;
Если глПроводитьБезПроверки() Тогда
Возврат;
КонецЕсли;
Если НабЗаписей <> Неопределено Тогда
стрСвертки = НабЗаписей.стрИзмерения + ",НомерСтроки";
ПроверкаУдаленияПроведения(НабЗаписей, Отказ, стрСвертки, Регистратор);
Иначе
Для Каждого рег Из Регистратор.Движения Цикл
Попытка
стрСвертки = рег.стрИзмерения + ",НомерСтроки";
Исключение
Продолжить;
КонецПопытки;
ПроверкаУдаленияПроведения(рег, Отказ, стрСвертки, Регистратор);
КонецЦикла;
КонецЕсли;
Конецпроцедуры
Процедура ПроверкаПроведения(НабЗаписей, Отказ, стрСвертки, Регистратор)
Перем тзНовДв, тзСтарДв;
тзНовДв = НабЗаписей.Выгрузить();
тзстарДв = НабЗаписей.тзСтарыеДвижения;
ПреобразоватьИСвернутьТаблицу(тзСтарДв, стрСвертки);
ПреобразоватьИСвернутьТаблицу(тзНовДв, стрСвертки);
ПодготовитьТаблицыДляАнализа(НабЗаписей.мИзмерения, тзСтарДв, тзНовДв);
Если БудетПерерасход(НабЗаписей, тзСтарДв, Регистратор) Тогда
Отказ = Истина;
КонецЕсли;
Если БудетПерерасход(НабЗаписей, тзНовДв, Регистратор) Тогда
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Процедура ПроверкаУдаленияПроведения(НабЗаписей, Отказ, стрСвертки, Регистратор)
Перем тзДв, стр;
НабЗаписей.Прочитать();
тзДв = НабЗаписей.Выгрузить();
ПреобразоватьИСвернутьТаблицу(тзДв, стрСвертки, -1);
Для Каждого Стр Из тзДв Цикл
стр.НомерСтроки = ?(стр.Количество < 0, 1, 0);
КонецЦикла;
Если БудетПерерасход(НабЗаписей, тзДв, Регистратор) Тогда
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Процедура ПреобразоватьИСвернутьТаблицу(тзДв, стрСвертки, множитель = 1)
Перем стр;
// Приведем все к виду движения Приход
Для Каждого стр ИЗ тзДв Цикл
стр.Количество = ?(стр.ВидДвижения = ВидДвиженияНакопления.Приход, стр.Количество, -стр.Количество) * множитель;
стр.НомерСтроки = 0; // это поле будем использовать для свих нужд
КонецЦикла;
// Свернем по измерениям
тзДв.Свернуть(стрСвертки, "Количество");
Конецпроцедуры
Процедура ПодготовитьТаблицыДляАнализа(мИзмерения, тзСтарыеДвижения, тзНовыеДвижения)
Перем стр, стрС, изм, двНайдено;
// При отсутствии старых движений проверять будем только расход
Если тзСтарыеДвижения.Количество() = 0 Тогда
Для Каждого стр ИЗ тзНовыеДвижения Цикл
стр.НомерСтроки = ?(стр.Количество < 0, 1, 0) // метим строки с расходом
КонецЦикла;
Возврат;
КонецЕсли;
// Более сложный случай - надо сопоставлять старые и новые движения
Для Каждого стр ИЗ тзНовыеДвижения Цикл
Если стр.Количество < 0 Тогда
стр.НомерСтроки = 1; // расход в любом случае надо контролировать
Продолжить;
КонецЕсли;
// приход надо сопоставить со старым приходом
Для Каждого стрС ИЗ тзСтарыеДвижения Цикл
Если стрС.НомерСтроки = 0 И стрС.Количество > 0 Тогда
// должно быть совпадение по всем измерениям
двНайдено = Истина;
Для Каждого изм Из мИзмерения Цикл
Если стр[изм] = стрС[изм] Тогда
Продолжить;
Иначе
двНайдено = Ложь;
Прервать;
КонецЕсли;
КонецЦикла;
Если двНайдено Тогда
стрС.НомерСтроки = 2; // выключаем из дальнейшего анализа
Если стр.Количество < стрС.Количество Тогда
стр.НомерСтроки = 1; // новый приход меньше старого - надо контролировать
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
// второй проход по старым движениям - пометим приход
Для Каждого стр ИЗ тзСтарыеДвижения Цикл
Если стр.НомерСтроки = 0 И стр.Количество > 0 Тогда
стр.НомерСтроки = 1;
стр.Количество = 0; // количество нам не нужно будет
КонецЕсли
КонецЦикла;
КонецПроцедуры
Функция БудетПерерасход(НабЗаписей, тзДвижения, Регистратор)
Перем стр, рез, отб, изм, регистр, СинонимРегистра;
рез = Ложь;
СинонимРегистра = НабЗаписей.Метаданные().Синоним;
регистр = РегистрыНакопления[НабЗаписей.Метаданные().Имя];
отб = Новый Структура(НабЗаписей.стрИзмерения);
Для Каждого стр ИЗ тзДвижения Цикл
Если стр.НомерСтроки = 1 Тогда
Для Каждого изм ИЗ НабЗаписей.мИзмерения Цикл
отб[изм] = стр[изм];
КонецЦикла;
кол = регистр.Остатки(, отб, НабЗаписей.стрИзмерения, "Количество");
кол = ?(кол.Количество() = 0, 0, кол[0].Количество);
кол = кол + стр.Количество;
Если кол >= 0 Тогда
Продолжить;
КонецЕсли;
// будем выдавать стандартную диагностику, если в регистраторе не переопределена онная
рез = Истина;
Попытка
Регистратор.Сообщить(набЗаписей, стр, кол);
Исключение
Сообщить("Перерасход остатка (" + Строка(кол) + ") по регистру '" + СинонимРегистра +"':", СтатусСообщения.Внимание);
Для Каждого изм ИЗ НабЗаписей.мИзмерения Цикл
Если НЕ глПустаяИлиНеопределено(стр[изм]) Тогда
Сообщить(" - " + изм + ": " + Строка(стр[изм]));
КонецЕсли;
КонецЦикла
КонецПопытки;
КонецЕсли;
КонецЦикла;
Возврат рез;
КонецФункции
ПроводитьБезПроверки = Ложь;
================= ЭТОТ КОД НАДО ВСТАВИТЬ В МОДУЛЬ НАБОРА ЗАПИСИ РЕГИСТРА НАКОПЛЕНИЯ ========
============ ПРОВЕРКА БУДЕТ ПРОИЗВОДИТСЯ ТОЛЬКО ПО РЕГИСТРАМ, В КОТОРЫЕ ДОБАВЛЕН ЭТОТ КОД ===
Перем тзСтарыеДвижения Экспорт; // Таблица значений для сохранения старых движений
Перем мИзмерения Экспорт; // Массив с именами измерений
Перем стрИзмерения Экспорт; // Имена измерений через запятую
Процедура ЗапомнитьСтарыеДвижения() Экспорт
ЭтотОбъект.Прочитать();
тзСтарыеДвижения = ЭтотОбъект.Выгрузить();
КонецПроцедуры;
// Инициализация переменных
стрИзмерения = "";
мИзмерения = Новый Массив();
Для Каждого стр ИЗ ЭтотОбъект.Метаданные().Измерения Цикл
мИзмерения.Добавить(стр.Имя);
стрИзмерения = стрИзмерения + "," + стр.Имя;
КонецЦикла;
стрИзмерения = Сред(стрИзмерения, 2);
Михей:
медленно и неэффективно. Контроль производится при соединении таблицы остатков и табличной части документа.
Nafanail:
1. На счет медленно - может быть, у кого проблемы с нормальным компьютером/сервером.
2. Неэффективно - что имеется ввиду? Проверка гарантировано не даст актульным остаткам в минус уйти - что еще может быть эффективней?
3. Табличная часть документа вообще никак не участвует. Михей! - будь внимательней....
|