gifts2017

Онлайн резервирование товаров

Опубликовал Вадим 1С911.BY (Вадимко) в раздел Программирование - Практика программирования

Бывало ли у Вас такое: зарезервировали товар по телефону, собрались проводить документ, а товара уже нет в доступном остатке на складе? Пока Вы разговаривали, кто-то успел провести документ, который зарезервировал товар. Что делать? Есть одна идея.

Бывало ли у Вас такое: зарезервировали товар по телефону, собрались проводить документ, а товара уже нет в доступном остатке на складе? Как же так? Что ж такое? Недавно был виден остаток в подборе. Приходится вычеркивать товар либо брать партию по другой цене или с неподходящими для покупателя параметрами. Приходится извиняться либо вообще перезванивать и согласовывать новые условия заказа. А дорога каждая минута.

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

Что делать? Есть одна идея. Резервировать товар в не проведенных документах во временных резервах. Более того резервировать товар в новых, еще незаписанных документах. И даже изменять резерв при редактировании количества в строке заявки, удалении строки, изменении склада и других действиях в записанных и новых документах. Резерв сразу увидят все пользователи.

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

Что нужно сделать? Рассмотрим работу с форматом SQL. Все это писалось в 2007 году, но до сих пор работает. smiley

Необходимо  создать свою таблицу SQL примерно такой структуры: Товар, Склад, Резерв, IDDOC. Если склад адресный - добавляется поле для ячейки.

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

Функция глПолучитьСвободныйОстаток(ВыбТовар, ВыбСклад, ПоложительныеОстатки =0, ОстаткиВТЗ =0)Экспорт
    ТекстЗапроса = "
    |SELECT SUM(Запрос.Остаток - Запрос.Резерв) AS Количество
    |FROM
    |(
    |SELECT ОстаткиТМЦОстатки.КоличествоОстаток Остаток
    |    , 0 Резерв
    |FROM $РегистрОстатки.ОстаткиТМЦ(,,
    |        (Номенклатура = :ВыбТовар)
    |        AND (Склад = :ВыбСклад)
    |        ,,
    |        Количество) AS ОстаткиТМЦОстатки
    |
    |UNION ALL
    |
    |SELECT 0 Остаток
    |, РезервыТМЦОстатки.КоличествоОстаток Резерв
    |FROM $РегистрОстатки.РезервыТМЦ(,,
    |        (Номенклатура = :ВыбТовар)
    |        AND (Склад = :ВыбСклад)
    |        ,,
    |        Количество) AS РезервыТМЦОстатки
    |
    |UNION ALL
    |
    |SELECT 0 Остаток
    |, ВременныйРезерв.Res Резерв
    |FROM Tempost AS ВременныйРезерв (NOLOCK)
    |WHERE ВременныйРезерв.Tov = :ВыбТовар
    |      AND ВременныйРезерв.Skl = :ВыбСклад
    |) AS Запрос
    |";
        
    Если ПоложительныеОстатки = 1 Тогда //только остатки > 0
        ТекстЗапроса = ТекстЗапроса + "
        |HAVING SUM(Запрос.Остаток - Запрос.Резерв) > 0
        |";
    КонецЕсли;
    
    RecordSet.УстановитьТекстовыйПараметр("ВыбТовар",    ВыбТовар);
    RecordSet.УстановитьТекстовыйПараметр("ВыбСклад",    ВыбСклад);
        
    ТЗ = RecordSet.ВыполнитьИнструкцию(ТекстЗапроса);
    
    Если ОстаткиВТЗ = 0 Тогда
        Возврат ТЗ.ПолучитьЗначение(1,1);
    Иначе 
        Возврат ТЗ;
    КонецЕсли;    
КонецФункции

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

Процедура ПриОтменеПроведенияДокумента(Док)//Переводим на всякий случай во временный резерв//Нужно следить за тем чтобы непроведенные документы долго не болталисьЕсли Док.Вид()= "ЗаявкаПокупателя" Тогда ТекстЗапроса ="|INSERT INTO Tempost
        |SELECT $РезервыТМЦ.Номенклатура
        |, $РезервыТМЦ.Склад
        |, $РезервыТМЦ.Количество
        |, РезервыТМЦ.IDDOC
        |FROM $Регистр.РезервыТМЦ AS РезервыТМЦ
        |WHERE (РезервыТМЦ.IDDOC = :ТекДок)
        |"; RecordSet.УстановитьТекстовыйПараметр("ТекДок", Док.ТекущийДокумент()); RecordSet.ВыполнитьИнструкцию(ТекстЗапроса);КонецЕсли;КонецПроцедурыПроцедура ПриУдаленииДокумента(Док, Реж)//Удаляем временный резерв по документуЕсли Док.Вид()= "ЗаявкаПокупателя" Тогда ТекстЗапроса ="|DELETE
        |FROM Tempost (HOLDLOCK)
        |WHERE IDDOC = :ВыбДок
        |"; RecordSet.УстановитьТекстовыйПараметр("ВыбДок", Док); RecordSet.ВыполнитьИнструкцию(ТекстЗапроса);КонецЕсли;КонецПроцедуры

