Наверное каждому кто пишет свою конфигурацию с нуля, рано или поздно понадобится подключить к ней какое то оборудование, будь то принтер чеков, кассовый аппарат, сканер штрихкодов и т.д. Если делать это с нуля без каких либо заготовок, задача может показаться не такой то и простой, так как потребуется изучить само оборудование, найти драйвера к нему и написать интерфейс обмена его со своей конфигурацией.
Однако не стоит идти путем сопротивление, когда есть достаточно простое и эффективное решение в виде Библиотеки подключаемого оборудования (БПО) от компании 1С. Библиотека предназначена для внедрения в любую конфигурацию и содержит все заготовки для подключения большого количества оборудования. Ее то мы и сегодня рассмотрим. На момент написания данной статьи, текущей версией БПО является 2.1.1.25. Скачать ее надо с сайта ИТС. Ну или если хорошо поискать можно найти на просторах рунета.
Итак скачав и установив БПО, откройте конфигуратор и сделайте операцию Сравнения и объединения и конфигурацией БПО. Сама она находится приблизительно по следующему адресу: C:\Users\nariman\AppData\Roaming\1C\1c8\tmplts\1c\CEL\2_1_1_25\ExtFiles
На вопрос о постановке на поддержку я бы посоветовал ответить Да.
Далее необходимо отметить необходимые вам подсистемы.
1) Для начала снимите галочки с корня конфигурации и диалоге сравнения и объединения, затем выполните действие «Отметить по подсистемам из файла» и отметьте подсистему – ПодключаемоеОборудование. Снимите флажок «Включать объекты подчинённых подсистем». Нажмите «Установить».
2) Ещё раз выполните действие «Отметить по подсистемам из файла». На этот раз отметьте подсистемы необходимого оборудования вложенные в ПодключаемоеОборудование. Нажмите «Установить».
3) Нажмите Выполнить и Продолжить.
Далее согласно инструкциям с ИТС:
В целевую конфигурацию в модуль управляемого приложения в процедуры ПередНачаломРаботыСистемы(), ПриНачалеРаботыСистемы(), ПередЗавершениемРаботыСистемы(), ОбработкаВнешнегоСобытия()перенести код отвечающий за инициализацию и работу библиотеки.
Далее включаем справочники ДрайверыОборудования и ПодключаемоеОборудование в свою подсистему и включаем мх в командный интерфейс управляемого приложения. При попытке запуска настройки рабочего места мы получим ошибку.
Чтож ожидаемо, так как мы не перенесли код в модуль управляемого приложения. Так давайте же сделаем это.
<code>// ПодключаемоеОборудование Перем глПодключаемоеОборудование Экспорт; // для кэширования на клиенте // Конец ПодключаемоеОборудование Процедура ПередНачаломРаботыСистемы(Отказ) // ПодключаемоеОборудование МенеджерОборудованияКлиент.ПередНачаломРаботыСистемы(); // Конец ПодключаемоеОборудование КонецПроцедуры Процедура ПриНачалеРаботыСистемы() // ПодключаемоеОборудование МенеджерОборудованияКлиент.ПриНачалеРаботыСистемы(); // Конец ПодключаемоеОборудование КонецПроцедуры Процедура ПередЗавершениемРаботыСистемы(Отказ) // ПодключаемоеОборудование МенеджерОборудованияКлиент.ПередЗавершениемРаботыСистемы(); // Конец ПодключаемоеОборудование КонецПроцедуры Процедура ОбработкаВнешнегоСобытия(Источник, Событие, Данные) // ПодключаемоеОборудование // Подготовить данные ОписаниеСобытия = Новый Структура(); ОписаниеОшибки = ""; ОписаниеСобытия.Вставить("Источник", Источник); ОписаниеСобытия.Вставить("Событие", Событие); ОписаниеСобытия.Вставить("Данные", Данные); // Передать на обработку данные. Результат = МенеджерОборудованияКлиент.ОбработатьСобытиеОтУстройства(ОписаниеСобытия, ОписаниеОшибки); Если Не Результат Тогда ОбщегоНазначенияКлиентСервер.СообщитьПользователю(НСтр("ru='При обработке внешнего события от устройства произошла ошибка.'") + Символы.ПС + ОписаниеОшибки); КонецЕсли; // Конец ПодключаемоеОборудование КонецПроцедуры </code>
Также нужно установить в параметрах сеанса код, ответственный за установку параметров. Пример кода можно посмотреть в Демо базае БПО, текущего релиза, в моем случае это было:
<code>#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда #Область ОбработчикиСобытий Процедура УстановкаПараметровСеанса(ИменаПараметровСеанса) // СтандартныеПодсистемы СтандартныеПодсистемыСервер.УстановкаПараметровСеанса(ИменаПараметровСеанса); // Конец СтандартныеПодсистемы // УниверсальныеМеханизмы Если ИменаПараметровСеанса = Неопределено Тогда // Раздел безусловной установки параметров сеанса. // Конец УниверсальныеМеханизмы // Переопределяемый блок // // Конец переопределяемого блока. // УниверсальныеМеханизмы Иначе // Установка параметров сеанса "по требованию". // Параметры сеанса, инициализация которых требует обращения к одним и тем же данным // следует инициализировать сразу группой. для того, чтобы избежать их повторной инициализации, // имена уже установленных параметров сеанса сохраняются в структуре УстановленныеПараметры. УстановленныеПараметры = Новый Массив(); Для Каждого ИмяПараметра Из ИменаПараметровСеанса Цикл УстановитьЗначениеПараметраСеанса(ИмяПараметра, УстановленныеПараметры); КонецЦикла; КонецЕсли; // Конец УниверсальныеМеханизмы КонецПроцедуры Процедура УстановитьЗначениеПараметраСеанса(Знач ИмяПараметра, УстановленныеПараметры) // Конец УниверсальныеМеханизмы Если УстановленныеПараметры.Найти(ИмяПараметра) <> Неопределено Тогда Возврат; КонецЕсли; // Конец УниверсальныеМеханизмы // Переопределяемый блок // ПодключаемоеОборудование МенеджерОборудованияВызовСервера.УстановитьПараметрыСеансаПодключаемогоОборудования(ИмяПараметра, УстановленныеПараметры); // Конец ПодключаемоеОборудование // Конец переопределяемого блока. КонецПроцедуры #КонецОбласти #КонецЕсли</code>
Итак запускаем базу и видим что ошибок нет. Добавляем драйвера, в конфу, я их выгрузил из справочники Драйверы оборудования и добавил ручками в свою конфу. И создаем экземпляр оборудования.
Ну а дальше дело за малым, читаем документацию соответственно подключаемому оборудованию и реализуем код. В моем случае для печати чека на принтере чеков я реализовал простейшую функцию.
<code>#Область Печать &НаКлиенте Процедура ПробитьЧек(Команда) Если Объект.Услуги.Количество() > 0 И Объект.СуммаДокумента > 0 Тогда ИдентификаторККТ = мд_ОбщиеПроцедурыСервер.ПолучитьИдентификаторПринтераЧеков(); ВходныеПараметры = ПолучитьДанныеЧека(Неопределено, ИдентификаторККТ); ВыходныеПараметры = Неопределено; ПослеОткрытияЧека = Неопределено; ОписаниеОповещения = Новый ОписаниеОповещения("ФискализацияЧека_Завершение", ЭтотОбъект,); МенеджерОборудованияКлиент.НачатьФискализациюЧекаНаФискальномУстройстве(ОписаниеОповещения, УникальныйИдентификатор, ВходныеПараметры, ИдентификаторККТ, , ПослеОткрытияЧека); Иначе Сообщить("В документе нет оказанных услуг"); КонецЕсли; КонецПроцедуры Функция ПолучитьДанныеЧека(ПараметрыОперации = Неопределено, ИдентификаторУстройства = Неопределено); // Подготовка соответствие секций для табличной части. ТаблицаТоваровСоответствииСекциям = Новый Соответствие(); //_ДемоПодключаемоеОборудованиеВызовСервера.ЗаполнитьСоответствиеСекцийДляТабличнойЧасти(Объект.РаспределенияВыручки, Объект.Товары, ТаблицаТоваровСоответствииСекциям); // Общие параметры чека ОбщиеПараметры = МенеджерОборудованияКлиентСервер.ПараметрыОперацииЧекаКоррекции(); ОбщиеПараметры.ДокументОснование = Объект.Ссылка; //Если ЗначениеЗаполнено(КорректируемыйДокумент) Тогда // ОбщиеПараметры.КорректируемыйДокумент = КорректируемыйДокумент //КонецЕсли; ФискальнаяОперации = МенеджерОборудованияВызовСервера.ДанныеФискальнойОперации(ОбщиеПараметры.ДокументОснование, XMLСтрока(ОбщиеПараметры.ДокументОснование)); Если ФискальнаяОперации <> Неопределено Тогда ПараметрыQRКода = МенеджерОборудованияКлиентСервер.ПараметрыQRКодаЧекаККТ(); ПараметрыQRКода.ДатаВремяРасчета = ФискальнаяОперации.Дата; ПараметрыQRКода.СуммаРасчета = ФискальнаяОперации.Сумма; ПараметрыQRКода.НомерФискальногоНакопителя = ФискальнаяОперации.ЗаводскойНомерФН; ПараметрыQRКода.НомерФискальногоДокумента = ФискальнаяОперации.НомерЧекаККМ; ПараметрыQRКода.ФискальныйПризнак = ФискальнаяОперации.ФискальныйПризнак; ПараметрыQRКода.ПризнакРасчета = Объект.ТипОперации; QRКод = МенеджерОборудованияКлиентСервер.СформироватьQRКодЧекаККТ(ПараметрыQRКода); ОбщиеПараметры.Вставить("QRКод", QRКод); КонецЕсли; НомерСтроки = 0; Для Каждого СтрокаТЧ Из Объект.Услуги Цикл НомерСтроки = НомерСтроки + 1; НомерСекции = ТаблицаТоваровСоответствииСекциям.Получить(СтрокаТЧ.НомерСтроки); СтрокаПозицииЧека = МенеджерОборудованияКлиентСервер.ПараметрыФискальнойСтрокиЧека(); СтрокаПозицииЧека.ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПредоплатаПолная; СтрокаПозицииЧека.ПризнакПредметаРасчета = Перечисления.ПризнакиПредметаРасчета.Услуга; СтрокаПозицииЧека.Наименование = Строка(СтрокаТЧ.Услуга); СтрокаПозицииЧека.Количество = СтрокаТЧ.Количество; СтрокаПозицииЧека.Цена = СтрокаТЧ.Цена; СтрокаПозицииЧека.Сумма = СтрокаТЧ.Сумма; СтрокаПозицииЧека.НомерСекции = НомерСекции; СтрокаПозицииЧека.СуммаСкидок = 0; СтрокаПозицииЧека.СтавкаНДС = 20; СтрокаПозицииЧека.НомерСтрокиТовара = НомерСтроки; СтрокаПозицииЧека.СуммаНДС = 0; СтрокаПозицииЧека.ЕдиницаИзмерения = "Штука"; //СтрокаПозицииЧека.ПризнакАгентаПоПредметуРасчета = СтрокаТЧ.Номенклатура.ПризнакАгента; //СтрокаПозицииЧека.ДанныеПоставщика.Телефон = СтрокаТЧ.Номенклатура.ДанныеПоставщикаТелефон; //СтрокаПозицииЧека.ДанныеПоставщика.Наименование = СтрокаТЧ.Номенклатура.ДанныеПоставщикаНаименование; //СтрокаПозицииЧека.ДанныеПоставщика.ИНН = СтрокаТЧ.Номенклатура.ДанныеПоставщикаИНН; //СтрокаПозицииЧека.ДанныеАгента.ПлатежныйАгент.Операция = СтрокаТЧ.Номенклатура.ПлатежныйАгентОперация; //СтрокаПозицииЧека.ДанныеАгента.ПлатежныйАгент.Телефон = СтрокаТЧ.Номенклатура.ПлатежныйАгентТелефон; //СтрокаПозицииЧека.ДанныеАгента.ОператорПеревода.Телефон = СтрокаТЧ.Номенклатура.ОператорПереводаТелефон; //СтрокаПозицииЧека.ДанныеАгента.ОператорПеревода.Наименование = СтрокаТЧ.Номенклатура.ОператорПереводаНаименование; //СтрокаПозицииЧека.ДанныеАгента.ОператорПеревода.Адрес = СтрокаТЧ.Номенклатура.ОператорПереводаАдрес; //СтрокаПозицииЧека.ДанныеАгента.ОператорПеревода.ИНН = СтрокаТЧ.Номенклатура.ОператорПереводаИНН; //СтрокаПозицииЧека.ДанныеАгента.ОператорПоПриемуПлатежей.Телефон = СтрокаТЧ.Номенклатура.ОператорПоПриемуПлатежейТелефон; //СтрокаПозицииЧека.ДанныеКодаТоварнойНоменклатуры.ТипМаркировки = СтрокаТЧ.ТипМаркировки; //СтрокаПозицииЧека.ДанныеКодаТоварнойНоменклатуры.КонтрольныйИдентификационныйЗнак = СтрокаТЧ.КонтрольныйИдентификационныйЗнак; //СтрокаПозицииЧека.ДанныеКодаТоварнойНоменклатуры.ГлобальныйИдентификаторТорговойЕдиницы = СтрокаТЧ.ГлобальныйИдентификаторТорговойЕдиницы; //СтрокаПозицииЧека.ДанныеКодаТоварнойНоменклатуры.СерийныйНомер = СтрокаТЧ.СерийныйНомер; //СтрокаПозицииЧека.КодСтраныПроисхожденияТовара = СтрокаТЧ.КодСтраныПроисхожденияТовара; //СтрокаПозицииЧека.НомерТаможеннойДекларации = СтрокаТЧ.НомерТаможеннойДекларации; //СтрокаПозицииЧека.СуммаАкциза = СтрокаТЧ.СуммаАкциза; //СтрокаПозицииЧека.ДополнительныйРеквизит = СтрокаТЧ.ДополнительныйРеквизит; ОбщиеПараметры.ПозицииЧека.Добавить(СтрокаПозицииЧека); КонецЦикла; // Подготовка таблицы оплат СтрокаОплаты = МенеджерОборудованияКлиентСервер.ПараметрыСтрокиОплаты(); СтрокаОплаты.ТипОплаты = Перечисления.ТипыОплатыККТ.Наличные; СтрокаОплаты.Сумма = Объект.СуммаДокумента; ОбщиеПараметры.ТаблицаОплат.Добавить(СтрокаОплаты); ОбщиеПараметры.ПризнакАгента = Ложь; //ОбщиеПараметры.ДанныеПоставщика.Телефон = Объект.ДанныеПоставщикаТелефон; //ОбщиеПараметры.ДанныеПоставщика.Наименование = Объект.ДанныеПоставщикаНаименование; //ОбщиеПараметры.ДанныеПоставщика.ИНН = Объект.ДанныеПоставщикаИНН; //ОбщиеПараметры.ДанныеАгента.ПлатежныйАгент.Операция = Объект.ПлатежныйАгентОперация; //ОбщиеПараметры.ДанныеАгента.ПлатежныйАгент.Телефон = Объект.ПлатежныйАгентТелефон; //ОбщиеПараметры.ДанныеАгента.ОператорПеревода.Телефон = Объект.ОператорПереводаТелефон; //ОбщиеПараметры.ДанныеАгента.ОператорПеревода.Наименование = Объект.ОператорПереводаНаименование; //ОбщиеПараметры.ДанныеАгента.ОператорПеревода.Адрес = Объект.ОператорПереводаАдрес; //ОбщиеПараметры.ДанныеАгента.ОператорПеревода.ИНН = Объект.ОператорПереводаИНН; //ОбщиеПараметры.ДанныеАгента.ОператорПоПриемуПлатежей.Телефон = Объект.ОператорПоПриемуПлатежейТелефон; // Общие реквизиты для всех типов оборудования. ОбщиеПараметры.ТипРасчета = Перечисления.ТипыРасчетаДенежнымиСредствами.ПриходДенежныхСредств; ОбщиеПараметры.Кассир = СокрЛП("Анна Каренина"); ОбщиеПараметры.КассирИНН = СокрЛП("4205454848484"); ОбщиеПараметры.ОрганизацияНазвание = "ИП Рамазанов Рамазан"; ОбщиеПараметры.ОрганизацияИНН = "123456789012"; ОбщиеПараметры.ОрганизацияКПП = "234523454542"; Если Не ПустаяСтрока(Объект.Пациент) Тогда ОбщиеПараметры.Получатель = Строка(Объект.Пациент); ОбщиеПараметры.ПолучательИНН = ""; КонецЕсли; ОбщиеПараметры.АдресРасчетов = "г.Москва, Дмитровское ш. д.9"; //ОбщиеПараметры.МестоРасчетов = "Северное крыло, комната 9441"; // Параметры необходимые для чека ЕНВД на принтере чеков ОбщиеПараметры.НомерКассы = "3463345"; ОбщиеПараметры.НомерЧека = Объект.Номер; ОбщиеПараметры.НомерСмены = 3; // Новые параметры для ККТ по ФЗ-54 ОбщиеПараметры.ОтправительEmail = "info@1c.ru"; Если ПараметрыОперации <> Неопределено И ПараметрыОперации.Свойство("Печать") Тогда ОбщиеПараметры.Электронно = НЕ ПараметрыОперации.Печать; Если ПараметрыОперации.Свойство("ПокупательEmail") И Не ПустаяСтрока(ПараметрыОперации.Свойство("ПокупательEmail")) Тогда ОбщиеПараметры.ПокупательEmail = ПараметрыОперации.ПокупательEmail; ОбщиеПараметры.Отправляет1СEmail = ПараметрыОперации.ОтправлятьEmailСредствами1С; КонецЕсли; Если ПараметрыОперации.Свойство("ПокупательНомер") И Не ПустаяСтрока(ПараметрыОперации.Свойство("ПокупательНомер")) Тогда ОбщиеПараметры.ПокупательНомер = ПараметрыОперации.ПокупательНомер; ОбщиеПараметры.Отправляет1СSMS = ПараметрыОперации.ОтправлятьSMSСредствами1С; КонецЕсли; Иначе ОбщиеПараметры.Электронно = Ложь; КонецЕсли; ОбщиеПараметры.СистемаНалогообложения = Перечисления.ТипыСистемНалогообложенияККТ.УСНДоход; ОбщиеПараметры.ДанныеКоррекции.ТипКоррекции = 0; ОбщиеПараметры.ДанныеКоррекции.ОписаниеКоррекции = ""; ОбщиеПараметры.ДанныеКоррекции.ДатаКоррекции = ТекущаяДатаСеанса(); ОбщиеПараметры.ДанныеКоррекции.НомерПредписания = 0; ОбщиеПараметры.НеприменениеККТ = Ложь; //ОбщиеПараметры.ДополнительныйРеквизит = Объект.ДополнительныйРеквизит; //ОбщиеПараметры.ДополнительныйРеквизитПользователя.Наименование = Объект.РеквизитПользователяНаименование; //ОбщиеПараметры.ДополнительныйРеквизитПользователя.Значение = Объект.РеквизитПользователяЗначение; Если ИдентификаторУстройства <> Неопределено Тогда РеквизитыОборудования = ФорматноЛогическийКонтрольВызовСервера.СтруктураДанныхФорматноЛогическогоКонтроля(ИдентификаторУстройства); ОбщиеПараметры.СпособФорматноЛогическогоКонтроля = РеквизитыОборудования.СпособФорматноЛогическогоКонтроля; ОбщиеПараметры.ДопустимоеРасхождениеФорматноЛогическогоКонтроля = РеквизитыОборудования.ДопустимоеРасхождениеФорматноЛогическогоКонтроля; Иначе ОбщиеПараметры.СпособФорматноЛогическогоКонтроля = Перечисления.СпособыФорматноЛогическогоКонтроля.РазделятьСтроки; КонецЕсли; ОбщиеПараметры.СформироватьЧекКоррекции = Ложь; Возврат ОбщиеПараметры; КонецФункции &НаКлиенте Процедура ФискализацияЧека_Завершение(РезультатВыполнения, Параметры) Экспорт Если РезультатВыполнения.Результат Тогда Иначе ТекстСообщения = РезультатВыполнения.ОписаниеОшибки; ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения); КонецЕсли; Доступность = Истина; КонецПроцедуры </code>