diff --git a/CHANGELOG.md b/CHANGELOG.md
index aa12a77..5867cf5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,16 @@
---
+## 1.3.1.112 (2026-09-03)
+
+### РК_ЗагрузкаОстатков — CSV таблиц и файловый лог (#29)
+
+- Реквизит `ПутьКФайламВыгрузкиТаблиц` (сохраняется между сеансами) + выбор каталога.
+- Реквизит `РежимЗаписиФайловВыгрузки`: **Клиент** / **Сервер** (сохраняется); CSV и логи пишутся на выбранной стороне.
+- Команда «Выгрузить таблицы в CSV»: own + comm, UTF-8, уникальные имена `crm-dev_to_crm3-26_*_yyyy-MM-dd_HHmmss.csv`.
+- Флажок формы `ПисатьЛогиВПапку` (не сохраняется, по умолчанию выкл.): подробный протокол «Создать документы» (тест/запись) в `.log` в том же каталоге.
+- Режим «Сервер»: путь — каталог/UNC, доступный серверу 1С; диалог выбора каталога клиента недоступен (подсказка).
+
## 1.3.1.111 (2026-09-03)
### РК_ЗагрузкаОстатков — договор комиссии 2.5 (#34)
diff --git a/VERSION b/VERSION
index cb98a83..b060239 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.3.1.111
+1.3.1.112
diff --git a/src/ConfigDumpInfo.xml b/src/ConfigDumpInfo.xml
index 69be812..9d3a8a1 100644
--- a/src/ConfigDumpInfo.xml
+++ b/src/ConfigDumpInfo.xml
@@ -57,7 +57,7 @@
-
+
@@ -176,6 +176,8 @@
+
+
@@ -201,9 +203,9 @@
-
-
-
+
+
+
diff --git a/src/Configuration.xml b/src/Configuration.xml
index a058240..d8d554d 100644
--- a/src/Configuration.xml
+++ b/src/Configuration.xml
@@ -62,7 +62,7 @@
Role.РК_ОсновнаяРоль
- 1.3.1.111
+ 1.3.1.112
Language.Русский
diff --git a/src/DataProcessors/РК_ЗагрузкаОстатков.xml b/src/DataProcessors/РК_ЗагрузкаОстатков.xml
index c060eab..1744c01 100644
--- a/src/DataProcessors/РК_ЗагрузкаОстатков.xml
+++ b/src/DataProcessors/РК_ЗагрузкаОстатков.xml
@@ -238,6 +238,82 @@
Auto
+
+
+ ПутьКФайламВыгрузкиТаблиц
+
+
+ ru
+ Путь к файлам выгрузки таблиц
+
+
+ Каталог для CSV таблиц и файловых логов (#29)
+
+ xs:string
+
+ 0
+ Variable
+
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ РежимЗаписиФайловВыгрузки
+
+
+ ru
+ Режим записи файлов
+
+
+ Клиент или Сервер (#29)
+
+ xs:string
+
+ 20
+ Variable
+
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
diff --git a/src/DataProcessors/РК_ЗагрузкаОстатков/Ext/ObjectModule.bsl b/src/DataProcessors/РК_ЗагрузкаОстатков/Ext/ObjectModule.bsl
index 21726ff..471959b 100644
--- a/src/DataProcessors/РК_ЗагрузкаОстатков/Ext/ObjectModule.bsl
+++ b/src/DataProcessors/РК_ЗагрузкаОстатков/Ext/ObjectModule.bsl
@@ -408,6 +408,7 @@
Результат.Вставить("Пропущено", 0);
Результат.Вставить("Ошибок", 0);
Результат.Вставить("ТекстТега", "");
+ Результат.Вставить("Протокол", Новый Массив);
Если Не ЗначениеЗаполнено(Организация) Тогда
ВызватьИсключение "Укажите организацию для документов ввода остатков";
@@ -418,15 +419,25 @@
ТекстТега = ТегКомментарияДокументов();
Результат.ТекстТега = ТекстТега;
+ ДобавитьСтрокуПротокола(Результат, "=== Создать документы ввода остатков ===");
+ ДобавитьСтрокуПротокола(Результат, "Режим: " + ?(РежимТест, "тест (без записи)", "запись"));
+ ДобавитьСтрокуПротокола(Результат, "Организация: " + СокрЛП(Строка(Организация)));
+ ДобавитьСтрокуПротокола(Результат, "Дата документов: " + Формат(ДатаДокументовВвода, "ДЛФ=DT"));
+ ДобавитьСтрокуПротокола(Результат, "Тег: " + ТекстТега);
+ ДобавитьСтрокуПротокола(Результат, "Примечание: команда выполняет Запись документов, проведение — отдельно.");
СозданныеДокументы.Очистить();
СтрокиПоСкладу = СгруппироватьСопоставленныеСтрокиПоСкладу();
СтрокиКомиссии = СгруппироватьСопоставленныеСтрокиКомиссии();
+ ДобавитьСтрокуПротокола(Результат, "Групп own (складов): " + Формат(СтрокиПоСкладу.Количество(), "ЧН=0; ЧГ=0")
+ + "; групп comm: " + Формат(СтрокиКомиссии.Количество(), "ЧН=0; ЧГ=0"));
Если ПерезаписатьДокументы И Не РежимТест
И (СтрокиПоСкладу.Количество() > 0 Или СтрокиКомиссии.Количество() > 0) Тогда
+ ДобавитьСтрокуПротокола(Результат, "Удаление прежних документов волны…");
Результат.УдаленоДокументов = УдалитьПрежниеДокументыВолны(ТекстТега);
+ ДобавитьСтрокуПротокола(Результат, "Удалено документов: " + Формат(Результат.УдаленоДокументов, "ЧН=0; ЧГ=0"));
КонецЕсли;
Для Каждого ЭлементСклада Из СтрокиПоСкладу Цикл
@@ -437,15 +448,19 @@
Продолжить;
КонецЕсли;
+ Контекст = "own / склад " + СокрЛП(Строка(СкладДок))
+ + " / строк " + Формат(СтрокиСклада.Количество(), "ЧН=0; ЧГ=0");
Попытка
Если РежимТест Тогда
Результат.СозданоДокументов = Результат.СозданоДокументов + 1;
Результат.СтрокВДокументах = Результат.СтрокВДокументах + СтрокиСклада.Количество();
ДобавитьВСозданныеДокументы(Неопределено, "Собственные", СкладДок,
Неопределено, Неопределено, СтрокиСклада, ТекстТега + ", склад " + СокрЛП(Строка(СкладДок)), Истина);
+ ДобавитьСтрокуПротокола(Результат, "[TEST] " + Контекст);
Продолжить;
КонецЕсли;
+ ДобавитьСтрокуПротокола(Результат, "[FILL] " + Контекст);
Док = СоздатьДокументПоСкладу(СкладДок, СтрокиСклада, ТекстТега);
Если ЗначениеЗаполнено(Док) Тогда
Результат.СозданоДокументов = Результат.СозданоДокументов + 1;
@@ -456,9 +471,16 @@
КонецЦикла;
ДобавитьВСозданныеДокументы(Док.Ссылка, "Собственные", СкладДок,
Неопределено, Неопределено, СтрокиСклада, Док.Комментарий, Ложь);
+ ДобавитьСтрокуПротокола(Результат, "[WRITE] " + Контекст
+ + " → " + СокрЛП(Строка(Док.Ссылка))
+ + "; товаров=" + Формат(Док.Товары.Количество(), "ЧН=0; ЧГ=0"));
+ Иначе
+ ДобавитьСтрокуПротокола(Результат, "[SKIP] " + Контекст + " — пустой документ");
КонецЕсли;
Исключение
Результат.Ошибок = Результат.Ошибок + 1;
+ ДобавитьСтрокуПротокола(Результат, "[ERROR] " + Контекст + ": "
+ + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЦикла;
@@ -470,17 +492,23 @@
Продолжить;
КонецЕсли;
+ Первая = СтрокиГруппы[0];
+ Контекст = "comm / партнёр " + СокрЛП(Строка(Первая.Партнер))
+ + " / соглашение " + СокрЛП(Строка(Первая.Соглашение))
+ + " / договор " + СокрЛП(Строка(Первая.Договор))
+ + " / строк " + Формат(СтрокиГруппы.Количество(), "ЧН=0; ЧГ=0");
Попытка
Если РежимТест Тогда
Результат.СозданоДокументовКомиссии = Результат.СозданоДокументовКомиссии + 1;
Результат.СтрокКомиссии = Результат.СтрокКомиссии + СтрокиГруппы.Количество();
- Первая = СтрокиГруппы[0];
ДобавитьВСозданныеДокументы(Неопределено, "Комиссия 2.5", Неопределено,
Первая.Соглашение, Первая.Партнер, СтрокиГруппы,
ТекстТега + ", комиссия 2.5, " + СокрЛП(Строка(Первая.Партнер)), Истина);
+ ДобавитьСтрокуПротокола(Результат, "[TEST] " + Контекст);
Продолжить;
КонецЕсли;
+ ДобавитьСтрокуПротокола(Результат, "[FILL] " + Контекст);
Док = СоздатьДокументКомиссии(СтрокиГруппы, ТекстТега);
Если ЗначениеЗаполнено(Док) Тогда
Результат.СозданоДокументовКомиссии = Результат.СозданоДокументовКомиссии + 1;
@@ -491,21 +519,109 @@
КонецЦикла;
ДобавитьВСозданныеДокументы(Док.Ссылка, "Комиссия 2.5", Неопределено,
Док.СоглашениеСКомиссионером, Док.Партнер, СтрокиГруппы, Док.Комментарий, Ложь);
+ ДобавитьСтрокуПротокола(Результат, "[WRITE] " + Контекст
+ + " → " + СокрЛП(Строка(Док.Ссылка))
+ + "; товаров=" + Формат(Док.Товары.Количество(), "ЧН=0; ЧГ=0")
+ + "; договор=" + СокрЛП(Строка(Док.Договор)));
+ Иначе
+ ДобавитьСтрокуПротокола(Результат, "[SKIP] " + Контекст + " — пустой документ");
КонецЕсли;
Исключение
Результат.Ошибок = Результат.Ошибок + 1;
+ ДобавитьСтрокуПротокола(Результат, "[ERROR] " + Контекст + ": "
+ + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЦикла;
+ ДобавитьСтрокуПротокола(Результат, "=== Итог ===");
+ ДобавитьСтрокуПротокола(Результат, "Own док.: " + Формат(Результат.СозданоДокументов, "ЧН=0; ЧГ=0")
+ + " (" + Формат(Результат.СтрокВДокументах, "ЧН=0; ЧГ=0") + " стр.); comm: "
+ + Формат(Результат.СозданоДокументовКомиссии, "ЧН=0; ЧГ=0")
+ + " (" + Формат(Результат.СтрокКомиссии, "ЧН=0; ЧГ=0") + " стр.); удалено: "
+ + Формат(Результат.УдаленоДокументов, "ЧН=0; ЧГ=0")
+ + "; ошибок: " + Формат(Результат.Ошибок, "ЧН=0; ЧГ=0"));
+
Возврат Результат;
КонецФункции
+// CSV-текст ТЧ обработки (UTF-8 готовит вызывающий при записи файла).
+//
+Функция ТекстCSVТабличнойЧасти(ИмяТЧ) Экспорт
+
+ Если ИмяТЧ <> "ВходящиеСобственныеТовары" И ИмяТЧ <> "ВходящиеКомиссионныеТовары" Тогда
+ ВызватьИсключение "Неизвестная табличная часть: " + ИмяТЧ;
+ КонецЕсли;
+
+ Таблица = ЭтотОбъект[ИмяТЧ];
+ МетаТЧ = Метаданные.Обработки.РК_ЗагрузкаОстатков.ТабличныеЧасти[ИмяТЧ];
+ ИменаКолонок = Новый Массив;
+ Для Каждого Рекв Из МетаТЧ.Реквизиты Цикл
+ ИменаКолонок.Добавить(Рекв.Имя);
+ КонецЦикла;
+
+ Строки = Новый Массив;
+ Строки.Добавить(СтрСоединить(ИменаКолонок, ";"));
+
+ Для Каждого СтрТ Из Таблица Цикл
+ Значения = Новый Массив;
+ Для Каждого ИмяКол Из ИменаКолонок Цикл
+ Значения.Добавить(ЗначениеДляCSV(СтрТ[ИмяКол]));
+ КонецЦикла;
+ Строки.Добавить(СтрСоединить(Значения, ";"));
+ КонецЦикла;
+
+ Возврат СтрСоединить(Строки, Символы.ПС);
+
+КонецФункции
+
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
+Процедура ДобавитьСтрокуПротокола(Результат, Текст)
+
+ Если ТипЗнч(Результат) <> Тип("Структура") Тогда
+ Возврат;
+ КонецЕсли;
+ Если Не Результат.Свойство("Протокол") Или ТипЗнч(Результат.Протокол) <> Тип("Массив") Тогда
+ Результат.Вставить("Протокол", Новый Массив);
+ КонецЕсли;
+ Результат.Протокол.Добавить(Формат(ТекущаяДатаСеанса(), "ДЛФ=T") + " " + Текст);
+
+КонецПроцедуры
+
+Функция ЗначениеДляCSV(Значение)
+
+ Если Значение = Неопределено Или Значение = Null Тогда
+ Возврат "";
+ КонецЕсли;
+ Если ТипЗнч(Значение) = Тип("Булево") Тогда
+ Возврат ?(Значение, "1", "0");
+ КонецЕсли;
+ Если ТипЗнч(Значение) = Тип("Число") Тогда
+ Возврат Формат(Значение, "ЧН=0; ЧГ=0; ЧР=.");
+ КонецЕсли;
+ Если ТипЗнч(Значение) = Тип("Дата") Тогда
+ Если Не ЗначениеЗаполнено(Значение) Тогда
+ Возврат "";
+ КонецЕсли;
+ Возврат Формат(Значение, "ДФ=yyyy-MM-ddTHH:mm:ss");
+ КонецЕсли;
+ Если ТипЗнч(Значение) = Тип("УникальныйИдентификатор") Тогда
+ Возврат Строка(Значение);
+ КонецЕсли;
+
+ Текст = СокрЛП(Строка(Значение));
+ Текст = СтрЗаменить(Текст, """", """""");
+ Если СтрНайти(Текст, ";") > 0 Или СтрНайти(Текст, Символы.ПС) > 0 Или СтрНайти(Текст, """") > 0 Тогда
+ Текст = """" + Текст + """";
+ КонецЕсли;
+ Возврат Текст;
+
+КонецФункции
+
Процедура ДобавитьВСозданныеДокументы(ДокументСсылка, ВидОстатков, Склад, Соглашение, Партнер, СтрокиТоваров, Комментарий, ЭтоТест)
НовСтр = СозданныеДокументы.Добавить();
diff --git a/src/DataProcessors/РК_ЗагрузкаОстатков/Forms/Форма/Ext/Form.xml b/src/DataProcessors/РК_ЗагрузкаОстатков/Forms/Форма/Ext/Form.xml
index 144273e..75be3d8 100644
--- a/src/DataProcessors/РК_ЗагрузкаОстатков/Forms/Форма/Ext/Form.xml
+++ b/src/DataProcessors/РК_ЗагрузкаОстатков/Forms/Форма/Ext/Form.xml
@@ -31,6 +31,11 @@
Form.Command.СоздатьДокументы
+