Также создаем функцию глИзменитьРезерв(), которая будет анализировать свободный остаток и сравнивать его с количеством товара в документе (при этом для проведенных документов будем учитывать не только содержимое временных резервов, но и движения документа). При возможности зарезервировать товар она будет записывать резерв во временное хранилище. Фактически каждое редактирование содержимого документа будет добавлять записи со знаком - или + в таблицу временных резервов. Вот фрагмент текста функции:

Если ПоДвижениямДокумента = 1 Тогда
    ТекстЗапроса = "
    |SELECT Sum(ISNULL($РезервыТМЦ.Количество,0) + ISNULL(ВременныйРезерв.Res,0)) Количество
    |FROM $Регистр.РезервыТМЦ AS РезервыТМЦ (NOLOCK)
    |LEFT JOIN Tempost AS ВременныйРезерв (NOLOCK) ON РезервыТМЦ.IDDOC = ВременныйРезерв.IDDOC
    |AND $РезервыТМЦ.Номенклатура    = ВременныйРезерв.Tov
    |AND $РезервыТМЦ.Склад           = ВременныйРезерв.Skl
    |WHERE (РезервыТМЦ.IDDOC            = :ТекДок)
    |    AND ($РезервыТМЦ.Номенклатура  = :Товар)
    |    AND ($РезервыТМЦ.Склад         = :Склад)
    |";

    RecordSet.УстановитьТекстовыйПараметр("ТекДок",   ТекДок);
    RecordSet.УстановитьТекстовыйПараметр("Товар",    Товар);
    RecordSet.УстановитьТекстовыйПараметр("Склад",    Склад);

    ТаблИтогов = RecordSet.ВыполнитьИнструкцию(ТекстЗапроса);
Иначе
    ВыдаватьСообщение = 0; //документ может быть старым и во временных резервах отсутствовать

    ТекстЗапроса = "
    |SELECT Sum(ВременныйРезерв.Res) Количество
    |FROM Tempost AS ВременныйРезерв
    |WHERE (ВременныйРезерв.IDDOC    = :ИДДок)
    |    AND (ВременныйРезерв.Tov    = :Товар)
    |    AND (ВременныйРезерв.Skl    = :Склад)
    |";

    RecordSet.УстановитьТекстовыйПараметр("ИДДок",    ИДДок);
    RecordSet.УстановитьТекстовыйПараметр("Товар",    Товар);
    RecordSet.УстановитьТекстовыйПараметр("Склад",    Склад);

    ТаблИтогов = RecordSet.ВыполнитьИнструкцию(ТекстЗапроса);
КонецЕсли;    

Далее модифицируем модуль формы документа "Заявка покупателя". В процедуру ПриОткрытии() добавляем код создания временной таблицы хранения резервов (для отката изменений):

Процедура ПриОткрытии() СтарыйСклад = Склад; ИДДок = Meta.ЗначениеВСтрокуБД(ТекущийДокумент()); ИДПольз = Meta.ЗначениеВСтрокуБД(глПользователь);Если Выбран()= 0 Тогда ВТ ="temp" + СокрЛП(ИДПольз);Иначе ВТ ="temp" + СокрЛП(ИДДок);КонецЕсли;//для отката изменений ТекстЗапроса ="|if exists (select name from dbo.sysobjects where name = '" + ВТ + "' and xtype = 'U ')
    |drop table " + ВТ + "
    |"; RecordSet.ВыполнитьИнструкцию(ТекстЗапроса); RecordSet.ВыполнитьИнструкцию("create table " + ВТ 
 +" (tov char(9), skl char(9), res numeric (15,3), iddoc char(9))"); RecordSet.УстановитьТекстовыйПараметр("ИДДок", ИДДок); RecordSet.ВыполнитьИнструкцию("insert into " + ВТ +" select * from tempost where iddoc = :ИДДок");КонецПроцедуры

При записи документа производим хитрые манипуляции с таблицей для отката изменений и изменяем Iddoc в таблице временных резервов:

Процедура ПриЗаписи()Если Выбран() =1 ТогдаВозврат;КонецЕсли; Записать(); СтарыйИДДок = Лев(Meta.ЗначениеВСтрокуБД(глПользователь),6) +"ZZZ"; ИДДок = Meta.ЗначениеВСтрокуБД(ТекущийДокумент());//назначаем постоянный иддок новому документу RecordSet.УстановитьТекстовыйПараметр("ИДДок", ИДДок); RecordSet.УстановитьТекстовыйПараметр("СтарыйИДДок", СтарыйИДДок); RecordSet.ВыполнитьИнструкцию("update tempost (HOLDLOCK) set iddoc = :ИДДок where iddoc = :СтарыйИДДок"); ИДДок = Meta.ЗначениеВСтрокуБД(ТекущийДокумент()); ИДПольз = Meta.ЗначениеВСтрокуБД(глПользователь);//таблицу для отката переименуем ВТ ="temp" + СокрЛП(ИДПольз); ВТНов ="temp" + СокрЛП(ИДДок); RecordSet.ВыполнитьИнструкцию("EXEC sp_rename '" + ВТ +"', '" + ВТНов +"'");//если документ записан - удалим его из таблицы открытых документов RecordSet.УстановитьТекстовыйПараметр("ИДДок", ИДДок); RecordSet.ВыполнитьИнструкцию("delete from openz with(HOLDLOCK) where iddoc = :ИДДок");КонецПроцедуры

Процедура ПриЗакрытии() не менее хитрая, нам нужно откатить изменения:

Процедура ПриЗакрытии()
    Если Выбран() = 1 Тогда
        //если документ не записывается но изменялся - возвращаем назад состояние таблицы
        ИДДок = Meta.ЗначениеВСтрокуБД(ТекущийДокумент());
        ВТ = "temp" + СокрЛП(ИДДок);
        
        Если Модифицированность() = 1 Тогда
            RecordSet.УстановитьТекстовыйПараметр("ИДДок", ИДДок);
            RecordSet.ВыполнитьИнструкцию("delete from tempost with(HOLDLOCK) where iddoc = :ИДДок");
            
            RecordSet.ВыполнитьИнструкцию("insert into tempost with(HOLDLOCK) select * from " + ВТ);
            
            //если документ не изменялся сознательно - удалим его из таблицы открытых документов
            RecordSet.УстановитьТекстовыйПараметр("ИДДок", ИДДок);
            RecordSet.ВыполнитьИнструкцию("delete from openz with(HOLDLOCK) where iddoc = :ИДДок");
        КонецЕсли;
        
        RecordSet.ВыполнитьИнструкцию("drop table " + ВТ);
        Возврат;
    КонецЕсли;
    
    //если новый документ не записывается - очищаем таблицу
    ИДДок = Лев(Meta.ЗначениеВСтрокуБД(глПользователь), 6) + "ZZZ";
    
    ИДПольз = Meta.ЗначениеВСтрокуБД(глПользователь);
    ВТ = "temp" + СокрЛП(ИДПольз);
    
    RecordSet.УстановитьТекстовыйПараметр("ИДДок", ИДДок);
    RecordSet.ВыполнитьИнструкцию("delete from tempost with(HOLDLOCK) where iddoc = :ИДДок");
    
    RecordSet.ВыполнитьИнструкцию("drop table " + ВТ);
КонецПроцедуры 

Самое сложное позади. Осталось предусмотреть редактирование данных документа. Приведу коды некоторых процедур:

Процедура ПриУдаленииСтроки() глИзменитьРезерв(ТекущийДокумент(), Номенклатура, Склад,0, -Количество);КонецПроцедурыПроцедура ПриНачалеРедактированияСтроки() СтароеКоличество = Количество;КонецПроцедурыПроцедура ПриОкончанииРедактированияСтроки() МаксКолво =0;Если глИзменитьРезерв(ТекущийДокумент(), Номенклатура, Склад, Количество, Количество - СтароеКоличество, МаксКолво) =0 Тогда Количество = МаксКолво;КонецЕсли;КонецПроцедуры

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

  • Товар, который подбирается в заявку, будет в свободном остатке и заявка однозначно будет проведена (не придется звонить и извиняться перед заказчиком)
  • Ускорение проведения документов
  • Ускорение вывода доступных остатков в форме подбора, отчетах, формах документов

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

Вот вкратце и все. Если тема показалась интересной - пишите, будем внедрять. smiley

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

Наименование Файл Версия Размер
Демо конфигурация online резервирования (только для SQL) 1
.rar 450,00Kb
10.10.13
1
.rar 1.0 450,00Kb Скачать

См. также

Подписаться Добавить вознаграждение
В этой теме еще нет сообщений.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа