Методика оперативного проведения и управляемые блокировки

26.07.13

Разработка - Механизмы платформы 1С

Несмотря на то, что у меня уже есть статья посвященная механизму оперативного проведения, очень часто, нет не так, очень-очень-очень часто спрашивают, что это такое. Я подумал и решил написать ну очень-очень-очень подробную статью про методику оперативного проведения и управляемые блокировки. А без управляемых блокировок, собственно, методику использовать бессмысленно.

Скачать файлы

Наименование Файл Версия Размер
Выгрузка информационной базы с примером
.dt 29,33Kb
46
.dt 29,33Kb 46 Скачать

 

Введение.

Несмотря на то, что у меня уже есть статья, посвященная механизму оперативного проведения, очень часто, нет не так, очень-очень-очень часто спрашивают, что это такое.

Я подумал и решил написать ну очень-очень-очень подробную статью про методику оперативного проведения и управляемые блокировки. А без управляемых блокировок, собственно, методику использовать бессмысленно.
 

При написании статьи демопример я вводил на платформе 8.3.3.641. Всю задачу создавал на пустой базе.
 

Постановка задачи.

Необходимо автоматизировать две операции: покупка и продажа товаров. При продаже товаров необходимо контролировать наличие товаров в остатках предприятия и рассчитывать себестоимость списания товаров по методу FIFO.
 

Начнем...

Структура метаданных:

  • Справочник: Товары
  • Документы: Приходная и Расходная с табличными частями "Список товаров" (товар, количество, сумма).
  • Регистры:
    • ОстаткиТоваров, в регистре одно измерение: Товар, и один ресурс: Количество. Назначение регистра - быстрое принятие решения, можно ли проводить документ.
    • СтоимостьТоваров, в регистре два измерения: Товар, Партия, и два ресурса: Количество, Стоимость. Назначение регистра: хранение информации о стоимости остатков товаров в разрезе партий.

Описывать проведение документа "Приходная" не буду, там все очевидно, все можно конструктором сделать.
 

В расходной опишем проведение по регистру ОстаткиТоваров...
 

Методика оперативного проведения.

Под термином "Методика оперативного проведения" я понимаю следующее: при проведении документа, вместо того, чтобы, сформировать запрос и понять хватает ли товаров или нет, мы сначала сформируем движения, а затем уже проверим, не ушли ли мы в минус? Подробнее с плюсами и минусами этой технологии можно познакомиться в моей прошлой статье.

Опишем формирование движений в документе "Расходная":

 

Процедура ОбработкаПроведения(Отказ, Режим)

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

 

Прокомментируем текст модуля:

1. Устанавливаем маркер необходимости записи движений. Это необходимо делать в том случае, если у документа установлено свойство "Записывать выбранные".

 

 

Что дает нам это свойство?

Интересную фишку, раньше мы могли принудительно записать движения документа (наборы данных), но при окончании транзакции проведения наборы данных записывались еще раз. Теперь мы вольны в выборе, что и когда нужно записывать. Маркер записи автоматически снимается при записи набора, тем самым, при окончании транзакции проведения, набор записей еще раз записываться не будет. К чему это приведет? Правильно, к уменьшению блокировок таблиц и к сокращению времени транзакций при проведении.

2. Очищаем движения. Зачем? Все дело в еще одном новом свойстве документов "Удалять движения".

 

 

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

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

И тут обычно я слышу: "Так и всегда можно было "Не очищать автоматически"! Ага, можно было, но в каждом документе приходилось описывать очистку каждого набора записей при отмене проведения... Удобно...

Короче, 1С пошла на поводу у здравого смысла и сделала новое свойство "Удалять автоматически при отмене проведения", Это гарантирует нам, что все движения документа будут очищены в том случае если у документа отработает событие "ОтменаПроведения".

Теперь вопрос, а зачем мы в модуле написали "Очистить()", тут все дело в том, что теперь у нас движения автоматом не очищаются... НО! При работе с управляемыми формами копия объекта БД может не загрузить старые движения, к примеру, зависит это и от свойства данных формы "Использовать всегда".

 

 

К примеру, в зависимости от этого свойства, движения документа будут прочитаны (если галка стоит) при открытии формы или нет. А если даже галка не установлена, и в форме отображаются движения, то движения будут прочитаны при открытии формы.

В обычных формах движения будут однозначно прочитаны при открытии формы.

А если движения прочитаны, то наборы записей в свойстве документа "Движения" не пустые, и добавление при проведении новых движений, как правило приводит к дублям движений.

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

Кстати, если свойство конфигурации "Основной режим запуска" будет установлено в значение "Обычное приложение", то такую строку "Движения.ИмяРегистра.Очистить()" будет писать и сам конструктор формирования движений.

 

 

3. Итак, движения сформировали, самое время их записать. Тут есть два варианта. либо Движения.Записать();  либо Движения.ОстаткиТоваров.Записать(); В чем разница и что выбрать?

Начнем с последнего: Движения.ОстаткиТоваров.Записать()Этот способ безусловно запишет данные в регистр накопления. Но при этом флаг "Записывать" у набора записей снят не будет. Но это ерунда, главное тут то, что при большом количестве наборов записей у документа нам придется самостоятельно контролировать что в каком порядке в базу пишется, это может (да что там "может", точно скажется) негативно сказаться на проблеме взаимных блокировок (DeadLock), когда одна транзакция заблокирует таблицу А и будет ждать освобождения таблицы Б, а другая транзакция будет вести себя строго наоборот. 

теперь посмотрим как работает метод Движения.Записать()Метод записывает только те движения документа у которых установлен флаг "Записывать", при этом флаг в итоге снимается, что не приводит к повторной записи движений по окончании транзакции проведения. И главное, Движения.Записать()всегда записывают движения в том порядке в котором таблицы указаны в дереве метаданных, что на порядок уменьшает шансы взаимных блокировок, ведь все транзакции в одинаковом порядке блокируют таблицы.

Теперь надеюсь очевидно, выбираем метод Движения.Записать();

Запишем движения и проверим остатки в регистре.

 

//3
Движения.Записать();

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

//5
Запрос.УстановитьПараметр("ТочкаИтогов", Новый Граница(МоментВремени(), ВидГраницы.Включая));

ПакетРезультатов = Запрос.ВыполнитьПакет();
РезультатЗапроса = ПакетРезультатов[1];
Если НЕ РезультатЗапроса.Пустой() Тогда
    //6
    Отказ = Истина;
    
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Мало товара " + Выборка.ТоварПредставление + " нужно еще " + (-Выборка.КоличествоОстаток);
        Сообщение.Сообщить();
    КонецЦикла;
КонецЕсли;

Если Отказ Тогда
    Возврат;
КонецЕсли;

 

Комментарии к модулю:

4. Привязываем к запросу менеджер временных таблиц. Это позволит использовать временные таблицы позже для проведения по регистру с партиями товаров.

В запросе выбираем из табличной части документа товары и количество, группируем все и индексируем по полю Товар. Это, в общем случае, благоприятно скажется на быстродействии запроса.

5. ТочкаИтогов, здесь мы используем объект Граница. Так как передав в запрос просто "МоментВремени()" мы получили бы остатки на начало проведения документа, то есть не включили бы в расчет итогов только что сформированные движения документа. А нам нужно понять, что произошло с итогами после проведения документа.
 

В целом все.
 

Мы используем новую методику проведения документа. Сначала записали данные в регистр, а потом через кэш транзакции смотрим не ушли ли мы в минус. Если ушли - транзакцию отказываем.
 

Но это далеко не все. Назревает вопрос о "грязном чтении".

 

Грязное чтение.

Что это такое?

Я не буду вдаваться в теорию (в отличии от вас, вам я советую в теорию копнуть, лишним не будет), поясню на примере:

