В дереве метаданных создается регламентное задание, назовем его усВыгрузкаОстатков. Назначется ему расписание и дается имя выполняемого метода, например, усРегламентныеЗадания.усВыгрузитьОстаткиТоваров. Соответственно в общем модуле усРегламентныеЗадания (галочки: «клиент», «сервер») добавляется экспортная процедура усВыгрузитьОстаткиТоваров:
Процедура усВыгрузитьОстаткиТоваров () Экспорт РегламентноеЗадание = РегламентныеЗадания.НайтиПредопределенное(Метаданные.РегламентныеЗадания.усВыгрузкаОстатков); МассивЗаданий = ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("РегламентноеЗадание, Состояние", РегламентноеЗадание, СостояниеФоновогоЗадания.Активно)); Если МассивЗаданий.Количество() > 1 Тогда Возврат; КонецЕсли; усВыполнитьВыгрузкуОстатков(); Конецпроцедуры
В этой процедуре вызывается другая процедура: усВыполнитьВыгрузкуОстатков. Опишем ее в общем модуле усОбменДанными (галочки: «Клиент», «Сервер», «Глобальный», кстати, благодаря галочке «глобальный» не нужно указывать имя этого общего модуля перед именем процедуры при ее вызове):
Процедура усВыполнитьВыгрузкуОстатков() Экспорт ОбработкаОбмена = Обработки.усОбменДанными.Создать(); ОбработкаОбмена.АвтоматическийОбмен = Истина; ОбработкаОбмена.ВыгрузитьОстаткиТоваров(); КонецПроцедуры
В модуле объекта обработки усОбменДанными определяем экспортную процедуру ВыгрузитьОстаткиТоваров:
Процедура ВыгрузитьОстаткиТоваров() Экспорт Если НЕ Получатели.Количество() = 0 Тогда СписокПолучателей = Новый Массив; Получатели_Строки = Получатели.НайтиСтроки(Новый Структура("Пометка", Истина)); Для Каждого Строка ИЗ Получатели_Строки Цикл СписокПолучателей.Добавить(Строка.Получатель); КонецЦикла; ТаблицаПолучателей = ПолучитьУзлыОбмена(СписокПолучателей); Иначе ТаблицаПолучателей = ПолучитьУзлыОбмена(); КонецЕсли; Для Каждого Получатель ИЗ ТаблицаПолучателей Цикл Отказ = 0; КаталогВыгрузки = ПолучитьКаталог(Получатель.КаталогВыгрузки); ЗаписьXML = Новый ЗаписьXML; ЗаписьXML.УстановитьСтроку(); ЗаписьXML.ЗаписатьНачалоЭлемента("ЛОГИСТИКА_1С"); СформироватьДанные_ОстаткиТоваров(ЗаписьXML); ПрефиксФайла = "OST_"; ЗаписьXML.ЗаписатьКонецЭлемента(); ТекстСообщения = ЗаписьXML.Закрыть(); ИмяФайла = ПрефиксФайла + Формат(ТекущаяДатаНаСервере(), мФорматнаяСтрока) + ".XML"; ЗаписьXML = Новый ЗаписьXML; Попытка ЗаписьXML.ОткрытьФайл(мВременныйКаталог + ИмяФайла); Исключение ВывестиСообщение(ОписаниеОшибки(),,1); Отказ = 1; Возврат; КонецПопытки; ЗаписьXML.ЗаписатьОбъявлениеXML(); ЗаписьXML.ЗаписатьБезОбработки(ТекстСообщения); ЗаписьXML.Закрыть(); ПереместитьФайл(мВременныйКаталог + ИмяФайла, КаталогВыгрузки + ИмяФайла); //Сообщить("Остатки выгружены",СтатусСообщения.Обычное); КонецЦикла; КонецПроцедуры
В этой процедуре используется функция получения данных о параметрах получателей остатков (см. текст запроса):
Функция ПолучитьУзлыОбмена(СписокУзлов = Неопределено) Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | тбУзлыОбмена.Ссылка, | тбУзлыОбмена.Предопределенный, | тбУзлыОбмена.Код, | тбУзлыОбмена.Наименование, | тбУзлыОбмена.АвтоматическийОбмен, | тбУзлыОбмена.КаталогВыгрузки, | тбУзлыОбмена.КаталогЗагрузки, | тбУзлыОбмена.КаталогУдача, | тбУзлыОбмена.КаталогНеудача, | тбУзлыОбмена.УровеньСообщений, | тбУзлыОбмена.Представление, | тбУзлыОбмена.КоэффициетПересчетаОбъема, | тбУзлыОбмена.КратностьОбъема, | тбУзлыОбмена.КоэффициентПересчетаВеса, | тбУзлыОбмена.КратностьВеса, | тбУзлыОбмена.Склад, | тбУзлыОбмена.КлассЕдиницХранения, | тбУзлыОбмена.МодельСкладскогоУчета |ИЗ | Справочник.усУзлыОбменаДанными КАК тбУзлыОбмена |ГДЕ | (НЕ тбУзлыОбмена.ПометкаУдаления)" + ?(АвтоматическийОбмен," И тбУзлыОбмена.АвтоматическийОбмен","") + ?(СписокУзлов = Неопределено,"", " И тбУзлыОбмена.Ссылка В(&СписокУзлов)"); Запрос.УстановитьПараметр("СписокУзлов", СписокУзлов); Возврат Запрос.Выполнить().Выгрузить(); КонецФункции
Рисунок ниже дает представление о справочнике «Узлы обмена»:
В этом справочнике задаются, например, пути к файлам обмена.
Используемая функция получения каталога:
Функция ПолучитьКаталог(Каталог) Возврат ?(ПустаяСтрока(Каталог), "", Каталог + ?(Прав(Каталог,1) = "", "", "")); КонецФункции
Там же в модуле обработки размещается используемая процедура СформироватьДанные_ОстаткиТоваров:
Процедура СформироватьДанные_ОстаткиТоваров(ТекстСообщения) Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | тбОстатки.НоменклатураКод, | тбОстатки.Качество, | тбОстатки.ЕдиницаХранения, | тбОстатки.ЕдиницаХраненияКод, | СУММА(тбОстатки.Количество) КАК Количество |ИЗ | (ВЫБРАТЬ | тбОстатки.Номенклатура.Код КАК НоменклатураКод, | тбОстатки.Качество.Наименование КАК Качество, | тбОстатки.ЕдиницаХранения.Наименование КАК ЕдиницаХранения, | тбОстатки.ЕдиницаХранения.Код КАК ЕдиницаХраненияКод, | СУММА(тбОстатки.Количество) КАК Количество | ИЗ | РегистрСведений.усОстаткиТоваров КАК тбОстатки | ГДЕ | тбОстатки.Номенклатура.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.усТипыНоменклатуры.Товар) | | СГРУППИРОВАТЬ ПО | тбОстатки.Номенклатура.Код, | тбОстатки.Качество, | тбОстатки.ЕдиницаХранения.Наименование, | тбОстатки.ЕдиницаХранения.Код, | тбОстатки.Качество.Наименование | | ОБЪЕДИНИТЬ ВСЕ | | ВЫБРАТЬ | тбСоставТЕ.Номенклатура.Код, | тбСоставТЕ.Качество.Наименование, | тбСоставТЕ.ЕдиницаХранения.Наименование, | тбСоставТЕ.ЕдиницаХранения.Код, | СУММА(тбСоставТЕ.Количество) | ИЗ | РегистрСведений.усОстаткиТоваров КАК тбОстатки | ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.усСоставТранспортнойЕдиницы КАК тбСоставТЕ | ПО тбОстатки.Номенклатура = тбСоставТЕ.ТранспортнаяЕдиница | ГДЕ | тбОстатки.Номенклатура.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.усТипыНоменклатуры.ТранспортнаяЕдиница) | И тбОстатки.Количество > 0 | | СГРУППИРОВАТЬ ПО | тбСоставТЕ.Номенклатура.Код, | тбСоставТЕ.Качество.Наименование, | тбСоставТЕ.ЕдиницаХранения.Наименование, | тбСоставТЕ.ЕдиницаХранения.Код) КАК тбОстатки |ГДЕ | тбОстатки.Количество <> 0 | |СГРУППИРОВАТЬ ПО | тбОстатки.НоменклатураКод, | тбОстатки.Качество, | тбОстатки.ЕдиницаХранения, | тбОстатки.ЕдиницаХраненияКод"; Выборка = Запрос.Выполнить().Выбрать(); ТекстСообщения.ЗаписатьНачалоЭлемента("Остатки"); Пока Выборка.Следующий() Цикл ТекстСообщения.ЗаписатьНачалоЭлемента("Строка"); ТекстСообщения.ЗаписатьАтрибут("КодТовара" , СокрЛП(Выборка.НоменклатураКод)); ТекстСообщения.ЗаписатьАтрибут("Качество" , СокрЛП(Выборка.Качество)); ТекстСообщения.ЗаписатьАтрибут("ЕдиницаИзмерения" , СокрЛП(Выборка.ЕдиницаХранения)); ТекстСообщения.ЗаписатьАтрибут("ЕдиницаИзмеренияКод", СокрЛП(Выборка.ЕдиницаХраненияКод)); ТекстСообщения.ЗаписатьАтрибут("Количество" , Формат(Выборка.Количество, мФорматнаяСтрока)); ТекстСообщения.ЗаписатьКонецЭлемента(); КонецЦикла; ТекстСообщения.ЗаписатьКонецЭлемента(); КонецПроцедуры // СформироватьДанные_ОстаткиТоваров()
В результате выполнения всего этого кода, по расписанию, указанному в регламентном задании, будут формироваться файлы с именем вида «OST_20110706105728.XML», содержащие актуальные на текущий момент остатки в базе.
Теперь приведем пример кода принимающей эти данные базы.
В метаданных принимающей базы создается регламентное задание, например, с именем ахОбменДанными, указывается расписание чтения данных и имя вызываемого обработчика ахОбменДанными.ВыполнитьРегламентныйОбменДанными. В общем модуле ахОбменДанными добавляем процедуру ВыполнитьРегламентныйОбменДанными:
// Выполняет обмен данными в соответствии с регламентными заданиями. // // Пареметры: // нет. // Процедура ВыполнитьРегламентныйОбменДанными() Экспорт // это не обязательный код, используемый для того, чтобы запуск обмена данными случайно не произошел на копии базы данных Если НЕ РазрешенЗапускРегламентногоЗаданияОбменДанными() Тогда Попытка РегламентноеЗадание = РегламентныеЗадания.НайтиПредопределенное(Метаданные.РегламентныеЗадания.ахОбменДанными); РегламентноеЗадание.Использование = Ложь; РегламентноеЗадание.Записать(); Константы.ахАвтоматическийОбмен.Установить(ложь); Об = Справочники.ахУзлыОбменаДанных.НайтиПоКоду("00001").ПолучитьОбъект(); Об.АвтоматическийОбмен = Ложь; Об.Записать(); Константы.агСинхронизацияССайтом.Установить(Ложь); Возврат; Исключение КонецПопытки; КонецЕсли; // конец не обязательного кода РегламентноеЗадание = РегламентныеЗадания.НайтиПредопределенное(Метаданные.РегламентныеЗадания.ахОбменДанными); МассивЗаданий = ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("РегламентноеЗадание, Состояние", РегламентноеЗадание, СостояниеФоновогоЗадания.Активно)); Если МассивЗаданий.Количество() > 1 Тогда Возврат; КонецЕсли; ВыполнитьОбменДанными(); КонецПроцедуры // ВыполнитьРегламентныйОбменДанными()
Вызываемая процедура ВыполнитьОбменДанными:
Процедура ВыполнитьОбменДанными() Экспорт //новый экземпляр обработки усОбменДанными Обработка = Обработки.ахОбменДанными.Создать(); Обработка.АвтоматическийОбмен = Истина; Обработка.ПолучитьСообщения(); // а это не обязательный код для отправки сообщений в ответ, в другую базу, нас он не интересует, можно его убрать: ТекстЗапроса = "ВЫБРАТЬ | ахРегистрацияИзменений.Объект |ИЗ | РегистрСведений.ахРегистрацияИзменений КАК ахРегистрацияИзменений |ГДЕ | ахРегистрацияИзменений.Выполнен = ЛОЖЬ"; Обработка.ОтправитьСообщения(ТекстЗапроса); // конец не обязательного кода КонецПроцедуры // ВыполнитьОбменДанными()
В этой процедуре вызвана процедура модуля обработки ахОбменДанными ПолучитьСообщения, вот ее код:
// Процедура предназначена для получения сообщения. // // Параметры: // Нет. // Процедура ПолучитьСообщения() Экспорт ОбработанныеДанные.Очистить(); ТаблицаПолучателей = ПолучитьУзлыОбмена(); #Если Клиент Тогда ФормаПроцесса = ПолучитьОбщуюФорму("ХодВыполненияОбработкиДанных"); #КонецЕсли Если ТаблицаПолучателей.Количество() = 0 Тогда ВывестиСообщение("Не удалось получить узлы обмена",,1); Возврат; КонецЕсли; Для Каждого Получатель ИЗ ТаблицаПолучателей Цикл КаталогЗагрузки = ПолучитьКаталог(Получатель.КаталогЗагрузки); КаталогУдача = ПолучитьКаталог(Получатель.КаталогУдача); КаталогНеудача = ПолучитьКаталог(Получатель.КаталогНеудача); Файлы = НайтиФайлы(КаталогЗагрузки,"*.xml"); КоличествоФайлов = Файлы.Количество(); Если КоличествоФайлов = 0 Тогда Продолжить; КонецЕсли; Заголовок = "Загрузка данных для узла " + Получатель.Представление; ВывестиСообщение(Заголовок,,2); #Если Клиент Тогда Если НЕ АвтоматическийОбмен Тогда ФормаПроцесса.КомментарийОбработкиДанных = Заголовок; ФормаПроцесса.МаксимальноеЗначение = КоличествоФайлов; ФормаПроцесса.Открыть(); КонецЕсли; #КонецЕсли Для Каждого Файл ИЗ Файлы Цикл Отказ = 0; ДеревоОбмена.Строки.Очистить(); ПолныйТекстСообщений.Очистить(); Заголовок = "Чтение файла " + Файл.ПолноеИмя; ВывестиСообщение(Заголовок,,2); КоличествоЭлементов = 0; #Если Клиент Тогда Если НЕ АвтоматическийОбмен Тогда ФормаПроцесса.Значение = ФормаПроцесса.Значение + 1; ФормаПроцесса.КомментарийЗначения = Заголовок; КонецЕсли; #КонецЕсли ЧтениеXML = Новый ЧтениеXML; Попытка ЧтениеXML.ОткрытьФайл(Файл.ПолноеИмя); Исключение ТекстОшибки = ОписаниеОшибки(); Если Найти(ТекстОшибки, "Ошибка совместного доступа к файлу") Тогда Продолжить; Иначе ВывестиСообщение(ОписаниеОшибки(),,1); Отказ = 1; КонецЕсли; КонецПопытки; Если НЕ Отказ Тогда Попытка ПрочитатьДанныеИзXML(ЧтениеXML,ДеревоОбмена,КоличествоЭлементов, ""); Исключение ВывестиСообщение(ОписаниеОшибки(),,1); Отказ = 1; КонецПопытки; ЧтениеXML.Закрыть(); #Если Клиент Тогда Если НЕ Отказ Тогда Если АвтоматическийОбмен Тогда ОбработатьПолученныеДанные(ДеревоОбмена,0,Отказ, , Файл.Имя); Иначе ФормаПроцесса.МаксимальноеЗначение = КоличествоЭлементов; ФормаПроцесса.КомментарийОбработкиДанных = "Загрузка данных " + Файл.ПолноеИмя; ФормаПроцесса.Открыть(); ОбработатьПолученныеДанные(ДеревоОбмена,ФормаПроцесса.Значение,Отказ, , Файл.Имя); КонецЕсли; КонецЕсли; #Иначе ОбработатьПолученныеДанные(ДеревоОбмена,0,Отказ, , Файл.Имя); #КонецЕсли КонецЕсли; ВыполнитьПеремещение = Истина; Если Отказ = 0 Тогда КаталогПолучатель = КаталогУдача; ИначеЕсли Отказ = 1 Тогда КаталогПолучатель = КаталогНеудача; Иначе КаталогПолучатель = КаталогЗагрузки; ВыполнитьПеремещение = Ложь; КонецЕсли; Если ВыполнитьПеремещение Тогда Попытка ПереместитьФайл(Файл.ПолноеИмя, КаталогПолучатель + Файл.Имя); Исключение ВывестиСообщение("Не удалось переместить файл " + ОписаниеОшибки(),,2); КонецПопытки; КонецЕсли; ЗаписатьТекстСообщений(КаталогПолучатель + Файл.ИмяБезРасширения + ".txt", Отказ); КонецЦикла; КонецЦикла; #Если Клиент Тогда Если НЕ АвтоматическийОбмен И ФормаПроцесса.Открыта() Тогда ФормаПроцесса.Закрыть(); КонецЕсли; #КонецЕсли КонецПроцедуры // ПолучитьСообщения()
Тут нас интересуют, во-первых, процедура, читающая структуру файла xml и записывающая все это в ДеревоОбмена:
Процедура ПрочитатьДанныеИзXML(ЧтениеXML,Родитель,СчетчикИтераций,Знач Имя) Экспорт Пока ЧтениеXML.Прочитать() Цикл Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда ИмяXML = ЧтениеXML.Имя; Стр = Родитель.Строки.Добавить(); Стр.ИмяОбъекта = ЧтениеXML.Имя; Параметры = Новый Соответствие; Пока ЧтениеXML.ПрочитатьАтрибут() Цикл Параметры.Вставить(ЧтениеXML.Имя,ЧтениеXML.Значение); КонецЦикла; Стр.СтруктураАтрибутов = Параметры; СчетчикИтераций = СчетчикИтераций + 1; ПрочитатьДанныеИзXML(ЧтениеXML,Стр,СчетчикИтераций,ЧтениеXML.Имя); Иначе Возврат; КонецЕсли; КонецЦикла; КонецПроцедуры // ПрочитатьДанныеИзXML()
Во-вторых, процедура, собственно анализирующая прочитанное:
Процедура ОбработатьПолученныеДанные(СтрокаРодитель,СчетчикИтераций,Отказ, Уровень = 0, ИмяФайла = "") Для Каждого Строка ИЗ СтрокаРодитель.Строки Цикл СчетчикИтераций = СчетчикИтераций + 1; Если Строка.ИмяОбъекта = "Номенклатура" Тогда ЗагрузитьДанные_Номенклатура(Строка, Отказ); ИначеЕсли Строка.ИмяОбъекта = "усЗаказНаОтгрузкуОтмена" Тогда ЗагрузитьДанные_ОтменаЗаказа(Строка,Отказ,ИмяФайла); // и т.д. - на каждый объект своя процедура-интерпретатор выгруженных данных // нас сейчас интересует только интерпретация остатков: ИначеЕсли Строка.ИмяОбъекта = "Остатки" Тогда ЗагрузитьДанные_ОстаткиПоЕдиницамИзмерения(Строка,Отказ); Иначе Если Строка.Строки.Количество() И НЕ Отказ Тогда Уровень = Уровень + 1; ОбработатьПолученныеДанные(Строка,СчетчикИтераций,Отказ, Уровень, ИмяФайла); КонецЕсли; КонецЕсли; Если Отказ Тогда Возврат; КонецЕсли; КонецЦикла; КонецПроцедуры // ОбработатьПолученныеДанные()
Процедура-интерпретатор данных ЗагрузитьДанные_ОстаткиПоЕдиницамИзмерения (остатки УС в этой базе хранятся в регистре сведений РегистрыСведений.ОстаткиУС, цель процедуры — очистить регистр от старых остатков и переписать на новые):
Процедура ЗагрузитьДанные_ОстаткиПоЕдиницамИзмерения(Данные, Отказ) ИмяОбъекта = "Остатки УС"; Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | Номенклатура.Ссылка |ИЗ | Справочник.Номенклатура КАК Номенклатура |ГДЕ | Номенклатура.Код = &Код | И Номенклатура.ЭтоГруппа = ЛОЖЬ"; Товары = Новый ТаблицаЗначений; Товары.Колонки.Добавить("Номенклатура"); Товары.Колонки.Добавить("Количество"); Товары.Колонки.Добавить("ЕдиницаИзмерения"); Товары.Колонки.Добавить("Качество"); Товары.Колонки.Добавить("Коэффициент"); СтруктураПоискаНоменклатуры = Новый Структура("Номенклатура"); Для Каждого Строка ИЗ Данные.Строки Цикл // Номенклатура Код = СокрЛП(мОписаниеТиповСтрока.ПривестиЗначение(Строка.СтруктураАтрибутов["КодТовара"])); Код = ДобавитьНули(Код, СтрДлина(Справочники.Номенклатура.ПустаяСсылка().Код)); Если ПустаяСтрока(Код) Тогда ВывестиСообщение("Не указан код товара",,1); Продолжить; КонецЕсли; Запрос.УстановитьПараметр("Код", Код); Результат = Запрос.Выполнить(); Если Результат.Пустой() Тогда ВывестиСообщение("Элемент справочника ""Номенклатура"" " + Код + " не найден",,1); Продолжить; КонецЕсли; Выборка = Результат.Выбрать(); Выборка.Следующий(); НоменклатураСсылка = Выборка.Ссылка; // Количество Количество = мОписаниеТиповЧисло.ПривестиЗначение(Строка.СтруктураАтрибутов["Количество"]); Если Количество = 0 Тогда ВывестиСообщение("Не удалось получить количество товара",,1); Продолжить; КонецЕсли; // Качество НаимКачество = мОписаниеТиповСтрока.ПривестиЗначение(Строка.СтруктураАтрибутов["Качество"]); Если НаимКачество = "Kондиция" Или НаимКачество = "Кондиция" Тогда Качество = Справочники.Качество.Новый; Иначе Качество = Справочники.Качество.НайтиПоНаименованию(НаимКачество, Истина); Если Не ЗначениеЗаполнено(Качество) Тогда ВывестиСообщение("Элемент справочника ""Качество"" """ + НаимКачество + """ не найден",,1); Продолжить; Качество = Справочники.Качество.ПустаяСсылка(); КонецЕсли; КонецЕсли; НаимЕИ = СокрЛП(мОписаниеТиповСтрока.ПривестиЗначение(Строка.СтруктураАтрибутов["ЕдиницаИзмерения"])); ТекЕИ = Справочники.ЕдиницыИзмерения.НайтиПоНаименованию(НаимЕИ, Истина, ,НоменклатураСсылка); Если Не ЗначениеЗаполнено(ТекЕИ) Тогда ВывестиСообщение("Не удалось найти единицу измерения: " + НаимЕИ + " для товара: " + НоменклатураСсылка, , 1); Продолжить; КонецЕсли; НоваяСтрока = Товары.Добавить(); НоваяСтрока.Номенклатура = НоменклатураСсылка; НоваяСтрока.Количество = Количество; НоваяСтрока.ЕдиницаИзмерения = ТекЕИ; НоваяСтрока.Качество = Качество; КонецЦикла; Товары.Свернуть("Номенклатура,ЕдиницаИзмерения,Качество,Коэффициент", "Количество"); НаборЗаписей = РегистрыСведений.ОстаткиУС.СоздатьНаборЗаписей(); НаборЗаписей.Записать(); Для Каждого ТекСтрока Из Товары Цикл НоваяЗапись = НаборЗаписей.Добавить(); ЗаполнитьЗначенияСвойств(НоваяЗапись, ТекСтрока); КонецЦикла; НаборЗаписей.Записать(); Если НЕ Отказ Тогда НоваяСтрока = ОбработанныеДанные.Добавить(); НоваяСтрока.Направление = "Получение"; НоваяСтрока.Синоним = ИмяОбъекта; НоваяСтрока.Объект = Неопределено; НоваяСтрока.Модифицирован = Истина; КонецЕсли; КонецПроцедуры
Рисунок пропал