Книга знаний

1С:Предприятие / v8 / Объекты конфигурации / Документы

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. Табличная часть документа вообще никак не участвует. Михей! - будь внимательней....

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2024 | Mista.ru

Яндекс.Метрика