IE2017

Секретные технологии O-Planet (или автоматизация, на…) ЧАСТЬ 1

Сообщество - Архив

Без сомнения, основным инструментом в работе с оптовым клиентом в программе «1С:Предприятие 7.7» становится окно подбора номенклатуры с количеством, которое устраивает абсолютное большинство пользователей кроме тех, у кого на складе находится до нескольких десятков тысяч номенклатурных позиций...
ЧАСТЬ 1. Техническая

Фирма, занимающаяся оптовыми продажами. Если учет автоматизирован, то все выглядит примерно так: клиент набирает товар, ему выписывается накладная, оплатив которую, можно отправляться на склад. Без сомнения, основным инструментом в работе с оптовым клиентом в программе «1С:Предприятие 7.7» становится окно подбора номенклатуры с количеством, которое устраивает абсолютное большинство пользователей кроме тех, у кого на складе находится до нескольких десятков тысяч номенклатурных позиций. Поскольку «1С:Торговля и склад» делает полный расчет остатка товаров по складу при открытии подбора, то в связи с этим на медленных компьютерах может возникнуть неуместная пауза в работе. «1С:Предприниматель» поступает еще «оригинальнее», рассчитывая остаток товара в реальном времени по каждой, отображаемой на экране товарной позиции. Представляете, как может «тормозить» обычная прокрутка списка товаров, ведь для расчета по каждой строке используется функция регистра «ПолучитьОстаток», а товаров в списке, как уже было сказано, несколько десятков тысяч?



Клиент, поставивший мне задачу ускорить процесс расчета остатков при подборе в 1С:Предприниматель, на тот момент обладал компьютерами на базе процессоров Celeron 1000, а в его базе находилось, без малого, 35000 товаров, поэтому, между переходами с одной строчки на другую в окне подбора, пауза возникала в полторы секунды, когда маркер уходил за границу видимого списка товаров, и на экране появлялась новая строка. Задача, фактически, могла быть перефразирована так: научить программу делать то, чего она делать не может по определению. Я люблю такие задачи.

Вариант переделки «Предпринимателя» в «Торговлю» я исключил сразу же, потому что пауза в минуту в начале подбора при присутствующем клиенте также никого не устраивала. Поддерживать в памяти актуальные итоги по остаткам номенклатуры для сервера с 256 МБ оперативной памяти, на котором висело еще пять компьютеров, сами понимаете, было бы жестоко. Требовалось нетривиальное решение.

Следует вспомнить об одной замечательной способности текстового поля на форме выводить на экран результат работы какой-либо функции, если эту функцию вписать в поле «Формула» окна текстового реквизита формы. При этом, значение функции всякий раз будет пересчитываться, когда на форме что-либо изменяется, например, когда меняется текущая строка в списке. Эту возможность я и использовал в собственном подборе.

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

В таком раскладе решение становилось делом техники. В форме списка я запустил в фоновом режиме накрутку «счетчика покоя», который сбрасывался в момент смены текущей строка в списке товаров. Если же этот счетчик достигал определенного значения, то предполагалось, что оператор нашел то, что ему нужно, и пора показывать остатки по товарам, которые реально видны на экране, а это – не 35000, а всего лишь несколько десятков. Вы спросите, как получить список этих нескольких десятков товаров? Очень просто, если воспользоваться замечательной возможностью платформы 7.7, вставлять в поле «Формула» окна настройки свойств чего-либо рассчитываемую on-line функцию. В версии 8.0, к сожалению, эта возможность исчезла.

Итак, я в колонке «Остаток» списка товаров, в поле «Формула» вписал вызов некой функции: «ПолучитьОстаток(1)». Интерес составляло то, что при перерисовке списка товаров, скажем, в 30 строк, эта функция рассчитывалась ровно 30 раз, по разу для каждой строки, и каждый раз внутри функции текущей считалась та строка, для которой она рассчитывалась. Разве это не то, что мне было нужно?

Кто не понял, попробуйте сделать следующее:


А функцию «ПолучитьОстаток» определите так:

Функция ПолучитьОстаток()
   Сообщить(ТекущийЭлемент());
   Возврат «»;