Проводим две расходных. И в одной и в другой продаем 5 ложек. А в остатках всего 6. Так получилось, что продавать ложки мы умудрились в один и тот-же момент времени. Что произойдет?

Первая накладная сформирует движения и начнет читать запрос по остаткам через кэш собственной транзакции, что, собственно, будет делать и вторая накладная. Но ложек-то всего 6, а в сумме две накладные продадут 10... Потому-что не знают о том, что данные в таблицах уже не актуальны...

Что делать? 

Надо заблокировать данные от параллельного чтения.

В нашем примере это сделать очень просто. У набора записей есть свойство "БлокироватьДляИзменения". Это, как и свойство "Записывать", лишь маркер указывающий, что необходимо установить блокировку на те записи, которые были сформированы в наборе записей.

Когда устанавливать свойство "БлокироватьДляИзменения"? Не важно, главное до самой записи данных.

Когда произойдет блокировка записей? В момент записи данных.

Что означает эта блокировка?

Никто параллельно с нами читать данные из регистра по тем товарам, движения по которым были сформированы, не сможет.

Когда блокировка будет снята?

При окончании транзакции в которой она началась, в нашем примере, при завершении проведения документа.
 

Установим блокировку:

 

//2
Движения.ОстаткиТоваров.Очистить();

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

//7 Движения.ОстаткиТоваров.БлокироватьДляИзменения = Истина;

//3
Движения.Записать();

 

Казалось бы все, а давайте проверим какие свойства нужно установить у объектов, для  того, чтобы такая блокировка не просто работала, а еще и блокировала только те записи по которым были сформированы движения...

 

А. Свойство конфигурации "Режим управления блокировкой данных в транзакции" -  если, "Управляемый", то все хорошо. 

Если "Автоматический", то системе глубоко фиолетово на то что мы с вами написали. В нашем частном случае, блокировка в режиме "Автоматический" не будет установлена.

Если "и то и другое" то важно проверить настройки объектов. Помните, что вид транзакции наследуется всеми вложенными транзакциями. То есть если документ записывается в автоматической транзакции, а движения делает по регистрам с управляемой... То система немного расстроится и вывалится с ошибкой.
 

Б. Так как блокируем мы не весь регистр, за что нам отдельное спасибо, а только те данные по которым мы сформировали движения, за что спасибо ребятам из 1С, то важно установить свойство регистра "Разрешить разделение итогов".

 

 

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

Вот теперь точно с регистром "ОстаткиТоваров" все. Займемся себестоимостью.
 

Партионное списание.

При списании партий из регистра "СтоимостьТоваров" использовать методику оперативного проведения мы не можем. Почему? А для того, чтобы списать партии нужно узнать какие конкретно, то есть нам нужно сначала прочитать данные из регистра, а потом уже формировать движения.

 

Если Отказ Тогда
    Возврат;
КонецЕсли;

//7
Запрос.Текст = "ВЫБРАТЬ
    | ДокТЧ.Товар КАК Товар,
    | ДокТЧ.Количество КАК Количество,
    | СтоимостьТоваров.Партия,
    | СтоимостьТоваров.КоличествоОстаток,
    | СтоимостьТоваров.СтоимостьОстаток
    |ИЗ
    | ДокТЧ КАК ДокТЧ
    | ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СтоимостьТоваров.Остатки(
    | &ТочкаИтоговДляСебестоимости,
    | Товар В
    | (ВЫБРАТЬ
    | ДокТЧ.Товар
    | ИЗ
    | ДокТЧ КАК ДокТЧ)) КАК СтоимостьТоваров
    | ПО ДокТЧ.Товар = СтоимостьТоваров.Товар
    |
    |УПОРЯДОЧИТЬ ПО
    | СтоимостьТоваров.Партия.МоментВремени
    |ИТОГИ
    | МИНИМУМ(Количество)
    |ПО
    | Товар";

//8
Запрос.УстановитьПараметр("ТочкаИтоговДляСебестоимости", МоментВремени());

//9
Движения.СтоимостьТоваров.Очистить();

//10
РезультатЗапроса = Запрос.Выполнить();
ВыборкаТовар = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

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

//11
Движения.СтоимостьТоваров.Записывать = Истина;

 

Комментарии к модулю:

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

8. Устанавливаем МоментВремени() как точку расчета итогов таблицы остатков. Как было описано выше, по умолчанию эта точка (то бишь старые движения, которые мог бы сформировать этот документ) в расчет итогов не попадут. Но тут есть одна хитрость к которой мы вернемся немного позже.

9. На всякий случай очистим движения документа, вдруг они были прочитаны.

10. Опишем списание по партиям.

11. Установим маркер необходимости записи данных по регистру "СтоимостьТоваров". 

Когда транзакция проведения будет завершена система самостоятельно запишет движения в базу.
 

Почти все, остались блокировки.
 

Управляемые блокировки.

По регистру "СтоимостьТоваров" воспользоваться флагом "БлокироватьДляИзменения" нам не удастся. Почему? Да потому-что блокировка с этим флагом происходит в момент записи данных набора записей, а нам блокировку нужно установить еще перед запросом, дабы не допустить параллельного чтения данных.
 

По этому, для установки блокировки будем использовать объект "БлокировкаДанных".

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

 

Если Отказ Тогда
    Возврат;
КонецЕсли;

//12
Блокировка = Новый БлокировкаДанных;
//13
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.СтоимостьТоваров");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
//14
ЭлементБлокировки.ИсточникДанных = ПакетРезультатов[2];
//15
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
Блокировка.Заблокировать();

//7
Запрос.Текст = "ВЫБРАТЬ

 

Перед нашим вторым текстом запроса опишем блокировку.

12. Создаем объект.

13. Добавляем в объект новую строку с описанием блокируемой таблицы.

14. Для того чтобы не блокировать весь регистр, установим фильтр по товарам, данные о том по каким товарам необходимо заблокировать таблицы хранятся у нас в результате первого запроса, из пакета результатов достаем последний. Можно было и саму табличную часть документа привязать к источнику данных, но тогда система еще раз строила бы запрос к табличной части документа, а так мы все данные уже получили, сгруппировали.

15. Так как в результате запроса поле содержащее список с товарами может называться не так как поле в регистре, описываем соответствие.
 

Установили блокировку. Теперь параллельно с нами никто не прочитает остатки по указанным товарам из регистра "СтоимостьТоваров". Обращаю внимание, так как мы не знаем заранее какие партии будут выбраны запросом, то фильтр по ним установить мы не можем и блокируем все партии. Чем меньше фильтров описано, тем больше данных блокируется.
 

И вот тут казалось бы все, а нет.
 

Проблема оперативного проведения.

Если сейчас попробовать провести документ оперативно 2 раза, указывая последние товары в регистре, то система вылетит с ошибкой. Почему?

При оперативном проведении, несмотря на то, что, мы указали в параметре второго запроса "МоментВремени()" и он вроде бы не должен включить старые движения документа, система рассчитает остатки на оперативную отметку времени, то есть старые движения будут включены в расчет итогов. Да, да... Вот такая "фишка".

Что делать? Отследим оперативное проведение и очистим старые движения.

Перед описание второго запроса вставим такой код:

 

//16
Если Режим = РежимПроведенияДокумента.Оперативный Тогда
    Движения.СтоимостьТоваров.Очистить();
    //17
    Движения.СтоимостьТоваров.БлокироватьДляИзменения = Истина;
    Движения.СтоимостьТоваров.Записать();
КонецЕсли;

//7
Запрос.Текст = "ВЫБРАТЬ

 

16. Проверяем оперативно ли проводится документ.

