Пара продовольственных розничных магазинов генерируют в год примерно 100 тысяч документов и миллионы записей регистров. Типовая свертка может продлиться 1 день и еще дольше будут удаляться помеченные на удаление документы. Если настроена распределенная база данных, то обмен тоже не будет работать до окончания прохождения огромных сообщений с удаленными объектами. А что делать магазинам несколько дней?
Если 1С:Розница используется как кассовая программа, то есть учет ведется в вышестоящей товароучетной системе (например, 1С:Управление торговлей), то свертку можно проводить почаще. Например, раз в месяц, оставляя документы за последний месяц.
Приложенная обработка производит следующие операции:
- удаляет непосредственно все документы ЧекККМ, а также все документы движений в регистры ТоварыНаСкладах, ЦеныНоменклатуры, БонусныеБаллы датой до начала предыдущего месяца (если запускаем 05.09.2022, то удаляются документы до 01.08.2022 00:00:00);
- формирует документ начальных остатков Корректировка регистров только по регистрам ТоварыНаСкладах, ЦеныНоменклатуры, БонусныеБаллы датой за секунду до начала предыдущего месяца (если запускаем 05.09.2022, то Корректировка регистров с датой 31.07.2022 23:59:59);
- создает лог-файл в папке C:\temp.
Внимание! Действия обработки необратимы! В обработке используется непосредственное удаление документов. Копия базы обязательна перед использованием!
Тестировалось на платформе 1С версии 8.3.21.1302, демо-базе конфигурации 1С:Розница версии 2.3.11.44. Код модуля объекта обработки представлен ниже.
#Область ПрограммныйИнтерфейс
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиДополнительнаяОбработка();
ПараметрыРегистрации.Версия = "22.09.05";
Разрешение = РаботаВБезопасномРежиме.РазрешениеНаИспользованиеКаталогаВременныхФайлов(Истина, Истина, "Обработка использует временное хранилище.");
ПараметрыРегистрации.Разрешения.Добавить(Разрешение);
НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
НоваяКоманда.Представление = НСтр("ru = 'Свертка базы'");
НоваяКоманда.Идентификатор = "СверткаБазы";
НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
НоваяКоманда.ПоказыватьОповещение = Истина;
Возврат ПараметрыРегистрации;
КонецФункции
Функция ВыполнитьКоманду(Идентификатор, ПараметрыКоманды) Экспорт
Если Идентификатор = "СверткаБазы" Тогда
СтруктураПараметров = Новый Структура;
СтруктураПараметров.Вставить("ДатаСвертки", НачалоМесяца(ДобавитьМесяц(ТекущаяДата(), -1)));
СтруктураПараметров.Вставить("ТолькоПосчитать", Ложь);
СтруктураПараметров.Вставить("ОтправлятьВТелеграм", Ложь);
СтруктураПараметров.Вставить("IDПользователяТелеграм", "0");
СоздатьКаталог("C:\temp");
СтруктураПараметров.Вставить("ПутьКЛогФайлу", "C:\temp\log1c.txt");
СверткаБазы(СтруктураПараметров);
КонецЕсли;
КонецФункции
#КонецОбласти
// Основные процедуры и функции
Процедура СверткаБазы(СтруктураПараметров, Параметр2 = Неопределено) Экспорт
УстановитьДатуСверткиУзлов(СтруктураПараметров.ДатаСвертки);
КорректировкаРегистровСсылка = СформироватьНачальныеОстаткиВажныхРегистров(СтруктураПараметров.ДатаСвертки);
ЗаписатьЛог("Ввод начальных остатков.",,,СтруктураПараметров.ПутьКЛогФайлу);
ЗаписатьЛог(Строка(КорректировкаРегистровСсылка), СтруктураПараметров.ОтправлятьВТелеграм, СтруктураПараметров.IDПользователяТелеграм, СтруктураПараметров.ПутьКЛогФайлу);
Запрос = Новый Запрос;
Запрос.Текст = ПолучитьТекстЗапроса();
Запрос.УстановитьПараметр("ДатаСвертки", СтруктураПараметров.ДатаСвертки - 1);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
ЗаписатьЛог("Кол-во: " + ВыборкаДетальныеЗаписи.Количество(),,,СтруктураПараметров.ПутьКЛогФайлу);
Если СтруктураПараметров.ТолькоПосчитать Тогда
Возврат;
КонецЕсли;
КоличествоУдаленныхДокументов = 0;
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ЗаписатьЛог("Удалено документов " + КоличествоУдаленныхДокументов + " из " + ВыборкаДетальныеЗаписи.Количество()
,,,СтруктураПараметров.ПутьКЛогФайлу);
ТекущийОбъект = ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект();
Если ТекущийОбъект = Неопределено Тогда
УдалитьДвиженияВажныхРегистров(ВыборкаДетальныеЗаписи.Ссылка);
КоличествоУдаленныхДокументов = КоличествоУдаленныхДокументов + 1;
Продолжить;
КонецЕсли;
Если ТипЗнч(ТекущийОбъект) = Тип("ДокументОбъект.КорректировкаРегистров") И
ТекущийОбъект.Дата = СтруктураПараметров.ДатаСвертки-1 И
ТекущийОбъект.Комментарий = "Свертка" Тогда
Продолжить;
КонецЕсли;
ЗаписатьЛог(Строка(ВыборкаДетальныеЗаписи.Ссылка),,,СтруктураПараметров.ПутьКЛогФайлу);
Попытка
Для каждого НаборДвижений из ТекущийОбъект.Движения Цикл
НаборДвижений.Прочитать();
Если НаборДвижений.Количество() > 0 Тогда
НаборДвижений.Очистить();
НаборДвижений.ОбменДанными.Загрузка = Истина;
НаборДвижений.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
НаборДвижений.Записать();
КонецЕсли;
КонецЦикла;
ТекущийОбъект.ОбменДанными.Загрузка = Истина;
ТекущийОбъект.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
ТекущийОбъект.Удалить();
КоличествоУдаленныхДокументов = КоличествоУдаленныхДокументов + 1;
Исключение
ЗаписатьЛог("====================",,,СтруктураПараметров.ПутьКЛогФайлу);
ЗаписатьЛог("Ошибка! " + Строка(ВыборкаДетальныеЗаписи.Ссылка),,, СтруктураПараметров.ПутьКЛогФайлу);
ЗаписатьЛог(ОписаниеОшибки(), СтруктураПараметров.ОтправлятьВТелеграм, СтруктураПараметров.IDПользователяТелеграм, СтруктураПараметров.ПутьКЛогФайлу);
ЗаписатьЛог("====================",,,СтруктураПараметров.ПутьКЛогФайлу);
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Функция ПолучитьТекстЗапроса();
Возврат "ВЫБРАТЬ
| ЧекККМ.Ссылка КАК Ссылка,
| ЧекККМ.Дата КАК Период
|ПОМЕСТИТЬ Документы
|ИЗ
| Документ.ЧекККМ КАК ЧекККМ
|ГДЕ
| ЧекККМ.Дата <= &ДатаСвертки
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ТоварыНаСкладахОбороты.Регистратор,
| ТоварыНаСкладахОбороты.Период
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Обороты(, &ДатаСвертки, Регистратор, ) КАК ТоварыНаСкладахОбороты
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| БонусныеБаллыОбороты.Регистратор,
| БонусныеБаллыОбороты.Период
|ИЗ
| РегистрНакопления.БонусныеБаллы.Обороты(, &ДатаСвертки, Регистратор, ) КАК БонусныеБаллыОбороты
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ЦеныНоменклатуры.Регистратор,
| ЦеныНоменклатуры.Период
|ИЗ
| РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
|ГДЕ
| ЦеныНоменклатуры.Период <= &ДатаСвертки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗЛИЧНЫЕ
| Документы.Ссылка КАК Ссылка,
| Документы.Период КАК Период
|ИЗ
| Документы КАК Документы
|
|УПОРЯДОЧИТЬ ПО
| Документы.Период";
КонецФункции
Процедура УстановитьДатуСверткиУзлов(ДатаСвертки)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПоМагазину.Ссылка КАК Ссылка
|ИЗ
| ПланОбмена.ПоМагазину КАК ПоМагазину
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ПоРабочемуМесту.Ссылка
|ИЗ
| ПланОбмена.ПоРабочемуМесту КАК ПоРабочемуМесту";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
УзелОбъект = ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект();
УзелОбъект.ОбменДанными.Загрузка = Истина;
УзелОбъект.ДатаНачалаВыгрузкиДокументов = ДатаСвертки;
УзелОбъект.Записать();
КонецЦикла;
КонецПроцедуры
Функция СформироватьНачальныеОстаткиВажныхРегистров(ДатаСвертки)
ДатаНачальныхОстатков = ДатаСвертки - 1;
Если НачальныеОстаткиСозданыРанее(ДатаНачальныхОстатков) Тогда
Возврат "Начальные остатки созданы ранее!";
КонецЕсли;
КорректировкаРегистровОбъект = Документы.КорректировкаРегистров.СоздатьДокумент();
КорректировкаРегистровОбъект.Ответственный = Пользователи.ТекущийПользователь();
КорректировкаРегистровОбъект.Дата = ДатаНачальныхОстатков;
КорректировкаРегистровОбъект.ОбменДанными.Загрузка = Истина;
КорректировкаРегистровОбъект.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
НоваяСтрокаТаблицыРегистров = КорректировкаРегистровОбъект.ТаблицаРегистров.Добавить();
НоваяСтрокаТаблицыРегистров.Имя = "БонусныеБаллы";
НоваяСтрокаТаблицыРегистров = КорректировкаРегистровОбъект.ТаблицаРегистров.Добавить();
НоваяСтрокаТаблицыРегистров.Имя = "ЦеныНоменклатуры";
НоваяСтрокаТаблицыРегистров = КорректировкаРегистровОбъект.ТаблицаРегистров.Добавить();
НоваяСтрокаТаблицыРегистров.Имя = "ТоварыНаСкладах";
КорректировкаРегистровОбъект.Комментарий = "Свертка"; // не менять
КорректировкаРегистровОбъект.Записать();
// Бонусы
НаборЗаписейБонусы = РегистрыНакопления.БонусныеБаллы.СоздатьНаборЗаписей();
НаборЗаписейБонусы.Отбор.Регистратор.Установить(КорректировкаРегистровОбъект.Ссылка);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БонусныеБаллыОстатки.ДисконтнаяКарта КАК ДисконтнаяКарта,
| БонусныеБаллыОстатки.БонуснаяПрограммаЛояльности КАК БонуснаяПрограммаЛояльности,
| БонусныеБаллыОстатки.НачисленоОстаток КАК Начислено,
| БонусныеБаллыОстатки.КСписаниюОстаток КАК КСписанию
|ИЗ
| РегистрНакопления.БонусныеБаллы.Остатки(&ДатаСвертки, ) КАК БонусныеБаллыОстатки";
Запрос.УстановитьПараметр("ДатаСвертки", ДатаСвертки);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
НоваяЗапись = НаборЗаписейБонусы.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, ВыборкаДетальныеЗаписи);
Если НоваяЗапись.Начислено < 0 Тогда
НоваяЗапись.ВидДвижения = ВидДвиженияНакопления.Расход;
НоваяЗапись.Начислено = -НоваяЗапись.Начислено;
КонецЕсли;
НоваяЗапись.Период = ДатаНачальныхОстатков;
НоваяЗапись.Регистратор = КорректировкаРегистровОбъект.Ссылка;
КонецЦикла;
НаборЗаписейБонусы.ОбменДанными.Загрузка = Истина;
НаборЗаписейБонусы.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
НаборЗаписейБонусы.Записать();
// Цены номенклатуры
НаборЗаписейЦены = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
НаборЗаписейЦены.Отбор.Регистратор.Установить(КорректировкаРегистровОбъект.Ссылка);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЦеныНоменклатурыСрезПоследних.ВидЦены КАК ВидЦены,
| ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,
| ЦеныНоменклатурыСрезПоследних.Характеристика КАК Характеристика,
| ЦеныНоменклатурыСрезПоследних.Цена КАК Цена,
| ЦеныНоменклатурыСрезПоследних.Упаковка КАК Упаковка,
| ЦеныНоменклатурыСрезПоследних.Валюта КАК Валюта
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаСвертки, ) КАК ЦеныНоменклатурыСрезПоследних";
Запрос.УстановитьПараметр("ДатаСвертки", ДатаСвертки);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
НоваяЗапись = НаборЗаписейЦены.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, ВыборкаДетальныеЗаписи);
НоваяЗапись.Период = ДатаНачальныхОстатков;
НоваяЗапись.Регистратор = КорректировкаРегистровОбъект.Ссылка;
КонецЦикла;
НаборЗаписейЦены.ОбменДанными.Загрузка = Истина;
НаборЗаписейЦены.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
НаборЗаписейЦены.Записать();
// Товары на складах
НаборЗаписейТовары = РегистрыНакопления.ТоварыНаСкладах.СоздатьНаборЗаписей();
НаборЗаписейТовары.Отбор.Регистратор.Установить(КорректировкаРегистровОбъект.Ссылка);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладахОстатки.Склад КАК Склад,
| ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
| ТоварыНаСкладахОстатки.Характеристика КАК Характеристика,
| ТоварыНаСкладахОстатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаСвертки, ) КАК ТоварыНаСкладахОстатки";
Запрос.УстановитьПараметр("ДатаСвертки", ДатаСвертки);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
НоваяЗапись = НаборЗаписейТовары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, ВыборкаДетальныеЗаписи);
Если НоваяЗапись.Количество < 0 Тогда
НоваяЗапись.ВидДвижения = ВидДвиженияНакопления.Расход;
НоваяЗапись.Количество = -НоваяЗапись.Количество;
КонецЕсли;
НоваяЗапись.Период = ДатаНачальныхОстатков;
НоваяЗапись.Регистратор = КорректировкаРегистровОбъект.Ссылка;
КонецЦикла;
НаборЗаписейТовары.ОбменДанными.Загрузка = Истина;
НаборЗаписейТовары.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
НаборЗаписейТовары.Записать();
Возврат КорректировкаРегистровОбъект.Ссылка;
КонецФункции
Функция НачальныеОстаткиСозданыРанее(ДатаНачальныхОстатков)
Результат = Ложь;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КорректировкаРегистров.Ссылка КАК Ссылка
|ИЗ
| Документ.КорректировкаРегистров КАК КорректировкаРегистров
|ГДЕ
| КорректировкаРегистров.Дата = &ДатаНачальныхОстатков
| И КорректировкаРегистров.Комментарий ПОДОБНО &Комментарий";
Запрос.УстановитьПараметр("ДатаНачальныхОстатков", ДатаНачальныхОстатков);
Запрос.УстановитьПараметр("Комментарий", "Свертка");
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Если ВыборкаДетальныеЗаписи.Следующий() Тогда
Результат = Истина;
КонецЕсли;
Возврат Результат;
КонецФункции
Процедура УдалитьДвиженияВажныхРегистров(Ссылка)
Попытка
НаборЗаписейБонусы = РегистрыНакопления.БонусныеБаллы.СоздатьНаборЗаписей();
НаборЗаписейБонусы.Отбор.Регистратор.Установить(Ссылка);
НаборЗаписейБонусы.ОбменДанными.Загрузка = Истина;
НаборЗаписейБонусы.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
НаборЗаписейБонусы.Записать();
Исключение
КонецПопытки;
Попытка
НаборЗаписейЦены = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
НаборЗаписейЦены.Отбор.Регистратор.Установить(Ссылка);
НаборЗаписейЦены.ОбменДанными.Загрузка = Истина;
НаборЗаписейЦены.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
НаборЗаписейЦены.Записать();
Исключение
КонецПопытки;
Попытка
НаборЗаписейТовары = РегистрыНакопления.ТоварыНаСкладах.СоздатьНаборЗаписей();
НаборЗаписейТовары.Отбор.Регистратор.Установить(Ссылка);
НаборЗаписейТовары.ОбменДанными.Загрузка = Истина;
НаборЗаписейТовары.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
НаборЗаписейТовары.Записать();
Исключение
КонецПопытки;
КонецПроцедуры
// Вспомогательные процедуры и функции
Процедура ЗаписатьЛог(Строка, ОтправитьВТелеграм = Ложь, IDПользователяТелеграм = "", ПутьКЛогФайлу)
СтрокаКЗаписи = Строка(ТекущаяДата()) + ": " + Строка;
Если ОтправитьВТелеграм Тогда
ОтправитьВТелеграм(СтрокаКЗаписи, IDПользователяТелеграм);
КонецЕсли;
Файл = Новый ЗаписьТекста(ПутьКЛогФайлу,,,Истина);
Файл.ЗаписатьСтроку(СтрокаКЗаписи);
Файл.Закрыть();
Сообщить(СтрокаКЗаписи);
КонецПроцедуры
Процедура ОтправитьВТелеграм(СтрокаКЗаписи, IDПользователяТелеграм);
// отправка в телеграм
КонецПроцедуры