6 февр. 2015 г.

Отправка документов из 1С по электронной почте


Бывают ситуации, когда руководитель организации хочет контролировать бухгалтеров и все внесенные ними изменения в документы информационной базы. Есть несколько вариантов, как решить эту задачу. Наиболее рациональный из них и будет рассмотрен в статье, а именно, отправка печатных форм документов в формате xls на электронную почту.
Попутно будет рассмотрено:

  • как программно настроить почтовый профиль в 1С;
  • как создать почтовое электронное сообщение в 1С;
  • как создавать и удалять временные файлы в 1С;
  • как добавлять и удалять записи в регистре сведений;
  • еще много чего полезного.
Теперь более подробное описание задачи.

Руководителю нужно контролировать своих подчиненных, какие они делают документы, что изменяют в документах, когда и кто проводит их. Для этого есть несколько решений. Например, установить расширенный журнал регистрации всех операций или распечатывать документы на бумаге при каждом их проведении. Недостатки этих вариантов в том, что журналы расширенной регистрации платные, а распечатывать документы - огромный расход бумаги и тонера. Но есть удобное решение - отправлять документы на электронную почту руководителя при первом проведении документа и при последующих проведениях, если в них что-то изменялось. Причем, если документов несколько типов, то они все будут красиво распределены по папочкам у директора в Gmail.
Сложность задачи в том, что иногда может пропадать интернет на несколько минут или его вовсе может не быть некоторое время. Поэтому, это мы тоже предусмотрим.
Мы рассмотрим задачу, когда нужно отправлять 2 типа документов: "Поступление товаров и услуг" и "Реализацию товаров и услуг". Конфигурация у нас Управление торговлей для Украины (2.3.23.1).

Общий алгоритм решения такой:

  1. Создаем на mail.google.com новый почтовый ящик (можно использовать и имеющийся).
  2. Настраиваем ярлыки к электронным входящим письмам, чтобы 2 документа автоматически распределялись в разные папки.
  3. Создадим регистр сведений для внесения данных о неотправленных документах, опять же таки, если в момент проведения пропадал интернет.
  4. Дальше будем работать с кодом 1С. Создаем общий модуль, где опишем отправку документов на электронную почту, создание почтового профиля, отправку документов в случае обрыва интернета.
  5. В модуле обычного приложения (у нас обычное приложение, не управляемое), в процедуре ПередНачаломРаботыСистемы() добавим код проверки наличия соединения с интернетом и отправки документов, если интернет есть.
  6. В модуле нужных документов добавим модульную переменную, в процедуре ПередЗаписью() будем присваивать ей значение. А в ОбработкаПроведения(), в зависимости от значения этой переменной - либо отправлять, либо не отправлять документ. 


1. Создание аккаунта Google


Мне кажется, объяснять, как создать аккаунт в Google - это лишнее, но все же, может и пригодится кому. 
Заходим на mail.google.com, нажимаем "Создать аккаунт" (Рис. 1). Все :)
Рис.1. Создание аккаунта Google

Более подробней и с картинками можете почитать, к примеру, тут: http://goldbusinessnet.com/poiskovye-sistemy-i-brauzery/gugl-sozdat-akkaunt-google-registraciya-vxod-uchetnaya-zapis/