17. Устанавливаем свойство "БлокироватьДляИзменения". Это позволит заблокировать от чтения те данные которые сейчас будут удалены из регистра, вдруг транзакция будет отменена, не хотим чтобы кто-либо вместе с нами работал с этими данными.

Подобное действие не помешает и при смене даты или времени документа, а не только при оперативном проведении.
 

Вот теперь все. Полный текст модуля документа "Расходная":

 

Процедура ОбработкаПроведения(Отказ, Режим)
    
    //1
    Движения.ОстаткиТоваров.Записывать = Истина;
    
    //2
    Движения.ОстаткиТоваров.Очистить();
    
    Для каждого Стр Из СписокТоваров Цикл
        Движение = Движения.ОстаткиТоваров.ДобавитьРасход();
        Движение.Период = Дата;
        Движение.Товар = Стр.Товар;
        Движение.Количество = Стр.Количество;
    КонецЦикла;
    
    //7
    Движения.ОстаткиТоваров.БлокироватьДляИзменения = Истина;
    
    //3
    Движения.Записать();
    
    Запрос = Новый Запрос;
    
    //4
    Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
    Запрос.Текст = "ВЫБРАТЬ
        | Док.Товар КАК Товар,
        | СУММА(Док.Количество) КАК Количество
        |ПОМЕСТИТЬ ДокТЧ
        |ИЗ
        | Документ.Расходная.СписокТоваров КАК Док
        |ГДЕ
        | Док.Ссылка = &Ссылка
        |
        |СГРУППИРОВАТЬ ПО
        | Док.Товар
        |
        |ИНДЕКСИРОВАТЬ ПО
        | Товар
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | Остатки.Товар.Представление КАК ТоварПредставление,
        | Остатки.КоличествоОстаток
        |ИЗ
        | РегистрНакопления.ОстаткиТоваров.Остатки(
        | &ТочкаИтогов,
        | Товар В
        | (ВЫБРАТЬ
        | ДокТЧ.Товар
        | ИЗ
        | ДокТЧ КАК ДокТЧ)) КАК Остатки
        |ГДЕ
        | Остатки.КоличествоОстаток < 0
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | ДокТЧ.Товар
        |ИЗ
        | ДокТЧ КАК ДокТЧ";
    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    
    //5
    Запрос.УстановитьПараметр("ТочкаИтогов", Новый Граница(МоментВремени(), ВидГраницы.Включая));
    
    ПакетРезультатов = Запрос.ВыполнитьПакет();
    РезультатЗапроса = ПакетРезультатов[1];
    
    Если НЕ РезультатЗапроса.Пустой() Тогда
        //6
        Отказ = Истина;
        
        Выборка = РезультатЗапроса.Выбрать();
        Пока Выборка.Следующий() Цикл
            Сообщение = Новый СообщениеПользователю;
            Сообщение.Текст = "Мало товара " + Выборка.ТоварПредставление + " нужно еще " + (-Выборка.КоличествоОстаток);
            Сообщение.Сообщить();
        КонецЦикла;
    КонецЕсли;
    
    Если Отказ Тогда
        Возврат;
    КонецЕсли;
    
    //12
    Блокировка = Новый БлокировкаДанных;
    //13
    ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.СтоимостьТоваров");
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    //14
    ЭлементБлокировки.ИсточникДанных = ПакетРезультатов[2];
    //15
    ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
    Блокировка.Заблокировать();
    
    //16
    Если Режим = РежимПроведенияДокумента.Оперативный Тогда
        Движения.СтоимостьТоваров.Очистить();
        //17
        Движения.СтоимостьТоваров.БлокироватьДляИзменения = Истина;
        движения.СтоимостьТоваров.Записать();
    КонецЕсли;
    
    //7
    Запрос.Текст = "ВЫБРАТЬ
        | ДокТЧ.Товар КАК Товар,
        | ДокТЧ.Количество КАК Количество,
        | СтоимостьТоваров.Партия,
        | СтоимостьТоваров.КоличествоОстаток,
        | СтоимостьТоваров.СтоимостьОстаток
        |ИЗ
        | ДокТЧ КАК ДокТЧ
        | ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СтоимостьТоваров.Остатки(
        | &ТочкаИтоговДляСебестоимости,
        | Товар В
        | (ВЫБРАТЬ
        | ДокТЧ.Товар
        | ИЗ
        | ДокТЧ КАК ДокТЧ)) КАК СтоимостьТоваров
        | ПО ДокТЧ.Товар = СтоимостьТоваров.Товар
        |
        |УПОРЯДОЧИТЬ ПО
        | СтоимостьТоваров.Партия.МоментВремени
        |ИТОГИ
        | МИНИМУМ(Количество)
        |ПО
        | Товар";
    
    //8
    Запрос.УстановитьПараметр("ТочкаИтоговДляСебестоимости", МоментВремени());
    
    //9
    Движения.СтоимостьТоваров.Очистить();
    
    //10
    РезультатЗапроса = Запрос.Выполнить();
    ВыборкаТовар = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    
    Пока ВыборкаТовар.Следующий() Цикл
        
        ОсталосьСписать = ВыборкаТовар.Количество;
        
        ВыборкаПартия = ВыборкаТовар.Выбрать();
        Пока ВыборкаПартия.Следующий() И ОсталосьСписать <> 0 Цикл
            
            Списать = МИН(ОсталосьСписать, ВыборкаПартия.КоличествоОстаток);
            
            Движение = Движения.СтоимостьТоваров.ДобавитьРасход();
            Движение.Период = Дата;
            Движение.Товар = ВыборкаПартия.Товар;
            Движение.Партия = ВыборкаПартия.Партия;
            Движение.Количество = Списать;
            Движение.Стоимость = Списать / ВыборкаПартия.КоличествоОстаток * ВыборкаПартия.СтоимостьОстаток;
            
        КонецЦикла;
        
    КонецЦикла;
    
    //11
    Движения.СтоимостьТоваров.Записывать = Истина;
КонецПроцедуры

 

Надеюсь после этой статьи большая часть вопросов о "новой" методике проведения и о том почему нельзя повсеместно писать "БлокироватьДляИзменения = Истина" будут закрыты.
 

Актуальная версия статьи располагается тут.

Контакты автора на сайте chistov.pro

См. также

Поинтегрируем: сервисы интеграции – новый стандарт или просто коннектор?

Обмен между базами 1C Администрирование СУБД Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

В платформе 8.3.17 появился замечательный механизм «Сервисы интеграции». Многие считают, что это просто коннектор 1С:Шины. Так ли это?

11.03.2024    3564    dsdred    48    

66

Как готовить и есть массивы

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Все мы используем массивы в своем коде. Это один из первых объектов, который дают ученикам при прохождении обучения программированию. Но умеем ли мы ими пользоваться? В этой статье я хочу показать все методы массива, а также некоторые фишки в работе с массивами.

24.01.2024    5032    YA_418728146    25    

62

Планы обмена VS История данных

Обмен между базами 1C Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    6160    dsdred    36    

110

1С-ная магия

Механизмы платформы 1С Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    18196    SeiOkami    46    

116

Дефрагментация и реиндексация после перехода на платформу 8.3.22

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Начиная с версии платформы 8.3.22 1С снимает стандартные блокировки БД на уровне страниц. Делаем рабочий скрипт, как раньше.

14.09.2023    11766    human_new    27    

72

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    8555    YA_418728146    6    

139

Внешние компоненты Native API на языке Rust - Просто!

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Внешние компоненты для 1С можно разработывать очень просто, пользуясь всеми преимуществами языка Rust - от безопасности и кроссплатформенности до удобного менеджера библиотек.

20.08.2023    6195    sebekerga    54    

93

Все скопируем и вставим! (Буфер обмена в 1С 8.3.24)

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Рассмотрим новую возможность 8.3.24 и как её можно эффективно использовать

