ПРЕДЫДУЩАЯ ЧАСТЬ. ЧАСТЬ І. СПРАВОЧНИКИ
В части первой мы уже коротко выяснили что такое OData и описали некоторые общие процедуры и функции (в первой части я называл их служебными), мы используем (или даже расширим) для разработки модуля обмена документами следующие из них:
Функция ПолучитьКаналСвязиССерверомСбораДаних() // не изменится в этой статье
Функция ОпределитьШапкуЗапроса(GUID = "00000000-0000-0000-0000-000000000000") // не изменится в этой статье
Функция НормализироватьКОбмену(ЭтотРеквизит) // не изменится в этой статье
Функция СоздатьОписанияДополнительнихРеквизитов(СсылкаОбъекта, СписокСсылочных = 0) // расширится
Функция СоздатьОписанияТабличныхЧастей(СсылкаОбъекта, ПрефиксОбъекта, Тень = Ложь) // расширится
Вот так расширится и "главная" экспортная процедура отправки данных на сервер:
Как видите добавились два новых параметры процедуры:
1) БезКопии - булево - по умолчанию - Ложь - мы начали передавать "ссылочные" реквизиты некоторых документов, но не хотели наполнять базу теневыми копиями, поэтому некоторые объекты НЕ копируются в тень для отложенной отправки;
2) ОписаниеРеквизитовОтправки - список значений - по умолчанию - Неопределено - не все реквизиты справочника для нас критически необходимо видеть на сервере, например, у нас на узле в справочнике ФизическиеЛица есть реквизиты ДатаРождения и ИНН, но на сервере нам необходимо видеть только реквизит ИНН - тогда в параметр ОписаниеРеквизитовОтправки мы передадим это:
Список = Новый СписокЗначений;
Список.Добавить("ИНН"); // имя реквизита как в конфигураторе
Тело функции для обмена документом:
Функция ДокументДоставленоУспешно(СсылкаДокумент, DELETE, Метод = "POST", Тень = Ложь, ФормироватьКлюч = Ложь, СписокРеквизитов = Неопределено)
Если СсылкаДокумент.Пустая() Тогда
Возврат Истина;
КонецЕсли;
Если Константы.СерверСбораДанныхОфлайн.Получить() И (НЕ Тень) Тогда // если связи нету можна просто перейти в офлайн используя Константы.СерверСбораДанныхОфлайн.Установить(Истина)
Возврат Ложь;
КонецЕсли;
Связь = ПолучитьКаналСвязиССерверомСбораДаних(); // служебное - см. часть І
ИмяДокумента = СсылкаДокумент.Метаданные().Имя;
Если Тень Тогда
// теневые копию я создавал в идентичном обьъкте как оригинальний док,
// только с префиксом «_с_». Зачем? Запрос по теневым копиям работают быстрее,
// и в случае успешной доставки теневой копию — копия удалялась
ИмяДокумента = СтрЗаменить(ИмяДокумента,"_с_","");
КонецЕсли;
guid = Строка(СсылкаДокумент.УникальныйИдентификатор());
Заголовки = Новый Соответствие;
Заголовки.Вставить("Accept", "application/atom+xml,application/xml");
Заголовки.Вставить("Accept-Charset", "UTF-8");
Заголовки.Вставить("Content-Type", "application/atom+xml");
Заголовки.Вставить("DataServiceVersion", "3.0;NetFx");
Заголовки.Вставить("MaxDataServiceVersion", "3.0;NetFx");
Заголовки.Вставить("User-Agent", "1C-Enterprise");
Заголовки.Вставить("Host", Связь.Сервер);
Если DELETE Тогда // если удалить объект
хАдресРесурса = "/" + Связь.Порт + "/odata/standard.odata/Document_" + ИмяДокумента + "(guid'"+ guid + "')";
Соединение = Новый HTTPСоединение(Связь.Сервер);
Заголовки.Вставить("DELETE" + " /" + Связь.Порт + "/odata/standard.odata/Catalog_" + ИмяДокумента + "(guid'"+ guid + "')");
Попытка
хЗапрос = Новый HTTPЗапрос(хАдресРесурса, Заголовки);
Ответ = Соединение.ВызватьHTTPМетод("DELETE", хЗапрос);
Возврат Ответ.КодСостояния = 204 // случае успешного удаления сервер вернет код состояния =204
Исключение
Возврат Ложь;
КонецПопытки;
Иначе // если создать/обновить объект
АдресРесурса = "/" + Связь.Порт + "/odata/standard.odata/Document_" +
ИмяДокумента + ?(Метод = "PUT", "(guid'" + guid + "')","");
ТекстЗапроса = ОпределитьШапкуЗапроса(guid); // служебное - см. часть І
// соответствие стандартных реквизитов
CписокCтандартныхРеквизитов = СоздатьОписанияОбязательнихРеквизитовДокумента(СсылкаДокумент); // новое служебное - смотри далее
Для Каждого ОписаниеРеквизита ИЗ CписокCтандартныхРеквизитов Цикл
ТекстЗапроса = ТекстЗапроса + "
| <d:" + ОписаниеРеквизита.Ключ + ">" + СокрЛП(ОписаниеРеквизита.Значение) + "</d:" + ОписаниеРеквизита.Ключ + ">";
КонецЦикла;
// ссылочные реквизиты шапки(суфикс _Key)
СписокКейс = Новый СписокЗначений();
Если ИмяДокумента = "ЧекККМ" Тогда
СписокКейс.Добавить("Магазин");
СписокКейс.Добавить("Ответственный");
СписокКейс.Добавить("Пользователь");
СписокКейс.Добавить("Место");
КонецЕсли;
// соответствие дополнительных реквизитов
СписокДопРеквизитов = СоздатьОписанияДополнительнихРеквизитов(СсылкаДокумент, СписокКейс); // служебное РАСШИРЕННОЕ - см. часть І
Для Каждого ОписаниеДопРеквизита ИЗ СписокДопРеквизитов Цикл
ТекстЗапроса = ТекстЗапроса + "
| <d:" + ОписаниеДопРеквизита.Ключ + ">" + СокрЛП(ОписаниеДопРеквизита.Значение) + "</d:" + ОписаниеДопРеквизита.Ключ + ">";
КонецЦикла;
// ссылочные реквизиты табличных частей(суфикс _Key)
СписокКейсТЧ = Новый СписокЗначений();
Если ИмяДокумента = "ЧекККМ" Тогда
СписокКейсТЧ.Добавить("Номенклатура");
КонецЕсли;
// соответствие табличных частей
ОписаниеТабличныхЧастей = СоздатьОписанияТабличныхЧастей(СсылкаДокумент, "Document", Тень, СписокКейсТЧ); // служебное РАСШИРЕННОЕ - см. часть І
Если НЕ ОписаниеТабличныхЧастей = "" Тогда
ТекстЗапроса = ТекстЗапроса + ОписаниеТабличныхЧастей;
КонецЕсли;
// ключ
Если ФормироватьКлюч Тогда // все объекты которые не копируются в тень передаються с ключем - у нас это GUID Основного магазина
ТекстЗапроса = ТекстЗапроса + "
| <d:Магазин_Key>" + СокрЛП(Строка(Константы.ОсновнойМагазин.Получить().УникальныйИдентификатор())) + "</d:Магазин_Key>";
КонецЕсли;
ТекстЗапроса = ТекстЗапроса + "
| </m:properties>
| </content>
|</entry>";
Соединение = Новый HTTPСоединение(Связь.Сервер);
Заголовки.Вставить("1C_OData_DataLoadMode", Истина); // ВАЖНО! Документы по старинке в режиме "ОбменДанными.Загрузка = Истина"
Заголовки.Вставить(Метод + " /" + Связь.Порт + "/odata/standard.odata/Document_" + ИмяДокумента + ?(Метод = "PUT", "(guid'" + guid + "')","") + " HTTP/1.1");
Попытка
Запрос = Новый HTTPЗапрос(АдресРесурса, Заголовки);
Запрос.УстановитьТелоИзСтроки(ТекстЗапроса);
Ответ = Соединение.ВызватьHTTPМетод(Метод, Запрос);
Если (Ответ.КодСостояния <> 201) И (Метод = "POST") Тогда // успешный POST когда КодСостояния = 201
Возврат ДокументДоставленоУспешно(СсылкаДокумент, DELETE,"PUT", Тень, ФормироватьКлюч, СписокРеквизитов);
ИначеЕсли (Ответ.КодСостояния <> 200) И (Метод = "PUT") Тогда // успешный PUT когда КодСостояния = 200
Возврат Ложь
Иначе
Возврат Истина
КонецЕсли;
Исключение
Возврат Ложь;
КонецПопытки;
КонецЕсли;
КонецФункции
Вот список необходимых функций обозначенных комментарием "новое служебное" или "служебное РАСШИРЕННОЕ" (не ругайте - знаю местам их можно и даже нужно оптимизировать):
Ну, вроде по документу все. Ах да - а что делать если пользователь выполнит отмену проведения или вообще удалит документ? Ну, тогда нужно прописать в соответствующих местах модуля объекта документа следующее (документ исчезнет на сервере):
Процедура ОбработкаУдаленияПроведения(Отказ)
ОбменССерверомСбораДанных.ОтправитьНаСерверСбораДанных(ЭтотОбъект.Ссылка,Истина,2);
КонецПроцедуры
СЛЕДУЮЩАЯ ЧАСТЬ. ЧАСТЬ ІІІ: РЕГИСТРЫ СВЕДЕНИЙ
Спасибо, что дочитали до конца! :)