SQL: удаление дублей записей в таблице

Ну задача написания нужного запроса сводится к следующим шагам:

  1. Сделаем выборку записей, сгруппировав по дублирующемуся полю. Получим количество дублей, и максимальный id дубля (чтобы знать какой удалять)
  2. Если количество записей больше 1, то запись с максимальным id удалим

В итоге запрос может получится примерно таким:

delete from ls_indications where id in (
	select max_id from (
		select count(ls) as cnt,max(id) as max_id from ls_indications where period='2025-03-01' group by ls
	) as zxc where zxc.cnt>1	
)

P.S. Если дублей больше 2-х, то запрос нужно выполнить несколько раз, т.к. он удаляет за один раз только одну дублирующуюся запись. Можно конечно наваять скрипт, который этот недостаток убирает, но смысла обычно нет, проще запрос выполнить несколько раз

Подключение к фоновым заданиям 1С

Случаются ситуации, что фоновое задание запущенное пользователем, продолжает работать и после выхода самого пользователя из 1С. Как же исключить повторный запуск фонового задания пользователем? Ну и заодно показать текущий прогресс выполнения задания. На самом деле всё оказалось просто. В 1С есть функционал получения полного списка выполняемых в текущий момент фоновых заданий:

ФоновыеЗадания.ПолучитьФоновыеЗадания();		

После чего остаётся только пролистать все фоновые задания, идентифицировать нужное задание и передать его идентификатор в обработчик ожидания, который собственно и отображает например прогресс бар:

&НаКлиенте
Процедура ВыгрузкаКвитанцийНаБумаге(Команда)
	этаформа.ИндикаторВыгрузкаКвитанций=0;
	ЗапуститьФЗВыгрузкиКвитанций();					
	ПодключитьОбработчикОжидания("ИндикаторВыполненияВыгрузкиКвитанцийИсточник1",1,ложь);
	ПодключитьОбработчикОжидания("ИндикаторВыполненияВыгрузкиКвитанцийИсточник2",1,ложь);
КонецПроцедуры       

&НаСервере
Процедура ЗапуститьФЗВыгрузкиКвитанций()		
	Фоновые = ФоновыеЗадания.ПолучитьФоновыеЗадания();		
	Для Каждого Фоновое из Фоновые Цикл
		если Фоновое.ИмяМетода="СК_ГР_ГеоаналитикаЗагрузки.ВыгрузитьКвитанцииВГеоаналитикуИсточник1" тогда
			если Фоновое.Состояние<>СостояниеФоновогоЗадания.ЗавершеноАварийно и Фоновое.Состояние<>СостояниеФоновогоЗадания.Завершено тогда
				сообщить("-предыдущее задание еще не завершено!");
				объект.ФЗ_ВыгрузкаКвитанций = Фоновое.УникальныйИдентификатор;									
				возврат;
			конецесли;
		конецесли;				
		если Фоновое.ИмяМетода="СК_ГР_ГеоаналитикаЗагрузки.ВыгрузитьКвитанцииВГеоаналитикуИсточник2" тогда
			сообщить("-предыдущее задание еще не завершено!");
			если Фоновое.Состояние<>СостояниеФоновогоЗадания.ЗавершеноАварийно и Фоновое.Состояние<>СостояниеФоновогоЗадания.Завершено тогда
				сообщить("-предыдущее задание еще не завершено!");
				объект.ФЗ_ВыгрузкаКвитанций2 = Фоновое.УникальныйИдентификатор;									
				возврат;
			конецесли;
			возврат;
		 конецесли;							
	конеццикла;	
	
	Парм=Новый Структура("Период,ПроверятьНаличиевБД",объект.Период,объект.ПроверятьНаличиевБД);
	МассивПараметров = Новый Массив;
	МассивПараметров.Добавить(Парм);
	ФЗ = ФоновыеЗадания.Выполнить("СК_ГР_ГеоаналитикаЗагрузки.ВыгрузитьКвитанцииВГеоаналитикуИсточник1",МассивПараметров);	
	объект.ФЗ_ВыгрузкаКвитанций = ФЗ.УникальныйИдентификатор;						
	ФЗ2 = ФоновыеЗадания.Выполнить("СК_ГР_ГеоаналитикаЗагрузки.ВыгрузитьКвитанцииВГеоаналитикуИсточник2",МассивПараметров);	
	объект.ФЗ_ВыгрузкаКвитанций2 = ФЗ2.УникальныйИдентификатор;							
