Учет и сверка Z-Отчетов ККМ в 1С

Оборудование - ККМ

z-отчет ККМ проверка чеков

14
Думаю, все крупные компании при внедрении/эксплуатации 1С делают значительные изменения в ее конфигурации. Как бы вы ни пытались все бизнес процессы решить типовыми решениями от 1С, все равно наступает момент, когда приходится заняться конфигурированием. Я решил написать серию статей, где постараюсь описать решения, которые мне пришлось реализовывать в разных компаниях. Если разработчики 1С посчитают что-то интересным для внедрения в типовые решения, мы будем только рады. В текущей статье речь пойдет о кассовых z-отчетах в 1С.

ПРЕДПОСЫЛКИ:

Если вы используете ККМ, у вас возникает необходимость сверять отраженную выручку в 1С с предоставленными z-отчетами.

Сразу развею миф: «Если все чеки печатаются из 1С, то и сумма выручки всегда будет совпадать с z-отчетом». В жизни все не так.

Если у вас всего 1-5 контрольно кассовых машин (ККМ), можно ежедневно вручную бухгалтеру сверять выручку по бумажному z-отчету. Но если у вас более 100 ККМ, вопрос автоматизации по проверке достоверности отражения выручки, соблюдения кассовой дисциплины и своевременного пробития чеков становится очень остро. Человеческий фактор в данном случае становится просто катастрофическим.

К сожалению, типовые конфигурации 1С нам не предлагают возможность вести учет z-отчетов, что ж, реализуем сами.

Внедрение бизнес процесса по контролю и учету z-отчетов можно разделить на три этапа:

1. Создание в 1С документа "z-отчет" с ручным вводом информации.
2. Контроль и формирование отчетов по проверке показателей, которые можно извлечь из Z-отчета.
3. Автоматизация загрузки с ККМ данных о z-отчетах (их автоматическое создание в 1С).

Маленькая ремарка: Предоставленная ниже реализация z-отчета, является решением во времена ККМ с ЭКЛЗ и плавно была трансформирована (с обратной совместимостью) в решение для онлайн касс.

ПРИСТУПИМ: 

1 ЭТАП

Создаем новый документ «_ZОтчетФискальный»
Реквизиты:
- Организация (СправочникСсылка.Организации)
- Подразделение (СправочникСсылка.Подразделения)
- ККМ (СправочникСсылка.КассыККМ)
- Накопление (Число15.2)  В новых он-лайн ККМ это поле теперь называется ГРОСС ИТОГ
- Выручка (Число15.2)
- Возвраты (Число15.2)
- ВыручкаБезТоварныхЧеков (Число15.2) В этом поле отражают выручку, регистрируемую при поступлении средств от «оптовых» покупателей (ТОРГ12+ПКО).
- Неиспользованные (Число15.2)
- НомерГашения (Число10.0) В новых он-лайн ККМ это поле теперь называется НОМЕР СМЕНЫ.
- СуммаДокумента (Число15.2)
Для отображения в журнале
- НачальныеСведения (Булево) Этот реквизит необходим для обозначения вноса первого
Z
-отчета ККМ, или при смене ФН. При его установке не проверяется хронология отчетов по ККМ.
- Ответственный (СправочникСсылка.Пользователи)
- Комментарий (Строка)


Z-Отчет. Вид из конфигуратора.

В модуле объекта пишем код для проверки заполнения при проведении документа.

