xmlTSDGalery/CommonModules/ФорматноЛогическийКонтроль/Ext/Module.bsl
kykaevv 88098aa86f Initial commit: конфигурация ТСД с обменом УНФ.
Включает перемещение по ячейкам, стикеровку МП, синхронизацию справочников и обработку кодов маркировки.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 12:50:57 +03:00

1431 lines
108 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#Область ПрограммныйИнтерфейс
// Выполняет проверку правильности заполнения тегов в соответствии с ФФД
//
// Параметры:
// ДанныеЧека - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
// ИдентификаторУстройства - СправочникСсылка.ПодключаемоеОборудование - Не обязательный
// Ошибки - Массив из строка - Строки ошибок по результатам контроля
// ИсправленыОсновныеПараметры - Булево
//
// Возвращаемое значение:
// Булево.
Функция ВыполненаПроверкаОбязательностиИПравильностиЗаполненияТэгов(ДанныеЧека, ИдентификаторУстройства, Ошибки = Неопределено, ИсправленыОсновныеПараметры = Неопределено) Экспорт
Результат = Ложь;
Проверка = ПараметрыПроверки();
Если Не ЗначениеЗаполнено(ИдентификаторУстройства) Тогда
Проверка.Ошибки.Добавить(НСтр("ru = 'Не выбрано устройство'"));
Ошибки = Проверка.Ошибки;
Возврат Результат;
КонецЕсли;
ПараметрыРегистрации = ОборудованиеЧекопечатающиеУстройства.ПараметрыРегистрацииУстройства(ИдентификаторУстройства);
ПривестиАдресИМестоРасчетов(ДанныеЧека, ПараметрыРегистрации);
ПривестиДанныеКТребуемомуФормату(ДанныеЧека, ИсправленыОсновныеПараметры, ПараметрыРегистрации);
СтруктураДанныхФЛК = СтруктураДанныхФорматноЛогическогоКонтроля(ИдентификаторУстройства);
Проверка.ВерсияФФД = СтруктураДанныхФЛК.ФорматФД;
ДанныеЧека.Вставить("ФорматФД", СтруктураДанныхФЛК.ФорматФД);
ЗаполнитьЗначенияСвойств(Проверка,
ОбщегоНазначенияБПО.ЗначенияРеквизитовОбъекта(ИдентификаторУстройства, "ИспользуетсяФН36, ТипОборудования"));
ПроверитьРеквизит(Проверка, ДанныеЧека, "АдресРасчетов");
ПроверитьРеквизит(Проверка, ДанныеЧека, "МестоРасчетов", "1.05, 1.1, 1.2");
ПроверитьРеквизит(Проверка, ДанныеЧека, "ОрганизацияНазвание");
ПроверитьРеквизит(Проверка, ДанныеЧека, "ОрганизацияИНН");
ПроверитьРеквизит(Проверка, ДанныеЧека, "ТипРасчета");
ПроверитьРеквизит(Проверка, ДанныеЧека, "СистемаНалогообложения");
ПроверитьРеквизит(Проверка, ДанныеЧека, "Кассир");
ПроверитьДлинуРеквизита(Проверка, ДанныеЧека, "Кассир", 64);
ПроверитьРеквизитИНН (Проверка, ДанныеЧека, "КассирИНН");
ПроверитьСведенияОПокупателе(Проверка, ДанныеЧека);
ПроверитьКодыНалогообложения(Проверка, ДанныеЧека, ПараметрыРегистрации.КодыСистемыНалогообложения);
ПроверитьЭлектронныйЧек(Проверка, ДанныеЧека);
СуммыПозиций = ПараметрыСуммыПозиций();
СуммыОплат = ПараметрыСуммыОплат();
ВыполнитьПроверкуФактическойОплаты = Ложь;
Для Каждого Позиция Из ДанныеЧека.ПозицииЧека Цикл
УвеличитьНомерПозиции(Проверка);
Если Позиция.Свойство("ФискальнаяСтрока") Тогда
ПроверитьРеквизит(Проверка, Позиция, "Количество");
ПроверитьРеквизит(Проверка, Позиция, "ПризнакСпособаРасчета", "1.05, 1.1, 1.2");
ПроверитьРеквизит(Проверка, Позиция, "ПризнакПредметаРасчета", "1.1, 1.2");
ПроверитьДанныеАгента(Проверка, ДанныеЧека, Позиция);
ПроверитьДанныеПоставщика(Проверка, ДанныеЧека, Позиция);
ВычислитьСуммыПозиций(СуммыПозиций, Позиция);
ПреобразоватьПризнакПредметаРасчета(Проверка, Позиция);
ПризнакСпособаРасчета = Позиция.ПризнакСпособаРасчета;
Если ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПредоплатаЧастичная
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ОплатаКредита Тогда
ВыполнитьПроверкуФактическойОплаты = Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Для Каждого ЭлементОплаты Из ДанныеЧека.ТаблицаОплат Цикл
ПроверитьРеквизит(Проверка, ЭлементОплаты, "ТипОплаты");
ВычислитьСуммыОплат(СуммыОплат, ЭлементОплаты);
КонецЦикла;
ВычислитьСуммуНаличныеБезСдачи(СуммыПозиций, СуммыОплат);
ПроверитьСуммыОплат(Проверка, СуммыПозиций, СуммыОплат);
Если ВыполнитьПроверкуФактическойОплаты Тогда
ПроверитьСуммуФактическихОплат(Проверка, СуммыПозиций, СуммыОплат);
КонецЕсли;
ПреобразоватьСистемуНалогообложения(Проверка, ДанныеЧека);
Результат = Проверка.Ошибки.Количество() = 0;
Ошибки = СтрСоединить(Проверка.Ошибки, Символы.ПС);
Возврат Результат;
КонецФункции
// Выполняет корректировку данных чека
//
// Параметры:
// ДанныеЧека - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
// ИсправленыОсновныеПараметры - Булево
//
Процедура ПривестиДанныеКТребуемомуФормату(ДанныеЧека, ИсправленыОсновныеПараметры = Неопределено, ПараметрыРегистрации = Неопределено) Экспорт
Если ДанныеЧека.ИндивидуальныйРежимПодготовкиДанныхКПередачеВОФД Тогда
Возврат;
КонецЕсли;
Если ДанныеЧека.СпособФорматноЛогическогоКонтроля = Перечисления.СпособыФорматноЛогическогоКонтроля.НеКонтролировать Тогда
Возврат;
КонецЕсли;
СуммыПозиций = ПараметрыСуммыПозиций();
НужноПривестиСуммы = Ложь;
ИсправленыОсновныеПараметры = Ложь;
Для Каждого Позиция Из ДанныеЧека.ПозицииЧека Цикл
Если Позиция.Свойство("ФискальнаяСтрока") Тогда
ПривестиЕслиСпособРасчетаБезПередачиТовара(Позиция, ИсправленыОсновныеПараметры);
ПривестиСтавкиНДС(Позиция);
ВычислитьСуммыПозиций(СуммыПозиций, Позиция);
ПризнакСпособаРасчета = Позиция.ПризнакСпособаРасчета;
Если ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПредоплатаЧастичная
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ОплатаКредита Тогда
НужноПривестиСуммы = Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если НужноПривестиСуммы Тогда
СуммыОплат = ПараметрыСуммыОплат();
Для Каждого СтрокаОплат Из ДанныеЧека.ТаблицаОплат Цикл
ВычислитьСуммыОплат(СуммыОплат, СтрокаОплат);
КонецЦикла;
ВычислитьСуммуНаличныеБезСдачи(СуммыПозиций, СуммыОплат);
ПривестиСуммыПозицийЧекаКСуммеТаблицыОплат(ДанныеЧека, СуммыПозиций, СуммыОплат);
КонецЕсли;
КонецПроцедуры
// Определяет требуется ли выполнить корректировку сумм в позициях чека
//
// Параметры:
// ДанныеЧека - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
// ИдентификаторУстройства - СправочникСсылка.ПодключаемоеОборудование - Не обязательный
// Если заполнено оборудование и не заполнен способ контроля в общих параметрах,
// то способ контроля и допустимое расхождение получаются из подключаемого оборудования.
//
// Возвращаемое значение:
// Булево
//
Функция НуженФорматноЛогическийКонтроль(ДанныеЧека, ИдентификаторУстройства = Неопределено) Экспорт
СтандартнаяОбработка = Истина;
ПереопределяемыйРезультат = ФорматноЛогическийКонтрольПереопределяемый.НуженФорматноЛогическийКонтроль(ДанныеЧека, СтандартнаяОбработка);
Если Не СтандартнаяОбработка Тогда
Возврат ПереопределяемыйРезультат;
КонецЕсли;
СпособФорматноЛогическогоКонтроля = ДанныеЧека.СпособФорматноЛогическогоКонтроля;
ДопустимоеРасхождение = ДанныеЧека.ДопустимоеРасхождениеФорматноЛогическогоКонтроля;
Если СпособФорматноЛогическогоКонтроля = Неопределено И ИдентификаторУстройства <> Неопределено Тогда
СтруктураДанныхФЛК = СтруктураДанныхФорматноЛогическогоКонтроля(ИдентификаторУстройства);
ДопустимоеРасхождение = СтруктураДанныхФЛК.ДопустимоеРасхождениеФорматноЛогическогоКонтроля;
СпособФорматноЛогическогоКонтроля = СтруктураДанныхФЛК.СпособФорматноЛогическогоКонтроля;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(СпособФорматноЛогическогоКонтроля)
ИЛИ СпособФорматноЛогическогоКонтроля = Перечисления.СпособыФорматноЛогическогоКонтроля.НеКонтролировать Тогда
Возврат Ложь;
КонецЕсли;
Для Каждого ТекущаяПозиция Из ДанныеЧека.ПозицииЧека Цикл
Если Не ТекущаяПозиция.Свойство("ФискальнаяСтрока") Тогда
Продолжить;
КонецЕсли;
Количество = ?(ТекущаяПозиция.Количество = 0, 1, ТекущаяПозиция.Количество);
Если Количество = 1 Тогда
Если ТекущаяПозиция.ЦенаСоСкидками <> ТекущаяПозиция.Сумма Тогда
Возврат Истина;
КонецЕсли;
Иначе
Если Окр(ТекущаяПозиция.ЦенаСоСкидками * Количество, 2, 1) <> ТекущаяПозиция.Сумма Тогда
Сумма = ТекущаяПозиция.Сумма;
// Умножаем входящее количество на заданную цену и получаем новую промежуточную сумму.
// Сумму НЕ округляем - Требования АСК ККТ ФНС РФ.
НоваяСуммаБезОкругления = Количество * ТекущаяПозиция.ЦенаСоСкидками;
// Вычисляем разницу между промежуточной и входящей суммой.
РазницаСумм = НоваяСуммаБезОкругления - Сумма;
// Если разница допустима - оставляем одну строку.
// Если разницы нет - оставляем одну строку.
Если РазницаСумм >= -ДопустимоеРасхождение И РазницаСумм <= ДопустимоеРасхождение Тогда
// Дополнительно получаем расчетную цену с учетом скидок делением входящей суммы на входящее количество.
// Цену округляем.
РасчетнаяЦена = Окр(Сумма / Количество, 2, 1);
Если ТекущаяПозиция.ЦенаСоСкидками <> РасчетнаяЦена Тогда
// Но. Если цена не совпадает с расчетной - все равно надо ее пересчитывать.
Возврат Истина;
КонецЕсли;
Иначе
// Надо разделять строку.
Возврат Истина;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
// Выполняет проверку сумм фискальных строк, осуществляя форматно-логический контроль чека.
// Функция переопределяется методом ФорматноЛогическийКонтрольПереопределяемый.ПровестиФорматноЛогическийКонтроль.
//
// Параметры:
// ДанныеЧека - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
// ИдентификаторУстройства - СправочникСсылка.ПодключаемоеОборудование - Не обязательный
// Если заполнено оборудование и не заполнен способ контроля в общих параметрах,
// то способ контроля и допустимое расхождение получаются из подключаемого оборудования.
//
Процедура ПровестиФорматноЛогическийКонтроль(ДанныеЧека, ИдентификаторУстройства = Неопределено) Экспорт
СтандартнаяОбработка = Истина;
ФорматноЛогическийКонтрольПереопределяемый.ПровестиФорматноЛогическийКонтроль(ДанныеЧека, СтандартнаяОбработка, ИдентификаторУстройства);
Если Не СтандартнаяОбработка Тогда
Возврат;
КонецЕсли;
Если Не НуженФорматноЛогическийКонтроль(ДанныеЧека, ИдентификаторУстройства) Тогда
Возврат;
КонецЕсли;
СпособФорматноЛогическогоКонтроля = ДанныеЧека.СпособФорматноЛогическогоКонтроля;
ДопустимоеРасхождение = ДанныеЧека.ДопустимоеРасхождениеФорматноЛогическогоКонтроля;
Если СпособФорматноЛогическогоКонтроля = Неопределено И ИдентификаторУстройства <> Неопределено Тогда
СтруктураДанныхФЛК = СтруктураДанныхФорматноЛогическогоКонтроля(ИдентификаторУстройства);
ДопустимоеРасхождение = СтруктураДанныхФЛК.ДопустимоеРасхождениеФорматноЛогическогоКонтроля;
СпособФорматноЛогическогоКонтроля = СтруктураДанныхФЛК.СпособФорматноЛогическогоКонтроля;
КонецЕсли;
Если ЗначениеЗаполнено(СпособФорматноЛогическогоКонтроля) Тогда
НовыеПозицииЧека = Новый Массив;
Если СпособФорматноЛогическогоКонтроля = Перечисления.СпособыФорматноЛогическогоКонтроля.НеКонтролировать Тогда
Возврат;
ИначеЕсли СпособФорматноЛогическогоКонтроля = Перечисления.СпособыФорматноЛогическогоКонтроля.ЗачитыватьСуммы Тогда
ПривестиПозицииЧекаПоАлгоритмуЗачитыватьСуммы(ДанныеЧека.ПозицииЧека, НовыеПозицииЧека, ДопустимоеРасхождение);
ДанныеЧека.ПозицииЧека = НовыеПозицииЧека;
ИначеЕсли СпособФорматноЛогическогоКонтроля = Перечисления.СпособыФорматноЛогическогоКонтроля.РазделятьСтроки Тогда
ПривестиПозицииЧекаПоАлгоритмуРазделятьСтроки(ДанныеЧека.ПозицииЧека, НовыеПозицииЧека, ДопустимоеРасхождение);
ДанныеЧека.ПозицииЧека = НовыеПозицииЧека;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
// Структура данных форматно-логического контроля
//
// Параметры:
// ПодключаемоеОборудование - СправочникСсылка.ПодключаемоеОборудование - Устройство, фискализирующее чек
//
// Возвращаемое значение:
// Структура - Структура данных форматно логического контроля:
// * СпособФорматноЛогическогоКонтроля - Неопределено
// - ПеречислениеСсылка.СпособыФорматноЛогическогоКонтроля
// * ДопустимоеРасхождениеФорматноЛогическогоКонтроля - Число -
// * ФорматФД - Строка -
//
Функция СтруктураДанныхФорматноЛогическогоКонтроля(ПодключаемоеОборудование) Экспорт
ФорматФД = "1.0";
ВозвращаемаяСтруктура = Новый Структура;
ВозвращаемаяСтруктура.Вставить("СпособФорматноЛогическогоКонтроля" , Неопределено);
ВозвращаемаяСтруктура.Вставить("ДопустимоеРасхождениеФорматноЛогическогоКонтроля", 0.01);
ВозвращаемаяСтруктура.Вставить("ФорматФД", ФорматФД);
СтандартнаяОбработка = Истина;
ФорматноЛогическийКонтрольПереопределяемый.ПолучитьСтруктуруДанныхФорматноЛогическогоКонтроля(ПодключаемоеОборудование, ВозвращаемаяСтруктура, СтандартнаяОбработка);
Если Не СтандартнаяОбработка Тогда
Возврат ВозвращаемаяСтруктура;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПодключаемоеОборудование.ДопустимоеРасхождениеФорматноЛогическогоКонтроля КАК ДопустимоеРасхождениеФорматноЛогическогоКонтроля,
| ПодключаемоеОборудование.СпособФорматноЛогическогоКонтроля КАК СпособФорматноЛогическогоКонтроля,
| ПодключаемоеОборудованиеПараметрыРегистрации.ЗначениеПараметра КАК ФорматФД
|ИЗ
| Справочник.ПодключаемоеОборудование КАК ПодключаемоеОборудование
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПодключаемоеОборудование.ПараметрыРегистрации КАК ПодключаемоеОборудованиеПараметрыРегистрации
| ПО ПодключаемоеОборудование.Ссылка = ПодключаемоеОборудованиеПараметрыРегистрации.Ссылка
| И (ПодключаемоеОборудованиеПараметрыРегистрации.Ссылка = &Ссылка)
| И (ПодключаемоеОборудованиеПараметрыРегистрации.НаименованиеПараметра = ""ВерсияФФДККТ"")
|ГДЕ
| ПодключаемоеОборудование.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ПодключаемоеОборудование);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Выборка.Следующий();
ВозвращаемаяСтруктура.ФорматФД = Выборка.ФорматФД;
ВозвращаемаяСтруктура.СпособФорматноЛогическогоКонтроля = Выборка.СпособФорматноЛогическогоКонтроля;
ВозвращаемаяСтруктура.ДопустимоеРасхождениеФорматноЛогическогоКонтроля = Выборка.ДопустимоеРасхождениеФорматноЛогическогоКонтроля;
КонецЕсли;
Возврат ВозвращаемаяСтруктура;
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
// Возвращает структуру проверки форматно-логического контроля
//
// Возвращаемое значение:
// Структура:
// * ВерсияФФД - Строка - Версия формата, по которому выполнять проверку
// * ТипОборудования - ПеречислениеСсылка.ТипыПодключаемогоОборудования
// * ИспользуетсяФН36 - Булево -
// * Ошибки - Массив из Строка - строки содержащие описание найденных ошибок
// * НомерПозиции - Число - номер проверяемой позиции в чеке
//
Функция ПараметрыПроверки()
Параметры = Новый Структура;
Параметры.Вставить("ВерсияФФД", "1.2");
Параметры.Вставить("ТипОборудования", Перечисления.ТипыПодключаемогоОборудования.ПустаяСсылка());
Параметры.Вставить("ИспользуетсяФН36", Ложь);
Параметры.Вставить("Ошибки", Новый Массив); // Массив из Строка
Параметры.Вставить("НомерПозиции",0);
Возврат Параметры;
КонецФункции
// Скопировать структуру.
//
// Параметры:
// Данные - Произвольный, Структура - Текущая позиция
//
// Возвращаемое значение:
// Структура
//
Функция СкопироватьСтруктуру(Данные)
НоваяСтруктура = Новый Структура;
Для Каждого ЭлементСтруктуры Из Данные Цикл
НоваяСтруктура.Вставить(ЭлементСтруктуры.Ключ, ЭлементСтруктуры.Значение);
КонецЦикла;
Возврат НоваяСтруктура;
КонецФункции
// Функция сортирует массив фискальных строк
//
// Параметры:
// МассивФискальныхСтрок - Массив из см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
// ИмяРеквизита - Строка
Процедура СортироватьМассивФискальныхСтрок(МассивФискальныхСтрок, ИмяРеквизита = "СтрокаПоследнегоТовара")
МаксимальныйИндекс = МассивФискальныхСтрок.ВГраница();
Если МаксимальныйИндекс >= 0 Тогда
// Начинаем со второго элемента.
Для ТекущийИндекс = 1 По МаксимальныйИндекс Цикл
ТекущаяСтрока = МассивФискальныхСтрок[ТекущийИндекс];
НоваяТекущаяСтрока = СкопироватьСтруктуру(ТекущаяСтрока);
НовыйИндексТекущейСтроки = -1;
Для ОбратныйСчетчик = 1 По ТекущийИндекс Цикл
ПредыдущаяСтрока = МассивФискальныхСтрок[ТекущийИндекс - ОбратныйСчетчик];
Если ПредыдущаяСтрока[ИмяРеквизита] > ТекущаяСтрока[ИмяРеквизита] Тогда
НоваяПредыдущаяСтрока = СкопироватьСтруктуру(ПредыдущаяСтрока);
НовыйИндексТекущейСтроки = ТекущийИндекс - ОбратныйСчетчик;
МассивФискальныхСтрок.Удалить(НовыйИндексТекущейСтроки + 1);
МассивФискальныхСтрок.Вставить(НовыйИндексТекущейСтроки + 1, НоваяПредыдущаяСтрока);
Иначе
Прервать;
КонецЕсли;
КонецЦикла;
Если НовыйИндексТекущейСтроки >=0 Тогда
МассивФискальныхСтрок.Удалить(НовыйИндексТекущейСтроки);
МассивФискальныхСтрок.Вставить(НовыйИндексТекущейСтроки, НоваяТекущаяСтрока);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
#Область СпециальныеПроверки
// Выполняет проверку кода налогообложения в данных чека
//
// Параметры:
// Проверка - см. ПараметрыПроверки.
// Данные - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
// ДоступныеКодыСистемыНалогообложения - Строка
//
Процедура ПроверитьКодыНалогообложения(Проверка, Данные, ДоступныеКодыСистемыНалогообложения)
Если Проверка.ТипОборудования = Перечисления.ТипыПодключаемогоОборудования.ККТ Тогда
// Проверка системы налогообложения
КодыНалогообложения = СтрРазделить(ДоступныеКодыСистемыНалогообложения, ",");
СистемаНалогообложения = ОборудованиеЧекопечатающиеУстройстваКлиентСервер.КодСистемыНалогообложенияККТ(Данные.СистемаНалогообложения);
Если КодыНалогообложения.Найти(Строка(СистемаНалогообложения)) = Неопределено Тогда
ТекстОшибки = НСтр("ru = 'ККТ не зарегистрирована с указанной системой налогообложения: ""%1""'");
ТекстОшибки = СтрШаблон(ТекстОшибки, Строка(Данные.СистемаНалогообложения));
Проверка.Ошибки.Добавить(ТекстОшибки);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
// Выполняет проверку реквизитов для электронного чека
//
// Параметры:
// Проверка - см. ПараметрыПроверки.
// Данные - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
//
Процедура ПроверитьЭлектронныйЧек(Проверка, Данные)
// Электронный чек
Если Данные.Электронно
И ПустаяСтрока(Данные.ПокупательEmail) И ПустаяСтрока(Данные.ПокупательНомер) Тогда
Проверка.Ошибки.Добавить(НСтр("ru = 'Для электронного чека нужно указать либо Email, либо телефон.'"));
КонецЕсли;
Если Данные.Отправляет1СEmail И ПустаяСтрока(Данные.ПокупательEmail) Тогда
Проверка.Ошибки.Добавить(НСтр("ru = 'Не заполнен E-mail'"));
КонецЕсли;
Если Данные.Отправляет1СSMS И НЕ ЗначениеЗаполнено(Данные.ПокупательНомер) Тогда
Проверка.Ошибки.Добавить(НСтр("ru = 'Не заполнен номер телефона'"));
КонецЕсли;
КонецПроцедуры
// Выполняет проверку сведений о покупателе в данных чека
//
// Параметры:
// Проверка - см. ПараметрыПроверки.
// Данные - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
//
Процедура ПроверитьСведенияОПокупателе(Проверка, Данные)
// при формате 1.05 реквизит необязателен
Если ВерсияФФД(Проверка, "1.05") Тогда
Возврат;
КонецЕсли;
СведенияОПокупателе = Данные.СведенияОПокупателе;
Если ПустаяСтрока(Данные.Получатель) И ПустаяСтрока(СведенияОПокупателе.Покупатель) Тогда
Возврат;
КонецЕсли;
Если ЗначениеЗаполнено(Данные.ПолучательИНН) Или ЗначениеЗаполнено(СведенияОПокупателе.ПокупательИНН) Тогда
Возврат;
КонецЕсли;
Если Данные.ЕстьПерсональныеДанные Тогда
ПерсональныеДанные = ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыПерсональныеДанныеПокупателя();
ТипПерсональныхДанных = Данные.ТипПерсональныхДанных;
МенеджерОборудованияВызовСервераПереопределяемый.ОбработкаЗаполненияПерсональныхДанных(
ПерсональныеДанные,
Данные.СубъектПерсональныхДанных,
ТипПерсональныхДанных,
?(ПустаяСтрока(Данные.ДатаВремя), ОбщегоНазначенияБПО.ДатаСеанса(), Данные.ДатаВремя));
Если ТипПерсональныхДанных = Перечисления.ТипыПерсональныхДанныхККТ.ИНН И ЗначениеЗаполнено(ПерсональныеДанные.ИНН) Тогда
Возврат;
ИначеЕсли ТипПерсональныхДанных = Перечисления.ТипыПерсональныхДанныхККТ.ПаспортныеДанные
И ЗначениеЗаполнено(ПерсональныеДанные.ДатаРождения)
И ЗначениеЗаполнено(ПерсональныеДанные.ВидДокумента)
И ЗначениеЗаполнено(ПерсональныеДанные.ДанныеДокумента) Тогда
Возврат;
КонецЕсли;
КонецЕсли;
Проверка.Ошибки.Добавить(НСтр("ru = 'Не указан ИНН или дата рождения и реквизиты документа удостоверяющего личность покупателя.'"));
КонецПроцедуры
// Выполняет проверку заполнения данных агента
//
// Параметры:
// Проверка - см. ПараметрыПроверки.
// ДанныеЧека - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
// Позиция - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыФискальнойСтрокиЧека
//
Процедура ПроверитьДанныеАгента(Проверка, ДанныеЧека, Позиция)
Если ЗаполненРеквизит(Проверка, Позиция, "ПризнакАгентаПоПредметуРасчета") Тогда
ПризнакАгента = Неопределено;
ПризнакАгентаПоПредметуРасчета = Неопределено;
Если ЕстьРеквизит(Проверка, ДанныеЧека, "ПризнакАгента", ПризнакАгента)
И ЗначениеЗаполнено(ПризнакАгента)
И ЕстьРеквизит(Проверка, Позиция, "ПризнакАгентаПоПредметуРасчета", ПризнакАгентаПоПредметуРасчета)
И ЗначениеЗаполнено(ПризнакАгентаПоПредметуРасчета) Тогда
Если ДанныеЧека.ПризнакАгента <> Позиция.ПризнакАгентаПоПредметуРасчета Тогда
ШаблонОшибки = НСтр("ru = 'Признак платежного агента в шапке не совпадает с признаком платежного агента по предмету расчета в строке №%2.'");
Проверка.Ошибки.Добавить(ТекстОшибки(ШаблонОшибки, "", Проверка.НомерПозиции));
КонецЕсли;
КонецЕсли;
МассивДанных = Новый Массив();
МассивДанных.Добавить(ДанныеЧека.ДанныеАгента.ОператорПеревода);
МассивДанных.Добавить(Позиция.ДанныеАгента.ОператорПеревода);
ПризнакАгентаПоПредметуРасчета = Позиция.ПризнакАгентаПоПредметуРасчета;
Если ПризнакАгентаПоПредметуРасчета = Перечисления.ПризнакиАгента.БанковскийПлатежныйАгент
Или ПризнакАгентаПоПредметуРасчета = Перечисления.ПризнакиАгента.БанковскийПлатежныйСубагент Тогда
ШаблонОшибки = НСтр("ru = 'Не установлен %1 оператора перевода банковского платежного агента.'");
ПроверитьРеквизитВМассивеДанных(Проверка, МассивДанных, "Телефон",, ШаблонОшибки);
ПроверитьРеквизитВМассивеДанных(Проверка, МассивДанных, "Адрес",, ШаблонОшибки);
ПроверитьРеквизитВМассивеДанных(Проверка, МассивДанных, "ИНН",, ШаблонОшибки);
ШаблонОшибки = НСтр("ru = 'Не установлено %1 оператора перевода банковского платежного агента.'");
ПроверитьРеквизитВМассивеДанных(Проверка, МассивДанных, "Наименование",, ШаблонОшибки);
КонецЕсли;
ШаблонОшибки = НСтр("ru = '%1 оператора перевода в строке %2 не равен этому значению в шапке.'");
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Телефон", ШаблонОшибки);
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Адрес", ШаблонОшибки);
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "ИНН", ШаблонОшибки);
ШаблонОшибки = НСтр("ru = '%1 оператора перевода в строке %2 не равно этому значению в шапке.'");
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Наименование", ШаблонОшибки);
МассивДанных.Очистить();
МассивДанных.Добавить(ДанныеЧека.ДанныеАгента.ОператорПоПриемуПлатежей);
МассивДанных.Добавить(Позиция.ДанныеАгента.ОператорПоПриемуПлатежей);
ШаблонОшибки = НСтр("ru = '%1 оператора по приему платежей в строке %2 не равен этому значению в шапке.'");
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Телефон", ШаблонОшибки);
МассивДанных.Очистить();
МассивДанных.Добавить(ДанныеЧека.ДанныеАгента.ПлатежныйАгент);
МассивДанных.Добавить(Позиция.ДанныеАгента.ПлатежныйАгент);
ШаблонОшибки = НСтр("ru = '%1 платежного агента в строке %2 не равен этому значению в шапке.'");
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Телефон", ШаблонОшибки);
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Операция", ШаблонОшибки);
МассивДанных.Очистить();
МассивДанных.Добавить(ДанныеЧека.ДанныеПоставщика);
МассивДанных.Добавить(Позиция.ДанныеПоставщика);
ШаблонОшибки = НСтр("ru = '%1 поставщика в строке %2 не равен этому значению в шапке.'");
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Телефон", ШаблонОшибки);
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "ИНН", ШаблонОшибки);
ШаблонОшибки = НСтр("ru = '%1 поставщика в строке %2 не равно этому значению в шапке.'");
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Наименование", ШаблонОшибки);
КонецЕсли;
КонецПроцедуры
// Выполняет проверку заполнения данных поставщика
//
// Параметры:
// Проверка - см. ПараметрыПроверки.
// ДанныеЧека - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
// Позиция - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыФискальнойСтрокиЧека
//
Процедура ПроверитьДанныеПоставщика(Проверка, ДанныеЧека, Позиция)
Если ЗаполненРеквизит(Проверка, Позиция, "ПризнакАгентаПоПредметуРасчета") Тогда
МассивДанных = Новый Массив();
МассивДанных.Добавить(ДанныеЧека.ДанныеПоставщика);
МассивДанных.Добавить(Позиция.ДанныеПоставщика);
ШаблонОшибки = НСтр("ru = '%1 поставщика в строке %2 не равен этому значению в шапке.'");
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Телефон", ШаблонОшибки);
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "ИНН", ШаблонОшибки);
ШаблонОшибки = НСтр("ru = '%1 поставщика в строке %2 не равно этому значению в шапке.'");
ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, "Наименование", ШаблонОшибки);
КонецЕсли;
КонецПроцедуры
// Выполняет проверку оплаты
//
// Параметры:
// Проверка - см. ПараметрыПроверки
// СуммыПозиций - см. ПараметрыСуммыПозиций
// СуммыОплат - см. ПараметрыСуммыОплат
Процедура ПроверитьСуммыОплат(Проверка, СуммыПозиций, СуммыОплат)
Если СуммыПозиций.Общая > СуммыОплат.Общая Тогда
Проверка.Ошибки.Добавить(НСтр("ru = 'Сумма товарных позиций больше суммы оплат'"));
ИначеЕсли СуммыОплат.Общая > СуммыПозиций.Общая Тогда
Если (СуммыОплат.Общая - СуммыОплат.Наличные) > СуммыПозиций.Общая Тогда
Проверка.Ошибки.Добавить(НСтр("ru = 'Сумма безналичных оплат больше суммы товарных позиций'"));
КонецЕсли;
КонецЕсли;
КонецПроцедуры
// Выполняет проверку фактической оплаты
//
// Параметры:
// Проверка - см. ПараметрыПроверки
// СуммыПозиций - см. ПараметрыСуммыПозиций
// СуммыОплат - см. ПараметрыСуммыОплат
Процедура ПроверитьСуммуФактическихОплат(Проверка, СуммыПозиций, СуммыОплат)
СуммаФактическихОплат = СуммыОплат.НаличныеБезСдачи + СуммыОплат.Электронно + СуммыОплат.ВстречноеПредоставление
- СуммыПозиций.ПолнаяОплата;
Если СуммаФактическихОплат < 0 Тогда
Проверка.Ошибки.Добавить(НСтр("ru = 'Сумма по строкам с указанием признака полной оплаты меньше чем сумма самих оплат'"));
ИначеЕсли СуммаФактическихОплат = 0 Тогда
Проверка.Ошибки.Добавить(НСтр("ru = 'Необходимо указать оплату'"));
КонецЕсли;
КонецПроцедуры
#КонецОбласти
#Область ПроцедурыИФункцииДляПриведенияКФормату
Процедура ПривестиЕслиСпособРасчетаБезПередачиТовара(Позиция, ИсправленыОсновныеПараметры)
ПризнакСпособаРасчета = Позиция.ПризнакСпособаРасчета;
Если ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПредоплатаЧастичная
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ОплатаКредита
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.Аванс
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПредоплатаПолная Тогда
Позиция.ЕдиницаИзмерения = "Платеж";
Позиция.Количество = 1;
Позиция.Цена = Позиция.Сумма;
Позиция.ЦенаСоСкидками = Позиция.Сумма;
Позиция.СуммаСкидок = 0;
Позиция.ПризнакПредметаРасчета = Перечисления.ПризнакиПредметаРасчета.ПлатежВыплата;
ИсправленыОсновныеПараметры = Истина;
КонецЕсли;
КонецПроцедуры
Процедура ПривестиСтавкиНДС(Позиция)
ПризнакСпособаРасчета = Позиция.ПризнакСпособаРасчета;
Если ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПредоплатаЧастичная
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.Аванс
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПредоплатаПолная Тогда
Если Позиция.СтавкаНДС = Неопределено Тогда
Позиция.СуммаНДС = 0;
ИначеЕсли Позиция.СтавкаНДС > 0 И Позиция.СтавкаНДС < 100 Тогда
Позиция.СтавкаНДС = 100 + Позиция.СтавкаНДС;
КонецЕсли;
ИначеЕсли ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ОплатаКредита Тогда
Позиция.СтавкаНДС = Неопределено;
Позиция.СуммаНДС = 0;
КонецЕсли;
КонецПроцедуры
// Приводит суммы позиций чека к сумме полной оплаты
//
// Параметры:
// ОсновныеПараметры - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыОперацииФискализацииЧека
// СуммыПозиций - см. ПараметрыСуммыПозиций
// СуммыОплат - см. ПараметрыСуммыОплат
//
Процедура ПривестиСуммыПозицийЧекаКСуммеТаблицыОплат(ОсновныеПараметры, СуммыПозиций, СуммыОплат)
СуммаФактическихОплат = СуммыОплат.НаличныеБезСдачи + СуммыОплат.Электронно + СуммыОплат.ВстречноеПредоставление;
СуммаФактическихОплат = СуммаФактическихОплат - СуммыПозиций.ПолнаяОплата;
СуммаНеобходимойПолнойОплаты = СуммыПозиций.Общая - СуммыПозиций.ПередачаБезОплаты - СуммыПозиций.ПолнаяОплата;
// Сумма всегда больше нулю, т.к. в чеке есть строки в частичной оплатой
Если СуммаФактическихОплат < СуммаНеобходимойПолнойОплаты Тогда
Коэффициент = СуммаФактическихОплат / СуммаНеобходимойПолнойОплаты;
МаксимальнаяСумма = 0;
РассчитаннаяСуммаЧека = 0;
ПозицияСМаксимальнойСуммой = Неопределено;
Для Каждого Позиция Из ОсновныеПараметры.ПозицииЧека Цикл
Если Позиция.Свойство("ФискальнаяСтрока") Тогда
ПризнакСпособаРасчета = Позиция.ПризнакСпособаРасчета;
Если ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПредоплатаЧастичная
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ОплатаКредита Тогда
Если Позиция.Сумма > МаксимальнаяСумма Тогда
МаксимальнаяСумма = Позиция.Сумма;
ПозицияСМаксимальнойСуммой = Позиция;
КонецЕсли;
ПриведеннаяСумма = Окр(Позиция.Сумма * Коэффициент,2,1);
Позиция.Сумма = ПриведеннаяСумма;
Позиция.Цена = ПриведеннаяСумма;
Позиция.ЦенаСоСкидками = ПриведеннаяСумма;
РассчитаннаяСуммаЧека = РассчитаннаяСуммаЧека + ПриведеннаяСумма;
Если Позиция.СуммаНДС <> Неопределено Тогда
Позиция.СуммаНДС = Окр(Позиция.СуммаНДС * Коэффициент,2,1);
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Иначе
Возврат;
КонецЕсли;
Отклонение = СуммаФактическихОплат - РассчитаннаяСуммаЧека;
Если Макс(Отклонение, -Отклонение) >= 0.01 Тогда
Позиция = ПозицияСМаксимальнойСуммой;
Сумма = Позиция.Сумма;
СуммаСОтклонением = Позиция.Сумма + Отклонение;
Позиция.Цена = СуммаСОтклонением;
Позиция.Сумма = СуммаСОтклонением;
Позиция.ЦенаСоСкидками = СуммаСОтклонением;
Если Позиция.СуммаНДС <> Неопределено И Сумма > 0 Тогда
// Максимальная сумма не может быть равна или меньше нуля
// Определяем отклонение НДС
Позиция.СуммаНДС = Окр(СуммаСОтклонением * Позиция.СуммаНДС / Сумма, 2,1);
КонецЕсли;
КонецЕсли;
// Общие суммы НДС по ставкам пересчитывается далее по алгоритму.
// ИсправитьТаблицуОплат
СуммаИсправленияОплат = СуммаНеобходимойПолнойОплаты - СуммаФактическихОплат;
Если СуммаИсправленияОплат > 0 Тогда
Для Каждого СтрокаОплат Из ОсновныеПараметры.ТаблицаОплат Цикл
ТипОплаты = СтрокаОплат.ТипОплаты;
Если ТипОплаты = Перечисления.ТипыОплатыККТ.Постоплата
Или ТипОплаты = Перечисления.ТипыОплатыККТ.Предоплата Тогда
Если СуммаИсправленияОплат >= СтрокаОплат.Сумма Тогда
СуммаИсправленияОплат = СуммаИсправленияОплат - СтрокаОплат.Сумма;
СтрокаОплат.Сумма = 0;
Иначе
СуммаИсправленияОплат = 0;
СтрокаОплат.Сумма = СтрокаОплат.Сумма - СуммаИсправленияОплат;
КонецЕсли;
Если СуммаИсправленияОплат <= 0 Тогда
Прервать;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КоличествоЭлементов = ОсновныеПараметры.ТаблицаОплат.Количество();
Для Индекс = 1 По КоличествоЭлементов Цикл
Если ОсновныеПараметры.ТаблицаОплат[КоличествоЭлементов - Индекс].Сумма = 0 Тогда
ОсновныеПараметры.ТаблицаОплат.Удалить(КоличествоЭлементов - Индекс);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Функция выполняет разделение фискальной строки.
//
Процедура РазделитьФискальнуюСтроку(ТекущаяПозиция, НовыеПозицииЧека, РасчетнаяЦена, РазницаСумм)
СтандартнаяОбработка = Истина;
ФорматноЛогическийКонтрольПереопределяемый.РазделитьФискальнуюСтроку(ТекущаяПозиция, НовыеПозицииЧека, РасчетнаяЦена, РазницаСумм, СтандартнаяОбработка);
Если Не СтандартнаяОбработка Тогда
Возврат;
КонецЕсли;
// Запоминаем начальные значения.
КоличествоНачальное = ТекущаяПозиция.Количество;
СуммаСкидокНачальная = ТекущаяПозиция.СуммаСкидок;
СуммаНДСНачальная = ТекущаяПозиция.СуммаНДС; // Рассчитывается формально, т.к. сумма НДС текущими драйверами игнорируется.
СуммаНачальная = ТекущаяПозиция.Сумма;
// Если разница есть, то ее делим на копейку.
// Получаем количество, которое нужно переоценить.
КоличествоПереоценки = Окр(РазницаСумм / 0.01, 3, 1);
Если КоличествоПереоценки < 0 Тогда
КоличествоПереоценки = -КоличествоПереоценки;
КонецЕсли;
// Из начального количества отнимаем количество переоценки.
// Получаем количество, которое остается по расчетной цене.
КоличествоРасчетное = ТекущаяПозиция.Количество - КоличествоПереоценки;
// Цена переоценки во всех примерах отличается от расчетной на 1 копейку.
// В большую или меньшую сторону в зависимости от знака разницы - зависимость обратная.
ЦенаПереоценки = РасчетнаяЦена - Окр(РазницаСумм / КоличествоПереоценки, 2, 1);
// Теперь считаются суммы, т.к. по ним выполняется анализ коэффициента для пересчета
СуммаПереоценки = Окр(КоличествоПереоценки * ЦенаПереоценки, 2, 1);
СуммаРасчетная = Окр(КоличествоРасчетное * РасчетнаяЦена, 2, 1);
// Распределяем суммы скидок пропорционально количеству.
// Если суммы есть. А если их нет, то должно остаться Неопределено.
Если СуммаСкидокНачальная <> Неопределено Тогда
Если КоличествоПереоценки > КоличествоРасчетное Тогда
СуммаСкидокПереоценки = Окр(СуммаСкидокНачальная * КоличествоПереоценки / КоличествоНачальное, 2, 1);
СуммаСкидокРасчетная = СуммаСкидокНачальная - СуммаСкидокПереоценки;
Иначе
СуммаСкидокРасчетная = Окр(СуммаСкидокНачальная * КоличествоРасчетное / КоличествоНачальное, 2, 1);
СуммаСкидокПереоценки = СуммаСкидокНачальная - СуммаСкидокРасчетная;
КонецЕсли;
КонецЕсли;
// А суммы распределяем НДС пропорционально суммам.
// Потому что распределение пропорционально количеству.
// Вызывает расхождения в некоторых критичных ситуациях.
// Если суммы есть. А если их нет, то должно остаться Неопределено.
Если СуммаНДСНачальная <> Неопределено Тогда
Если СуммаПереоценки > СуммаРасчетная Тогда
СуммаНДСПереоценки = Окр(СуммаНДСНачальная * СуммаПереоценки / СуммаНачальная, 2, 1);
СуммаНДСРасчетная = СуммаНДСНачальная - СуммаНДСПереоценки;
Иначе
СуммаНДСРасчетная = Окр(СуммаНДСНачальная * СуммаРасчетная / СуммаНачальная, 2, 1);
СуммаНДСПереоценки = СуммаНДСНачальная - СуммаНДСРасчетная;
КонецЕсли;
КонецЕсли;
// Выводим вместо первоначальной строки две новых.
// Сначала с расчетной ценой и остатком количества.
// И суммой - произведение цены и количества.
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
НоваяПозиция.Количество = КоличествоРасчетное;
НоваяПозиция.ЦенаСоСкидками = РасчетнаяЦена;
НоваяПозиция.Сумма = СуммаРасчетная;
Если СуммаСкидокНачальная <> Неопределено Тогда
НоваяПозиция.СуммаСкидок = СуммаСкидокНачальная - СуммаСкидокПереоценки;
КонецЕсли;
Если СуммаНДСНачальная <> Неопределено Тогда
НоваяПозиция.СуммаНДС = СуммаНДСРасчетная;
КонецЕсли;
НовыеПозицииЧека.Добавить(НоваяПозиция);
// Потом с переоцененной ценой и переоцененным количеством.
// И суммой - произведение цены и количества.
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
НоваяПозиция.Количество = КоличествоПереоценки;
НоваяПозиция.ЦенаСоСкидками = ЦенаПереоценки;
НоваяПозиция.Сумма = СуммаПереоценки;
Если СуммаСкидокНачальная <> Неопределено Тогда
НоваяПозиция.СуммаСкидок = СуммаСкидокПереоценки;
КонецЕсли;
Если СуммаНДСНачальная <> Неопределено Тогда
НоваяПозиция.СуммаНДС = СуммаНДСПереоценки;
КонецЕсли;
НовыеПозицииЧека.Добавить(НоваяПозиция);
КонецПроцедуры
Процедура ПривестиПозицииЧекаПоАлгоритмуРазделятьСтроки(ПозицииЧека, НовыеПозицииЧека, ДопустимоеРасхождение)
Для Каждого ТекущаяПозиция Из ПозицииЧека Цикл
Если Не ТекущаяПозиция.Свойство("ФискальнаяСтрока") Тогда
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
КонецЕсли;
Количество = ?(ТекущаяПозиция.Количество = 0, 1, ТекущаяПозиция.Количество);
Если Количество = 1 Тогда
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
НоваяПозиция.ЦенаСоСкидками = НоваяПозиция.Сумма;
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
КонецЕсли;
НоваяСуммаБезОкругления = Количество * ТекущаяПозиция.ЦенаСоСкидками;
Сумма = ТекущаяПозиция.Сумма;
РазницаСуммДляПроверки = НоваяСуммаБезОкругления - Сумма;
// Получаем расчетную цену с учетом скидок делением входящей суммы на входящее количество.
РасчетнаяЦена = Окр(Сумма / Количество, 2, 1);
// Здесь и далее все цены и суммы округляем до 2 знаков после запятой, количества - до 3 знаков.
// Умножаем входящее количество на расчетную цену и получаем новую промежуточную сумму.
НоваяСумма = Окр(Количество * РасчетнаяЦена, 2, 1);
// Вычисляем разницу между промежуточной и входящей суммой.
РазницаСумм = НоваяСумма - Сумма;
Если Окр(ТекущаяПозиция.ЦенаСоСкидками * Количество, 2, 1) = ТекущаяПозиция.Сумма Тогда
// Цену не пересчитываем.
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
ИначеЕсли РазницаСуммДляПроверки >= -ДопустимоеРасхождение И РазницаСуммДляПроверки <= ДопустимоеРасхождение Тогда
// Если разница допустима - оставляем одну строку.
// Если разницы нет - оставляем одну строку.
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
// Но подменяем расчетную цену.
НоваяПозиция.ЦенаСоСкидками = РасчетнаяЦена;
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
ИначеЕсли РазницаСумм = 0 Тогда
// Если разница неокругленная есть, а округленной нет.
// То передаем в ККТ как есть - оставляем одну строку.
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
// Но подменяем расчетную цену.
НоваяПозиция.ЦенаСоСкидками = РасчетнаяЦена;
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
Иначе
// Разделяем строку.
РазделитьФискальнуюСтроку(ТекущаяПозиция, НовыеПозицииЧека, РасчетнаяЦена, РазницаСумм);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Процедура ПривестиПозицииЧекаПоАлгоритмуЗачитыватьСуммы(ПозицииЧека, НовыеПозицииЧека, ДопустимоеРасхождение)
СтрокиПоложительные = Новый Массив;
СтрокиОтрицательные = Новый Массив;
СтрокаПоследнегоТовара = 0;
Для Каждого ТекущаяПозиция Из ПозицииЧека Цикл
Если Не ТекущаяПозиция.Свойство("ФискальнаяСтрока") Тогда
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
Если СтрокаПоследнегоТовара = 0 Тогда
НоваяПозиция.Вставить("СтрокаПоследнегоТовара", 0);
Иначе
НоваяПозиция.Вставить("СтрокаПоследнегоТовара", 99999);
КонецЕсли;
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
КонецЕсли;
СтрокаПоследнегоТовара = ТекущаяПозиция.НомерСтрокиТовара;
Количество = ?(ТекущаяПозиция.Количество = 0, 1, ТекущаяПозиция.Количество);
Если Количество = 1 Тогда
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
НоваяПозиция.ЦенаСоСкидками = НоваяПозиция.Сумма;
НоваяПозиция.Вставить("СтрокаПоследнегоТовара", СтрокаПоследнегоТовара);
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
КонецЕсли;
НоваяСуммаБезОкругления = Количество * ТекущаяПозиция.ЦенаСоСкидками;
Сумма = ТекущаяПозиция.Сумма;
РазницаСуммДляПроверки = НоваяСуммаБезОкругления - Сумма;
// Получаем расчетную цену с учетом скидок делением входящей суммы на входящее количество.
РасчетнаяЦена = Окр(Сумма / Количество, 2, 1);
// Здесь и далее все цены и суммы округляем до 2 знаков после запятой, количества - до 3 знаков.
// Умножаем входящее количество на расчетную цену и получаем новую промежуточную сумму.
НоваяСумма = Окр(Количество * РасчетнаяЦена, 2, 1);
// Вычисляем разницу между промежуточной и входящей суммой.
РазницаСумм = НоваяСумма - Сумма;
НоваяПозиция = СкопироватьСтруктуру(ТекущаяПозиция);
НоваяПозиция.Вставить("РасчетнаяЦена", РасчетнаяЦена);
НоваяПозиция.Вставить("РазницаСумм", РазницаСумм);
НоваяПозиция.Вставить("РазницаСуммДляПроверки", РазницаСуммДляПроверки);
НоваяПозиция.Вставить("СтрокаПоследнегоТовара", СтрокаПоследнегоТовара);
Если Окр(ТекущаяПозиция.ЦенаСоСкидками * Количество, 2, 1) = ТекущаяПозиция.Сумма Тогда
// Строку оставляем в первоначальном виде.
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
ИначеЕсли РазницаСуммДляПроверки >= -ДопустимоеРасхождение И РазницаСуммДляПроверки <= ДопустимоеРасхождение Тогда
// Если разница допустима - оставляем одну строку.
// Но подменяем расчетную цену.
НоваяПозиция.ЦенаСоСкидками = РасчетнаяЦена;
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
ИначеЕсли РазницаСумм = 0 Тогда
// Если разница неокругленная есть, а округленной нет.
// То передаем в ККТ как есть - оставляем одну строку.
// Но подменяем расчетную цену.
НоваяПозиция.ЦенаСоСкидками = РасчетнаяЦена;
НовыеПозицииЧека.Добавить(НоваяПозиция);
Продолжить;
ИначеЕсли РазницаСумм > 0 Тогда
// Если разница есть, то строку с положительной разницей добавляем в массив положительных строк.
СтрокиПоложительные.Добавить(НоваяПозиция);
Иначе
// Если разница есть, то строку с отрицательной разницей добавляем в массив отрицательных строк.
НоваяПозиция.РазницаСумм = -РазницаСумм;
СтрокиОтрицательные.Добавить(НоваяПозиция);
КонецЕсли;
КонецЦикла;
ИндексПоложительных = СтрокиПоложительные.ВГраница();
ИндексОтрицательных = СтрокиОтрицательные.ВГраница();
Если ИндексПоложительных >= 0 И ИндексОтрицательных >= 0 Тогда
// В массиве положительных и отрицательных.
// Находим строки с совпадающей по модулю сумой расхождения.
// Строки идут одна к одному.
СдвигПоложительного = 0;
Для СчетчикПоложительный = 0 По ИндексПоложительных Цикл
ПоложительнаяСтрока = СтрокиПоложительные[СчетчикПоложительный - СдвигПоложительного];
СдвигОтрицательного = 0;
ИндексОтрицательных = СтрокиОтрицательные.ВГраница();
Для СчетчикОтрицательный = 0 По ИндексОтрицательных Цикл
ОтрицательнаяСтрока = СтрокиОтрицательные[СчетчикОтрицательный - СдвигОтрицательного];
Если ПоложительнаяСтрока.РазницаСумм = ОтрицательнаяСтрока.РазницаСумм Тогда
// Перекрываем разницу по совпадающим строкам из одного массива суммами другого.
// Увеличивая сумму в положительной строке и уменьшая в отрицательной.
ПоложительнаяСтрока.Сумма = ПоложительнаяСтрока.Сумма + ПоложительнаяСтрока.РазницаСумм;
ОтрицательнаяСтрока.Сумма = ОтрицательнаяСтрока.Сумма - ПоложительнаяСтрока.РазницаСумм;
// Перекрытые строки переносим в массив для печати и удаляем из массивов.
НоваяПозиция = СкопироватьСтруктуру(ПоложительнаяСтрока);
НовыеПозицииЧека.Добавить(НоваяПозиция);
НоваяПозиция = СкопироватьСтруктуру(ОтрицательнаяСтрока);
НовыеПозицииЧека.Добавить(НоваяПозиция);
СтрокиПоложительные.Удалить(СчетчикПоложительный - СдвигПоложительного);
СтрокиОтрицательные.Удалить(СчетчикОтрицательный - СдвигОтрицательного);
СдвигПоложительного = СдвигПоложительного + 1;
СдвигОтрицательного = СдвигОтрицательного + 1;
Прервать;
КонецЕсли;
КонецЦикла;
Если ИндексПоложительных < 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
ИндексПоложительных = СтрокиПоложительные.ВГраница();
ИндексОтрицательных = СтрокиОтрицательные.ВГраница();
Если ИндексПоложительных >= 0 И ИндексОтрицательных >= 0 Тогда
// Обходим строки положительного массива.
// Увеличивая сумму в каждой из них на сумму расхождения.
// Которую распределяем по строкам с отрицательным расхождением, уменьшая сумму там.
СдвигПоложительного = 0;
Для СчетчикПоложительный = 0 По ИндексПоложительных Цикл
ПоложительнаяСтрока = СтрокиПоложительные[СчетчикПоложительный - СдвигПоложительного];
Если ПоложительнаяСтрока.РазницаСумм <= ДопустимоеРасхождение Тогда
// Разница сумм в строке всегда по модулю.
Продолжить;
КонецЕсли;
СдвигОтрицательного = 0;
ИндексОтрицательных = СтрокиОтрицательные.ВГраница();
Для СчетчикОтрицательный = 0 По ИндексОтрицательных Цикл
ОтрицательнаяСтрока = СтрокиОтрицательные[СчетчикОтрицательный - СдвигОтрицательного];
Если ОтрицательнаяСтрока.РазницаСумм <= ДопустимоеРасхождение
И ОтрицательнаяСтрока.РазницаСумм <> ПоложительнаяСтрока.РазницаСумм Тогда
// Разница сумм в строке всегда по модулю.
Продолжить;
КонецЕсли;
// Перекрываем разницу по совпадающим строкам из одного массива суммами другого.
// Увеличивая сумму в положительной строке и уменьшая в отрицательной.
Если ПоложительнаяСтрока.РазницаСумм > ОтрицательнаяСтрока.РазницаСумм Тогда
ПоложительнаяСтрока.Сумма = ПоложительнаяСтрока.Сумма + ОтрицательнаяСтрока.РазницаСумм;
ОтрицательнаяСтрока.Сумма = ОтрицательнаяСтрока.Сумма - ОтрицательнаяСтрока.РазницаСумм;
// Уменьшаем остаток разницы положительной строки
ПоложительнаяСтрока.РазницаСумм = ПоложительнаяСтрока.РазницаСумм - ОтрицательнаяСтрока.РазницаСумм;
// Перекрытую строку переносим в массив для печати.
НоваяПозиция = СкопироватьСтруктуру(ОтрицательнаяСтрока);
НовыеПозицииЧека.Добавить(НоваяПозиция);
// И удаляем из массивов.
СтрокиОтрицательные.Удалить(СчетчикОтрицательный - СдвигОтрицательного);
// Сдвигаем отрицательные счетчики
СдвигОтрицательного = СдвигОтрицательного + 1;
Иначе
НадоУдалятьОтрицательные = ПоложительнаяСтрока.РазницаСумм = ОтрицательнаяСтрока.РазницаСумм;
ПоложительнаяСтрока.Сумма = ПоложительнаяСтрока.Сумма + ПоложительнаяСтрока.РазницаСумм;
ОтрицательнаяСтрока.Сумма = ОтрицательнаяСтрока.Сумма - ПоложительнаяСтрока.РазницаСумм;
// Уменьшаем остаток разницы положительной строки
ОтрицательнаяСтрока.РазницаСумм = ОтрицательнаяСтрока.РазницаСумм - ПоложительнаяСтрока.РазницаСумм;
// Перекрытую строку переносим в массив для печати.
НоваяПозиция = СкопироватьСтруктуру(ПоложительнаяСтрока);
НовыеПозицииЧека.Добавить(НоваяПозиция);
Если НадоУдалятьОтрицательные Тогда
НоваяПозиция = СкопироватьСтруктуру(ОтрицательнаяСтрока);
НовыеПозицииЧека.Добавить(НоваяПозиция);
КонецЕсли;
// И удаляем из массивов.
СтрокиПоложительные.Удалить(СчетчикПоложительный - СдвигПоложительного);
// Сдвигаем положительные счетчики
СдвигПоложительного = СдвигПоложительного + 1;
Если НадоУдалятьОтрицательные Тогда
// И удаляем из массивов.
СтрокиОтрицательные.Удалить(СчетчикОтрицательный - СдвигОтрицательного);
// Сдвигаем отрицательные счетчики
СдвигОтрицательного = СдвигОтрицательного + 1;
КонецЕсли;
// Отрицательный цикл прерываем.
// Ибо закрывать текущую строку придется следующей положительной.
// Если она есть.
Прервать;
КонецЕсли;
КонецЦикла;
Если ИндексПоложительных < 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
ИндексПоложительных = СтрокиПоложительные.ВГраница();
// Оставшиеся строки обоих массивов разделяются.
// Каждая на две по первому алгоритму и добавляются в массив для печати.
Если ИндексПоложительных >= 0 Тогда
Для Каждого ТекущаяПозиция Из СтрокиПоложительные Цикл
// Еще раз пересчитываем разницу
// Получаем расчетную цену с учетом скидок делением входящей суммы на входящее количество.
РасчетнаяЦена = Окр(ТекущаяПозиция.Сумма / ТекущаяПозиция.Количество, 2, 1);
// Здесь и далее все цены и суммы округляем до 2 знаков после запятой, количества - до 3 знаков.
// Умножаем входящее количество на расчетную цену и получаем новую промежуточную сумму.
НоваяСумма = Окр(ТекущаяПозиция.Количество * РасчетнаяЦена, 2, 1);
// Вычисляем разницу между промежуточной и входящей суммой.
РазницаСумм = НоваяСумма - ТекущаяПозиция.Сумма;
Если ТекущаяПозиция.РазницаСуммДляПроверки >= -ДопустимоеРасхождение И ТекущаяПозиция.РазницаСуммДляПроверки <= ДопустимоеРасхождение Тогда
// Если разница допустима - оставляем одну строку.
НовыеПозицииЧека.Добавить(ТекущаяПозиция);
ИначеЕсли РазницаСумм = 0 Тогда
// Если разница неокругленная есть, а округленной нет.
// То передаем в ККТ как есть - оставляем одну строку.
НовыеПозицииЧека.Добавить(ТекущаяПозиция);
Иначе
// Разделяем строку.
РазделитьФискальнуюСтроку(ТекущаяПозиция, НовыеПозицииЧека, РасчетнаяЦена, РазницаСумм);
КонецЕсли;
КонецЦикла;
КонецЕсли;
ИндексОтрицательных = СтрокиОтрицательные.ВГраница();
Если ИндексОтрицательных >= 0 Тогда
Для Каждого ТекущаяПозиция Из СтрокиОтрицательные Цикл
// Еще раз пересчитываем разницу
// Получаем расчетную цену с учетом скидок делением входящей суммы на входящее количество.
РасчетнаяЦена = Окр(ТекущаяПозиция.Сумма / ТекущаяПозиция.Количество, 2, 1);
// Здесь и далее все цены и суммы округляем до 2 знаков после запятой, количества - до 3 знаков.
// Умножаем входящее количество на расчетную цену и получаем новую промежуточную сумму.
НоваяСумма = Окр(ТекущаяПозиция.Количество * РасчетнаяЦена, 2, 1);
// Вычисляем разницу между промежуточной и входящей суммой.
РазницаСумм = НоваяСумма - ТекущаяПозиция.Сумма;
Если ТекущаяПозиция.РазницаСуммДляПроверки >= -ДопустимоеРасхождение И ТекущаяПозиция.РазницаСуммДляПроверки <= ДопустимоеРасхождение Тогда
// Если разница допустима - оставляем одну строку.
НовыеПозицииЧека.Добавить(ТекущаяПозиция);
ИначеЕсли РазницаСумм = 0 Тогда
// Если разница неокругленная есть, а округленной нет.
// То передаем в ККТ как есть - оставляем одну строку.
НовыеПозицииЧека.Добавить(ТекущаяПозиция);
Иначе
// Разделяем строку.
РазделитьФискальнуюСтроку(ТекущаяПозиция, НовыеПозицииЧека, РасчетнаяЦена, РазницаСумм);
КонецЕсли;
КонецЦикла;
КонецЕсли;
// Массив для печати сортируется по первоначальному номеру строки.
СортироватьМассивФискальныхСтрок(НовыеПозицииЧека);
КонецПроцедуры
Процедура ПривестиАдресИМестоРасчетов(ДанныеЧека, ПараметрыРегистрации)
Если ПараметрыРегистрации = Неопределено Тогда
Возврат;
КонецЕсли;
Если Не ЗначениеЗаполнено(ДанныеЧека.АдресРасчетов) Тогда
ДанныеЧека.АдресРасчетов = ПараметрыРегистрации.АдресПроведенияРасчетов;
КонецЕсли;
Если Не ЗначениеЗаполнено(ДанныеЧека.МестоРасчетов) Тогда
ДанныеЧека.МестоРасчетов = ПараметрыРегистрации.МестоПроведенияРасчетов;
КонецЕсли;
КонецПроцедуры
#КонецОбласти
#Область ВспомогательныеПроцедурыИФункции
// Увеличивает номер позиции в структуре проверки
//
// Параметры:
// Проверка - см. ПараметрыПроверки
//
Процедура УвеличитьНомерПозиции(Проверка)
Проверка.НомерПозиции = Проверка.НомерПозиции + 1;
КонецПроцедуры
// Формирует текст ошибки подставляя имя реквизита и номер позиции
//
// Параметры:
// ШаблонОшибки - Строка
// ИмяРеквизита - Строка
// НомерПозиции - Строка
//
Функция ТекстОшибки(ШаблонОшибки, Знач ИмяРеквизита, Знач НомерПозиции)
НомерПозиции = Формат(НомерПозиции, "ЧГ=0");
Ошибка = СтрЗаменить(ШаблонОшибки,"%1", ИмяРеквизита);
Ошибка = СтрЗаменить(Ошибка,"%2", НомерПозиции);
Возврат Ошибка;
КонецФункции
#КонецОбласти
#Область ПроцедурыИФункцииДляПроверки
// Выполняет проверку на соответствие формата фискальных документов
//
// Параметры:
// Проверка - см. ПараметрыПроверки
// ВерсииФФД - Неопределено, Строка - версия на которую нужно проверить, может быть указано несколько если задано Неопределено, тогда любая версия ФФД
//
Функция ВерсияФФД(Проверка, ВерсииФФД = Неопределено)
Если ВерсииФФД = Неопределено Тогда
Возврат Истина;
КонецЕсли;
Возврат СтрНайти(ВерсииФФД, Проверка.ВерсияФФД)>0;
КонецФункции
Функция ЕстьРеквизит(Проверка, Данные, Знач ИмяРеквизита, Значение)
Если Данные.Свойство(ИмяРеквизита, Значение) Тогда
Возврат Истина;
КонецЕсли;
Проверка.Ошибки.Добавить(СтрШаблон(НСтр("ru = 'Реквизит %1 отсутствует.'"), ИмяРеквизита));
Возврат Ложь;
КонецФункции
Функция ЗаполненРеквизит(Проверка, Данные, Знач ИмяРеквизита)
Значение = Неопределено;
Если ЕстьРеквизит(Проверка, Данные, ИмяРеквизита, Значение) Тогда
Возврат ЗначениеЗаполнено(Значение);
КонецЕсли;
Возврат Ложь;
КонецФункции
Процедура ПроверитьРеквизит(Проверка, Данные, Знач ИмяРеквизита = Неопределено, Знач ВерсииФФД = Неопределено, Знач ШаблонОшибки = Неопределено)
Если Не ВерсияФФД(Проверка, ВерсииФФД) Тогда
Возврат;
КонецЕсли;
Значение = Неопределено;
Если ЕстьРеквизит(Проверка, Данные, ИмяРеквизита, Значение) Тогда
Если Не ЗначениеЗаполнено(Значение) Тогда
Если ЗначениеЗаполнено(Проверка.НомерПозиции) Тогда
ШаблонОшибки = ?(ШаблонОшибки = Неопределено, НСтр("ru = '%1 не заполнен в строке №%2.'"), ШаблонОшибки);
Иначе
ШаблонОшибки = ?(ШаблонОшибки = Неопределено, НСтр("ru = '%1 не заполнен.'"), ШаблонОшибки);
КонецЕсли;
Идентификатор = ОбщегоНазначенияБПОКлиентСервер.ПредставлениеИдентификатора(ИмяРеквизита);
Идентификатор = ВРег(Лев(Идентификатор,1)) + Сред(Идентификатор,2);
Проверка.Ошибки.Добавить(ТекстОшибки(ШаблонОшибки, Идентификатор, Проверка.НомерПозиции));
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьРеквизитВМассивеДанных(Проверка, МассивДанных, Знач ИмяРеквизита = Неопределено, Знач ВерсииФФД = Неопределено, Знач ШаблонОшибки = Неопределено)
РеквизитЗаполнен = Ложь;
Для Каждого Данные Из МассивДанных Цикл
Значение = Неопределено;
Если ЕстьРеквизит(Проверка, Данные, ИмяРеквизита, Значение) И ЗначениеЗаполнено(Значение) Тогда
РеквизитЗаполнен = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Если Не РеквизитЗаполнен Тогда
ШаблонОшибки = ?(ШаблонОшибки = Неопределено,
НСтр("ru = 'Реквизит %1 не заполнен в шапке или в строке %2.'"),
ШаблонОшибки);
Проверка.Ошибки.Добавить(ТекстОшибки(ШаблонОшибки, ИмяРеквизита, Проверка.НомерПозиции));
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьСовпадениеРеквизитаВМассивеДанных(Проверка, МассивДанных, Знач ИмяРеквизита = Неопределено, Знач ШаблонОшибки = Неопределено)
РеквизитСовпадает = Истина;
ЗначениеРеквизита = Неопределено;
Для Каждого Данные Из МассивДанных Цикл
Значение = Неопределено;
Если ЕстьРеквизит(Проверка, Данные, ИмяРеквизита, Значение) И ЗначениеЗаполнено(Значение) Тогда
Если ЗначениеРеквизита = Неопределено Тогда
ЗначениеРеквизита = Значение;
ИначеЕсли ЗначениеРеквизита <> Значение Тогда
РеквизитСовпадает = Ложь;
Прервать;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если Не РеквизитСовпадает Тогда
ШаблонОшибки = ?(ШаблонОшибки = Неопределено,
НСтр("ru = 'Значение реквизита %1 в строке %2 и шапке неравны.'"),
ШаблонОшибки);
Проверка.Ошибки.Добавить(ТекстОшибки(ШаблонОшибки, ИмяРеквизита, Проверка.НомерПозиции));
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьРеквизитИНН(Проверка, Данные, Знач ИмяРеквизита)
Значение = Неопределено;
Если ЕстьРеквизит(Проверка, Данные, ИмяРеквизита, Значение) Тогда
ОписаниеОшибки = "";
Если ЗначениеЗаполнено(Значение)
И НЕ ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ИННСоответствуетТребованиям(Значение, Ложь, ОписаниеОшибки) Тогда
Проверка.Ошибки.Добавить(СтрШаблон(НСтр("ru = 'ИНН (%1) некорректен (%2)'"), ИмяРеквизита, ОписаниеОшибки));
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьДлинуРеквизита(Проверка, Данные, Знач ИмяРеквизита, Длина)
Ошибки = Проверка.Ошибки;
Значение = Неопределено;
Если ЕстьРеквизит(Проверка, Данные, ИмяРеквизита, Значение) Тогда
Если ЗначениеЗаполнено(Значение) И СтрДлина(Значение) > Длина Тогда
Ошибки.Добавить(СтрШаблон(НСтр("ru = 'Длинна реквизита (%1) превышает %2 символа'"), ИмяРеквизита, Длина));
КонецЕсли;
КонецЕсли;
КонецПроцедуры
// Возвращает структуру сумм позиций чека
//
// Возвращаемое значение:
// Структура
Функция ПараметрыСуммыПозиций()
Структура = Новый Структура();
Структура.Вставить("Общая", 0);
Структура.Вставить("ПолнаяОплата", 0);
Структура.Вставить("ПередачаБезОплаты", 0);
Возврат Структура;
КонецФункции
Функция ПараметрыСуммыОплат()
Структура = Новый Структура();
Структура.Вставить("Общая", 0);
Структура.Вставить("Наличные", 0);
Структура.Вставить("НаличныеБезСдачи", 0);
Структура.Вставить("Электронно", 0);
Структура.Вставить("ВстречноеПредоставление", 0);
Структура.Вставить("Предоплата", 0);
Структура.Вставить("Постоплата", 0);
Возврат Структура;
КонецФункции
// Вычисляет суммы позиций чека по позиции
//
// Параметры:
// СуммыПозицийЧека - см. ПараметрыСуммыПозицийЧека
// Позиция - см. ОборудованиеЧекопечатающиеУстройстваКлиентСервер.ПараметрыФискальнойСтрокиЧека
//
Процедура ВычислитьСуммыПозиций(СуммыПозицийЧека, Позиция)
ПризнакСпособаРасчета = Позиция.ПризнакСпособаРасчета;
Если ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.Аванс
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПредоплатаПолная
Или ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПередачаСПолнойОплатой Тогда
СуммыПозицийЧека.ПолнаяОплата = СуммыПозицийЧека.ПолнаяОплата + Позиция.Сумма;
ИначеЕсли ПризнакСпособаРасчета = Перечисления.ПризнакиСпособаРасчета.ПередачаБезОплаты Тогда
СуммыПозицийЧека.ПередачаБезОплаты = СуммыПозицийЧека.ПередачаБезОплаты + Позиция.Сумма;
КонецЕсли;
СуммыПозицийЧека.Общая = СуммыПозицийЧека.Общая + Позиция.Сумма;
КонецПроцедуры
Процедура ВычислитьСуммыОплат(СуммыОплат, СтрокаОплат)
Если СтрокаОплат.ТипОплаты = ПредопределенноеЗначение("Перечисление.ТипыОплатыККТ.Наличные") Тогда
СуммыОплат.Наличные = СуммыОплат.Наличные + СтрокаОплат.Сумма;
ИначеЕсли СтрокаОплат.ТипОплаты = ПредопределенноеЗначение("Перечисление.ТипыОплатыККТ.Электронно") Тогда
СуммыОплат.Электронно = СуммыОплат.Электронно + СтрокаОплат.Сумма;
ИначеЕсли СтрокаОплат.ТипОплаты = ПредопределенноеЗначение("Перечисление.ТипыОплатыККТ.ВстречноеПредоставление") Тогда
СуммыОплат.ВстречноеПредоставление = СуммыОплат.ВстречноеПредоставление + СтрокаОплат.Сумма;
ИначеЕсли СтрокаОплат.ТипОплаты = ПредопределенноеЗначение("Перечисление.ТипыОплатыККТ.Предоплата") Тогда
СуммыОплат.Предоплата = СуммыОплат.Предоплата + СтрокаОплат.Сумма;
ИначеЕсли СтрокаОплат.ТипОплаты = ПредопределенноеЗначение("Перечисление.ТипыОплатыККТ.Постоплата") Тогда
СуммыОплат.Постоплата = СуммыОплат.Постоплата + СтрокаОплат.Сумма;
КонецЕсли;
СуммыОплат.Общая = СуммыОплат.Общая + СтрокаОплат.Сумма;
КонецПроцедуры
Процедура ВычислитьСуммуНаличныеБезСдачи(СуммыПозиций, СуммыОплат)
СуммыОплат.НаличныеБезСдачи = СуммыОплат.Наличные;
// Проверяем что сумма наличные указана со сдачей.
Если СуммыОплат.Общая > СуммыПозиций.Общая И СуммыОплат.Наличные > 0 Тогда
НаличныеСдача = СуммыОплат.Общая - СуммыПозиций.Общая;
Если СуммыОплат.Наличные > НаличныеСдача Тогда
СуммыОплат.НаличныеБезСдачи = СуммыОплат.Наличные - НаличныеСдача;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура ПреобразоватьСистемуНалогообложения(Проверка, Данные)
ИспользуетсяФН36 = Проверка.ИспользуетсяФН36;
// Проверка системы налогообложения
Если Проверка.ТипОборудования = Перечисления.ТипыПодключаемогоОборудования.ККТ Тогда
// Пользователи, переходящие с 01.01.2021 с ЕНВД на ОСН могут не менять систему налогообложения
// в настройках кассы, если используют фискальный накопитель со сроком действия 36 мес.
// Подробнее см. Письмо ФНС России от 03.12.2020 № АБ-4-20/19907@.
Если ИспользуетсяФН36 И Данные.СистемаНалогообложения = Перечисления.ТипыСистемНалогообложенияККТ.ОСН Тогда
// Если в фискализируемом в чеке приходит ОСН и для ККТ установлена галка «Используется ФН 36 мес.(ЕНВД)» меняем ОСН на ЕНВД.
Данные.СистемаНалогообложения = Перечисления.ТипыСистемНалогообложенияККТ.ЕНВД;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура ПреобразоватьПризнакПредметаРасчета(Проверка, Данные)
Если Не ВерсияФФД(Проверка, "1.2") Тогда
// Значения доступные только в ФФД 1.2 преобразуем в значения доступные в ФФД 1.1, 1.05
ПризнакПредметаРасчета = Данные.ПризнакПредметаРасчета;
Если ПризнакПредметаРасчета = Перечисления.ПризнакиПредметаРасчета.ПодакцизныйТоварМаркируемыйСИНеИмеющийКМ
Или ПризнакПредметаРасчета = Перечисления.ПризнакиПредметаРасчета.ПодакцизныйТоварМаркируемыйСИИмеющийКМ Тогда
Данные.ПризнакПредметаРасчета = Перечисления.ПризнакиПредметаРасчета.ПодакцизныйТовар;
ИначеЕсли ПризнакПредметаРасчета = Перечисления.ПризнакиПредметаРасчета.ТоварМаркируемыйСИНеИмеющийКМ
Или ПризнакПредметаРасчета = Перечисления.ПризнакиПредметаРасчета.ТоварМаркируемыйСИИмеющийКМ Тогда
Данные.ПризнакПредметаРасчета = Перечисления.ПризнакиПредметаРасчета.Товар;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
#КонецОбласти
#КонецОбласти