27.06.2023    15525    SeiOkami    31    

103
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
101. GROOVY 2503 31.07.13 19:06 Сейчас в теме
(99) kiruha, ага, только блокировку на них поставить не забудьте :)
100. kiruha 388 31.07.13 18:45 Сейчас в теме
Возможен даже вариант - данные по регистру не менялись.
Тогда в варианте без очистки платформа убедится что наборы совпадают и проведение будет практически моментальным (для больших наборов), если очистка была проведена - будет долгая запись (для больших наборов)
102. Andreynikus 1360 31.07.13 21:43 Сейчас в теме
Очень хорошая и полезная статья, большое вам за нее спасибо.

Но не могу пройти мимо и не добавить пару ложек дегтя, уж пардонте.

1. "... если документ записывается в автоматической транзакции, а движения делает по регистрам с управляемой... То система немного расстроится и вывалится с ошибкой."

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

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

Блокировка будет автоматически наложена только на записываемые данные и это никак не зависит от того включено ли разделение итогов или нет.
Объясните пожалуйста, что это за случаи при которых будет блокировка на всю таблицу в управляемом режиме?

3. И на последок почему-то здесь у автора путаница с понятиями "Грязное чтение" и "Неповторяющееся чтение", при уровне изоляции "READ COMMITTED" (который используется в упр. режиме) грязное чтение не возможно. На самом деле вы говорите про проблему "Неповторяющееся чтение"

Еще раз повторюсь что смысл статьи верный и в целом все правильно, но вот такие "детали" очень портят впечатление.
103. GROOVY 2503 31.07.13 21:50 Сейчас в теме
(102) and000, спасибо.

1. Это я тупо ошибся. Поправлю.
2. Тут я имею в виду таблицу итогов. Параллельное чтение мы блокируем, но при разделении итогов не блокируется параллельная запись по указанным позициям. Надо подумать как это сформулировать по-другому.
3. Отдельное спасибо.
104. Andreynikus 1360 31.07.13 23:54 Сейчас в теме
(103) со 2-мы пунктом не очень понятно.
"Параллельное чтение мы блокируем, но при разделении итогов не блокируется параллельная запись по указанным позициям"

Почему же не блокируется запись?
Если ставить "БлокироватьДляИзменения = Истина" то блокируется все и чтение и запись, по факту эта команда отключает разделение итогов пока не закончится запись.
105. GROOVY 2503 01.08.13 00:08 Сейчас в теме
(104) and000, "о факту эта команда отключает разделение итогов пока не закончится запись" кто это сказал?

Вот ссылка на партнерский форум: http://partners.v8.1c.ru/forum/thread.jsp?id=1122683

БлокироватьДляИзменения не выключает разделение итогов.
106. Andreynikus 1360 01.08.13 10:50 Сейчас в теме
(105)
"Кто это сказал?" это говорю я ну и Нуралиев Сергей тоже это говорит :) и проведенные вчера опыты

Возможно мы с вами по разному трактуем одно и тоже понятие.

"Будут заблокированы итоги по всем комбинациям измерений удаляемых запиесй регистра. Без учета разделителя."

Повторю еще раз, именно "Без учета разделителя", т.е. разделитель не работает, он выключен пока идет запись, и параллельная запись не возможна.

Одей говорит что "БлокироватьДляИзменения" не отключает свойство "Разрешить разделение итогов" и это правда, свойство остается, но на время записи разделитель выключается, еще раз повторюсь, только на время записи!

Вот еще одна ссылка на партренский форум http://partners.v8.1c.ru/forum/thread.jsp?id=739246#739246
Тут Нуралиев подробно отвечает на этот вопрос.

Вчера не поленился и провел опыт, он так же подтверждает что при установке БлокироватьДляИзменения в Истина параллельная запись не возможна, если поставить Ложь, все прекрасно записывается.
107. ZLENKO 398 01.08.13 11:38 Сейчас в теме
(106) and000, я в этом даже и не сомневался, что при "БлокироватьДляИзменения = Истина" во время записи вместо разделяемой накладывается эксклюзивная блокировка, соответственно, параллельная запись невозможна. Поэтому и говорил что не совсем понятен смысл этого свойства с практической точки зрения. Констатируем факт - новая методика контроля остатка несовместима с разделением итогов. Для регистров по которым контролируется остаток по новой методике разделение итогов проще отключить и не заморачиваться с установкой свойства БлокироватьДляИзменения.
108. Andreynikus 1360 01.08.13 11:55 Сейчас в теме
(107) ZLENKO.PRO,
Да, при контроле остатков разделение итогов теряет смысл, но насчет отключить разделение совсем надо смотреть в каждом конкретном случае. Ведь наверняка возможны ситуации, когда по одному регистру делают движения разные документы, и в каких-то случаях контроль остатков по этому регистру нужен, а в каких-то нет. Например, если при приходе контролировать остатки не обязательно, то можно (и нужно) включить разделение итогов и параллельно писать в регистр по одному и тому же набору измерений.
Вобщем возможность хорошая и правильная, но при контроле остатков выигрыша в производительности она не дает.
109. Gilev.Vyacheslav 1910 01.08.13 23:14 Сейчас в теме
тема не такая простая как кажется, некоторые вещи "додумать, а точнее предвидеть" можно, но мало вероятно, до сих пор ни кто не упомянул о взаимоблокировках в управляемом режиме, связанных с фактом включения разделения итогов :)
в реальной практике надо собирать статистику работы и смотреть на конкретный контекст, так как многие "потенциальные" проблемы у многих так и останутся потенциальными и они о них не узнают

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

надеюсь что со временем методики как минимум научаться "получать обратную связь" от истории исполнения и корректировать "по какой ветке кода идти в этих условиях"...
111. ZLENKO 398 02.08.13 12:11 Сейчас в теме
(109) Gilev.Vyacheslav, "до сих пор ни кто не упомянул о взаимоблокировках в управляемом режиме, связанных с фактом включения разделения итогов"
Так ведь наличие свойства "БлокироватьДляИзменения" как раз и связано с тем чтобы препятствовать взаимоблокировкам при включенном разделении итогов. Я наивно предполагал что участникам обсуждения это и так понятно :-)
110. GROOVY 2503 01.08.13 23:21 Сейчас в теме
Так... Надо найти время и с учетом критики актуализировать статью.

Думаю, неоценимый опыт получен мной :) Надо еще пару статей опубликовать :)))
amaksimov; smit1c; serg952050; charushkin; +4 Ответить
112. ZLENKO 398 02.08.13 12:40 Сейчас в теме
Я считаю что бороться с ожиданием на блокировках и взаимоблокировками надо в первую очередь уменьшением длительности транзакции и накладывать управляемые блокировки как можно позже к концу транзакции:
1) Оптимизируем код проведения документов - убираем "узкие места" в виде неоптимального кода;
2) Не удаляем существующие движения документа при перепроведении, т.к. это сразу же наложит блокировки в самом начале транзакции и они будут наложены до самого конца транзакции.
3) Не накладываем явно блокировки в коде - они не нужны, т.к. система сама заблокирует регистры при записи наборов.
4) Не записываем явно движения в коде - это может вызвать взаимоблокировки и преждевременное наложение блокировок, т.к. система сама запишет регистры в самом конце транзакции при проведении.
5) Используем "новую" схему контроля остатка.
6) Отключаем возможность разделение итогов по регистрам остатков и плана счетов чтобы предотвратить возможные взаимоблокировки. "Параллельность" записи мы повышаем уменьшением времени блокировки регистра.
7) Используем версионную СУБД (MS SQL в режиме блокировщика сам накладывает блокировки, которые нам не нужны и все наши труды по оптимизации уходят "в трубу").

Enjoy and Relax ;-)

P.S.: Подход реально опробован на базе УПП (~80 Гб, ~100 пользователей) - результат более чем положительный!
113. ZLENKO 398 05.08.13 16:15 Сейчас в теме
Интересующимся темой топика рекомендую к прочтению http://infostart.ru/public/196565/
114. утюгчеловек 38 09.08.13 19:49 Сейчас в теме
Не понятно зачем два раза очищать записи - //16 и //9?
115. charushkin 104 10.08.13 13:47 Сейчас в теме
(114) утюгчеловек, //16 нужно сделать, если дату документа двигают вперед (оперативное проведение - частный случай). В принципе, если //16 уже сделано, то //9 не нужно. Но вреда, я думаю, от этого особого нет.
Но если вам что-то смущает, можно написать примерно так:
Движения.СтоимостьТоваров.Очистить();
Если Режим = РежимПроведенияДокумента.Оперативный Тогда
// записать пустой набор надо только в случае сдвига даты даты документа вперед
  Движения.СтоимостьТоваров.БлокироватьДляИзменения = Истина;
  движения.СтоимостьТоваров.Записать();
 КонецЕсли; 
утюгчеловек; +1 Ответить
132. slazzy 42 03.12.13 21:54 Сейчас в теме
(115) hulio, что-то меня в последнее время озадачил вопрос. А если дата сместилась вперед, но при этом проведение всё равно осталось не оперативным. Как быть? Ну например изначально документ от 1-ого числа, сегодня 3-е число, а документу меняют дату на 2-ое. В итоге документ проводится неоперативно, но мы читаем неправильные остатки.
Я ничего не напутал?
133. GROOVY 2503 03.12.13 22:14 Сейчас в теме
(132) slazzy, Это все можно проверить с использованием свойства объекта "ДополнительныеСвойства". Статья не об этом.
116. sss999 48 30.09.13 14:35 Сейчас в теме
Тема блокировок нифига не раскрыта а еще более все запуталось из-за ZLENKO.PRO ,это человек по-видимому не отличает блокировать на чтение от блокировать на изменение..Регистры сами могут блокироваться на изменение а мы блокируем на чтение.
117. ZLENKO 398 02.10.13 18:25 Сейчас в теме
(116) Как раз мне все понятно... Повторюсь еще раз - я проблему избыточных блокировок в базе УПП решил :-)
Если Вам что то не понятно, то это не повод утверждать что кому то еще не понятно :-)
118. ZLENKO 398 02.10.13 18:40 Сейчас в теме
(116) Хотя по причине своей академичности, статья действительно больше запутывает чем объясняет как надо поступать на практике, т.е. в реальных конфигурациях. Я в принципе не использовал (и не рекомендую использовать) БлокироватьДляИзменения и не использовал явное (только автоматически накладываемые платформой при записи регистров) наложение блокировок в коде...
У меня нет цели кому то что то тут доказывать - я себе уже все доказал :-) Что будут стоить тысячи слов если вот оно стоит и работает ? Если есть вопросы - спрашивайте.
119. GROOVY 2503 02.10.13 18:48 Сейчас в теме
(118) Что мешает написать статью и вести предметное обсуждение?
121. ZLENKO 398 02.10.13 18:59 Сейчас в теме
(119) Мешает отсутствие мотивации для написания статьи :-) За скачивание файлов стартмани начисляются, а за статью только плюсики :-) Да и много ли людей рискнут существенно переписывать код проведения, например, в УПП :-)
123. hogik 443 02.10.13 21:34 Сейчас в теме
(121)
"За скачивание файлов стартмани начисляются, а за статью только плюсики"(с)
Владимир (ZLENKO.PRO).
Опять заблуждаетесь. :-)
http://infostart.ru/service/startmoney/
Пишите статью...
125. ZLENKO 398 02.10.13 22:19 Сейчас в теме
(123) Возможно в ближайшем будущем соберусь "препарировать" УТ 11.1 на платформе 8.3 на тему проведения и блокировок - тогда и напишу статью. Чисто теоретическую статью писать желания нет, а про УПП или УТ 10.3 на платформе 8.2 уже неинтересно писать.
127. ZLENKO 398 03.10.13 10:32 Сейчас в теме
(123) Формально неправ по поводу стартмани, но что такое 20-30 стартмани за статью в сравнении с 700 (скачиваний) стартмани, например, за мой отчет http://infostart.ru/public/117647/
120. ZLENKO 398 02.10.13 18:51 Сейчас в теме
(116) При установленном разделении итогов при записи регистров будет установлена разделяемая блокировка на запись регистра, а для того чтобы в этой же транзакции потом прочитать данные из регистра (идет обращение к итогам) нужна монопольная блокировка на чтение, которая не может быть наложена если есть еще разделяемые блокировки на запись в других транзакциях. Поэтому мы свойством БлокироватьДляИзменения запрещаем устанавливать разделяемую блокировку на запись по регистру и не даем писать в других транзакциях чтобы не возникало взаимоблокировок. Что тут непонятного ?
122. sss999 48 02.10.13 19:53 Сейчас в теме
Я говорил про чтение а не запись,про запись и так понятно что автоматом блокирует запись,а для того что бы не прочитали регистр при взаимном проведении,из другого чуть позже начавшего проведение документа нужна блокировка на чтение.И здесь не понятно про итоги и все такое.
124. ZLENKO 398 02.10.13 22:14 Сейчас в теме
(122) Ну если Вам нужна блокировка на чтение, то надо ее явно установить. Это тоже понятно.
126. ZLENKO 398 03.10.13 08:18 Сейчас в теме
Вопрос не в том заблуждаюсь я или нет, а в том готов ли мир понять и принять то что я написал выше. А прав я или нет покажет время. Я всего лишь предлагаю выбор - "красная таблетка или синяя" (с) Матрица
Готовы ли вы сделать выбор ?
128. hogik 443 03.10.13 18:35 Сейчас в теме
(126)
"готов ли мир понять и принять то что я написал выше"(с)
Владимир (ZLENKO.PRO).
Где написали? В (112) сообщении?
Там нет ничего нового. Очевидные лозунги. ;-)
Еще раз - пишите статью...
129. ZLENKO 398 04.10.13 08:27 Сейчас в теме
(128) Вы путаете лозунги и руководство к действию :-) Новое там только одно - все это вместе дает результат !
Каждый из пунктов по отдельности не дает практически ничего - что то улучшается но значимого эффекта не дает.

P.S.: По поводу нового - приходится повторяться... :-) Новую методику контроля остатков я предлагал еще в 2007 году (не претендую на авторство, но ветка по этому поводу на партнерском форуме 1С есть). Но тогда видимо мир еще не был готов :-)
130. maxx 991 13.11.13 17:28 Сейчас в теме
Конечно обсуждение ярое действительно всё запутывает.

Вопрос к автору. Ради интереса запустил глобальный поиск слово "БлокироватьДляИзменения" (явная установка блокировка на чтение) в УНФ ред.1.4, нет такого слово в конфигурации.