Функция НайтиПредыдущееГашение() Экспорт
	СведенияОПредыдущем = Новый Структура("НомерГашения,Накопление,СсылкаНаДокумент");
	СведенияОПредыдущем.НомерГашения=Неопределено;
	СведенияОПредыдущем.Накопление=Неопределено;
	СведенияОПредыдущем.СсылкаНаДокумент=Неопределено;
	Если НЕ НачальныеСведения Тогда
		Запрос=Новый Запрос("ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 1 
		|Ссылка, 
		|Дата, 
		|Организация, 
		|ККМ, 
		|Накопление, 
		|НомерГашения,
		|Проведен
		|ИЗ Документ._ZОтчетФискальный
		|ГДЕ Дата<&ДатаКонец И Организация=&ВыбОрганизация И ККМ=&ВыбКасса И Проведен
		|УПОРЯДОЧИТЬ ПО Дата УБЫВ");
		Запрос.УстановитьПараметр("ВыбОрганизация",Организация);					
		Запрос.УстановитьПараметр("ВыбКасса",ККМ);					
		Запрос.УстановитьПараметр("ДатаКонец",Дата);					
		Выборка =  Запрос.Выполнить().Выбрать();
		Если Выборка.Следующий() Тогда
			СведенияОПредыдущем.НомерГашения=Выборка.НомерГашения;
			СведенияОПредыдущем.Накопление=Выборка.Накопление;
			СведенияОПредыдущем.СсылкаНаДокумент=Выборка.Ссылка;
		КонецЕсли;
	КонецЕсли;						
	Возврат СведенияОПредыдущем;
КонецФункции

Процедура ОбработкаПроведения(Отказ, Режим)
	Если НЕ НачальныеСведения Тогда
		СведенияОПредыдущем= НайтиПредыдущееГашение();
		Если СведенияОПредыдущем.НомерГашения=Неопределено Тогда
			Сообщить("Не найден предыдущий Финансовый отчет (гашение) для ККМ: "+Строка(ККМ));
			Если НЕ РольДоступна("ПолныеПрава") Тогда
				Отказ = Истина;
			КонецЕсли;
			
		Иначе
			Если НомерГашения=СведенияОПредыдущем.НомерГашения Тогда
				Сообщить("Уже существует документ с таким номером гашения для выбранной ККМ. Возможно вы задублировали документ.");
				Сообщить("Ожидается номер "+Строка(СведенияОПредыдущем.НомерГашения+1));
				Если НЕ РольДоступна("ПолныеПрава") Тогда
					Отказ = Истина;
				КонецЕсли;
			ИначеЕсли (НомерГашения<СведенияОПредыдущем.НомерГашения) Тогда
				Сообщить("Существует документ с более поздним номером гашения для выбранной ККМ. Отмените более поздние гашения, и проведите их в порядке возрастания номера гашения.");
				Сообщить("Ожидается номер "+Строка(СведенияОПредыдущем.НомерГашения+1));
				Если НЕ РольДоступна("ПолныеПрава") Тогда
					Отказ = Истина;
				КонецЕсли;
			ИначеЕсли (НомерГашения>(СведенияОПредыдущем.НомерГашения+1)) Тогда
				Сообщить("Существуют пропущенные номера гашения для выбранной ККМ. Преверте номер гашения или проведите гашения для пропущенных номеров.");
				Сообщить("Ожидается номер "+Строка(СведенияОПредыдущем.НомерГашения+1));
				Если НЕ РольДоступна("ПолныеПрава") Тогда
					Отказ = Истина;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		
		//РасчВозврат=?(Касса.ОтражениеВозвратовВНакоплении=Перечисления.ВлияниеНаИтоги.Увеличивают,Возвраты,?(Касса.ОтражениеВозвратовВНакоплении=Перечисления.ВлияниеНаИтоги.Уменьшают,-Возвраты,0));
		//РасчНеиспользованные=?(Касса.ОтражениеНеиспользованныхВНакоплении=Перечисления.ВлияниеНаИтоги.Увеличивают,Неиспользованные,?(Касса.ОтражениеНеиспользованныхВНакоплении=Перечисления.ВлияниеНаИтоги.Уменьшают,-Неиспользованные,0));
		РасчВозврат = Возвраты; //Считаем что всегда увеличивают;
		РасчНеиспользованные = 0; //Считаем что не влияют;
		Если (Накопление<>(СведенияОПредыдущем.Накопление+Выручка+РасчВозврат+РасчНеиспользованные)) Тогда
			Сообщить("Контроль накопления не пройден! Проверьте правильность внесения данных с бумажного чека ""Z-ОТЧЕТ (фискальный)""!");
			Если НЕ РольДоступна("ПолныеПрава") Тогда
				Отказ = Истина;
			КонецЕсли;
		КонецЕсли;
	Конецесли;
КонецПроцедуры

Если вы обратили внимание, пользователь не может провести документ «_ZОтчетФискальный», если не выполняется математика или не соблюдается очередность отчетов.

Закомментированный код применялся при подсчете накопления в старых моделях ККМ с ЭКЛЗ, теперь в нем нет необходимости.

Документ «_ZОтчетФискальный» все проверки производит по указанной в документе конкретной кассе ККМ.

Небольшое лирическое отступление:

При эксплуатации касс АТОЛ 11Ф у нас неоднократно случались ошибки математики «ГРОСС ИТОГА» во время пробития z-отчета на ККМ, как правило при сбое (окончание бумаги или разряд аккумулятора).
Что ж, не беда, в нашем случае предусмотрен флаг «НачальныеСведения» в документе, при его установки документ позволяет начать отсчет номер гашения и накопление заново.

Вызов форма списка документа поместил в меню: Документы>Управление денежными средствами>Z-Отчеты

Форма списка выглядит так:

Форму документа я оформил в наглядном и понятном бухгалтеру виде:

Форма документа z-отчет в 1С

Код формы документа:

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

Процедура ОбновитьНадписиПриИзмененииЭлемента(Элемент)
	ОбновитьНадписи();
	ОбновитьОбразецЧека();
КонецПроцедуры

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
	ДокументОбъект.СуммаДокумента=ДокументОбъект.Выручка-ДокументОбъект.Возвраты-ДокументОбъект.Неиспользованные;
КонецПроцедуры

Функция НайтиПредыдущееГашениеНаСервере() 
	Возврат НайтиПредыдущееГашение(); 
КонецФункции

Процедура ОбновитьНадписи()
	СведенияОПредыдущем= НайтиПредыдущееГашениеНаСервере();
	ЭлементыФормы.РамкаСтарыйОтчет.Видимость=ДокументОбъект.НачальныеСведения;
	Если СведенияОПредыдущем.НомерГашения=Неопределено Тогда
		ЭлементыФормы.ПРНомерГашения.Заголовок="Номер гашения: Нет сведений";
		ЭлементыФормы.ПРНакопление.Заголовок="Накопление: Нет сведений";
		ЭлементыФормы.ПРНомерГашения.Видимость = Ложь;
		ЭлементыФормы.ПРНакопление.Видимость = Ложь;
	Иначе
		ЭлементыФормы.ПРНомерГашения.Заголовок="Номер гашения: "+ФОРМАТ(СведенияОПредыдущем.НомерГашения,"ЧГ=");
		ЭлементыФормы.ПРНакопление.Заголовок="Накопление: "+ФОРМАТ(СведенияОПредыдущем.Накопление)+" руб.";
		ЭлементыФормы.ПРНомерГашения.Видимость = Истина;
		ЭлементыФормы.ПРНакопление.Видимость = Истина;
	КонецЕсли;
КонецПроцедуры

Процедура ОбновитьОбразецЧека()	
	ЭлементыФормы.ФД.Очистить();
	ЭлементыФормы.ФД.ДобавитьСтроку("ИНН "+ДокументОбъект.Организация.ИНН);
	ЭлементыФормы.ФД.ДобавитьСтроку("Z-ОТЧЕТ ФИСКАЛЬНЫЙ");
	ЭлементыФормы.ФД.ДобавитьСтроку("ККМ "+ДокументОбъект.ККМ.СерийныйНомер);
	ЭлементыФормы.ФД.ДобавитьСтроку("ЗАКР.СМЕНЫ  "+ПРАВ("0000"+ФОРМАТ(ДокументОбъект.НомерГашения,"ЧГ="),4));
	ЭлементыФормы.ФД.ДобавитьСтроку("");
	ЭлементыФормы.ФД.ДобавитьСтроку("НАКОПЛЕНИЕ");
	ЭлементыФормы.ФД.ДобавитьСтроку("ГРОСС-ИТОГ");
	ЭлементыФормы.ФД.ДобавитьСтроку("  "+ПРАВ("..........."+ФОРМАТ(ДокументОбъект.Накопление,"ЧЦ=15; ЧДЦ=2; ЧРД=.; ЧН=0.00; ЧГ="),15));
	ЭлементыФормы.ФД.ДобавитьСтроку("ВЫРУЧКА");
	ЭлементыФормы.ФД.ДобавитьСтроку("  "+ПРАВ("..........."+ФОРМАТ(ДокументОбъект.Выручка,"ЧЦ=15; ЧДЦ=2; ЧРД=.; ЧН=0.00; ЧГ="),15));
	ЭлементыФормы.ФД.ДобавитьСтроку(Формат(ДокументОбъект.Дата,"ДФ=""дд.ММ.гг ЧЧ.мм""")+"Ф");
	ЭлементыФормы.ФД.ДобавитьСтроку("ВОЗВРАТЫ");
	ЭлементыФормы.ФД.ДобавитьСтроку("  "+ПРАВ("..........."+ФОРМАТ(ДокументОбъект.Возвраты,"ЧЦ=15; ЧДЦ=2; ЧРД=.; ЧН=0.00; ЧГ="),15));
КонецПроцедуры

Если используете управляемые формы, то получать ИНН организации и серийный номер ККМ нужно через вызов функции на сервере.

 

2 ЭТАП

Создаем средства проверки и контроля данных в z-отчетах. Для этого у нас уже существует решение для бухгалтеров «ЕДИНАЯ ПРОВЕРКА ОШИБОК». Добавляем раздел по проверке сразу всех Z-отчетов, внесенных в 1С за определенный период.

Макет для вывода результатов проверки там выглядит как-то так:

Макет отчета

Создаем процедуры по проверке документов «_ZОтчетФискальный»:

Перечисляю все варианты сообщений при проверке, по ним вы сможете понять, какие проверки производятся с документом z-отчет.

ОШИБКИ:

- В Z-отчете указаны неиспользованные чеки, но при этом ПКО оформлен на всю сумму выручки в Z-отчете на дату ХХХ
- В Z-отчете указаны оптовые продажи, но при этом ПКО с видом ""Прием розничной выручки"" оформлен на всю сумму выручки в Z-отчете на дату ХХХ

- Чистая выручка по Z-отчету ХХХ не соответсвует сумме ПКО ХХХ по кассе ККМ ХХХ на дату ХХХ
- Сумма неиспользованных чеков и оптовых продаж превышает общую выручку
- Сумма опта Z-отчетов ХХХ по подразделению ХХХ не сходится с суммой ПКО ХХХ на дату ХХХ
-
Не найден предыдущий Финансовый отчет (гашение) для ККМ ХХХ
- Уже существует документ с таким номером гашения для выбранной ККМ. Возможно вы задублировали документ. Ожидается номер ХХХ
- Существует документ с более поздним номером гашения для выбранной ККМ. Отмените более поздние гашения, и проведите их в порядке возрастания номера гашения.  Ожидается номер ХХХ
- Существуют пропущенные номера гашения для выбранной ККМ. Преверте номер гашения или проведите гашения для пропущенных номеров. Ожидается номер ХХХ
- Контроль накопления не пройден! Проверьте правильность внесения данных с бумажного чека "Z-ОТЧЕТ (фискальный)"!

 

ПРЕДУПРЕЖДЕНИЯ:

- По кассе ККМ ХХХ на дату ХХХ должен быть оформлен акт по неиспользованным кассовым чекам(КМ-3) на сумму ХХХ
- По кассе ККМ ХХХ на дату ХХХ должен быть оформлено заявление на возврат на сумму ХХХ
- По кассе ККМ ХХХ найдено несколько Z-отчетов с признаком "Начальные сведения"

 

 

 
 Кто хочет посмотреть код проверок

 

Процедура ПроверкаZОтчетов(ТД,Макет)
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ
	|	_ZОтчетФискальный.ККМ КАК ККМ,
	|	МИНИМУМ(_ZОтчетФискальный.Дата) КАК Дата
	|ИЗ
	|	Документ._ZОтчетФискальный КАК _ZОтчетФискальный
	|ГДЕ
	|	_ZОтчетФискальный.Дата >= &Дата1
	|
	|СГРУППИРОВАТЬ ПО
	|	_ZОтчетФискальный.ККМ";
	Запрос.Параметры.Вставить("Дата1",НачалоДня(ОбработкаОбъект.Период.ДатаНачала));
	ТаблицаКасс = Запрос.Выполнить().Выбрать();
	Пока ТаблицаКасс.Следующий() Цикл
		Запрос = Новый Запрос;
		Запрос.Текст="ВЫБРАТЬ
		|	ПКО.День КАК Дата,
		|	ЕСТЬNULL(ПКО.СуммаДокумента, 0) КАК СуммаПКО,
		|	ПКО.Контрагент КАК ККМ,
		|	ЕСТЬNULL(Отчеты.Выручка, 0) КАК СуммаОтчета,
		|	ЕСТЬNULL(Отчеты.Неиспользованные, 0) КАК Неиспользованные,
		|	ЕСТЬNULL(Отчеты.Возвраты, 0) КАК Возвраты,
		|	Отчеты.День КАК ДатаОтчета,
		|	Отчеты.ККМ КАК ККМОтчета,
		|	ЕСТЬNULL(Отчеты.ВыручкаБезТоварныхЧеков, 0) КАК Опт,
		|	ЕСТЬNULL(Отчеты.ВыручкаЧистая, 0) КАК СуммаОтчетаЧистая
		|ИЗ
		|	(ВЫБРАТЬ
		|		_ZОтчетФискальный.Организация КАК Организация,
		|		_ZОтчетФискальный.ККМ КАК ККМ,
		|		СУММА(_ZОтчетФискальный.Выручка) КАК Выручка,
		|		НАЧАЛОПЕРИОДА(_ZОтчетФискальный.Дата, ДЕНЬ) КАК День,
		|		СУММА(_ZОтчетФискальный.Неиспользованные) КАК Неиспользованные,
		|		СУММА(_ZОтчетФискальный.Возвраты) КАК Возвраты,
		|		СУММА(_ZОтчетФискальный.ВыручкаБезТоварныхЧеков) КАК ВыручкаБезТоварныхЧеков,
		|		СУММА(_ZОтчетФискальный.Выручка - _ZОтчетФискальный.Неиспользованные -  _ZОтчетФискальный.ВыручкаБезТоварныхЧеков) КАК ВыручкаЧистая
		|	ИЗ
		|		Документ._ZОтчетФискальный КАК _ZОтчетФискальный
		|	ГДЕ
		|		_ZОтчетФискальный.Дата МЕЖДУ &Дата1 И &Дата2
		|		И _ZОтчетФискальный.Проведен = ИСТИНА
		|		И _ZОтчетФискальный.Организация = &Организация
		|		И _ZОтчетФискальный.ККМ = &ККМ
		|	
		|	СГРУППИРОВАТЬ ПО
		|		_ZОтчетФискальный.Организация,
		|		_ZОтчетФискальный.ККМ,
		|		НАЧАЛОПЕРИОДА(_ZОтчетФискальный.Дата, ДЕНЬ)) КАК Отчеты
		|		ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
		|			ПриходныйКассовыйОрдер.Организация КАК Организация,
		|			ПриходныйКассовыйОрдер.Контрагент КАК Контрагент,
		|			СУММА(ПриходныйКассовыйОрдер.СуммаДокумента) КАК СуммаДокумента,
		|			НАЧАЛОПЕРИОДА(ПриходныйКассовыйОрдер.Дата, ДЕНЬ) КАК День
		|		ИЗ
		|			Документ.ПриходныйКассовыйОрдер КАК ПриходныйКассовыйОрдер
		|		ГДЕ
		|			ПриходныйКассовыйОрдер.Проведен = ИСТИНА
		|			И ПриходныйКассовыйОрдер.Дата МЕЖДУ &Дата1 И &Дата2
		|			И ПриходныйКассовыйОрдер.Организация = &Организация
		|			И ТИПЗНАЧЕНИЯ(ПриходныйКассовыйОрдер.Контрагент) = ТИП(Справочник.КассыККМ)
		|			И ПриходныйКассовыйОрдер.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийПКО.ПриходДенежныхСредствРозничнаяВыручка)
		|			И ПриходныйКассовыйОрдер.ОтражатьВБухгалтерскомУчете = ИСТИНА
		|			И ПриходныйКассовыйОрдер.Контрагент = &ККМ
		|		
		|		СГРУППИРОВАТЬ ПО
		|			ПриходныйКассовыйОрдер.Организация,
		|			ПриходныйКассовыйОрдер.Контрагент,
		|			НАЧАЛОПЕРИОДА(ПриходныйКассовыйОрдер.Дата, ДЕНЬ)) КАК ПКО
		|		ПО Отчеты.Организация = ПКО.Организация
		|			И Отчеты.ККМ = ПКО.Контрагент
		|			И Отчеты.День = ПКО.День";
		Запрос.УстановитьПараметр("Организация",ОбработкаОбъект.Организация);					
		Запрос.УстановитьПараметр("Дата1",?(ТаблицаКасс.Дата>ОбработкаОбъект.Период.ДатаНачала,НачалоДня(ТаблицаКасс.Дата),НачалоДня(ОбработкаОбъект.Период.ДатаНачала)));					
		Запрос.УстановитьПараметр("Дата2",КонецДня(ОбработкаОбъект.Период.ДатаОкончания));
		Запрос.УстановитьПараметр("ККМ",ТаблицаКасс.ККМ);
		Выборка = Запрос.Выполнить().Выбрать();
		Пока Выборка.Следующий() Цикл
			Если Выборка.СуммаПКО = Выборка.СуммаОтчета И Выборка.Неиспользованные >0 Тогда			
				ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
				ОбластьМакета.Параметры.ОбъектаПроверки        =?(Выборка.ККМ = Null,Выборка.ККМОтчета,Выборка.ККМ);
				ОбластьМакета.Параметры.Ошибка				   ="В Z-отчете указаны неиспользованные чеки, но при этом ПКО оформлен на всю сумму выручки в Z-отчете на дату: " + Формат(?(Выборка.Дата = Null,Выборка.ДатаОтчета,Выборка.Дата),"ДФ=dd.MM.yy; ДЛФ=D");
				ТД.Вывести(ОбластьМакета);
			КонецЕсли;
				Если Выборка.СуммаПКО = Выборка.СуммаОтчета И Выборка.Опт >0 Тогда			
				ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
				ОбластьМакета.Параметры.ОбъектаПроверки        =?(Выборка.ККМ = Null,Выборка.ККМОтчета,Выборка.ККМ);
				ОбластьМакета.Параметры.Ошибка				   ="В Z-отчете указаны оптовые продажи, но при этом ПКО с видом ""Прием розничной выручки"" оформлен на всю сумму выручки в Z-отчете на дату: " + Формат(?(Выборка.Дата = Null,Выборка.ДатаОтчета,Выборка.Дата),"ДФ=dd.MM.yy; ДЛФ=D");
				ТД.Вывести(ОбластьМакета);
			КонецЕсли;
			Если Выборка.СуммаПКО <> Выборка.СуммаОтчетаЧистая Тогда			
				ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
				ОбластьМакета.Параметры.ОбъектаПроверки        =?(Выборка.ККМ = Null,Выборка.ККМОтчета,Выборка.ККМ);
				ОбластьМакета.Параметры.Ошибка				   ="Чистая выручка по Z-отчету: "+Выборка.СуммаОтчетаЧистая+" не соответсвует сумме ПКО: "+Выборка.СуммаПКО+" по кассе ККМ: "+Выборка.ККМ+" на дату: "+Формат(?(Выборка.Дата = Null,Выборка.ДатаОтчета,Выборка.Дата),"ДФ=dd.MM.yy; ДЛФ=D");
				ТД.Вывести(ОбластьМакета);
			КонецЕсли;
			Если Выборка.Неиспользованные>0 Тогда
				ОбластьМакета         = Макет.ПолучитьОбласть("Предупреждение");
				ОбластьМакета.Параметры.ОбъектаПроверки        =?(Выборка.ККМ = Null,Выборка.ККМОтчета,Выборка.ККМ);
				ОбластьМакета.Параметры.Ошибка				   ="По кассе ККМ: "+?(Выборка.ККМ = Null,Выборка.ККМОтчета,Выборка.ККМ)+" на дату: "+Формат(?(Выборка.Дата = Null,Выборка.ДатаОтчета,Выборка.Дата),"ДФ=dd.MM.yy; ДЛФ=D") + " должен быть оформлен акт по неиспользованным кассовым чекам(КМ-3) на сумму " + Выборка.Неиспользованные;
				ТД.Вывести(ОбластьМакета);
			КонецЕсли;	
			Если Выборка.Возвраты>0 Тогда
				ОбластьМакета         = Макет.ПолучитьОбласть("Предупреждение");
				ОбластьМакета.Параметры.ОбъектаПроверки        =?(Выборка.ККМ = Null,Выборка.ККМОтчета,Выборка.ККМ);
				ОбластьМакета.Параметры.Ошибка				   ="По кассе ККМ: "+?(Выборка.ККМ = Null,Выборка.ККМОтчета,Выборка.ККМ)+" на дату: "+Формат(?(Выборка.Дата = Null,Выборка.ДатаОтчета,Выборка.Дата),"ДФ=dd.MM.yy; ДЛФ=D") + " должен быть оформлено заявление на возврат на сумму " + Выборка.Возвраты;
				ТД.Вывести(ОбластьМакета);
			КонецЕсли;	
		КонецЦикла;
		Запрос = Новый Запрос;
		ЕстьНачальный = Ложь;
		Запрос.Текст="ВЫБРАТЬ
		|	_ZОтчетФискальный.Ссылка КАК Ссылка,
		|	_ZОтчетФискальный.НачальныеСведения КАК НачальныеСведения
		|ИЗ
		|	Документ._ZОтчетФискальный КАК _ZОтчетФискальный
		|ГДЕ
		|	_ZОтчетФискальный.ККМ = &ККМ
		|	И _ZОтчетФискальный.Дата МЕЖДУ &Дата1 И &Дата2
		|	И _ZОтчетФискальный.Проведен = ИСТИНА
		|	И _ZОтчетФискальный.Организация = &Организация";
		Запрос.УстановитьПараметр("Дата1",?(ТаблицаКасс.Дата>ОбработкаОбъект.Период.ДатаНачала,НачалоДня(ТаблицаКасс.Дата),НачалоДня(ОбработкаОбъект.Период.ДатаНачала)));					
		Запрос.УстановитьПараметр("Дата2",КонецДня(ОбработкаОбъект.Период.ДатаОкончания));
		Запрос.УстановитьПараметр("ККМ",ТаблицаКасс.ККМ);
		Запрос.УстановитьПараметр("Организация",ОбработкаОбъект.Организация);
		ZОтчеты = Запрос.Выполнить().Выбрать();
		Пока ZОтчеты.Следующий() Цикл
			Если ЕстьНачальный И ZОтчеты.НачальныеСведения Тогда
				ОбластьМакета         = Макет.ПолучитьОбласть("Предупреждение");
				ОбластьМакета.Параметры.ОбъектаПроверки        =?(Выборка.ККМ = Null,Выборка.ККМОтчета,Выборка.ККМ);
				ОбластьМакета.Параметры.Ошибка				   ="По кассе ККМ " + ?(Выборка.ККМ = Null,Выборка.ККМОтчета,Выборка.ККМ) + " найдено несколько Z-отчетов с признаком ""Начальные сведения""";
				ТД.Вывести(ОбластьМакета);
			КонецЕсли;
			Если ZОтчеты.НачальныеСведения Тогда
				ЕстьНачальный = Истина;
			КонецЕсли;
			ПроверкаКорректностиZОтчетов(ZОтчеты.Ссылка,Выборка.ККМ,ТД,Макет);
		КонецЦикла;
	КонецЦикла;
	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ
	|	МИНИМУМ(_ZОтчетФискальный.Дата) КАК Дата,
	|	_ZОтчетФискальный.ККМ.ПодразделениеОрганизации КАК Подразделение
	|ИЗ
	|	Документ._ZОтчетФискальный КАК _ZОтчетФискальный
	|ГДЕ
	|	_ZОтчетФискальный.Дата >= &Дата1
	|
	|СГРУППИРОВАТЬ ПО
	|	_ZОтчетФискальный.ККМ.ПодразделениеОрганизации";
	Запрос.Параметры.Вставить("Дата1",НачалоДня(ОбработкаОбъект.Период.ДатаНачала));
	ТаблицаПодразделений = Запрос.Выполнить().Выбрать();
	Пока ТаблицаПодразделений.Следующий() Цикл
		ЗапросОпт = Новый Запрос;
		ЗапросОпт.Текст = "ВЫБРАТЬ
		|	ПКО.День КАК Дата,
		|	ЕСТЬNULL(ПКО.СуммаДокумента, 0) КАК СуммаПКО,
		|	Отчеты.День КАК ДатаОтчета,
		|	ЕСТЬNULL(Отчеты.ВыручкаБезТоварныхЧеков, 0) КАК Опт,
		|	Отчеты.Подразделение,
		|	ПКО.Подразделение КАК ПодразделениеПКО
		|ИЗ
		|	(ВЫБРАТЬ
		|		_ZОтчетФискальный.Организация КАК Организация,
		|		НАЧАЛОПЕРИОДА(_ZОтчетФискальный.Дата, ДЕНЬ) КАК День,
		|		СУММА(_ZОтчетФискальный.ВыручкаБезТоварныхЧеков) КАК ВыручкаБезТоварныхЧеков,
		|		_ZОтчетФискальный.Подразделение КАК Подразделение
		|	ИЗ
		|		Документ._ZОтчетФискальный КАК _ZОтчетФискальный
		|	ГДЕ
		|		_ZОтчетФискальный.Дата МЕЖДУ &Дата1 И &Дата2
		|		И _ZОтчетФискальный.Проведен = ИСТИНА
		|		И _ZОтчетФискальный.Организация = &Организация
		|		И _ZОтчетФискальный.ВыручкаБезТоварныхЧеков > 0
		|		И _ZОтчетФискальный.Подразделение = &Подразделение
		|	
		|	СГРУППИРОВАТЬ ПО
		|		_ZОтчетФискальный.Организация,
		|		НАЧАЛОПЕРИОДА(_ZОтчетФискальный.Дата, ДЕНЬ),
		|		_ZОтчетФискальный.Подразделение) КАК Отчеты
		|		ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
		|			ПриходныйКассовыйОрдер.Организация КАК Организация,
		|			СУММА(ПриходныйКассовыйОрдер.СуммаДокумента) КАК СуммаДокумента,
		|			НАЧАЛОПЕРИОДА(ПриходныйКассовыйОрдер.Дата, ДЕНЬ) КАК День,
		|			ПриходныйКассовыйОрдер.Подразделение КАК Подразделение
		|		ИЗ
		|			Документ.ПриходныйКассовыйОрдер КАК ПриходныйКассовыйОрдер
		|		ГДЕ
		|			ПриходныйКассовыйОрдер.Проведен = ИСТИНА
		|			И ПриходныйКассовыйОрдер.Дата МЕЖДУ &Дата1 И &Дата2
		|			И ПриходныйКассовыйОрдер.Организация = &Организация
		|			И ПриходныйКассовыйОрдер.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийПКО.ОплатаПокупателя)
		|			И ПриходныйКассовыйОрдер.ОтражатьВБухгалтерскомУчете = ИСТИНА
		|			И ПриходныйКассовыйОрдер.Подразделение = &Подразделение
		|		
		|		СГРУППИРОВАТЬ ПО
		|			ПриходныйКассовыйОрдер.Организация,
		|			НАЧАЛОПЕРИОДА(ПриходныйКассовыйОрдер.Дата, ДЕНЬ),
		|			ПриходныйКассовыйОрдер.Подразделение) КАК ПКО
		|		ПО Отчеты.Организация = ПКО.Организация
		|			И Отчеты.День = ПКО.День
		|			И Отчеты.Подразделение = ПКО.Подразделение";
		ЗапросОпт.УстановитьПараметр("Организация",ОбработкаОбъект.Организация);					
		ЗапросОпт.УстановитьПараметр("Дата1",?(ТаблицаКасс.Дата>ОбработкаОбъект.Период.ДатаНачала,НачалоДня(ТаблицаКасс.Дата),НачалоДня(ОбработкаОбъект.Период.ДатаНачала)));					
		ЗапросОпт.УстановитьПараметр("Дата2",КонецДня(ОбработкаОбъект.Период.ДатаОкончания));
		ЗапросОпт.УстановитьПараметр("Подразделение",ПодразделениеОрганизацииВПодразделение(ТаблицаПодразделений.Подразделение));
		ВыборкаОпт = ЗапросОпт.Выполнить().Выбрать();
		Пока ВыборкаОпт.Следующий() Цикл
			Если ВыборкаОпт.Опт <> ВыборкаОпт.СуммаПКО Тогда
				ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
				ОбластьМакета.Параметры.ОбъектаПроверки        =?(ВыборкаОпт.Подразделение = Null,ВыборкаОпт.ПодразделениеПКО,ВыборкаОпт.Подразделение);
				ОбластьМакета.Параметры.Ошибка				   ="Сумма опта Z-отчетов: " + ВыборкаОпт.Опт + " по подразделению " + ?(ВыборкаОпт.Подразделение = Null,ВыборкаОпт.ПодразделениеПКО,ВыборкаОпт.Подразделение) + " не сходится с суммой ПКО: " + ВыборкаОпт.СуммаПКО + " на дату " + Формат(?(ВыборкаОпт.Дата = Null,ВыборкаОпт.ДатаОтчета,ВыборкаОпт.Дата),"ДФ=dd.MM.yy; ДЛФ=D");
				ТД.Вывести(ОбластьМакета);
			КонецЕсли;	
		КонецЦикла;
	КонецЦикла;	
