Фирма, занимающаяся оптовыми продажами. Если учет автоматизирован, то все выглядит примерно так: клиент набирает товар, ему выписывается накладная, оплатив которую, можно отправляться на склад. Без сомнения, основным инструментом в работе с оптовым клиентом в программе «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)); КонецФункции
Задача решена полностью.