На днях возникла задача по загрузке файлов, содержащих сведения об оплате физическими лицами некой коммунальной услуги. Информацию предоставлял расчетный центр, ведущий учет начислений и платежей физических лиц в небольшом городе с населением чуть более 1 млн. жителей. Каждый файл в среднем содержал по 300 тысяч записей о поступивших оплатах по лицевым счетам физ.лиц. Условием заказчика было, что файл должен один-к-одному загружаться в документ 1С, чтобы пользователи видели однозначное соответствие между исходным файлом и результатом загрузки в виде документа 1С.
Задача вроде бы простая, если бы не один нюанс: как известно, табличная часть документа 1С может содержать максимум 99999 строк. Это ограничение во многом искусственное и призвано обеспечить соблюдение некоторых критериев производительности в типовых решениях. Для меня же оно означало, что либо придется отступить от требований заказчика и загружать исходные файлы с разбивкой по нескольким результирующим документам, либо искать нестандартное решение.
Сделав выбор в пользу второго варианта, я стал искать способ обойти ограничение, накладываемое платформой на максимальное количество записей в табличной части документа. Первое что стало очевидно, механизм ограничения применяется только в момент записи документа платформой в базу данных. То есть мы совершенно спокойно можем интерактивно или программно добавить в документ любое количество строк и никаких ограничений платформа на эти действия не накладывает. Проблема возникает лишь тогда, когда мы пытаемся записать созданный документ в БД. Платформа сигнализирует нам о проблеме следующим информационным сообщением.
После чего транзакция записи документа прерывается. А что если на момент этой проверки мы временно переложим все данные из табличной части в какое-то иное хранилище, а уже после проверки снова вернем их в табличную часть. Давайте попробуем?
Для начала добавим в документ реквизит ХранилищеТабличнойЧастиДокумента, тип - хранилище значений.
Создаем в модуле формы документа серверную процедуру ПередЗаписьюНаСервере(), в которую добавляем следующий код:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
ВремТЗ = ТекущийОбъект.ТабличнаяЧастьСписок.Выгрузить();
ТекущийОбъект.ХранилищеТабличнойЧастиДокумента = Новый ХранилищеЗначения(ВремТЗ, Новый СжатиеДанных(9));
ТекущийОбъект.ТабличнаяЧастьСписок.Очистить();
КонецПроцедуры
Смысл в том, чтобы выгрузить табличную часть в реквизит документа ХранилищеТабличнойЧастиДокумента и затем удалить все строки из неё. Таким образом на момент выполнения платформенной проверки на количество записей в табличной части - она у нас будет пустая.
Далее уже в модуле документа в процедуре ПриЗаписи() нам необходимо повторно прочитать состояние табличной части до её очистки.
Процедура ПриЗаписи(Отказ)
ТабличнаяЧастьСписок.Загрузить(ХранилищеТабличнойЧастиДокумента.Получить());
КонецПроцедуры
К этому моменту все проверки пройдены и документ уже записан в БД, а в нашей табличной части нет вообще ни одной строки. Однако транзакция записи еще не завершена. Документ существует в виде двух сущностей. Первая - это записи в sql-таблицах на сервере базы данных, вторая - это объект документа в оперативной памяти сервера 1С. На содержимое первой сущности мы никак повлиять не можем. Что бы мы ни делали, записанный в БД документ так и останется с пустой табличной частью. Но вот на вторую сущность - а именно на объект документа на сервере 1С мы в этот момент всё еще можем влиять. Так давайте восстановим нашу табличную часть и загрузим данные из хранилища значений, куда мы их предварительно поместили.
Зачем мы это делаем? Да, записать в базу документ с восстановленной табличной частью уже не получится. Однако, нам в первую очередь нужно не это. Во-первых пользователь мог нажать просто на кнопку Записать без закрытия формы документа. И нам нужно обеспечить отображение данных в табличной части. Во-вторых, если вызов процедуры записи документа делается из формы списка, например, при выборе в контекстном меню команды Провести, нам нужно обеспечить передачу в процедуру ОбработкаПроведения() объекта, содержащего непустую табличную часть.
При таком подходе в процедуру ОбработкаПроведения попадет уже объект документа, который вновь содержит всю ранее удаленную из табличной части информацию, а значит все алгоритмы проведения могут спокойно отработать именно так, как если бы они были разработаны для документа с непустой табличной частью. Никаких дополнительных хитростей и вмешательств не нужно, всё сработает штатно так, как будто бы мы никаких манипуляций и не делали вовсе.
Хочу обратить ваше внимание, что в результате возникает очень странная и неочевидная для многих ситуация. В базу данных записан документ с пустой табличной частью, но его движения сформированы на основании объекта, в котором мы перед процедурой проведения восстановили содержимое ТЧ. Я не знаю, насколько такую ситуацию можно назвать багом платформы или недокументированной особенностью, но это работает на сегодняшний день именно так. На этом можно было бы и поставить точку. Все 300 тысяч строк мы загрузили в документ, провели его, и сформировали нужные нам движения. Но остался еще один момент... Что делать, если пользователь захочет открыть документ и отредактировать его? Ведь табличная часть после считывания документа из БД будет пустой! Как быть?
Элементарно. У нас же есть сохраненные в реквизите ХранилищеТабличнойЧастиДокумента данные. Так давайте при чтении содержимого документа на сервере вновь загрузим их в табличную часть.
В модуле формы документа создаем серверную процедуру:
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
Объект.ТабличнаяЧастьСписок.Загрузить(ТекущийОбъект.ТабличнаяЧастьДокумента.Получить());
КонецПроцедуры
На этом всё!
Поделитесь, пожалуйста, вашим мнением: какие подводные камни и ограничения могут быть при эксплуатации такого решения? И какие альтернативные варианты записи объектов с табличной частью, содержащей большое количество записей, превышающие лимиты платформы 1С, используете вы?