КонецПроцедуры

Процедура ПроверкаКорректностиZОтчетов(СсылкаНаОтчет,ККМ,ТД,Макет)
	Если НЕ СсылкаНаОтчет.НачальныеСведения Тогда
		СведенияОПредыдущем= НайтиПредыдущееГашение(СсылкаНаОтчет);
		Если СведенияОПредыдущем.НомерГашения=Неопределено Тогда
			ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
			ОбластьМакета.Параметры.ОбъектаПроверки        =СсылкаНаОтчет;
			ОбластьМакета.Параметры.Ошибка				   ="Не найден предыдущий Финансовый отчет (гашение) для ККМ: "+Строка(ККМ);
			ТД.Вывести(ОбластьМакета);					
		Иначе
			Если СсылкаНаОтчет.НомерГашения=СведенияОПредыдущем.НомерГашения Тогда				
				ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
				ОбластьМакета.Параметры.ОбъектаПроверки        =СсылкаНаОтчет;
				ОбластьМакета.Параметры.Ошибка				   ="Уже существует документ с таким номером гашения для выбранной ККМ. Возможно вы задублировали документ." + Символы.ПС + "Ожидается номер "+Строка(СведенияОПредыдущем.НомерГашения+1);
				ТД.Вывести(ОбластьМакета);
			ИначеЕсли (СсылкаНаОтчет.НомерГашения<СведенияОПредыдущем.НомерГашения) Тогда				
				ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
				ОбластьМакета.Параметры.ОбъектаПроверки        =СсылкаНаОтчет;
				ОбластьМакета.Параметры.Ошибка				   ="Существует документ с более поздним номером гашения для выбранной ККМ. Отмените более поздние гашения, и проведите их в порядке возрастания номера гашения." + Символы.ПС + "Ожидается номер "+Строка(СведенияОПредыдущем.НомерГашения+1);
				ТД.Вывести(ОбластьМакета);
			ИначеЕсли (СсылкаНаОтчет.НомерГашения>(СведенияОПредыдущем.НомерГашения+1)) Тогда
				ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
				ОбластьМакета.Параметры.ОбъектаПроверки        =СсылкаНаОтчет;
				ОбластьМакета.Параметры.Ошибка				   ="Существуют пропущенные номера гашения для выбранной ККМ. Преверте номер гашения или проведите гашения для пропущенных номеров." + Символы.ПС + "Ожидается номер "+Строка(СведенияОПредыдущем.НомерГашения+1);
				ТД.Вывести(ОбластьМакета);				
			КонецЕсли;
		КонецЕсли;
		
		РасчВозврат = СсылкаНаОтчет.Возвраты; //Считаем что всегда увеличивают;
		РасчНеиспользованные = 0; //Считаем что не влияют;
		Если (СсылкаНаОтчет.Накопление<>(СведенияОПредыдущем.Накопление+СсылкаНаОтчет.Выручка+РасчВозврат+РасчНеиспользованные)) Тогда
			ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
			ОбластьМакета.Параметры.ОбъектаПроверки        =СсылкаНаОтчет;
			ОбластьМакета.Параметры.Ошибка				   ="Контроль накопления не пройден! Проверьте правильность внесения данных с бумажного чека ""Z-ОТЧЕТ (фискальный)""!";
			ТД.Вывести(ОбластьМакета);
		КонецЕсли;
	КонецЕсли;
	Если СсылкаНаОтчет.Неиспользованные + СсылкаНаОтчет.ВыручкаБезТоварныхЧеков>СсылкаНаОтчет.Выручка Тогда
		ОбластьМакета         = Макет.ПолучитьОбласть("Ошибка");
		ОбластьМакета.Параметры.ОбъектаПроверки        =СсылкаНаОтчет;
		ОбластьМакета.Параметры.Ошибка				   ="Сумма неиспользованных чеков и оптовых продаж превышает общую выручку";
		ТД.Вывести(ОбластьМакета);
	КонецЕсли;
	