И что-то думать, либо разработчики считают, что можно для небольшой фирмой контролировать остатки без особой методики блокировок или разработчики считают, что и всё и так платформа нормально блокирует и никакого «грязное» чтение нет?
131. GROOVY 2503 13.11.13 20:17 Сейчас в теме
(130) maxx, это вопрос к разработчикам конфигурации. Я тут ни при чем.
135. ZLENKO 398 06.12.13 18:19 Сейчас в теме
(130) Разработчики типовых конфигураций обычно отвечают, что были приняты такие вот проектные решения... Правильные это решения или нет сказать невозможно, т.к. это не только субъективно, но даже зависит от точки зрения одного и того же человека. Я считаю что остатки нужно контролировать всегда - в прошлом, настоящем и будущем :-) Запрещать отрицательные остатки или нет - это уже надо решать в каждом конкретном случае, а контролировать (проверять и сообщать) нужно однозначно!
Про саму УНФ ничего сказать не могу - не "ковырял" :-)
134. zombi81 8 06.12.13 16:45 Сейчас в теме
Почему-то на SQL базе не работает.
Если убрать
// 3
Движения.Записать()
тогда можно провести второй документ по непересекающимся комбинациям измерений (если переписать момоент времени в параметре запроса как граница от МоментВремени() , исключая). Но если дату проведенного документа подвинуть вперед на день, тогда не хватает остатков, т к движения от ранее проведенного документа будут учитываться.
Собственно вопрос, есть ли рабочий вариант под SQL?

Вот такой код не работает (работает по вышепривеенной схеме).

Движения.ОстаткиТоваров.Очистить();
	Движения.Взаиморасчеты.Очистить();
	
	Движения.ОстаткиТоваров.Записывать = Истина;
	Движения.Взаиморасчеты.Записывать = Истина;
	
	
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиТоваров");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
	ЭлементБлокировки.ИсточникДанных = Товары;
	ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
	ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
	
	ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Взаиморасчеты");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
	ЭлементБлокировки.УстановитьЗначение("Контрагент", Контрагент);
	
	
	Блокировка.Заблокировать();
	
	
	Движения.Записать(); // удаление старых движений ранее проведенного документа
	
	ВидСписания = РегистрыСведений.УчПолитика.ПолучитьПоследнее(Дата).ВидСписания;
	
	Запрос = Новый Запрос;
	
	Запрос.Текст = "ВЫБРАТЬ
	|	РасходнаяТовары.Номенклатура КАК Номенклатура,
	|	СУММА(РасходнаяТовары.Количество) КАК ТребКоличество,
	|	СУММА(РасходнаяТовары.Сумма) КАК Сумма
	|ПОМЕСТИТЬ ВТ_Потребность
	|ИЗ
	|	Документ.Расходная.Товары КАК РасходнаяТовары
	|ГДЕ
	|	РасходнаяТовары.Ссылка = &Ссылка
	|
	|СГРУППИРОВАТЬ ПО
	|	РасходнаяТовары.Номенклатура
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Номенклатура
	|;
	|
	| X 
	|ВЫБРАТЬ
	|	ВТ_Потребность.Номенклатура КАК Номенклатура,
	|	ПРЕДСТАВЛЕНИЕ(ВТ_Потребность.Номенклатура) КАК ТоварПредставление,
	|	ВТ_Потребность.ТребКоличество КАК ТребКоличество,
	|	ВТ_Потребность.Сумма КАК СуммаПродажи,
	|	ОстаткиТоваровОстатки.Партия КАК Партия,
	|	ЕСТЬNULL(ОстаткиТоваровОстатки.КоличествоОстаток, 0) КАК КолвоЕсть,
	|	ЕСТЬNULL(ОстаткиТоваровОстатки.СуммаОстаток, 0) КАК СуммаОстаток
	|ИЗ
	|	ВТ_Потребность КАК ВТ_Потребность
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки(
	|				&МоментВремени,
	|				Номенклатура В
	|						(ВЫБРАТЬ
	|							Т.Номенклатура
	|						ИЗ
	|							ВТ_Потребность КАК Т)
	|					И Склад = &Склад) КАК ОстаткиТоваровОстатки
	|		ПО ВТ_Потребность.Номенклатура = ОстаткиТоваровОстатки.Номенклатура
	|
	|УПОРЯДОЧИТЬ ПО
	|	Номенклатура,
	|	Партия  " + ?(ВидСписания = Перечисления.ВидыСписанияТМЦ.ЛИФО, "Убыв", "") + " 
	|ИТОГИ
	|	МИНИМУМ(ТребКоличество),
	|	СУММА(КолвоЕсть), МИНИМУМ(СуммаПродажи)
	|ПО
	|	Номенклатура";
	
	
	Запрос.УстановитьПараметр("Склад", Склад);
	Запрос.УстановитьПараметр("Ссылка", Ссылка);
	Запрос.УстановитьПараметр("МоментВремени", ?(РежимПроведения = РежимПроведенияДокумента.Оперативный, Неопределено, МоментВремени()));
	
	
	РЗ  = Запрос.Выполнить();
	
	ОбщаяСуммаПродажи = 0;
	
	Выборка = РЗ.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Номенклатура");
	Пока Выборка.Следующий() Цикл
		Если Выборка.ТребКоличество > Выборка.КолвоЕсть Тогда
			Сообщить("Не хватает " + Выборка.ТоварПредставление + ". Есть " + Выборка.КолвоЕсть + ", нужно " + Выборка.ТребКоличество + ".");
			Отказ = Истина; 
			Продолжить;
		КонецЕсли; 
		
		
		ОбщаяСуммаПродажи = ОбщаяСуммаПродажи + Выборка.СуммаПродажи;
		
		НеобходимоСписать = Выборка.ТребКоличество;
		
		ВыборкаДетали = Выборка.Выбрать();
		
		Пока ВыборкаДетали.Следующий() Цикл
			
			
			Движение = Движения.ОстаткиТоваров.Добавить();
			Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
			Движение.Период = Дата;
			Движение.Номенклатура = ВыборкаДетали.Номенклатура;
			Движение.Склад = Склад;
			Движение.Партия = ВыборкаДетали.Партия;
			Движение.Количество = Мин(ВыборкаДетали.КолвоЕсть, НеобходимоСписать);
			
			Если Движение.Количество = ВыборкаДетали.КолвоЕсть Тогда  			
				Движение.Сумма = ВыборкаДетали.СуммаОстаток;    			
			Иначе
				Движение.Сумма = ?(ВыборкаДетали.КолвоЕсть <> 0, ВыборкаДетали.СуммаОстаток * Движение.Количество / ВыборкаДетали.КолвоЕсть, 0);
			КонецЕсли;
			
			НеобходимоСписать = НеобходимоСписать - Движение.Количество;
			
			Если НеобходимоСписать = 0 Тогда
				Прервать;
			КонецЕсли;	
		КонецЦикла;
		
	КонецЦикла; 
	
	
	Запрос.Текст = 
	"ВЫБРАТЬ
	|	ВзаиморасчетыОбороты.СуммаПриход КАК Сумма
	|ИЗ
	|	РегистрНакопления.Взаиморасчеты.Обороты(&НачПериода, &КонПериода, , Контрагент = &Контрагент) КАК ВзаиморасчетыОбороты";
	
	Запрос.УстановитьПараметр("НачПериода", НачалоМесяца(ДобавитьМесяц(Дата, -1)));	
	Запрос.УстановитьПараметр("КонПериода", Новый Граница(КонецМесяца(ДобавитьМесяц(Дата, -1)), ВидГраницы.Включая));
	Запрос.УстановитьПараметр("Контрагент", Контрагент);
	
	Результат = Запрос.Выполнить();
	
	ВыборкаДетальныеЗаписи = Результат.Выбрать();
	
	ПроцентСкидки = 0;
	Если ВыборкаДетальныеЗаписи.Следующий() Тогда
		СуммаЗакупкиПредМесяц = ВыборкаДетальныеЗаписи.Сумма;
		
		Если СуммаЗакупкиПредМесяц >= 10000 Тогда
			ПроцентСкидки = 15;	
		ИначеЕсли СуммаЗакупкиПредМесяц >= 5000 Тогда
			ПроцентСкидки = 10;	
		ИначеЕсли СуммаЗакупкиПредМесяц >= 3000 Тогда
			ПроцентСкидки = 5;
		ИначеЕсли СуммаЗакупкиПредМесяц >= 1000 Тогда
			ПроцентСкидки = 2;
		КонецЕсли;	                                                                                               

	КонецЕсли;
	
	
	
	Движение = Движения.Взаиморасчеты.Добавить();
	Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
	Движение.Период = Дата;
	Движение.Контрагент = Контрагент;
	Движение.Сумма = ОбщаяСуммаПродажи;
	
	Если ПроцентСкидки <> 0 Тогда
		Движение = Движения.Взаиморасчеты.Добавить();
		Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
		Движение.Период = Дата;
		Движение.Контрагент = Контрагент;
		Движение.Сумма = (-1) * ОбщаяСуммаПродажи * ПроцентСкидки/100;
	КонецЕсли;
	
	
	
	
	Движения.ОстаткиТоваров.Записывать = Истина;
	Движения.Взаиморасчеты.Записывать = Истина;
