Подписание документов — неотъемлемая часть многих бизнес-процессов. Будь то заключение трудового договора, выдача материальных ценностей или оформление счетов: часто в таких случаях необходимо распечатать документ и подписать его. Чтобы упростить и автоматизировать этот процесс, мы разработали решение, которое позволяет получать подписанные документы прямо в 1С, исключая необходимость печати, подписи и последующего сканирования.

В этом примере мы используем графический планшет One by Wacom S-size. Это компактное устройство, которое легко подключается к компьютерам на базе Windows (версии 7 и выше) и Mac OS (версии 10.10 и выше).

Как работает наше решение

Пользователь, находясь на своем рабочем месте, выводит на печать документ. В момент формирования табличного документа пользователю предоставляется возможность «нарисовать» свою подпись на графическом планшете. Изображение с планшета отображается в отдельном окне формы. 

Далее мы преобразуем картинку с подписью в двоичные данные и вставляем ее в нужное место на печатной форме документа. В результате получается готовый документ, который можно сохранить в базе 1С или вывести на печать.

Алгоритм захвата подписи

Алгоритм захвата подписи рассмотрим на примере создания внешней обработки. В форме обработки добавим реквизит типа «Строка» (неограниченной длины) и назовем его «ПолеHTML». Этот реквизит будет отображаться на форме.

Чтобы пользователь мог «рисовать» подпись прямо на форме, присвоим этому реквизиту определенное значение. Сделаем это через событие «ПриСозданииНаСервере». Для удобства мы используем макет обработки типа «Текстовый документ», который содержит JavaScript-код.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body style="margin:0;padding:0">
<canvas id="canvas" width="800" height="600"
    style="background-color:#eee; border: 0px solid #ccc; margin:0px;"></canvas>
<canvas id="canvas2" hidden width="330" height="90"
    style="background-color:#fff; border: 0px solid #aaa; margin:0px;"></canvas>
<script>
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d"); 
  
const canvas2 = document.getElementById("canvas2");
const context2 = canvas2.getContext("2d");

const mouse = { x:0, y:0};      // координаты мыши
let draw = false;     
  
function resizeCanvas() {
	canvas.width = window.innerWidth-5;
	canvas.height = window.innerHeight-5;
}

resizeCanvas();

function clearImage() {
	context.clearRect(0, 0, canvas.width, canvas.height);
	context2.clearRect(0, 0, canvas2.width, canvas2.height);
}

function saveImage(saveTo) {
   
	const w = canvas.width;
	const h = canvas.height;
 
    let minX = w;
	let minY = h;
	let maxX = 0;
	let maxY = 0;

	let imgData = context.getImageData(0,0,w,h);
	for (let i = 0; i < imgData.data.length; i += 4) {
		if (imgData.data[i+3] > 0){
			let current_top = Math.floor(i/4/w);
  			let current_left = Math.floor((i/4/w - current_top)*w);
           
			if (minX > current_left)
				minX = current_left;	
           
			if (maxX < current_left)
				maxX = current_left; 

			if (minY > current_top)
				minY = current_top; 				

			if (maxY < current_top)
				maxY = current_top;
		}
	}       
   
	let cropWidth = maxX-minX;
	let cropHeight = maxY-minY;
   	let aspect = cropWidth/cropHeight;
	let destX = (330-90*aspect)/2; 
	context2.drawImage(canvas, minX, minY, maxX-minX, maxY-minY, destX, 0, 90*aspect, 90);
	var image = canvas2.toDataURL("image/png").replace("image/png", "image/octet-stream"); 
	return image;
} 

window.addEventListener('resize', (e) => {
	resizeCanvas();
});

// нажатие мыши
canvas.addEventListener("mousedown", function(e){
      
    mouse.x = e.pageX - this.offsetLeft;
    mouse.y = e.pageY - this.offsetTop;
    draw = true;
    context.beginPath();
    context.moveTo(mouse.x, mouse.y);
});
// перемещение мыши
canvas.addEventListener("mousemove", function(e){
      
    if(draw==true){
      
        mouse.x = e.pageX - this.offsetLeft;
        mouse.y = e.pageY - this.offsetTop;
        context.lineTo(mouse.x, mouse.y);
        context.stroke();
    }
});
 
// отпускаем мышь
canvas.addEventListener("mouseup", function(e){
      
    mouse.x = e.pageX - this.offsetLeft;
    mouse.y = e.pageY - this.offsetTop;
    context.lineTo(mouse.x, mouse.y);
    context.stroke();
    context.closePath();
    draw = false;
	
});
</script>
</body>
</html>

Создаем макет обработки, добавляем необходимый JavaScript-код, а затем в событии «ПриСозданииНаСервере» присваиваем значение реквизиту «ПолеHTML».

Что получилось

Мы создали некое подобие графического редактора прямо в 1С. Теперь перемещая курсор мыши в «ПолеHTML», можно рисовать на платформе, что очень удобно. 

Теперь нам нужно «захватить» нарисованную подпись и преобразовать ее в изображение, представленное в виде двоичных данных. Это позволит использовать подпись в дальнейшем как обычное изображение с прозрачным фоном для вставки в табличные документы или вывода на печать.

Для этого создадим команду на форме и зададим для нее действие с директивой компиляции «&НаКлиенте».

Результаты

Решение успешно применено в реальной практике при работе с заказчиком. Оно помогло сократить число выполняемых действий: начиная с печати документа и ручной подписи на бумаге, заканчивая последующим сканированием. Теперь весь процесс подписания документов происходит быстрее и удобнее, прямо в 1С.

Комментарии (6)


  1. KawaiiSelbst
    31.10.2024 08:00

    Это всё чудесно. Но зачем тут графический планшет, если картинку подписи можно просто сохранить и вставлять прямиком, не рисуя её каждый раз заново?


    1. lad_habrablog Автор
      31.10.2024 08:00

      Добрый день!
      Для каждого нового контрагента неудобно сохранять картинку подписи, особенно если поток клиентов огромный или у контрагента несколько ответственных лиц. Мы предложили более оптимальный вариант работы с 1С.


  1. CherepninNazar
    31.10.2024 08:00

    С юридической точки зрения это выглядит очень слабо. На руках у вас остается документ, на котором вместо подписи клиента - вставленная картинка. В суде это обещает обернуться большими проблемами.


    1. lad_habrablog Автор
      31.10.2024 08:00

      Добрый день!
      Это реальный кейс по ТЗ заказчика. Заказчик остался доволен, претензий с юридической точки зрения у него не было. Если возникнут вопросы, конечно, возьмем на доработку.


      1. NAI
        31.10.2024 08:00

        ТЗ всегда должен писать исполнитель т.к. заказчик может не знать всех тонкостей.

        Например, в случае разбирательств, суд может затребовать бумагу для экспертизы и... как бы что предоставит ваш заказчик?

        Выглядит так как будто проще было вкорячить ЭЦП, через тот же госключ (у всех же есть госуслуги) - признать на уровне компании не квалиф. подпись и закрыть проблему :)


        1. lad_habrablog Автор
          31.10.2024 08:00

          Согласны: ТЗ обычно подготавливаем мы. В данном случае функциональные требования исходили от заказчика. В связи с особенностями и требованиями к проекту мы реализовали функционал в том формате, который описали в статье.

          Спасибо, что делитесь обратной связью, поскольку юридическая сторона вопроса всегда требует внимательного изучения :)