КонецПроцедуры

Функция ПодразделениеОрганизацииВПодразделение(ПодразделениеОрганизации) Экспорт
	    Запрос=Новый запрос;
		Запрос.Текст=
		"ВЫБРАТЬ РАЗРЕШЕННЫЕ
		|	СоответствиеПодразделенийИПодразделенийОрганизаций.Подразделение,
		|	СоответствиеПодразделенийИПодразделенийОрганизаций.ПодразделениеОрганизации
		|ИЗ
		|	РегистрСведений.СоответствиеПодразделенийИПодразделенийОрганизаций КАК СоответствиеПодразделенийИПодразделенийОрганизаций
		|ГДЕ
		|	СоответствиеПодразделенийИПодразделенийОрганизаций.ПодразделениеОрганизации = &ПодразделениеОрганизации
		|	И СоответствиеПодразделенийИПодразделенийОрганизаций.Организация = &Организация";
		
		Запрос.УстановитьПараметр("ПодразделениеОрганизации",ПодразделениеОрганизации);
		Запрос.УстановитьПараметр("Организация",ПодразделениеОрганизации.Владелец);
		
		Результат=Запрос.Выполнить().Выбрать();
		Если Результат.Следующий() Тогда
			Возврат  Результат.Подразделение;
		КонецЕсли;
		Возврат Справочники.Подразделения.ПустаяСсылка();