КонецФункции


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

// Таблица рассчитанных остатков, пополняемая в процессе работы.
// Имеет колонки «Номенклатура» и «Количество»
Перем ТаблицаОстатков; 

// Список новых товаров, по которым остатки предполагается расчитать
Перем НовыеТовары; 

// Вспомогательные переменные
Перем БылНовыйТовар, БылЭлемент, Счетчик;


Функцию «ПолучитьОстаток» я определил примерно так:

Функция ПолучитьОстаток()
	Переем Стр;
// Получаю товар из текущей строки
	ТекТовар= ТекущийЭлемент();

// Ищу, есть ли этот товар в рассчитанных ранее остатках
	Если ТаблицаОстатков.НайтиЗначение(ТекТовар,Стр,"Номенклатура")=0 Тогда   
// Остатка по тек. товару рассчитано ранее не было … 

// Для группы остаток рассчитывать не нужно
		Если ТекТовар.ЭтоГруппа()=1 Тогда
		    Возврат "";
		КонецЕсли;

// Добавляю товар в список товаров, по которым еще предстоит рассчитать остатки
		Если НовыеТовары.НайтиЗначение(ТекТовар)=0 Тогда
			НовыеТовары.ДобавитьЗначение(ТекТовар);

			// Сбрасываю счетчик и обновляю форму, говорю, что был новый товар
			Счетчик=0; 
			Форма.Обновить(1);

			// Включаем режим накрутки счетчика
			БылНовыйТовар=1;
		КонецЕсли;    
		Остаток="...";
	Иначе
// Остатка по тек. товару уже был рассчитан ранее …
		Остаток= ТаблицаОстатков.ПолучитьЗначение(Стр,"Количество");
	КонецЕсли;
	
	Возврат Остаток;
	
КонецФункции // ПолучитьОстаток


Фактически, в наш алгоритм осталось добавить только звено расчета остатков по товарам в списке «НовыеТовары». Для этого на форму помещаем текстовое поле, в формулу которого вешаем функцию «РасчетОстатков».



Разумеется, эта функция должна сработать не сразу.

Функция РассчетОстатков()

	// Если на экране есть товар, по которому остатки не рассчитаны
	Если БылНовыйТовар=1 Тогда
		// Тупо плюсуем счетчик
		Счетчик=Счетчик+1;

		// Сменилось положение маркера в списке товаров, значит 
// идет прокрутка и счетчик надо сбросить 
		Если БылЭлемент<>ТекущийЭлемент() Тогда
			Счетчик=0;
			БылЭлемент=ТекущийЭлемент();
		КонецЕсли;

	КонецЕсли;

	// Если счетчик достиг критического значения, то 
// рассчитываем остатки по списку «НовыеТовары»,
// добавляя их в таблицу «ТаблицаОстатков»
Если Счетчик=30 Тогда
		
		РасчитатьОстатки();

		// Список новых товаров очищаем
		НовыеТовары.УдалитьВсе();

		// Отключаем режим накрутки счетчика, счетчик сбрасываем
		БылНовыйТовар=0;
		Счетчик=0;

	КонецЕсли;

	// Если включен режим накрутки счетчика, то не забываем обновлять постоянно форму
	Если БылНовыйТовар=1 Тогда
		Форма.Обновить(1);
	КонецЕсли;	

// На форме можно отражать состояние счетчика до начала расчета остатков
	Возврат ?(Счетчик=0,"Ок",Окр(Счетчик*100/30,0,1));
КонецФункции


Задача решена полностью.

См. также

Комментарии
1. Олег Пономаренко (O-Planet) 6689 09.01.07 17:55 Сейчас в теме
2. Страпот (Bobak) 10.01.07 09:30 Сейчас в теме
Прикольно! +1
Я, когда решал подобную проблему, не додумался до формулы в колонке :(. А так бы еще красивше получилось :). Ну теперь буду знать.
3. Олег Пономаренко (O-Planet) 6689 11.01.07 21:33 Сейчас в теме
Ты еще, вероятно, не знаешь про возможность включения функций в тексты запросов. Это - прикольнее даже ))
4. Страпот (Bobak) 12.01.07 07:27 Сейчас в теме
>Ты еще, вероятно, не знаешь про возможность включения функций в тексты запросов. Это - прикольнее даже ))