Показать


Причем добавление в //3 строк
Движения.ОстаткиТоваров.БлокироватьДляИзменения = Истина;
Движения.Взаиморасчеты.БлокироватьДляИзменения = Истина;
ситуацию не меняет. SQL 2005 sp4, платформа (8.2.19.68).
136. ZLENKO 398 06.12.13 18:32 Сейчас в теме
(134) Рабочий, т.е. вариант кода, который можно использовать на реальных конфигурациях будет существенно отличаться от вышенаписанного. Принудительная запись наборов записей регистров в коде проведения - ЗЛО!
137. zombi81 8 06.12.13 18:46 Сейчас в теме
(136) А Вы бы могли привести рабочий пример кода?
140. ZLENKO 398 09.12.13 17:01 Сейчас в теме
(137) Я уже изложил в (112) концепцию оптимизации кода проведения - там все достаточно просто и понятно.
144. kiruha 388 22.01.14 15:00 Сейчас в теме
(140)
Все кроме
>>Отключаем возможность разделение итогов по регистрам остатков и плана счетов чтобы предотвратить возможные взаимоблокировки. "Параллельность" записи мы повышаем уменьшением времени блокировки регистра.

Это как бы ездить на велосипеде, т.к. на машине можно врезаться в столб
145. ZLENKO 398 22.01.14 15:54 Сейчас в теме
(144) Неправильное сравнение :-) Это как бы выехать на автомагистраль вместо того чтобы ездить по проселочной дороге с кучей столбов :-) Если Вы не понимаете суть моих постов это еще не повод их критиковать :-)
146. ZLENKO 398 22.01.14 16:04 Сейчас в теме
(144) Кстати по поводу отключения режима разделения итогов, то в его необходимости в версионной СУБД я не уверен. Но по факту пришлось убрать разделение, т.к. платформа 8.2 падала при наличие разделения итогов по регистру плана счетов :-( Однако с другой стороны разделение итогов по плану счетов дает небольшой эффект, поэтому я не сильно заморачивался именно по этому вопросу.
Еще раз скажу что одно дело теория (некая теоретическая идеальная конфигурация), а другое дело практика (приблизить реальную конфигурацию к теоретическому идеалу). На практике всегда приходится чем то жертвовать...
138. Hany 09.12.13 16:15 Сейчас в теме
Мне кажется, в тексте статьи нужно поставить заметку: "рекомендуется для тех, кто будет сдавать экзамен на Специалиста".
Тогда коллеги, которым "теория от 1С" не по вкусу, просто пройдут мимо статьи и не разведут тут диспут на тему "красное? нет, легкое".
Ведь всем и так понятно, что приведен код, который нужно применять, чтобы получить сертификат. А как показывает опыт, практика и теория далеко не родственники. И спор тут ни о чем. То, что на практике успешние программисты применяют совсем другие методы, это известно уже давно. Осознание этого приходит с опытом:)

P.S.Точно так же настоящие квалифицированные специалисты, например, Cisco, не могут сдать экзамен на сертификацию, потому что теоретическое описание и реальная эксплуатация платформ никак не связаны:)
139. Bukaska 140 09.12.13 16:47 Сейчас в теме
(138) Hany, Здесь довольно глубокая тема.. Даже я не все галочки знаю, как они работают, а чтобы экзамен сдать, нужно знать не только управляемые(Объектные и пессимистические блокировки), но и ещё кучу всякой информации)))
А то что другие программеры применяют другие методы.. тоже видала.. Как-то мне даже давали "потестить" результаты некоторых. Такое ощущение, что народ отладчик не умеет включать, в режиме юзверя ошибка на ошибке.. А кода мама не горюй! Сиди и расхлебывай)))
По мне так уж лучше бы большинство умело пользоваться типовыми механизмами, нежели кодить все вверх тормашками)))
141. ZLENKO 398 09.12.13 17:06 Сейчас в теме
(139) Безусловно типовые механизмы нужно знать и уметь ними пользоваться. Однако теория (для сдачи на спеца), типовые механизмы и практика имеют между собой мало общего :-) А "вверх тормашками" кодить, конечно, не нужно!
142. dyak84 19.01.14 16:56 Сейчас в теме
Автору спасибо за проделанную работу жаль только что ето не в 8,2 тогда былоб супер актуально. Но з другой стороны нужно рано или позно продвигатся вперед. Тогда и ета статья станет актуальной. Так держать.
143. GROOVY 2503 19.01.14 17:16 Сейчас в теме
(142) dyak84, Поясните, что тут для 8.2 не подходит?
147. kiruha 388 27.01.14 14:08 Сейчас в теме
Ну я как бы не в ларьке работаю. Я бы даже сказал в top 100 росс компаний.
Разделение итогов включено.
Ок, возможно это неправильно - есть объяснение, почему разделение итогов нужно отключать, кроме практических наблюдений ?

PS - для неверсионника, зачем оно в версионнике - не знаю
148. Bukaska 140 27.01.14 14:28 Сейчас в теме
(147) kiruha, Как минимум то, что из за режима разделения итогов база "пухнет"
149. charushkin 104 27.01.14 15:01 Сейчас в теме
(148) Bukaska, не пойму, с чего ради будет какое-либо существенное распухание?
Поправьте, если я не прав, но:
1. Записи в итогах будут плодиться только при параллельных операциях по одинаковым наборам измерений - не думаю, что существенно скажется на количестве записей в таблице итогов
2. В любом случае, итоги должны и будут пересчитаны, при этом количество записей сократится.
Откуда же в таком случае "распухание"?
151. Bukaska 140 30.01.14 10:57 Сейчас в теме
(149) hulio, А бог его знает.. Значала база пухнет, а потом когда идет пересчет итогов, тогда все должно вставать на свои места)
150. kiruha 388 30.01.14 10:50 Сейчас в теме
Здесь rsv рассматривает причины и решение возможных проблем при разделении итогов
http://www.forum.mista.ru/topic.php?id=448994
К сожалению 2009. Далее ничего более подробного нет
152. ZLENKO 398 25.06.14 13:37 Сейчас в теме
Касательно 1С 8.3 может кто то высказать практические наблюдения по управляемым блокировкам.
В 8.3 СУБД MS SQL работает же уже как версионник...
153. Seneka7608 7 06.11.14 09:44 Сейчас в теме
Добрый день.
Есть ли смысл использовать новую методику контроля остатков если в конфигурации установлен Автоматический режим управления блокировкой данных? И если да, то как его правильно реализовать?

Предположим что в документе и регистре накопления тоже установлен автоматический режим, режим разделения итогов в регистре включен.
154. Bukaska 140 06.11.14 13:04 Сейчас в теме
(153) Seneka7608, Там же русским языком сказано, что смысл имеет только при управляемых блокировках)))
156. ZLENKO 398 06.11.14 13:10 Сейчас в теме
(154) Bukaska, "Там же русским языком сказано, что смысл имеет только при управляемых блокировках"