КонецФункции



 

 

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

Пример работы проверки:

Единая проверка ошибок

 

3 ЭТАП

Когда количество касс вырастает к трехзначной цифре, остро назревает вопрос об автоматизации процесса получения сведений из z-отчета ККМ в автоматическом режиме.

Рассмотрим на примере POS систем АТОЛ и их ККМ.

Стандартный обмен текстовыми файлами с POS системами АТОЛ нам не особо помогает в процессе автоматизации.
При пробитии z-отчета POS система АТОЛа пишет в файл report.txt  строку с кодом "63"
Пример строки:

 

1937;19.04.2018;20:09:47;63;1;195;2;;19;1379;1379;1379;9;2;0;0;0;;;1379;;0;8;0;;146/245/19;1;;;;;;;0;0 - Ошибок нет,0 - Ошибок нет,0 - Ошибок нет;;;;;;;; 

Что можно из нее выдернуть :
дата - 19.04.2018,
время – 20:09:47
порядковый номер
z
-отчета -  19
выручку -1379.00
наличную часть выручки – 1379.00
накопление (ГРОСС ИТОГ) – такой информации АТОЛ в файл не записывает.

АТОЛ дает обработку «82АТОЛ.epf». В ее модуле объекта можно найти участок кода:

Функция ПрочитатьФайлВыгрузки(Объект, Файл, Отчет, Карты, Оплаты)
//++Комментарий автора
// Эта функция вызывается из Функции ЗагрузитьОтчет(Объект, Отчет, Карты = Неопределено, Оплаты = Неопределено)
// А она вызывается из функции «ЗагрузитьОтчетОПродажах» обработки конфигурации «ТОСервер»
//--Комментарий автора
       //… разбор файла и запись его в таблицу «Отчет»…

		ИначеЕсли ТипТранзакции = 63 Тогда
			// Z-отчёт
			Если Объект.Параметры.КритичныеОперации Тогда
				Попытка
					НомерСмены		= Число(СтрПолучитьСтроку(ТекущаяСтрока, 9));
					Выручка		= Число(СтрПолучитьСтроку(ТекущаяСтрока, 10));
					Наличность	= Число(СтрПолучитьСтроку(ТекущаяСтрока, 11));
					СменныйИтог = Число(СтрПолучитьСтроку(ТекущаяСтрока, 12));

				Исключение
				КонецПопытки;
			КонецЕсли;