Шутка не дошла :(.

Не очень понятно назначение
Код
       Если БылЭлемент<>ТекущийЭлемент() Тогда
         Счетчик=0;
         БылЭлемент=ТекущийЭлемент();
      КонецЕсли;
Показать полностью

Получается что, если я прокручиваю список клавиатурой, то остатки никогда не будут расчитаны!?
Да и зачем
Код
Счетчик
Показать полностью
? Я прокручиваю список и пока не выйдет новых 30 элементов, я не увижу остатки!?
По моему
Код
Счетчик
Показать полностью
лишний. А для исключения "подвисания" при быстрой прокрутке лучше сделать паузу перед расчетом.
5. Страпот (Bobak) 12.01.07 07:30 Сейчас в теме
Упс, глюк небольшой. Повторяю вторую часть.

Да и зачем Счетчик ? Я прокручиваю список и пока не выйдет новых 30 элементов, я не увижу остатки!?
По моему Счетчик лишний. А для исключения "подвисания" при быстрой прокрутке лучше сделать паузу перед расчетом.
6. Олег Пономаренко (O-Planet) 6689 12.01.07 08:22 Сейчас в теме
> Шутка не дошла :(

Код
Функция ХорошийТовар(Тов)
   Возврат ?((Тов.ЭтоГруппа()=0) и (Найти(Тов.Наименование,"чебурашка")>0),1,0);
КонецФункции

...

ТекстЗапроса="
|Тов=Справочник.Номенклатура.ТекущийЭлемент;
|Группировка Тов;
|Условие(ХорошийТовар(Тов)=1);
|";

...
Показать полностью

7. Олег Пономаренко (O-Planet) 6689 12.01.07 08:27 Сейчас в теме
> Не очень понятно назначение
Счетчик, вообще-то, накручивается независимо от прокрутки списка по функции, прописанной в формуле строкового поля. Обрати внимание, что в конце функции "РасчетОстатков" стоит команда обновления формы. Это, по сути, таймер и есть. Короче, если ты в списке ничего не делаешь, то счетчик успешно доходит до 30 и производится расчет остатков. Если же счетчик еще не дощел до 30, и ты в списке сдвинул маркер, то есть, сменил ТекущийЭлемент, то счетчик сбросился и снова ждет, пока не появится пауза, позволяющая ему дойти до 30.
8. Страпот (Bobak) 12.01.07 18:05 Сейчас в теме
Шутка все равно не дошла :).
Про эту шутку я знаю, правда ни разу не пользовался.
И все же прием описанной в этой статье гораздо прикольней :).