Для того чтобы избавиться от избыточных блокировок, то конечно имеет смысл только при управляемых блокировках.
Для того чтобы контролировать остатки при помощи "новой методики", то тип блокировок не имеет значения :-)
157. Seneka7608 7 06.11.14 14:46 Сейчас в теме
(156) ZLENKO.PRO, а какой тогда смысл использования новой методики если она не уменьшит количества избыточных блокировок? А видимо даже увеличит из-за дополнительных записей движений
158. ZLENKO 398 06.11.14 15:33 Сейчас в теме
(157) Seneka7608, "какой тогда смысл использования новой методики если она не уменьшит количества избыточных блокировок?"

Смысл в полноценном контроле на возникновение отрицательных остатков, в отличие от типового контроля остатка :-)
159. ZLENKO 398 06.11.14 15:38 Сейчас в теме
(157) Seneka7608, "А видимо даже увеличит из-за дополнительных записей движений"

Да. Именно этот аргумент выдвигали разработчики платформы 1С против "новой методики контроля остатка", когда в платформе еще не было управляемых блокировок. Теперь есть управляемые блокировки, есть "новая методика контроля остатка. Что еще нужно для счастья ? Правильно! Версионная СУБД :-)
155. ZLENKO 398 06.11.14 13:07 Сейчас в теме
(153) Seneka7608, "Есть ли смысл использовать новую методику контроля остатков если в конфигурации установлен Автоматический режим управления блокировкой данных?"

Я использовал "новую методику контроля остатков" еще на версии 8.0 в 2007 году :-) Смотря какого смысла вы хотите достичь ?
160. AlexanderKai 09.06.15 15:01 Сейчас в теме
Не понятно только, для чего вообще разделение итогов.
Платформа 8.2.19.80, MS SQL 2008 R2
Пробуем провести параллельно документы в автоматическом режиме по совершенно разным измерениям - ошибка блокировки.
Кто понимает - проясните, пожалуйста.
161. ZLENKO 398 10.06.15 16:04 Сейчас в теме
(160) AlexanderKai, "Пробуем провести параллельно документы в автоматическом режиме по совершенно разным измерениям - ошибка блокировки."

Ошибку блокировки от кого получаете ? От сервера 1С или от MS SQL Server ?
162. ZLENKO 398 10.06.15 16:07 Сейчас в теме
(160) AlexanderKai, "Не понятно только, для чего вообще разделение итогов."

Замечательно работает для оборотных регистров и для регистров по которым не нужен контроль остатка по "новой методике".
163. ZLENKO 398 10.06.15 16:09 Сейчас в теме
(160) AlexanderKai, "Пробуем провести параллельно документы в автоматическом режиме по совершенно разным измерениям - ошибка блокировки."

В автоматическом режиме все печально с блокировками. Переходите на управляемый режим блокировок.
164. KrakoZyabl 77 31.07.15 12:51 Сейчас в теме
GROOVY, Спасибо огромное за эту статью!!! Были недопонимания многих моментов в этой области, прочитав статью заполнил многие пустоты этих неясных пониманий ясными :))) , вернее нашёл им реальное объяснение +++
165. malex 06.09.15 10:44 Сейчас в теме
Ошибка в:

//14
ЭлементБлокировки.ИсточникДанных = ПакетРезультатов[2];

Нужно:
//14
ЭлементБлокировки.ИсточникДанных = ПакетРезультатов[1];
166. GROOVY 2503 06.09.15 15:48 Сейчас в теме
(165) malex, А на мой взгляд все правильно.
167. bpc222 2013 18.11.15 17:43 Сейчас в теме
(166)

Павел, вопрос позвольте по:

Движения.СтоимостьТоваров.БлокироватьДляИзменения = Истина;


проверял на "тестовом стенде", неявные блокировки накладываются вне зависимости от значения БлокироватьДляИзменения. Проверял на 8.2.19.79. Хотя в документации сказано, что это свойство определяет автоустановку неявных блокировок платформой.

Не встречали ли "мат часть" на такое поведение платформы?
168. ZLENKO 398 26.08.16 16:22 Сейчас в теме
(167) bpc222, "вопрос позвольте по:Движения.СтоимостьТоваров.БлокироватьДляИзменения = Истина;"

Вот статья есть на эту тему http://infostart.ru/public/196565/
169. ZLENKO 398 26.08.16 16:35 Сейчас в теме
(167) bpc222, "Не встречали ли "мат часть" на такое поведение платформы?"

Книга "Профессиональная разработка в системе «1С:Предприятие 8»" редакция 2012 года
Глава 11. Учет движения средств Стр 612

Чтобы избежать изменения итогов между моментом записи и моментом
чтения остатков, еще до формирования и записи движений регистра
свойство набора записей БлокироватьДляИзменения устанавливается
в значение Истина. В этом случае в момент записи набора записей плат-
форма отключит разделитель итогов в регистре. Таким образом, другая
транзакция не сможет записать движения по тем же измерениям, что и
текущая, до окончания проведения документа. В результате после записи
движений будут получены правильные остатки из регистров.
170. demart-omsk 20 21.10.16 17:51 Сейчас в теме
Думаю не лишним будет убрать перемножение в знаменателе, бухи не любят, когда копеечка гуляет)

Движение.Стоимость = Списать / ВыборкаПартия.КоличествоОстаток * ВыборкаПартия.СтоимостьОстаток;
НА
Движение.Стоимость = Списать * ВыборкаПартия.СтоимостьОстаток/ ВыборкаПартия.КоличествоОстаток;


perepetulichka; +1 Ответить
171. GROOVY 2503 23.10.16 17:24 Сейчас в теме
(170) demart-omsk, когда числитель равен знаменателю выходит единица, и копейка тут как раз не гуляет. Это математика.
172. perepetulichka 884 22.02.17 10:30 Сейчас в теме
Павел, вы лучший! Огромное спасибо!
173. talych 17 02.07.17 18:45 Сейчас в теме
Спасибо за статью и за комментарии(!). Разобрался с блокировками. А с учетом того факта, что уже 2017 год, что-нибудь поменялось?
174. Gilev.Vyacheslav 1910 04.07.17 10:57 Сейчас в теме
(173) избегайте объектного чтения движений, получайте запросом
175. YaroslavS 23.10.21 14:34 Сейчас в теме
Имхо под проблемой грязного чтения в статье, на самом деле описана проблема неповторяемого чтения. Проблема грязного чтения проявляется при безответсвенном чтении (чтение вне транзакции) при использовании файловых баз или СУБД блокировочников. При использовании 8.3 в комбинации с постгрес, оракл или MS sql начиная от 2005 нет грязного чтения и при безответственном чтении. Прошу прощения, если тут уже это обсудили, не вникал.
176. rozer 305 23.04.22 09:37 Сейчас в теме
Остался непонятным момент


Запрос.УстановитьПараметр("ТочкаИтогов", Новый Граница(МоментВремени(), ВидГраницы.Включая));


Зачем?
Оптимально не указывать момент расчета в данном случае вообще ну или делать это только для неоперативного проведения.
177. DennyPhilord 65 29.02.24 00:21 Сейчас в теме
    //15
    ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Товар", "Товар");
    Блокировка.Заблокировать();
    
    //16
    Если Режим = РежимПроведенияДокумента.Оперативный Тогда
        Движения.СтоимостьТоваров.Очистить();
        //17
        Движения.СтоимостьТоваров.БлокироватьДляИзменения = Истина;
        движения.СтоимостьТоваров.Записать();
    КонецЕсли;
Показать

На какие ещё поля мы накладываем Блокировки в 17, если эти же поля заблокированы в 15?
Рассказывайте
Оставьте свое сообщение