//....
КонецФункции

Для автоматизации загрузки Z-отчетов нужно передать по функциям пустую табличку с полями z-отчета и заполнить ее в указанном участке кода, а по возврату таблицы из функций - создать документ _ZОтчетФискальный в  модуле обработки «ТОСервер».

Нам этот способ не подходил, поскольку АТОЛ не выдает накопление (ГРОСС ИТОГ) и возвраты, а их проверка была жизненно необходима.

Мы пошли другим путем, написали свою POS  систему и сведения брали из драйвера ККМ в полном объеме в виде XML.

Пример xml

О обработках по автоматической загрузке данных с ККМ в 1С парсим приходящие файлы XML от ККМ.

Функция ЗагрузитьXML_reg(ПолноеИмяФайла) Экспорт    
	Перем ПостроитьДУМ,ДокументДУМ;
	ПостроитьДУМ = Новый ПостроительDOM; 
	ПолучитьКорневойУзелXMLФайла(ПолноеИмяФайла,ПостроитьДУМ,ДокументДУМ);
	Если ДокументДУМ=Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	КорневойУзел = ДокументДУМ.ПервыйДочерний;
	Если КорневойУзел=Неопределено Тогда
		Возврат Истина;
	КонецЕсли;
	
//...
	ЕстьОшибки = Ложь;
	Если КорневойУзел.ЕстьДочерниеУзлы() Тогда
		Для Каждого Узел_entry ИЗ КорневойУзел.ПолучитьЭлементыПоИмени("Документы") Цикл
			Если Узел_entry.ЕстьДочерниеУзлы() Тогда
//…
				Для Каждого Узел_requests ИЗ Узел_entry.ПолучитьЭлементыПоИмени("Документ.ZОтчёт") Цикл  //ZОтчет
					СтруктураДок = Новый Структура("ИДОтчета,НомерГашения,НомерСмены,ДатаККМ,НомерККМ,СуммаВыручки,СуммаВозвратов,СуммаВКассе,СуммаПрихода,СуммаНеобнуляемая","",0,0,Дата(1,1,1),"",0,0,0,0,0);
					Для Каждого УзелРеквизитЭлемента ИЗ Узел_requests.ПолучитьЭлементыПоИмени("*") Цикл
						ТекИмяУзла = УзелРеквизитЭлемента.ИмяУзла;
						ТекЗначениеУзла = СокрЛП(УзелРеквизитЭлемента.ТекстовоеСодержимое);
						Если ТекИмяУзла="ДатаККМ"  Тогда
							СтруктураДок.Вставить(ТекИмяУзла,ПолучитьДатуВремяИзСтроки(ТекЗначениеУзла,6)); 
						ИначеЕсли ТекИмяУзла="СуммаВыручки" ИЛИ ТекИмяУзла="СуммаВозвратов" ИЛИ  ТекИмяУзла="СуммаВКассе" ИЛИ  ТекИмяУзла="СуммаПрихода" ИЛИ  ТекИмяУзла="СуммаНеобнуляемая" Тогда
							СтруктураДок.Вставить(ТекИмяУзла,Число(ТекЗначениеУзла));
						Иначе
							СтруктураДок.Вставить(ТекИмяУзла,СокрЛП(ТекЗначениеУзла));
						КонецЕсли;
					КонецЦикла;//конец шапки документа
					НовЧек = ZОтчет.Добавить();
					НовЧек.ИДОтчета				=СтруктураДок.ИДОтчета;
					НовЧек.НомерГашения			=СтруктураДок.НомерГашения;
					НовЧек.НомерСмены			=СтруктураДок.НомерСмены;
					НовЧек.ДатаККМ				=СтруктураДок.ДатаККМ;
					НовЧек.НомерККМ				=СтруктураДок.НомерККМ;
					НовЧек.СуммаВыручки			=СтруктураДок.СуммаВыручки;
					НовЧек.СуммаВозвратов		=СтруктураДок.СуммаВозвратов;
					НовЧек.СуммаВКассе			=СтруктураДок.СуммаВКассе;
					НовЧек.СуммаПрихода			=СтруктураДок.СуммаПрихода;
					НовЧек.СуммаНеобнуляемая	=СтруктураДок.СуммаНеобнуляемая;
					НовЧек.СсылкаZОтчета		= ПолучитьДокZОтчетПоИД(СтруктураДок.ИдОтчета, СтруктураДок.ДатаККМ);
				КонецЦикла;
			КонецЕсли;
			
		КонецЦикла;
	КонецЕсли;
	
	
	Возврат ЕстьОшибки;
