Штрихкодирование документов
Эта статья будет полезна для тех, перед кем стоит задача нетипового штрихкодирования документов.
- Описание
- Подробнее
Описание
Раз, два, три!.. Проверка связи. Это моя первая статья на Инфостарте, причём с материалом, который частично был разработан не мной.
Но эта наработка уже не раз помогала мне быстро и весело внедрять штрихкодирование документов в любую конфигурацию на обычных формах.
Также у этой публикации есть ещё одна цель: скорее всего, мои алгоритмы не оптимизированы, поэтому публикация может вызвать шквал объективной критики, которую можно будет взять на вооружение.
Итак, мы хотим, чтобы печатные формы документов содержали штрихкод-ссылку на документ.
1. Скачиваем шрифт Barcode.ttf, ставим его на все компы пользователей. Установить можно двумя способами: а) двойной клик на файле -> Установить; б) положить файл в директорию C:\Windows\Fonts.
2. В нужных макетах печатных форм документов завести ячейку под штрихкод, в качестве шрифта ячейки указать barcode. Размер я обычно ставлю 28, дело вкуса. Это будет наш параметр "ШтрихКод"
3. Простой регистр сведений "ШтрихкодыДокументов": Измерение: Штрихкод — строка, 13 (я обычно использую EAN13, просто и сердито), ресурсы: Документ — составной тип, состоящий из штрихкодируемых документов (или просто ДокументСсылка), GUID — УникальныйИдентификатор.
4. Подписка на событие при записи документа. В подписке на событие проверяем есть ли у документа штрихкод запросом к регистру, если нет генерируем новый, не забываем проверять его уникальность.
Результат = Ложь;
// первое: получим штрихкод из GUID-а, чтобы сразу в запросе проверить его уникальность, дабы убрать лишний запрос к БД
GUID = СсылкаНаОбъект.УникальныйИдентификатор();
Штрихкод = ПодготовитьШтрихКодИзGIUDа(GUID);
// второе: проверяем наличие штрихкода, а так же уникальность кода в пакетном запросе
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ШтрихкодыДокументов.Штрихкод
|ИЗ
| РегистрСведений.ШтрихкодыДокументов КАК ШтрихкодыДокументов
|ГДЕ
| ШтрихкодыДокументов.Документ = &Документ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ШтрихкодыДокументов.Штрихкод
|ИЗ
| РегистрСведений.ШтрихкодыДокументов КАК ШтрихкодыДокументов
|ГДЕ
| ШтрихкодыДокументов.Штрихкод = &Штрихкод";
Запрос.УстановитьПараметр("Документ", СсылкаНаОбъект);
Запрос.УстановитьПараметр("Штрихкод", Штрихкод);
Результат = Запрос.ВыполнитьПакет();
ВыборкаДетальныеЗаписи = Результат[0].Выбрать();
Если ВыборкаДетальныеЗаписи.Следующий() Тогда
// штрихкод есть - возвращаем Истину
Возврат Истина
КонецЕсли;
// третье: штрихкода нет - запишем
// сначала проверим ШК на основе GUID на уникальность:
ВыборкаУникальность = Результат[1].Выбрать();
Если ВыборкаУникальность.Следующий() Тогда
// ШК не уникален. генерируем новый
КодУникален = Ложь;
Пока НЕ КодУникален Цикл
Штрихкод = ПодготовитьШтрихКодИзGIUDа(Новый УникальныйИдентификатор);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ШтрихкодыДокументов.Штрихкод
|ИЗ
| РегистрСведений.ШтрихкодыДокументов КАК ШтрихкодыДокументов
|ГДЕ
| ШтрихкодыДокументов.Штрихкод = &Штрихкод";
Запрос.УстановитьПараметр("Штрихкод", Штрихкод);
Результат = Запрос.Выполнить();
КодУникален = Результат.Пустой();
КонецЦикла;
КонецЕсли;
ЗаписьРегистра = РегистрыСведений.ШтрихкодыДокументов.СоздатьМенеджерЗаписи();
ЗаписьРегистра.Документ = СсылкаНаОбъект;
ЗаписьРегистра.Штрихкод = Штрихкод;
ЗаписьРегистра.GUID = GUID;
ЗаписьРегистра.Период = ТекущаяДата();
ЗаписьРегистра.Ответственный = ПараметрыСеанса.ТекущийПользователь;
Попытка
ЗаписьРегистра.Записать();
Результат = Истина;
Исключение
Сообщить(ОписаниеОшибки());
Результат = Ложь;
КонецПопытки;
Возврат Результат;
Процедура ПодготовитьШтрихкодИзGUID
Суть проста — убираем из Уникального идентификатора все дефисы и буквы, если цифр не хватает — дополняем нулями. Здесь использован типовой справочник ФорматыШтрихКодов, в котором определяются префиксы для документов. Если такого нет в конфигурации, то я обычно добавляю свой, чтобы по префиксам различать типы документов.
фШтрихКод = фGIUD;
фШтрихКод = СтрЗаменить(фШтрихКод, "-", "");
фШтрихКод = СтрЗаменить(фШтрихКод, "a", "");
фШтрихКод = СтрЗаменить(фШтрихКод, "b", "");
фШтрихКод = СтрЗаменить(фШтрихКод, "c", "");
фШтрихКод = СтрЗаменить(фШтрихКод, "d", "");
фШтрихКод = СтрЗаменить(фШтрихКод, "e", "");
фШтрихКод = СтрЗаменить(фШтрихКод, "f", "");
фШтрихКод = Лев(фШтрихКод, фДлина);
// на случай, если штрихкод оказался меньше нужной длины, дополняем его ведущими нулями
Пока СтрДлина(фШтрихКод) < фДлина Цикл
фШтрихКод = "0" + фШтрихКод;
КонецЦикла;
//фШтрихКод = фШтрихКод + КонтрольныйСимволEAN(фШтрихКод,13);
фШтрихКод = Лев((""+Справочники.ФорматыШтрихКодов.ШКДокумента.Код + фШтрихКод), 12) + КонтрольныйСимволEAN(фШтрихКод,13);
Возврат фШтрихКод;
На практике столкнулся с проблемой, что пометка удаления — это тоже запись, и алгоритм не работал, поэтому дописал удаление существующего штрихкода из регистра сведений при установке на документ пометки удаления.
5. У нас есть соответствие цифр и документов. Осталось превратить это в штрихкод на бумаге. Для этого в общий модуль добавляем следующие процедуры, на которые будем ссылаться при выводе на экран макета печатной формы. Здесь не нужно ничего менять и дописывать.
Функция Код128(A) Экспорт
Перем BCode;
Перем BInd;
Перем CurMode;
Перем Ch;
Перем Ch2;
Перем I;
Перем LenA;
Перем CCode;
Перем S;
Перем BarArray;
СписокКБ = Новый Соответствие;
СписокКБ.Вставить("0","0082");
СписокКБ.Вставить("1","00:0");
СписокКБ.Вставить("2","0802");
СписокКБ.Вставить("3",":000");
СписокКБ.Вставить("4","0280");
СписокКБ.Вставить("5","2080");
СписокКБ.Вставить("6","8002");
СписокКБ.Вставить("7","8020");
СписокКБ.Вставить("8","8200");
СписокКБ.Вставить("9","2800");
СписокКБ.Вставить("-","0802");
СписокКБ.Вставить("$","0:00");
СписокКБ.Вставить(":","2022");
СписокКБ.Вставить("/","2202");
СписокКБ.Вставить(".","2220");
СписокКБ.Вставить("+","0222");
СписокКБ.Вставить("a","0:00");
СписокКБ.Вставить("b","8802");
СписокКБ.Вставить("c","0882");
СписокКБ.Вставить("d","08:0");
СписокКБ.Вставить("t","0:80");
СписокКБ.Вставить("n","8802");
СписокКБ.Вставить("*","0882");
СписокКБ.Вставить("e","08:0");
Список128 = Новый СписокЗначений;
Список128.Добавить("212222");
Список128.Добавить("222122");
Список128.Добавить("222221");
Список128.Добавить("121223");
Список128.Добавить("121322");
Список128.Добавить("131222");
Список128.Добавить("122213");
Список128.Добавить("122312");
Список128.Добавить("132212");
Список128.Добавить("221213");
Список128.Добавить("221312");
Список128.Добавить("231212");
Список128.Добавить("112232");
Список128.Добавить("122132");
Список128.Добавить("122231");
Список128.Добавить("113222");
Список128.Добавить("123122");
Список128.Добавить("123221");
Список128.Добавить("223211");
Список128.Добавить("221132");
Список128.Добавить("221231");
Список128.Добавить("213212");
Список128.Добавить("223112");
Список128.Добавить("312131");
Список128.Добавить("311222");
Список128.Добавить("321122");
Список128.Добавить("321221");
Список128.Добавить("312212");
Список128.Добавить("322112");
Список128.Добавить("322211");
Список128.Добавить("212123");
Список128.Добавить("212321");
Список128.Добавить("232121");
Список128.Добавить("111323");
Список128.Добавить("131123");
Список128.Добавить("131321");
Список128.Добавить("112313");
Список128.Добавить("132113");
Список128.Добавить("132311");
Список128.Добавить("211313");
Список128.Добавить("231113");
Список128.Добавить("231311");
Список128.Добавить("112133");
Список128.Добавить("112331");
Список128.Добавить("132131");
Список128.Добавить("113123");
Список128.Добавить("113321");
Список128.Добавить("133121");
Список128.Добавить("313121");
Список128.Добавить("211331");
Список128.Добавить("231131");
Список128.Добавить("213113");
Список128.Добавить("213311");
Список128.Добавить("213131");
Список128.Добавить("311123");
Список128.Добавить("311321");
Список128.Добавить("331121");
Список128.Добавить("312113");
Список128.Добавить("312311");
Список128.Добавить("332111");
Список128.Добавить("314111");
Список128.Добавить("221411");
Список128.Добавить("431111");
Список128.Добавить("111224");
Список128.Добавить("111422");
Список128.Добавить("121124");
Список128.Добавить("121421");
Список128.Добавить("141122");
Список128.Добавить("141221");
Список128.Добавить("112214");
Список128.Добавить("112412");
Список128.Добавить("122114");
Список128.Добавить("122411");
Список128.Добавить("142112");
Список128.Добавить("142211");
Список128.Добавить("241211");
Список128.Добавить("221114");
Список128.Добавить("413111");
Список128.Добавить("241112");
Список128.Добавить("134111");
Список128.Добавить("111242");
Список128.Добавить("121142");
Список128.Добавить("121241");
Список128.Добавить("114212");
Список128.Добавить("124112");
Список128.Добавить("124211");
Список128.Добавить("411212");
Список128.Добавить("421112");
Список128.Добавить("421211");
Список128.Добавить("212141");
Список128.Добавить("214121");
Список128.Добавить("412121");
Список128.Добавить("111143");
Список128.Добавить("111341");
Список128.Добавить("131141");
Список128.Добавить("114113");
Список128.Добавить("114311");
Список128.Добавить("411113");
Список128.Добавить("411311");
Список128.Добавить("113141");
Список128.Добавить("114131");
Список128.Добавить("311141");
Список128.Добавить("411131");
Список128.Добавить("211412");
Список128.Добавить("211214");
Список128.Добавить("211232");
Список128.Добавить("2331112");
BCode = Новый Массив(1024);
//Собираем строку кодов
BInd = 0;
CurMode = "";
I = 1;
LenA = СтрДлина(A);
Пока (I <= LenA) Цикл
//Текущий символ в строке
Ch = КодСимвола(Сред(A, I, 1));
I = I + 1;
//Разбираем только символы от 0 до 127
Если Ch <= 127 Тогда
//Следующий символ
Если I <= LenA Тогда
Ch2 = КодСимвола(Сред(A, I, 1));
Иначе
Ch2 = 0;
КонецЕсли;
//'Пара цифр - режим С
Если (48 <= Ch)И(Ch <= 57)И(48 <= Ch2)И(Ch2 <= 57) Тогда
I = I + 1;
Если BInd = 0 Тогда
//Начало с режима С
CurMode = "C";
BCode[BInd] = 105;
BInd = BInd + 1;
ИначеЕсли CurMode <> "C" Тогда
//Переключиться на режим С
CurMode = "C";
BCode[BInd] = 99;
BInd = BInd + 1;
КонецЕсли;
//Добавить символ режима С
BCode[BInd] = Число(""+Символ(Ch) + Символ(Ch2));
BInd = BInd + 1;
Иначе
Если BInd = 0 Тогда
Если Ch < 32 Тогда
//Начало с режима A
CurMode = "A";
BCode[BInd] = 103;
BInd = BInd + 1;
Иначе
//Начало с режима B
CurMode = "B";
BCode[BInd] = 104;
BInd = BInd + 1;
КонецЕсли;
КонецЕсли;
//Переключение по надобности в режим A
Если (Ch < 32)И(CurMode <> "A") Тогда
CurMode = "A";
BCode[BInd] = 101;
BInd = BInd + 1;
//Переключение по надобности в режим B
ИначеЕсли ((64 <= Ch)И(CurMode <> "B"))ИЛИ(CurMode = "C") Тогда
CurMode = "B";
BCode[BInd] = 100;
BInd = BInd + 1;
КонецЕсли;
//Служебные символы
Если (Ch < 32) Тогда
BCode[BInd] = Ch + 64;
BInd = BInd + 1;
//Все другие символы
Иначе
BCode[BInd] = Ch - 32;
BInd = BInd + 1;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
//Подсчитываем контрольную сумму
CCode = MOD(BCode[0],103);
Для I = 1 По BInd - 1 Цикл
CCode = MOD(CCode + BCode[I] * I,103);
КонецЦикла;
BCode[BInd] = CCode;
BInd = BInd + 1;
//Завершающий символ
BCode[BInd] = 106;
BInd = BInd + 1;
//Собираем строку символов шрифта
S = "";
Для I = 0 По BInd - 1 Цикл
S = S + Code_Char(Список128[BCode[I]].Значение);
КонецЦикла;
Возврат S;
КонецФункции
Процедура КонструкторШК()
СписокКБ = Новый Соответствие;
СписокКБ.Вставить("0","0082");
СписокКБ.Вставить("1","00:0");
СписокКБ.Вставить("2","0802");
СписокКБ.Вставить("3",":000");
СписокКБ.Вставить("4","0280");
СписокКБ.Вставить("5","2080");
СписокКБ.Вставить("6","8002");
СписокКБ.Вставить("7","8020");
СписокКБ.Вставить("8","8200");
СписокКБ.Вставить("9","2800");
СписокКБ.Вставить("-","0802");
СписокКБ.Вставить("$","0:00");
СписокКБ.Вставить(":","2022");
СписокКБ.Вставить("/","2202");
СписокКБ.Вставить(".","2220");
СписокКБ.Вставить("+","0222");
СписокКБ.Вставить("a","0:00");
СписокКБ.Вставить("b","8802");
СписокКБ.Вставить("c","0882");
СписокКБ.Вставить("d","08:0");
СписокКБ.Вставить("t","0:80");
СписокКБ.Вставить("n","8802");
СписокКБ.Вставить("*","0882");
СписокКБ.Вставить("e","08:0");
Список128 = Новый СписокЗначений;
Список128.Добавить("212222");
Список128.Добавить("222122");
Список128.Добавить("222221");
Список128.Добавить("121223");
Список128.Добавить("121322");
Список128.Добавить("131222");
Список128.Добавить("122213");
Список128.Добавить("122312");
Список128.Добавить("132212");
Список128.Добавить("221213");
Список128.Добавить("221312");
Список128.Добавить("231212");
Список128.Добавить("112232");
Список128.Добавить("122132");
Список128.Добавить("122231");
Список128.Добавить("113222");
Список128.Добавить("123122");
Список128.Добавить("123221");
Список128.Добавить("223211");
Список128.Добавить("221132");
Список128.Добавить("221231");
Список128.Добавить("213212");
Список128.Добавить("223112");
Список128.Добавить("312131");
Список128.Добавить("311222");
Список128.Добавить("321122");
Список128.Добавить("321221");
Список128.Добавить("312212");
Список128.Добавить("322112");
Список128.Добавить("322211");
Список128.Добавить("212123");
Список128.Добавить("212321");
Список128.Добавить("232121");
Список128.Добавить("111323");
Список128.Добавить("131123");
Список128.Добавить("131321");
Список128.Добавить("112313");
Список128.Добавить("132113");
Список128.Добавить("132311");
Список128.Добавить("211313");
Список128.Добавить("231113");
Список128.Добавить("231311");
Список128.Добавить("112133");
Список128.Добавить("112331");
Список128.Добавить("132131");
Список128.Добавить("113123");
Список128.Добавить("113321");
Список128.Добавить("133121");
Список128.Добавить("313121");
Список128.Добавить("211331");
Список128.Добавить("231131");
Список128.Добавить("213113");
Список128.Добавить("213311");
Список128.Добавить("213131");
Список128.Добавить("311123");
Список128.Добавить("311321");
Список128.Добавить("331121");
Список128.Добавить("312113");
Список128.Добавить("312311");
Список128.Добавить("332111");
Список128.Добавить("314111");
Список128.Добавить("221411");
Список128.Добавить("431111");
Список128.Добавить("111224");
Список128.Добавить("111422");
Список128.Добавить("121124");
Список128.Добавить("121421");
Список128.Добавить("141122");
Список128.Добавить("141221");
Список128.Добавить("112214");
Список128.Добавить("112412");
Список128.Добавить("122114");
Список128.Добавить("122411");
Список128.Добавить("142112");
Список128.Добавить("142211");
Список128.Добавить("241211");
Список128.Добавить("221114");
Список128.Добавить("413111");
Список128.Добавить("241112");
Список128.Добавить("134111");
Список128.Добавить("111242");
Список128.Добавить("121142");
Список128.Добавить("121241");
Список128.Добавить("114212");
Список128.Добавить("124112");
Список128.Добавить("124211");
Список128.Добавить("411212");
Список128.Добавить("421112");
Список128.Добавить("421211");
Список128.Добавить("212141");
Список128.Добавить("214121");
Список128.Добавить("412121");
Список128.Добавить("111143");
Список128.Добавить("111341");
Список128.Добавить("131141");
Список128.Добавить("114113");
Список128.Добавить("114311");
Список128.Добавить("411113");
Список128.Добавить("411311");
Список128.Добавить("113141");
Список128.Добавить("114131");
Список128.Добавить("311141");
Список128.Добавить("411131");
Список128.Добавить("211412");
Список128.Добавить("211214");
Список128.Добавить("211232");
Список128.Добавить("2331112");
КонецПроцедуры //КонструкторШК()
////
Функция Кодабар(КодШК)
//Перем ШтрихКод;
//ШтрихКод = "";
//Для Н = 1 По СтрДлина(КодШК) Цикл
// ШтрихКод = ШтрихКод + СписокКБ.Получить(Сред(КодШК, Н, 1));
//КонецЦикла;
//ШтрихКод = СписокКБ.Получить("d") + ШтрихКод + СписокКБ.Получить("e");
//
//Возврат ШтрихКод;
КонецФункции //Кодабар()
//Функция получает остаток при делении
// Параметры:
// Делимое <число> - число, которое делим
// Делитель <число> - число, на которое делим
// Возвращаемое значение:
// Рез <число> - остаток при делении
//
Функция MOD(Делимое,Делитель)
Перем Рез;
Рез=0;
Частное=Цел(Делимое/Делитель);
Рез=Делимое-Частное*Делитель;
Возврат Рез;
КонецФункции //MOD()
//Штриховые символы шрифта iQs Code 128 по набору полос
//
Функция Code_Char(A)
Перем S;
Перем I;
Перем B;
Если A="211412" Тогда S = "A"; Возврат S; КонецЕсли;
Если A="211214" Тогда S = "B"; Возврат S; КонецЕсли;
Если A="211232" Тогда S = "C"; Возврат S; КонецЕсли;
Если A="2331112"Тогда S = "@"; Возврат S; КонецЕсли;
S = "";
LenA=СтрДлина(A);
Для I = 0 По (LenA/ 2 - 1) Цикл
Стр=Сред(A, 2 * I + 1, 2);
Если Стр="11" Тогда S = S+"0";Продолжить;
ИначеЕсли Стр="21" Тогда S = S+"1";Продолжить;
ИначеЕсли Стр="31" Тогда S = S+"2";Продолжить;
ИначеЕсли Стр="41" Тогда S = S+"3";Продолжить;
ИначеЕсли Стр="12" Тогда S = S+"4";Продолжить;
ИначеЕсли Стр="22" Тогда S = S+"5";Продолжить;
ИначеЕсли Стр="32" Тогда S = S+"6";Продолжить;
ИначеЕсли Стр="42" Тогда S = S+"7";Продолжить;
ИначеЕсли Стр="13" Тогда S = S+"8";Продолжить;
ИначеЕсли Стр="23" Тогда S = S+"9";Продолжить;
ИначеЕсли Стр="33" Тогда S = S+":";Продолжить;
ИначеЕсли Стр="43" Тогда S = S+";";Продолжить;
ИначеЕсли Стр="14" Тогда S = S+"<";Продолжить;
ИначеЕсли Стр="24" Тогда S = S+"=";Продолжить;
ИначеЕсли Стр="34" Тогда S = S+">";Продолжить;
ИначеЕсли Стр="44" Тогда S = S+"?";Продолжить;
КонецЕсли;
КонецЦикла;
Возврат S;
КонецФункции //Code_Char()
6. В процедуре вывода табличного документа на экран нужно добавить только две строки кода:
ШтрихКод = ПолучитьШтрихКодДокумента(ЭтотОбъект.Ссылка);
ОбластьМакета.Параметры.ШтрихКод = [ИмяВашегоОбщегоМодуля].Код128(ШтрихКод);
ОбластьМакета.Параметры.Код = ШтрихКод;
ПолучитьШтрихКодДокумента — просто получает цифры штрихкода из регистра сведений "ШтрихкодыДокументов".
Всё! Штрихкод готов, я получаю профит, клиент доволен. В дальнейшем обрабатывать событие сканирования очень легко — просто подтягиваем по номеру ссылку на документ из регистра и делаем с ним что угодно.