2. Настройка фильтров для присваивания ярлыков входящим письмам


  1. Зайти в почту созданного аккаунта.
  2. Нажать на шестеренку, выбрать "Настройки".
  3. Нажать на закладку "Фильтры" (Рис. 2).
    Рис.2. Настройки фильтров входящей почты
  4. Нажать "Создать новый фильтр". В окне ввести адрес, с которого будут приходить письма, ввести слово, которое будет присутствовать в теме письма, как правило, название документа. Нажать далее (Рис. 3).
    Рис.3. Создание фильтра
  5. Присвоить фильтру ярлык. Ярлык - это и будет папка для входящего документа (Рис. 4). Ярлык создается один раз, присваивать можно разным фильтрам.
    Рис.4. Продолжение создания фильтра. Присваивание ярлыка
    1. На этом работа с аккаунтом гугл закончена. Переходим к 1С.

    3. Создаем регистр сведений

    В регистр будем добавлять данные тех документов, что не удалось отправить. Отправлять будем пробовать при следующем старте системы, о чем речь пойдет позже.
    В регистре будут записи. Каждая строка - отдельный документ, который не удалось отправить из-за обрыва интернет-соединения.
    Регистр у нас следующего вида (Рис.5)
    Измерения:
    ИмяФайла - Строка, длина = 100 знаков
    Ресурсы:
    СообщениеТекст - Строка, неограниченная длина
    ДокНомерДата - Строка, длина = 50 знаков.


    Рис.5. Регистр сведений ДокументыНаПочту

    4. Создание общего модуля с механизмами отправки документов

    Открываем окно конфигурации, находим общие модули, добавляем новый, даем ему имя. И далее в него вставляем код, приведенный ниже.
    Внимание! В коде есть процедура СоздатьПрофиль(). Там нужно указывать АдресСервераSMTP, его порт, пароль, пользователя. Так вот, порт прокатывает лишь только тот, где нет SSL шифрования. Через google мне так и не удалось отправить сообщение. Говорят, это можно сделать с помощью программы stunnel (почитать можно здесь http://infostart.ru/public/58093/). Поэтому я настроил отправку через сервер "mail.ukraine.com.ua", порт, указывал без SSL.


    //Функция отправляет документ по электронной почте, с случае обрыва интернета
    //ТабДокумент - это печатная форма нашего документа, сформированная в ОбработкеПроведения
    //из которой мы и входим в текущую процедуру
    //Документ - сам ДокументОбъект
    Функция ОтправитьДокументПоЭлектроннойПочте(ТабДокумент, Документ) Экспорт
          ТабДокумент.Автомасштаб = Истина;
         
          //проверяем ДокументОбъект на его тип. У нас только 2 типа
          //проверяем и формируем текстовые строки для подстановки в тему письма, наименование вложения
          //и, если нужно, записи в регистр сведений
          Если ТипЗнч(Документ) = Тип("ДокументОбъект.ПоступлениеТоваровУслуг") Тогда
                СтрДокНомер = "Поступление №" + Документ.Номер;
                СтрДокНомерДата = СтрДокНомер + " от " + Формат(Документ.Дата,"ДФ=dd.MM.yyyy");
                СообщениеТекст = "Документ <Поступление товаров и услуг> был проведен." + Символы.ПС;
          ИначеЕсли ТипЗнч(Документ) = Тип("ДокументОбъект.РеализацияТоваровУслуг") Тогда
                СтрДокНомер = "Реализация №" + Документ.Номер;
                СтрДокНомерДата = СтрДокНомер + " от " + Формат(Документ.Дата,"ДФ=dd.MM.yyyy");
                СообщениеТекст = "Документ <Реализация товаров и услуг> был проведен." + Символы.ПС;
          КонецЕсли;
         
          //запоминаем имя файла, который будет записываться на диск в каталог временных файлов,
          //(по-умолчанию "C:\Users\user\AppData\Local\Temp")
          //а также, прикрепляться во вложение и, потом, удаляться с временных файлов
          Попытка
                ИмяФайла = КаталогВременныхФайлов() + СтрДокНомерДата + " " + Формат(ТекущаяДата(), "ДФ=ddMMyyHHmmss") + ".xls";
                ЗаписьФайла(ТабДокумент, ИмяФайла);
          Исключение
                Сообщить("Ошибка при записи файла " + ОписаниеОшибки());
                Возврат Неопределено;
          КонецПопытки;
         
          //формируем текст сообщения
          СообщениеТекст = СообщениеТекст + Символы.ПС;
          СообщениеТекст = СообщениеТекст + "Дата и время документа: " + Документ.Дата + Символы.ПС;
          СообщениеТекст = СообщениеТекст + "Номер документа: "+ Документ.Номер + Символы.ПС;
          СообщениеТекст = СообщениеТекст + Символы.ПС;
          СообщениеТекст = СообщениеТекст + "Контрагент: " + Документ.Контрагент + Символы.ПС;
          СообщениеТекст = СообщениеТекст + "Договор контрагента: " + Документ.ДоговорКонтрагента + Символы.ПС;
          СообщениеТекст = СообщениеТекст + "Провел: " + ПараметрыСеанса.ТекущийПользователь + Символы.ПС;
          СообщениеТекст = СообщениеТекст + Символы.ПС;
          СообщениеТекст = СообщениеТекст + "Сумма документа: " + Документ.СуммаДокумента + " " + Документ.ВалютаДокумента;
         
          //создаем новый объект ИнтернетПочта и профиль, пытаемся подключиться к почте
          Почта = Новый ИнтернетПочта;
          Профиль = СоздатьПрофиль();
          Попытка    
                Почта.Подключиться(Профиль);
          Исключение
                //если не получилось подключиться (нет интернета)
                Сообщить(" - Ошибка при подключении" + ОписаниеОшибки());
               
                //добавляем строку в наш регистр сведений
                ДокументыНаПочту = РегистрыСведений.ДокументыНаПочту.СоздатьНаборЗаписей();
                НоваяЗаписьДокументы = ДокументыНаПочту.Добавить();
                НоваяЗаписьДокументы.ИмяФайла = ИмяФайла;
                НоваяЗаписьДокументы.СообщениеТекст = СообщениеТекст;
                НоваяЗаписьДокументы.ДокНомерДата = СтрДокНомерДата;
                НоваяЗаписьДокументы.Период = ТекущаяДата();
                ДокументыНаПочту.Записать(Ложь);
               
                Возврат Неопределено;
          КонецПопытки;
         
          //создаем сообщение, добавляем вложение, тему, указываем отправителя,
          //получателя, добавляем текст
          Сообщение = Новый ИнтернетПочтовоеСообщение;  
          Сообщение.Вложения.Добавить(ИмяФайла, СтрДокНомер);
          Сообщение.Тема = СтрДокНомерДата;       
          Сообщение.Отправитель.Адрес = "constant@mail.ua";
          Сообщение.Тексты.Добавить(СообщениеТекст, ТипТекстаПочтовогоСообщения.ПростойТекст);
         
          Сообщение.Получатели.Добавить("poluchatel@gmail.com");
         
          //пытаемся отправить почту. Интернет-соединение у нас точно есть, т.к. мы пробовали
          //подключиться (Почта.Подключиться(Профиль))
          //если ошибка при передачи, значит у вас неправильно указан адрес SMTP сервера или порт
      //порт может быть правильный, но с SSL шифрованием (перечитать текст с п.4 этой статьи)
          Попытка
                Почта.Послать(Сообщение);
                Сообщить("Проведенный документ отправлен на электронную почту!");
          Исключение
                Сообщить(" - Ошибка при передаче" + ОписаниеОшибки());
                Сообщить("Отключились от почты - " + ТекущаяДата());
          КонецПопытки;
          Почта.Отключиться();   
         
          //возвращаем имя файла, чтобы его потом удалить вне этой процедуры
          //иначе - не удалится
          Возврат ИмяФайла;
    КонецФункции

    Функция СоздатьПрофиль()
          Профиль = Новый ИнтернетПочтовыйПрофиль;
          ПрофильАдресСервераSMTP = "mail.ukraine.com.ua";
          Профиль.ПортSMTP = 2525;
          Профиль.АдресСервераPOP3 = "mail.ukraine.com.ua";
          Профиль.ПортPOP3 = 110;
          Профиль.Пароль = "password";
          Профиль.ПарольSMTP = "password";
          Профиль.Пользователь = "constant@mail.ua";
          Профиль.ПользовательSMTP = "constant@mail.ua";
          Профиль.ВремяОжидания = 50;
          Профиль.АутентификацияSMTP = СпособSMTPАутентификации.ПоУмолчанию;
         
          Возврат Профиль;
    КонецФункции

    //функция вызывается при каждом старте системы, если есть записи о
    //неотправленных документах
    //возвращает ТЗ с путями к временным файлам (файлам xls)
    Функция ОтправитьПослеОбрыва(Результат) Экспорт
          Почта = Новый ИнтернетПочта;
          Профиль = СоздатьПрофиль();
         
          //создаем новую ТЗ
          ВременныеФайлы = Новый ТаблицаЗначений;
          ВременныеФайлы.Колонки.Добавить("ИмяФайла");
         
          //для каждой строки из регистра сведений ДокументыНаПочту
          //будем пытаться отправить документ опять
          //каждая строка - отдельный документ
          Для каждого СтрРегСв Из Результат Цикл
                ИмяФайла = СтрРегСв.ИмяФайла;
                СтрДокНомер = Лев(СтрРегСв.ДокНомерДата, СтрДлина(СтрРегСв.ДокНомерДата) - 27);
                СтрДокНомерДата = СтрРегСв.ДокНомерДата;
               
                Попытка    
                      Почта.Подключиться(Профиль);
                Исключение       
                      //подключиться опять не удалось, все, - выходим и в следующий раз
                      //попытаемся подключиться при следующем старте системы
                      Возврат Неопределено;
                КонецПопытки;
               
                СообщениеТекст = СтрРегСв.СообщениеТекст;
               
                Сообщение = Новый ИнтернетПочтовоеСообщение;  
                Сообщение.Вложения.Добавить(ИмяФайла, СтрДокНомер);
                Сообщение.Тема = СтрДокНомерДата;       
                Сообщение.Отправитель.Адрес = "constant@mail.ua";
                Сообщение.Тексты.Добавить(СообщениеТекст, ТипТекстаПочтовогоСообщения.ПростойТекст);
               
                Сообщение.Получатели.Добавить("poluchatel@gmail.com");
               
         //отправка так же, как и в функции ОтправитьДокументПоЭлектроннойПочте() (выше)
                Попытка
                      Почта.Послать(Сообщение);
                      Сообщить("Проведенный документ отправлен на электронную почту!");
                Исключение
                      Сообщить(" - Ошибка при передаче" + ОписаниеОшибки());
                      Сообщить("Отключились от почты - " + ТекущаяДата());
                      Возврат Неопределено;
                КонецПопытки;

                //удаление записи с регистра сведений
                ДокиРегСвд = РегистрыСведений.ДокументыНаПочту.СоздатьНаборЗаписей();
                ДокиРегСвд.Отбор.ИмяФайла.Установить(ИмяФайла);
                ДокиРегСвд.Прочитать();

                Для Каждого Док Из ДокиРегСвд Цикл
                    ДокиРегСвд.Удалить(Док);
                КонецЦикла;
                ДокиРегСвд.Записать();
                ////
                     
                //добавляем путь в нашу ТЗ, чтобы потом, вне этой функции, его удалить
                НовВрФайл = ВременныеФайлы.Добавить();
                НовВрФайл.ИмяФайла = ИмяФайла;
          КонецЦикла;
         
          Почта.Отключиться();
         
          Возврат ВременныеФайлы;
    КонецФункции


    5. Модуль обычного приложения, процедура ПередНачаломРаботыСистемы()


    //модуль обычного приложения
    Процедура ПередНачаломРаботыСистемы(Отказ)    
          //...тут типовый код

          //в конец процедуры вставляем это:
          //из нашего регистра мы выбираем все записи, чтобы отправить документы
          Запрос = Новый Запрос;
          Запрос.Текст =
          "ВЫБРАТЬ
          |     ДокументыНаПочту.ИмяФайла,
          |     ДокументыНаПочту.СообщениеТекст,
          |     ДокументыНаПочту.ДокНомерДата
          |ИЗ
          |     РегистрСведений.ДокументыНаПочту КАК ДокументыНаПочту";
          Результат = Запрос.Выполнить().Выгрузить();
         
          //функция общего модуля возвращает нам ТЗ, с
          //путями файла, которые удалось отправить
          ВременныеФайлы = общМодульДокПочта.ОтправитьПослеОбрыва(Результат);
          //если удалось отправить хоть один файл - удаляем их с диска
          Если ВременныеФайлы <> Неопределено Тогда
                Для Каждого ВрФайл Из ВременныеФайлы Цикл
                      Попытка
                            УдалитьФайлы(ВрФайл.ИмяФайла);
                      Исключение
                      КонецПопытки;
                КонецЦикла;
          КонецЕсли;
    КонецПроцедуры


    6. Модуль документа, который мы отправляем

    В начало модуля пишем переменную, значение которой будем присваивать в процедуре ПередЗаписью().
    Переменная служит для того, чтобы отправлять на почту только тогда, когда проводится измененный документ. Если просто перепроведение - не отправлять.

    Перем ОтправлятьНаПочту

    Процедура ПередЗаписью().
    Некоторые строки процедуры, например, вот эта: 
    ОбработкаТабличныхЧастей.ПриЗаписиПроверитьСтавкуНДС(ЭтотОбъект, Товары)
    изменяют свойство Модифицированность() и документ будет отправляться всегда, даже если по факту не был изменен пользователем.
    Поэтому, лучше всего вставить этот кусок кода в начало процедуры ПередЗаписью().

    Если ЭтотОбъект.Модифицированность() Тогда
          ОтправлятьНаПочту = Истина;
    Иначе
          ОтправлятьНаПочту = Ложь;
    КонецЕсли;

    Процедура ОбработкаПроведения()

    //в этот блок (Отказ) нужно вставить наш код
    Если Не Отказ Тогда
          // тут код типовых механизмов (движения по регистрам и т.д.),
          // после него вставляем:
                                
          //{c}onstant-solutions

          //если документ проведен и нужно отправлять на почту
          Если Проведен И ОтправлятьНаПочту Тогда 
                //формируем печатную форму документа    
                ТабДокумент = ПечатьДокумента(Ложь);
                //передаем ее в функцию, где отправим на почту
                ВременныйФайл = общМодульДокПочта
                                                        .ОтправитьДокументПоЭлектроннойПочте(ТабДокумент, ЭтотОбъект);
                Попытка
                      УдалитьФайлы(ВременныйФайл);
                Исключение
                КонецПопытки;
          КонецЕсли;
          ////
    КонецЕсли;

    Комментариев нет:

    Отправить комментарий

    Спрашивайте, критикуйте, оставьте свое мнение