КонецФункции

Отдельно загружает загруженную таблицу z-отчетов

Функция ПолучитьКассуККМПоСерийномуНомеру(СерНомер)
	Запрос = новый Запрос;
	Запрос.УстановитьПараметр("СерНомер", СерНомер);
	Запрос.Текст= "ВЫБРАТЬ РАЗРЕШЕННЫЕ
	|	КассыККМ.Ссылка
	|ИЗ
	|	Справочник.КассыККМ КАК КассыККМ
	|ГДЕ
	|	КассыККМ.СерийныйНомер = &СерНомер";
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Количество() = 0 тогда 
		Сообщить("Не удалось найти кассу с серийным номером " + СерНомер +" !!!");
		Возврат Неопределено;
	ИначеЕсли Выборка.Количество() > 1 тогда 
		ТекстЗаголовок = "В базе найдено две Кассы с одинаковым серийным номером"; 
		Сообщить(ТекстЗаголовок);
		Возврат Неопределено;
	ИначеЕсли Выборка.Количество() = 1 тогда 
		Выборка.Следующий();
		
		Возврат Выборка.Ссылка;
	КонецЕсли;	
	
КонецФункции	

Функция ПроверитьПервыйВвод(КассаККМ)
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ККМ", КассаККМ);
	Запрос.Текст= "ВЫБРАТЬ
	|	_ZОтчетФискальный.Ссылка
	|ИЗ
	|	Документ._ZОтчетФискальный КАК _ZОтчетФискальный
	|ГДЕ
	|	_ZОтчетФискальный.ККМ = &ККМ
	|	И _ZОтчетФискальный.Проведен
	|	И НЕ _ZОтчетФискальный.ПометкаУдаления";
	Выборка = Запрос.Выполнить().Выбрать();			  
	Возврат НЕ Выборка.Следующий();			  
КонецФункции	

Процедура СоздатьДокументыZОтчеты(Кнопка)
	// Вставить содержимое обработчика.
	Если ZОтчет.Количество() = 0 тогда 
		Сообщить("Нет Z-Отчетов доступных для загрузки");
	КонецЕсли;
	ДЛя Каждого Ст Из ZОтчет Цикл 
		Если Ст.СоздатьZ тогда 
			ДокZ = Документы._ZОтчетФискальный.СоздатьДокумент();
			ЗаполнениеДокументов.ЗаполнитьШапкуДокумента(ДокZ);
			ДокZ.Дата = Ст.ДатаККМ;
			//Проверим организации
			Если ДокZ.Организация.Пустая() тогда 
				Организация = Справочники.Организации.НайтиПоКоду("000000001");
				Если Организация = Неопределено тогда 
					Сообщить("Не удалось установить организацию");
					Возврат;
				КонецЕсли;
				ДокZ.Организация = Организация;
			КонецЕсли;
			КассаККМ = ПолучитьКассуККМПоСерийномуНомеру(Ст.НомерККМ);
			Если не КассаККМ = Неопределено тогда 
				ДокZ.ККМ = КассаККМ;
			Иначе
				Возврат;
			КонецЕсли;
			
			Если ДокZ.Ответственный.Пустая() Тогда 
				ДокZ.Ответственный = ПараметрыСеанса.ТекущийПользователь;
			КонецЕсли;
			ДокZ.НачальныеСведения = ПроверитьПервыйВвод(ДокZ.ККМ);
			ДокZ.Подразделение = Подразделение;
			ДокZ.Возвраты 		= Ст.СуммаВозвратов;
			ДокZ.Выручка  		= Ст.СуммаВыручки;
			ДокZ.Накопление 	= Ст.СуммаНеобнуляемая;
			ДокZ.НомерГашения   = Ст.НомерСмены;
			ДокZ.СуммаДокумента =ДокZ.Выручка-ДокZ.Возвраты-ДокZ.Неиспользованные;
			ДокZ.Комментарий    = "АвтоСоздание ##"+Ст.ИДОтчета+"##";  
			Попытка
				ДокZ.Записать(РежимЗаписиДокумента.Проведение);
				Ст.СоздатьZ = Ложь;
			Исключение
				Сообщить("Документ не удалось записать и провести!!! " + ОписаниеОшибки());
				ДокZ.Записать(РежимЗаписиДокумента.Запись);
				Ст.СоздатьZ = Ложь;
			КонецПопытки; 
			Ст.СсылкаZОтчета = ДокZ.Ссылка;
		КонецЕсли;	
	КонецЦикла;		
	
КонецПроцедуры

 

В РЕЗУЛЬТАТЕ:

Теперь у нас за несколькими сотнями ККМ наблюдает один бухгалтер, благодаря автоматической загрузке и единой проверке ошибок.

Настало время переходить на сверку данных не с ККМ, а с ОФД. Но это тема уже для следующих статей.

14

См. также

Специальные предложения

Комментарии
Избранное Подписка Сортировка: Древо
2. Goruch 8 07.06.18 13:27 Сейчас в теме
Я правильно понимаю, что вы сверяете данные полученные и кассовой pos системы с данными в товароучетной системе?
Т.к. я не нашел никакого упоминание о том, как вытащить данные по z-отчету напрямую из кассы.
3. dima_home 99 08.06.18 10:46 Сейчас в теме
Мы сверяем данные о продажах и поступлении денежных средств, отраженные в учетной системе-1С с реальными чеками, пробитыми в ККМ, посредством ручного переноса контрольных сведений(дублирование) с чека ККМ "Z-ОТЧЕТ" в документ 1С "z-отчет" и их дальнейшее автоматическое сравнение.

Когда ККМ много, как у нас, бухгалтеру слишком сложно переносить с печатного чека ККМ "Z-ОТЧЕТ" сведения (ВЫРУЧКА,НОМЕРСМЕНЫ,ВОЗВРАТ_ПРИХОДА,ГРОСС_ИТОГ,НОМЕР_СМЕНЫ) в 1С, и тогда можно автоматизировать процесс получения данных Z-ОТЧЕТА напрямую из ККМ или из POS.

Сегодня можно уйти с пути получения контрольных сведений из ККМ в сторону получения контрольных сведений непосредственно из ОФД. Правда пока операторы ОФД не предоставляют API, но всегда есть выгрузки CSV за период.
4. dima_home 99 08.06.18 10:52 Сейчас в теме
Еще об автоматизации получения данных по z-отчету.
Драйвер ККМ АТОЛ при передачи задания на закрытие смены в кассу, возвращает все сведения, напечатанные в чеке Z-Отчет в POS систему. В свою очередь, POS системы АТОЛ при закрытии смены пишут в лог файл report лишь часть данных о z-отчете.