>Счетчик, вообще-то, накручивается независимо от ...
Дошло :). Реализация таймера средствами 1С :(.
Сбил с толку Счетчик. Если бы был замер времени, то было бы понятней и, по моему, правильней.
А то как то нехорошо, что время срабатывания зависит от быстродействия компьютера :).
Правда 1С умеет считать минимум секундами, если верить ЖКК :). Так что типовыми средствами не обойдешься :(.
В общем, наверное лучше не сделаешь.
9. Олег Пономаренко (O-Planet) 6689 13.01.07 04:49 Сейчас в теме
> А то как то нехорошо, что время срабатывания зависит от быстродействия компьютера :).
В этом есть минус, но есть и плюс. Можно, к примеру, на каждой машине свой интервал установить...
10. Искренний (Искренний) 17.03.07 13:45 Сейчас в теме
ну насчет времени помоему вообще всё просто в данном случае и было бы понятней для юзера.
по поводу вообще решения. мне не нравится постоянное обновление формы... и то что ограничения количества строк для пересчета задаются платформой, а в коде нет ограничений.
а так, как обычно: работает, быстро - зачот.
11. Антон Рощин (wolfsoft) 2416 17.03.07 17:48 Сейчас в теме
И как обычно есть фича :)
Пример глюка-фичи:
Структура справочника "Номенклатура":
------
Группа "1", в ней
Группа "2"
Элемент "3"
------
При первом открытии группы "1" курсор текущего элемента на группе "2", в остатке по элементу "3" выводит "...". После повторного закрытия-открытия папки "1" остаток выводится нормально.
12. Олег Пономаренко (O-Planet) 6689 18.03.07 04:07 Сейчас в теме
Рабят! Ну я ведь не псих, чтобы весь код выводить! Это уже не статья будет, а мануал для программера. Там в реальной проге проверок у меня тьма и код в два раза объемнее. Сами уж доходите...
13. Олег Пономаренко (O-Planet) 6689 18.03.07 04:08 Сейчас в теме
Кстати, ... при первом открытии - это не глюк...
14. Антон Рощин (wolfsoft) 2416 18.03.07 20:55 Сейчас в теме
2 O-Planet: Да у меня-то все заработало ;) Я просто про этот "конкретный" код сказал. Чуть-чуть подправил и все нормально :)
ЗЫ: > Кстати, ... при первом открытии - это не глюк...
Не, не красиво два раза папку открывать, чтоб остатки увидеть, ИМХО :)
15. Олег Пономаренко (O-Planet) 6689 19.03.07 03:29 Сейчас в теме
16. Антон Рощин (wolfsoft) 2416 19.03.07 10:14 Сейчас в теме
Заработало - заработало, куда ж оно денется! :)
17. Владимир (NewNick) 15.04.07 20:56 Сейчас в теме
>Очень просто, если воспользоваться замечательной возможностью платформы 7.7, вставлять в
>поле «Формула» окна настройки свойств чего-либо рассчитываемую on-line функцию. В версии 8.0, к
>сожалению, эта возможность исчезла.

Уважаемый автор программы, придя по вашей ссылки для изучения методологических(и тд по списку) ляпов 8.0. Хочу задать вопрос почему в платформе 8.0 нельзя заменить реализованый в 7.7 функционал полем "Формула" при помощи события ПриВыводеСтроки?
18. Олег Пономаренко (O-Planet) 6689 19.04.07 03:29 Сейчас в теме
В контексте статьи такая досадная утрата не была озоглавлена словом ляп. Я тут просто сравниваю 7.7 и 8.0. "ПриВыводеСтроки", вероятно, решит вопрос конвертации алгоритма из статьи в 8.0. но речь-то - не об этом. Я сетовал про то, что в 7.7 любое изменение на форме поля, таблицы, флажка, клик по кнопке, запускал волну обхода формы. и можно было решить вопрос "ПриИзмененииЧегоЛибо" очень просто: текстовым полем с формулой. В 8.0 такое простое решение уже не проходит. И не должно, собственно. Там вся логика постоения работы с платформой и объектами конфигурации иная. Я не говорю, в данном случае, что 8.0 хуже. нет, это более высокий уровень программинга, чем 7.7. Мой всдох - скорее ностальгический. Я помню, как в Borland C 3.11 мог составить программу на уровне винтиков. В Builder C++ 6.0 возможности несомненно шире, но вот ощущения контроля всего на низком уровне поэтому-то и не остается. Это - целая философия, что прогресс ведет к катастрофе. Когда становится меньше ручного труда, тогда утрачивается ощищение того, что ты часть этого мира.
19. Бубузяка (Бубузяка) 62 29.06.07 12:30 Сейчас в теме
Идея отловить "видимый" список хороша, зачет.
В 8 ке поможет событие списка "ПриПолученииДанных()", этого достаточно что бы получить состав выведенный строк.
20. Drock (d.snissarenko) 26.09.07 14:26 Сейчас в теме
решение старо как мир, у меня с ТиС 8 используется, но зато подсказал людям которые не знают, я после 7ки перейдя в 8ку долго над этим парился, но в итоге все запихнул в один запрос и вывожу процедурой ПриПолученииДанных()
21. Drock (d.snissarenko) 26.09.07 14:26 Сейчас в теме
ппс - плюсовать не буду - поскольку решение тривиальное
22. Олег Пономаренко (O-Planet) 6689 27.09.07 03:50 Сейчас в теме
Оставьте свое сообщение