КонецПроцедуры

Т.е. если фоновое уже запущено — то просто получаем его идентификатор и отображаем прогресс бар. Если нет — запускаем

1с: Работа с деревом значений

Задача: создать инструмент для распределения заявок по дням.

Решение: Наиболее удобным вариант я подумал что будет создание некого «дерева», узлами которого будут даты, а «ветвями» — заявки. Заявки можно будет перетаскивать между датами, тем самым равномерно распределяя нагрузку по дням.

Для того чтобы работать с деревьями, в 1С есть специальный тип: Дерево значений. Основной сущностностью у него являются строки. У каждой строки могут быть реквизиты (колонки). В то-же время каждая строка может иметь «потомков» — другие строки.

Создадим на форме «ДеревоОтключений» с типом «Дерево значений», и добавим у него реквизиты:

И перетащим его на форму. Далее по событию открытия, заполним дерево:

ДО = РеквизитФормыВЗначение("ДеревоОтключений");
	ДО.Строки.Очистить();
	
   	Для Счетчик = 0 По Число(Сред(КонецМесяца(объект.ПериодОтключения), 1, 2)) Цикл
    	УДеньМесяца=ДО.Строки.Добавить();
		если Счетчик=0 тогда
			УДеньМесяца.ДатаОтключения="не распределено";
		иначе	
			УДеньМесяца.ДатаОтключения=Формат(Дата(Год(объект.ПериодОтключения),Месяц(объект.ПериодОтключения),Счетчик),"ДФ=dd.MM.yyyy");						
		конецесли;
		УДеньМесяца.expanded=false;
		кол=0;
		для каждого стр из МассивОтключаемых[Счетчик] цикл
			ссср=УДеньМесяца.Строки.Добавить();
			ссср.Отключаемый=стр;
			ссср.ЛС=Строка(стр.ЛС);
			ссср.Адрес=стр.АдресПотребителя;
			кол=кол+1;
		конеццикла;			
		если кол>0 тогда
			УДеньМесяца.ЛС=Строка(кол);
		конецесли;
		
		
    КонецЦикла;

	ЗначениеВРеквизитФормы(ДО,"ДеревоОтключений");

Получаем такую чудную картинку:

Теперь осталось только запретить перетаскивание во все колонки кроме «ДатаОтключения», чтобы избежать «не правильных» деревьев. Для этого заполним событие «ПриПеретаскивании»:

&НаКлиенте
Процедура ДеревоОтключенийПеретаскивание(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Поле)
//запретим если имя колонки куда тащим отличается от "ДатаОтключения"
	если Поле.Имя<>"ДеревоОтключенийДатаОтключения" тогда
		СтандартнаяОбработка=ложь;
	конецесли;	                            
//запретим если дата отключения пустая
	Куда=ДеревоОтключений.НайтиПоИдентификатору(Строка);
	если Куда.ДатаОтключения="" тогда
		СтандартнаяОбработка=ложь;
	конецесли;	
//запретим перетаскивание "в корень"
	если Куда.ДатаОтключения=ДеревоОтключений.НайтиПоИдентификатору(Элементы.ДеревоОтключений.ТекущаяСтрока).ПолучитьРодителя().ДатаОтключения тогда
		СтандартнаяОбработка=ложь;
	конецесли;	
КонецПроцедуры

Для того чтобы обновить цифру с количеством «веток» в каждом узле, реализовал нечто подобное, разместив в событии «ОкончаниеПеретаскивания»:

&НаКлиенте
Процедура ДеревоОтключенийОкончаниеПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)
	ОбновляюКоличествоЛС();	
КонецПроцедуры

&НаКлиенте
Функция ОбновляюКоличествоЛС()
	 ЭлементыДерева = ДеревоОтключений.ПолучитьЭлементы();	
	 для каждого стр из ЭлементыДерева цикл
		 клв=стр.ПолучитьЭлементы().Количество();
		 если клв<50 тогда
	 		стр.ЛС=клв;
		 конецесли;
	 конеццикла;	 
КонецФункции

В рекруссию не пошел, т.к. в моём случае заведомо известно, что ветвей не более 1

1С: использование HTML поля на форме

Сиё может понадобиться, если необходимо ну…например вывести какую-то динамическую информацию на форму, причём со стилями не стандартными для 1С, т.е. штатными средствами 1С не выполнимыми или трудновыполнимыми.

В моём случае, я в это поле просто вывожу динамический список напоминаний. При клике на который открывается соответствующий документ.

Итак, создаём на форме реквизит «Прочие напоминания» с типом строка. Длина -0, т.е. не ограниченная. Затем перетаскиваем реквизит на форму и выбираем вид «Поле HTML документа»:

При открытии формы, заполняем HTML:

&НаСервере
Функция СформироватьСписокНапоминанийНаСервере(Исполнитель)
список_напоминаний=СК_ОбщиеФункции.СформироватьУведомленияОНапоминаемыхДатах(ложь,Исполнитель);
	body="<ul>";
	для каждого стр из список_напоминаний цикл
		body=body+"<li><a href='"+стр.ссылка.номер+"'>"+стр.Пояснение+"</a></li>";
	конеццикла;	
	body=body+"</ul>";
	возврат body;
КонецФункции

&НаКлиенте
Процедура ПриОткрытии(Отказ)
	ПрочиеНапоминания="<html><head></head><body>"+СформироватьСписокНапоминанийНаСервере(Исполнитель)+"</body></html>";	
КонецПроцедуры

Далее отлавливаем клик по полю HTML, и открываем документ:

&НаКлиенте
Процедура ПрочиеНапоминанияПриНажатии(Элемент, ДанныеСобытия, СтандартнаяОбработка)
	если ДанныеСобытия.Свойство("href") тогда
		РазделёныйМассивСсылки=СтрРазделить(ДанныеСобытия.href,"/");		
		НомерДокумента=РазделёныйМассивСсылки[РазделёныйМассивСсылки.Количество()-1];
		ОткрытьЗначение(ПолучитьСсылкуНаДокумент(НомерДокумента));
	конецесли;
	СтандартнаяОбработка=ложь;
КонецПроцедуры

1С: Кнопка печати на управляемой форме

Для того чтобы на форме автоматически появилась кнопка печати, достаточно:

1.Создать команду, с именем надписи которая будет в выпадающей кнопке печати

    2. В модуле команды разместить код вывода печатной формы. Примерно что-то в этом духе:

    Функция КомпонентаФормированияQRКода()
    	
    	QRCodeGenerator = Неопределено;
    	
    	Попытка
    		Если ПодключитьВнешнююКомпоненту("ОбщийМакет.КомпонентаПечатиQRКода24", "QR") Тогда
    			QRCodeGenerator = Новый("AddIn.QR.QRCodeExtension");
    		Иначе
    			Возврат Неопределено;
    		КонецЕсли;
    	Исключение
    		сообщить(ОписаниеОшибки());
    		Возврат Неопределено;
    	КонецПопытки;
    	
    	Возврат QRCodeGenerator;
    	
    КонецФункции
    
    
    &НаКлиенте
    Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
    
    	СК_ЖурналДействийПользователей.ЗарегистрироватьДействие(
    		"ВызовКоманды",
    		ПараметрыВыполненияКоманды.НавигационнаяСсылка,
    		ПараметрКоманды
    	);
    	
    
    	ТабДок = ПолучитьТабДок(ПараметрКоманды);
    	ТабДок.Показать();
    	
    КонецПроцедуры
    
    &НаСервере
    Функция ПолучитьТабДок(ДокСсылка)
    	
    	ТабДок = Новый ТабличныйДокумент;
    	ТабДок.АвтоМасштаб			= Истина;
    	ТабДок.ОриентацияСтраницы	= ОриентацияСтраницы.Ландшафт;
    	ТабДок.КлючПараметровПечати	= "ПАРАМЕТРЫ_ПЕЧАТИ_СК_ГР_РеестрУведомленныхПотребителей";
    	
    	QRCodeGenerator = КомпонентаФормированияQRКода();
    	
    	Макет = Документы.СК_ГР_РеестрУведомленныхПотребителей.ПолучитьМакет("Квитанция");
    	КоличествоКвитанций=0;
    	для каждого стр из  ДокСсылка цикл
    		КоличествоКвитанций =КоличествоКвитанций + 1; 
    		ОбластьШапка  = Макет.ПолучитьОбласть("Шапка");	
    		
    		ОбластьШапка.Параметры.Адрес 			= стр.АдресПотребителя;
    		если ТипЗнч(стр.лс)=Тип("Строка") тогда
    			лицсч=стр.лс;			
    		иначе	
    			лицсч=стр.лс.код;
    		конецесли;	
    		ОбластьШапка.Параметры.ЛС= "9" + Строка(лицсч);
    		ОбластьШапка.Параметры.ФИО 	= стр.ФИО_потребителя;
    		ОбластьШапка.Параметры.СуммаРуб 	=Цел(стр.ПолнаяОплатаЗадолженостиСумма);
    		ОбластьШапка.Параметры.СуммаКоп 	= Прав(Формат(стр.ПолнаяОплатаЗадолженостиСумма,"ЧДЦ=2"),2);
    	
    		ИмяQRКода = "ШтрихКод";
    		
    		Попытка
    			УровеньКоррекции = 0;
    			Размер = 120;
    			ДанныеВШК = "ST00012|Name=ООО Сукацукая|PersonalAcc=234к234к34к|BankName=ПАО СБЕРБАНК|BIC=044525225|CorrespAcc=епукепк|Category=екпукеп|PayeeINN=4543е345е|Purpose=ОТКЛЮЧЕНИЕ/ПОДКЛЮЧЕНИЕ|PersAcc="+"9" + Строка(лицсч)+"|PayerAddress="+стр.АдресПотребителя+"|Sum="+формат(Цел(стр.ПолнаяОплатаЗадолженостиСумма*100),"ЧГ=")+"";
    			ДанныеQRКода = QRCodeGenerator.GenerateQRCode(ДанныеВШК, УровеньКоррекции, Размер);
    		Исключение  
    		сообщить(описаниеОшибки());	
    		КонецПопытки;
    		
    		Если ДанныеQRКода <> Неопределено Тогда 
    			ОбластьШапка.Рисунки[ИмяQRКода].Картинка = Новый Картинка(ДанныеQRКода);
    		КонецЕсли;
    		Если КоличествоКвитанций = 2 Тогда
    			ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
    			КоличествоКвитанций = 0;
    		КонецЕСли;
    	
    	
    	ТабДок.Вывести(ОбластьШапка);
    	конеццикла;
    	
    	ТабДок.ТолькоПросмотр = истина;
    	ТабДок.АвтоМасштаб = истина;
    	
    	
    	Возврат ТабДок;
    	
    КонецФункции

    3. В модуле менеджера документа разместить функцию:

    Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
    	ПараметрыВывода.ДоступнаПечатьПоКомплектно = Истина;		
    КонецПроцедуры

    И всё. Теперь на форме документа и форме списка появится кнопка «Печать» с выпадающим списком.

    1 11 12 13 14 15 310