Поскольку мы для своей сети магазинов писали свою POS систему с нуля, то брали полные данные непосредственно из драйвера ККМ и передавали их в 1С уже сами через XML.
5. Bosma 68 08.06.18 12:09 Сейчас в теме
Интересная публикация. Смежный вопрос: Если используется в качестве POS системы АТОЛ, и соответственно смену в ККМ закрывает именно эта система, можно ли из другой внешней программы (пусть та же 1С) через драйвер ККМ получить данные напечатанные в Z-Отчете? У Вас, насколько я понял, совя POS система именно в момент закрытия смены ККМ получает ответ о Z-Отчете и складывает его в xml. Вопрос в том, что можно ли получить данные Z-Отчета из ККМ уже после закрытия смены.
7. dima_home 99 08.06.18 14:57 Сейчас в теме
К сожаление дважды войти в реку нельзя... то есть, либо вы закрываете смену в POS системе АТОЛ и она понимает это событие и соответственно закрывает у себя смену, либо вы сами пишете всю POS систему и делает что хотите.


Что касается прямого обращения к ККМ, можешьтут посмотреть ... даже есть примеры на C++. Можно например не закрывать смену, а попробовать получить с сведения по последней операции.


PS:/Отдельно: "ГРОСС ИТОГ".
Из POS АТОЛА можно получать строку с кодом 63... я писал в 3 ЭТАПЕ... но тогда придётся отказаться от контроля накопления (ГРОСС ИТОГА).
Как рассказывали тех.инженеры компании АТОЛ, когда мы разбирались, почему иногда ККМ не верно считает ГРОСС ИТОГ в новых ККМ, они сказали что ГРОСС ИТОГ не ведется в ФН (который подписывает чеки перед передачей в ОФД), он даже не хранит его, как это было раньше в ЭКЛЗ. Хранение и расчет ГРОСС ИТОГА сегодня возложен на сам ККМ, а она иногда, пробивая чеки не достоверно понимает статус(результат) пробития чеков. Это, кстати, та-же причина расхождения данных ККМ и 1С, когда ККМ (прямо подсоединенный к 1С) возвращает код ошибки при пробитии чека (например бумага закончилась), 1С считает что чек не пробит, а заходишь в ОФД, чек уже пробит.
6. Goruch 8 08.06.18 14:55 Сейчас в теме
Вы сказали:

Поскольку мы для своей сети магазинов писали свою POS систему с нуля, то брали полные данные непосредственно из драйвера ККМ и передавали их в 1С уже сами через XML.

Скажите, подкскажите каким образом вы получали данные о z-отчетах с ККМ?
Это какой-то обмен/запрос с ФР? Если да то подскажите где по -этому поводу почитать.
8. Denger 13.06.18 08:03 Сейчас в теме
(6)http://partner.atol.ru/files/dc/590/Drivers8_Um.pdf

ККМ хранит данные в регистрах. Посмотреть можно в прикрепленном скриншоте

Например, //ВНЕСЕНИЯ
driver.CheckType=1;
	driver.RegisterNumber=4;
	driver.GetRegister();
	ВНЕСЕНИЙ=Формат(driver.Summ,"Ч4.2");
	//ВЫПЛАТЫ
	driver.RegisterNumber=5;
	driver.GetRegister();
	ВЫПЛАТ=Формат(driver.Summ,"Ч4.2");
	
	
	Инкассация=ВозвратПараметра(10,0,0);
	Выручка=ВозвратПараметра(11,0,0);
	
	//КОЛ ПРОДАЖ
	driver.RegisterNumber=6;
	driver.CheckType=1;
	driver.GetRegister();
	КолПродаж=Формат(driver.count,"Ч(0)4");
	//КОЛ ВОЗВР
	driver.RegisterNumber=6;
	driver.CheckType=2;
	driver.GetRegister();
	КолВозв=Формат(driver.count,"Ч(0)4");
	
	//НОМЕР ККМ	
	driver.RegisterNumber=22;
	driver.GetRegister();
	НОМККМ=driver.SerialNumber;
	
	//НОМЕР СМЕНЫ, ДАТА
	driver.RegisterNumber=17;
	driver.GetRegister();
	НОМСМЕНЫ=driver.session;
	г=driver.Year;
	м=driver.Month;
	д=driver.Day;
	ДАТАСМЕНЫ=Дата(г,м,д);
Показать
Прикрепленные файлы:
9. dima_home 99 14.06.18 09:13 Сейчас в теме
гросс итог можно получить
driver.RegisterNumber=14;
//14 - Необнуляемая сумма после последней перерегистрации + сменный итог текущей смены (Summ)
driver.OperationType=0;
//0 – продажа;1 – покупка;2 – возврат продажи; 3 – возврат покупки.
driver.GetRegister();
НеобнуляемаяСумма=Формат(driver.count,"Ч(0)4");


Я вам советую направить силы не по съему данных с ККМ, а по съему данных с ОФД.

Хотя и в этом направлении еще много препятствий. Например наше ОФД - "Платформа О-Ф-Д" совсем сдурела - за возможность получить данные о ККМ требует оплату по 300 рублей в месяц за каждую ККМ. У нас более 400 касс это получается более 120'000 рублей в месяц. Легче посадить бухгалтера, который будет выгружать CSV за месяц, и его уже сверять в 1С внешней обработкой. Чтоб они от жадности лопнули ;)
12. dima_home 99 02.11.18 10:46 Сейчас в теме
Из практики
(9)
Например наше ОФД - "Платформа О-Ф-Д" совсем сдурела

Принято решение переходить на ОФД ТАКСКОМ... у них АПИ бесплатное (для нас по крайней мере)
10. Goruch 8 14.06.18 13:07 Сейчас в теме
У нас Контур. У него все хорошо и красиво, но нет API для того чтобы считать данные из ОФД в автоматизированном режиме...
Вот по-этому думаю как данные автоматизированно получать....
Плюс на данных из ОФД нет номера чека. Есть номер ФД и номер чека за смену, а это не та номера...
11. popenko 31.10.18 12:02 Сейчас в теме
понравился ваш подход. но есть вопрос, чисто практический - У вас много касс, а как работается бухгалтеру с формой списка документа (z-отчеты), может надо закладки хотя бы по подразделениям типа infostart.ru https://infostart.ru/public/16536/ или это лишнее? Я задал вопрос со стороны бухгалтера - если надо что -то посмотреть (найти, сравнить, да мало ли еще чего).
И еще не заметил.... может для подстраховки организовать хранилище с файлами из пос-системы
справочник -ХранилищеВнешнихФайлов
реквизиты - объект - документ ZОтчетФискальный
и Файл с типом ХранилищеЗначения
что вы на это скажите, исходя из большого кол-ва касс это надо или нет?
Прикрепленные файлы:
13. dima_home 99 02.11.18 11:33 Сейчас в теме
Напишите ваше сообщение
(11)
Журнал нужен лишь во время разбора полетов. С ним как правило работает только ревизор.
Он делает отбор по кассе и смотрит все z-отчеты кассы в хронологии.
Единственное в журнале есть колонка "Начальные сведения" - это булево является "красным" флагом для проверки - означает что бухгалтер начал подсчет ГРОСИТОГа или НОМЕРА СМЕНЫ с начало, без сверки с предыдущим z-отчетом.
Снимок1

За всеми кассами следит один бухгалтер-ревизор запуская единую проверку (сразу по всем ККМ), которая все и проверяет (пишу статью по этой единой проверке...).
Снимок2

А сам момент загрузки с касс, например от торговых представителей выглядит так:
Бухгалтер филиалов когда принимает от представителя выручку... может сразу увидеть все продажи, все чеки и все z-отчеты торгового представителя за всю смену его работы.
Снимок3
А потом нажав кнопочку выделить все, и нажав кнопки создать документы... загружает их в базу.

Для POS систем нет интерфейсов - из магазинов все грузится автоматом, без участия человека.
Прикрепленные файлы:
Оставьте свое сообщение