Загрузка данных из внешних файлов (XML, JSON) в документ Отчет о розничных продажах (ОРП) — это классическая задача при интеграции 1С со сторонними кассовыми аппаратами или фронт-офисными системами. Сложность заключается в том, что ОРП имеет разветвленную структуру табличных частей: Товары, Услуги, Возвраты и Оплата. В этой статье мы подробно разберем, как правильно спроектировать алгоритм загрузки, избежать дублирования документов и корректно распределить товары по разделам.
Прежде чем приступать к созданию документов, необходимо прочитать данные из файла. В 1С для работы с XML чаще всего используются ЧтениеXML совместно с ПостроительDOM или объектная модель XDTO. Рассмотрим пример с использованием DOM, который позволяет обходить узлы файла как дерево объектов. Проанализируем типичную структуру, где один файл содержит данные по нескольким магазинам и сменам.
Для начала создадим вспомогательную таблицу значений (ТЗ), в которую выгрузим все "сырые" данные из файла. Это позволит нам сгруппировать информацию и уменьшить количество обращений к базе данных.
&НаСервере
Процедура ПрочитатьФайлXML(ПутьКФайлу)
ТЗ_Данные = Новый ТаблицаЗначений;
ТЗ_Данные.Колонки.Добавить("МагазинКод");
ТЗ_Данные.Колонки.Добавить("НомерСмены");
ТЗ_Данные.Колонки.Добавить("Дата");
ТЗ_Данные.Колонки.Добавить("КодНоменклатуры");
ТЗ_Данные.Колонки.Добавить("Количество");
ТЗ_Данные.Колонки.Добавить("Цена");
ТЗ_Данные.Колонки.Добавить("Сумма");
ТЗ_Данные.Колонки.Добавить("ЭтоВозврат", Новый ОписаниеТипов("Булево"));
ТЗ_Данные.Колонки.Добавить("ОплатаБезналом", Новый ОписаниеТипов("Булево"));
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ПутьКФайлу);
Построитель = Новый ПостроительDOM;
ДокументDOM = Построитель.Прочитать(Чтение);
// Обход узлов и заполнение ТЗ_Данные (логика зависит от структуры вашего XML)
// ...
Чтение.Закрыть();
КонецПроцедуры
Одной из главных ошибок начинающих разработчиков является создание документа на каждую строку файла. Это приводит к хаосу в базе. Выясним причину: документ Отчет о розничных продажах в 1С создается один на одну кассовую смену для конкретной кассы (склада).
Следовательно, перед созданием документов нам нужно "свернуть" нашу таблицу данных, чтобы получить уникальные пары Магазин + НомерСмены. Именно столько документов и должно быть создано в итоге.
ТаблицаДокументов = ТЗ_Данные.Скопировать();
ТаблицаДокументов.Свернуть("МагазинКод, НомерСмены, Дата");
Для Каждого СтрокаДок Из ТаблицаДокументов Цикл
// Здесь начинается логика создания одного документа ОРП
СоздатьДокументОРП(СтрокаДок, ТЗ_Данные);
КонецЦикла;
Использование методов НайтиПоКоду() или НайтиПоНаименованию() внутри цикла загрузки — это прямой путь к низкой производительности. Если в файле 1000 строк, система выполнит 1000 запросов к БД. Рассмотрим более эффективный метод: заранее соберем все коды номенклатуры из файла и одним запросом получим ссылки на объекты.
Рекомендуемый алгоритм:
КодНоменклатуры в массив.Номенклатура с фильтром ГДЕ Код В (&МассивКодов).Проанализируем ситуацию распределения позиций. В документе ОРП логика должна быть следующей:
Важный нюанс: при добавлении строки в ТЧ Товары или Возвраты необходимо также заполнять счета учета. В типовых конфигурациях для этого используются специализированные процедуры.
Процедура ЗаполнитьСтрокуДокумента(НовыйДокумент, ДанныеСтроки, СсылкаНоменклатура)
// Проверяем, услуга это или товар
ЭтоУслуга = (СсылкаНоменклатура.ТипНоменклатуры = Перечисления.ТипыНоменклатуры.Услуга);
Если ДанныеСтроки.ЭтоВозврат Тогда
Если ЭтоУслуга Тогда
НоваяСтрока = НовыйДокумент.ВозвратУслуг.Добавить();
Иначе
НоваяСтрока = НовыйДокумент.Возвраты.Добавить();
КонецЕсли;
Иначе
Если ЭтоУслуга Тогда
НоваяСтрока = НовыйДокумент.Услуги.Добавить();
Иначе
НоваяСтрока = НовыйДокумент.Товары.Добавить();
КонецЕсли;
КонецЕсли;
ЗаполнитьЗначенияСвойств(НоваяСтрока, ДанныеСтроки);
НоваяСтрока.Номенклатура = СсылкаНоменклатура;
// Автоматическое заполнение счетов учета (пример для БП 3.0)
ЗаполнитьСчетаУчетаДляСтроки(НовыйДокумент, НоваяСтрока, ЭтоУслуга);
КонецПроцедуры
Сумма по ТЧ Товары и Услуги за вычетом Возвратов должна совпадать с итогом по ТЧ Оплата. Если в XML указано, что чек оплачен картой, необходимо добавить строку в таблицу оплат. Рассмотрим, как это реализовать:
Проанализируем пример: если в рамках одной смены было 10 чеков безналом, в документе ОРП в таблице "Оплата" достаточно создать одну строку с общим итогом по данному виду оплаты (например, "Эквайринг"), либо распределить их по видам карт.
// Внутри цикла создания документа
СуммаБезнала = ТЗ_Данные.Итог("Сумма", "ОплатаБезналом = Истина");
Если СуммаБезнала > 0 Тогда
СтрокаОплаты = НовыйДокумент.Оплата.Добавить();
СтрокаОплаты.ВидОплаты = Справочники.ВидыОплатЧековККМ.НайтиПоНаименованию("Карта");
СтрокаОплаты.СуммаОплаты = СуммаБезнала;
КонецЕсли;
Соберем всё воедино. Главное правило: метод Записать() вызывается один раз для каждого документа после того, как заполнены все его части. Посмотрим на итоговую структуру функции создания документа на сервере:
Функция СоздатьДокументНаСервере(ПараметрыШапки, ТЗ_ВсеДанные)
НовыйДокумент = Документы.ОтчетОРозничныхПродажах.СоздатьДокумент();
НовыйДокумент.Дата = ПараметрыШапки.Дата;
НовыйДокумент.Организация = Справочники.Организации.ОрганизацияПоУмолчанию();
// Поиск кассы по коду магазина из файла
НовыйДокумент.Касса = Справочники.Кассы.НайтиПоРеквизиту("КодМагазина", ПараметрыШапки.МагазинКод);
// Получаем строки именно для этой смены и магазина
Отбор = Новый Структура("МагазинКод, НомерСмены", ПараметрыШапки.МагазинКод, ПараметрыШапки.НомерСмены);
СтрокиСмены = ТЗ_ВсеДанные.НайтиСтроки(Отбор);
Для Каждого Стр Из СтрокиСмены Цикл
СсылкаНомен = ПолучитьНоменклатуруИзКэша(Стр.КодНоменклатуры);
ЗаполнитьСтрокуДокумента(НовыйДокумент, Стр, СсылкаНомен);
КонецЦикла;
// Заполнение итогов по оплате
ЗаполнитьТаблицуОплат(НовыйДокумент, СтрокиСмены);
Попытка
НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
Возврат НовыйДокумент.Ссылка;
Исключение
Сообщить("Ошибка при записи: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
1. Дублирование товаров в Товарах и Возвратах. Проверьте условия Если...ИначеЕсли. Если вы сначала добавляете строку в ТЧ "Товары", а потом проверяете "Если ЭтоВозврат" и добавляете её же в "Возвраты" — вы удваиваете оборот. Строка должна попадать строго в одну из таблиц.
2. Пустые документы при открытии формы. Если вы создали документ программно, записали его, а при открытии через ОткрытьФорму он пустой — убедитесь, что вы передаете ссылку на уже существующий документ в параметре Ключ, а не пытаетесь создать "Новый" через параметры формы.
3. Отсутствие группировки. Если в файле 500 чеков за одну смену, а вы создали 500 документов ОРП — это грубая методическая ошибка. В 1С один ОРП соответствует одному Z-отчету (одной смене).
Соблюдение этих правил позволит вам создать надежный и быстрый механизм загрузки, который будет корректно отражать розничные продажи в бухгалтерском и управленческом учете.