Некоторые картинки не загружаются из РФ и РК, используйте VPN.
Показаны сообщения с ярлыком 1сЗаготовка. Показать все сообщения
Показаны сообщения с ярлыком 1сЗаготовка. Показать все сообщения

пятница, 29 марта 2024 г.

1С:Заготовка Поиск по GTIN номенклатуры при загрузке из ТабДокумента

Менеджер получил новые GTIN для продукции, которая уже заведена в 1С УТ и имеет собственный GTIN, при этом наименование товара также заменили в соответствии с новыми правилами ЧЗ. Плюс к списку прилагались 2000 марок к остаткам. Все было выполнено в специальной конфигурации Хамелеон. Бухгалтер попросила помочь с загрузкой марок в 1С УТ, так как  в УТ остаток есть, а приход размазан на год.

Самый простой вариант для разового варианта - ЗагрузкаДанныхИзТабличногоДокумента_УФ_v2.epf, но он не умеет находить по GTIN из коробки. А мне нужно:

  • Добавить GTIN к существующей карточке номенклатуры
  • Заменить наименования в карточке
  • Загрузить марки в базу
Поэтому я воспользовался возможностью Вычислить


Вот такой код:

М = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьНаборЗаписей();
М.Отбор.Штрихкод.Установить(?(СтрДлина(ТекстЯчейки)=13,ТекстЯчейки,строка("0"+ТекстЯчейки)));
М.Прочитать();
Результат=М[0].Номенклатура;
Сообщить(М[0].Номенклатура);

Да, код не оптимален, но для разовой загрузки подойдет, ибо на загрузку в таком варианте ушло не больше 20 минут (вспомнить как писать на 1С тоже занимает время X-D), а на разработку полноценного решения ушло бы часа 2 О_О

Один момент, для исправления наименования GTIN помещаем в поле Код, для поля код ставим галку поле поиска, а алгоритм вычисления кода правим вот так:

М = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьНаборЗаписей();
М.Отбор.Штрихкод.Установить(?(СтрДлина(ТекстЯчейки)=13,ТекстЯчейки,строка("0"+ТекстЯчейки)));
М.Прочитать();
Результат=М[0].Номенклатура.Код;
Сообщить(М[0].Номенклатура);

пятница, 17 марта 2023 г.

1СЗаготовока запрос свободная секунда (список секунд за дату)

Была мысль сделать проверку времени документа и подставлять свободные даты. В ходе размышлений, пришел к выводу что мне это не надо и забросил, а вот код выдающий список секунд за дату я решил не выбрасывать. А потом пришел к выводу что надо и переделал

Функция ПолучитьДатуДокумента()
  Если НЕ ЭтоНовый() Тогда
    ШестьдесятСекундТЗ=Новый ТаблицаЗначений;
    ШестьдесятСекундТЗ.Колонки.Добавить("Секунд",Новый ОписаниеТипов("Число"));
    Секунд=0;
    Пока Секунд<=59 цикл
      ШестьдесятСекундТЗ.Добавить().Секунд=Секунд;
      Секунд=Секунд+1;  
    КонецЦикла;
      

    Запрос = Новый Запрос;
    Запрос.Текст = 
      "ВЫБРАТЬ
      |  секунды.секунд КАК секунд
      |ПОМЕСТИТЬ ШестьдесятСекунд
      |ИЗ
      |  &ШестьдесятСекунд КАК секунды
      |;
      |
      |////////////////////////////////////////////////////////////////////////////////
      |ВЫБРАТЬ
      |  ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ), СЕКУНДА, часов.часы * минут.минуты * секунд.секунды) КАК СекундаДня
      |ПОМЕСТИТЬ ДатаПоСекундам
      |ИЗ
      |  (ВЫБРАТЬ
      |    секунды.секунд КАК часы
      |  ИЗ
      |    ШестьдесятСекунд КАК секунды
      |  ГДЕ
      |    секунды.секунд <= 23) КАК часов,
      |  (ВЫБРАТЬ
      |    секунды.секунд КАК минуты
      |  ИЗ
      |    ШестьдесятСекунд КАК секунды) КАК минут,
      |  (ВЫБРАТЬ
      |    секунды.секунд КАК секунды
      |  ИЗ
      |    ШестьдесятСекунд КАК секунды) КАК секунд
      |;
      |
      |////////////////////////////////////////////////////////////////////////////////
      |ВЫБРАТЬ
      |  ВЫБОР
      |    КОГДА КоличествоДатТекущегоДокумента.Количество = 0
      |      ТОГДА &Дата
      |    ИНАЧЕ МИНИМУМ(ДатаПоСекундам.СекундаДня)
      |  КОНЕЦ КАК НоваяДата
      |ИЗ
      |  (ВЫБРАТЬ
      |    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ACG_ЗаказНаПроизводство.Дата) КАК Количество
      |  ИЗ
      |    Документ.ACG_ЗаказНаПроизводство КАК ACG_ЗаказНаПроизводство
      |  ГДЕ
      |    ACG_ЗаказНаПроизводство.Дата = &Дата
      |    И ACG_ЗаказНаПроизводство.Ссылка <> &Ссылка) КАК КоличествоДатТекущегоДокумента,
      |  ДатаПоСекундам КАК ДатаПоСекундам
      |ГДЕ
      |  НЕ ДатаПоСекундам.СекундаДня В
      |        (ВЫБРАТЬ РАЗЛИЧНЫЕ
      |          ACG_ЗаказНаПроизводство.Дата КАК Дата
      |        ИЗ
      |          Документ.ACG_ЗаказНаПроизводство КАК ACG_ЗаказНаПроизводство
      |        ГДЕ
      |          ACG_ЗаказНаПроизводство.Дата >= НАЧАЛОПЕРИОДА(&дата, ДЕНЬ)
      |          И ACG_ЗаказНаПроизводство.Дата <= КОНЕЦПЕРИОДА(&дата, ДЕНЬ))
      |
      |СГРУППИРОВАТЬ ПО
      |  КоличествоДатТекущегоДокумента.Количество";

    Запрос.УстановитьПараметр("Дата", Дата);
    Запрос.УстановитьПараметр("ШестьдесятСекунд",ШестьдесятСекундТЗ);
        Запрос.УстановитьПараметр("Ссылка",Ссылка);

    РезультатЗапроса = Запрос.Выполнить();

    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
      Если ВыборкаДетальныеЗаписи.Количество()=0 Тогда
      НоваяДата=Дата;
      Сообщить("Необходимо изменить время/дату документа, программно не получается - лапки");
    Иначе
      НоваяДата=РезультатЗапроса[0].НоваяДата;    
    КонецЕсли;
  ИНаче
    НоваяДата=ТекущаяДата();
  КонецЕсли;
  Возврат(НоваяДата);
КонецФункции

Банально и просто

четверг, 26 января 2023 г.

1СЗаготовка СБИС сопоставление номенклатуры по наименованию

 Для УТ очень надо. Форма ФормаПросмотраДокумента. Добавить на форму Команду и связать с процедурой. Также добавить Булево Отладка или удалить кусок кода

//arshanskiyav+
&НаСервере
Функция НайтиНоменклатуруПоНаименованию(Наименование) 
	Номенклатура=Справочники.Номенклатура.НайтиПоНаименованию(Наименование,Истина);
	Возврат(Новый Структура("Номенклатура,ЕдИзм",Номенклатура,Номенклатура.ЕдиницаИзмерения));
КонецФункции
&НаСервере
Функция НайтиЕдИзмПоНаименованию(Наименование) 
	Возврат(Справочники.УпаковкиЕдиницыИзмерения.НайтиПоНаименованию(Наименование,Истина));
КонецФункции 
&НаКлиенте
Процедура СопоставитьПоНаименованию(Команда)
	//СопоставитьПоНаименованиюНаСервере();
	
	Для Каждого СтрокаТЧ Из ТабличнаяЧасть Цикл  
		Элементы.ТабличнаяЧасть.ТекущаяСтрока=СтрокаТЧ.ПолучитьИдентификатор();
		Если НЕ ЗначениеЗаполнено(СтрокаТЧ.Номенклатура) Тогда 
			ТабЧасть = сбисЭлементФормы(ЭтаФорма,"ТабличнаяЧасть");
			СбисСтрокаТабличнаяЧастьДоИзменения(МестныйКэш, ТабЧасть);
			
			НайденнаяНом=НайтиНоменклатуруПоНаименованию(СтрокаТЧ.Название);
			СтрокаТЧ.Номенклатура=НайденнаяНом.Номенклатура;
			Если ЗначениеЗаполнено(СтрокаТЧ.Номенклатура) Тогда
				СтрокаТЧ.ЕдИзмОрг=НайденнаяНом.ЕдИзм;
			КонецЕсли;
			Если НЕ ЗначениеЗаполнено(СтрокаТЧ.ЕдИзмОрг) и ЗначениеЗаполнено(СтрокаТЧ.ЕдИзм) Тогда
				СтрокаТЧ.ЕдИзмОрг=НайтиЕдИзмПоНаименованию(СтрокаТЧ.ЕдИзм);
			КонецЕсли;
			Если Отладка Тогда
				Сообщить(СтрокаТЧ.Название);   
				Сообщить(НайденнаяНом);
			КонецЕсли;    
			
			ТабЧасть = сбисЭлементФормы(ЭтаФорма,"ТабличнаяЧасть");
			СбисСтрокаТабличнаяЧастьПриИзменении(МестныйКэш, ТабЧасть);
			
		КонецЕсли;
	КонецЦикла;

КонецПроцедуры
//arshanskiyav-

Продуктивностью не отличается, т.к. сбисовские обработчики регистрируют каждое изменение в dbf, но это намного быстрее, нежели вручную!

среда, 4 января 2023 г.

1СЗаготовка ADODB.Connection OLEDB UTF-8

Загружал прайс сварога и получил кривую кодировку. Посмотрел какая кодировка и выснилось, что стоит UTF-8. Сначала решений не было, но вернувшись к вопросу пару месяцев спустя нашел случайно ответ


Функция ПрочитатьCSVвТЗ_ADO(ИмяФайла, Разделитель=";", ЗаголовкиИзПервойСтроки = Ложь)
	ТаблицаРезультат = Новый ТаблицаЗначений;
	Файл = Новый Файл(ИмяФайла);
    
	Connection=Новый COMОбъект("ADODB.Connection");
	Connection.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source="+Файл.Путь+";Extended Properties=""text;HDR=No;IMEX=1;FMT=Delimited;CharacterSet=65001""");

	Схема = СтрШаблон("[%1]
		|ColNameHeader=%2
		|Format=Delimited(%3)
		|DecimalSymbol=.", Файл.Имя, Формат(ЗаголовкиИзПервойСтроки, "БЛ=False; БИ=True"), Разделитель);
	ФайлСхемы = Новый ТекстовыйДокумент;
	ФайлСхемы.УстановитьТекст(Схема);
	ФайлСхемы.Записать(Файл.Путь + "Schema.ini", "CESU-8"); // UTF-8 без BOM
	
	RecordSet=Новый COMОбъект("ADODB.Recordset");
	RecordSet.ActiveConnection = Connection;
	ЗапросЛист="select * from "+Файл.Имя;
	RecordSet.Open(ЗапросЛист, Connection);
    
	//Здесь читаем файл в ТаблицаРезультат
    
	RecordSet.Close();
	Connection.Close();
	Возврат(ТаблицаРезультат);
КонецФункции

Собственно кодировка указывается в настройке подключения

среда, 26 октября 2022 г.

1СЗаготовка Чтение CSV (ТонкийКлиент,ВебКлиент)

 Понадобилось использовать обработку на опубликованной базе и тут начались проблемы:

  1. Классический выбор файла здесь не работает
  2. Прочитать файл просто так не получилось
  3. Пришлось отказаться от чтения при помощи Excel (типа универсальная была)
Поэтому пришлось использовать БСП, сохранять во временный файл и читать оттуда:


&НаКлиенте
Процедура ИмяФайлаНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    ПараметрыЗагрузки = ФайловаяСистемаКлиент.ПараметрыЗагрузкиФайла();
    ПараметрыЗагрузки.ИдентификаторФормы = УникальныйИдентификатор;
    
	ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
	ДиалогВыбораФайла.Фильтр = "Excel CSV (*.csv)|*.csv"; 
	ДиалогВыбораФайла.МножественныйВыбор=Ложь;
	ДиалогВыбораФайла.Заголовок = "Выберите файл";
	ДиалогВыбораФайла.ПредварительныйПросмотр = Истина;
	ДиалогВыбораФайла.ИндексФильтра = 0;
    
    ПараметрыЗагрузки.Диалог = ДиалогВыбораФайла;
    
    ОповещениеЗавершенияВыбораФайла = Новый ОписаниеОповещения("ВыборФАйлаЗавершение", ЭтотОбъект);
    
    ФайловаяСистемаКлиент.ЗагрузитьФайлы(ОповещениеЗавершенияВыбораФайла, ПараметрыЗагрузки); //БСП
КонецПроцедуры
&НаКлиенте
Процедура ВыборФАйлаЗавершение(Результат, ДополнительныеПараметры) Экспорт 
	Если Результат<>Неопределено Тогда
		ИмяФайла=Результат[0].ПолноеИмя;
		АдресВХранилище=Результат[0].Хранение;
		ПрочитатьФайлCSV(АдресВХранилище);
		Элементы.Таблица.Обновить();
		ДоступностьКнопкиСоздания();
	КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура ПрочитатьФайлCSV(АдресВХранилище)
	Данные = ПолучитьИзВременногоХранилища(АдресВХранилище);
	ИмяПромежуточногоФайла = ПолучитьИмяВременногоФайла("csv");
	Данные.Записать(ИмяПромежуточногоФайла);
	УдалитьИзВременногоХранилища(АдресВХранилище);	//очищаем временное хранилище
	
	оТаблица=Новый ТаблицаЗначений;	
	//очищаем таблицу и удаляем колонки
	Таблица.Очистить();
	оТаблица=Таблица.Выгрузить();
	
	Разделитель=";";
	ЗаголовкиИзПервойСтроки=Истина;
	
	Текст = Новый ЧтениеТекста(ИмяПромежуточногоФайла);
	
	 //создадим колонки
	ТекСтрока = Текст.ПрочитатьСтроку();
	Если ТекСтрока <> Неопределено Тогда
		МассивЗначений = СтрРазделить(ТекСтрока, Разделитель);
		ИндексКолонки = 0;
		Для Каждого ИмяКолонки Из МассивЗначений Цикл
			ИмяКолонки = ?(ЗаголовкиИзПервойСтроки, "Кол"+ИндексКолонки, ИмяКолонки);
			оТаблица.Колонки.Добавить(ИмяКолонки);
			ИндексКолонки = ИндексКолонки + 1;
		КонецЦикла;
		Если ЗаголовкиИзПервойСтроки Тогда
			ТекСтрока = Текст.ПрочитатьСтроку();
		КонецЕсли;
	КонецЕсли;

	Пока ТекСтрока <> Неопределено Цикл // строки читаются до символа перевода строки
		НоваяСтрока = оТаблица.Добавить();
		
		МассивЗначений = СтрРазделить(ТекСтрока, Разделитель);
		ИндексКолонки = 0;
		Для Каждого ТекЗначение Из МассивЗначений Цикл
			Попытка
				ЗначениеЯчейки=Дата(ТекЗначение+":00");
			Исключение
				ЗначениеЯчейки=Число(СтрЗаменить(ТекЗначение," ",""));
			КонецПопытки;
			НоваяСтрока[ИндексКолонки] = ЗначениеЯчейки;
			ИндексКолонки = ИндексКолонки + 1;
		КонецЦикла;

		ТекСтрока = Текст.ПрочитатьСтроку();
	КонецЦикла;
		
	//Возврат Результат;
	
	Таблица.Загрузить(оТаблица);
	
	//удаляем временный файл
	Попытка
		УдалитьФайлы(ИмяПромежуточногоФайла);
	Исключение
	   ЗаписьЖурналаРегистрации(НСтр("ru = 'Мой механизм.Действие'"), УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
	КонецПопытки;
КонецПроцедуры


четверг, 20 октября 2022 г.

1СЗаготовка RegExp поиск слова

Задача: нужно проверять строку на наличие различных слов (масса, вес, длина, ширина, высота).

Решение: понятное дело, что простой поиск по строке СтрНайти(),Найти() не подойдут, т.к. вхождение может быть частью большего слова: масса = массаж, вес = весна и т.д. 

Решил использовать регулярные выражения, но и тут я обломался, т.к. регулярка \bbla\b не работает с Юникодом (кириллица), да и у 1С реализовано через ComObject(VBScript.RegExp), который принимает не все. Например параметр \i (регистронезависимость) вообще не принимает, в данном случае это параметр самого объекта (IgnoreCase), а не шаблона. В итоге использовав примеры гугла и поправки на VBScript.Regexp пришел к такой конструкции: 

"(^|[^А-Яа-яA-Za-z])(вес|масса)([^А-Яа-яA-Za-z]|$)"

Собственно разобрать такое легко:

(НачалоСтроки ИЛИ НеБуква(англ и кириллица)) И (вес ИЛИ масса) И (НеБуква ИЛИ КонецСтроки)

Для моей задачи подходит, мне нужно получить булево, а не замену. У данного варианта есть минусы, для меня не существенные:

  1. Любой небуквенный символ, предшествующий или последующий, попадает под шаблон, например "рполрволпва .вес. выраорл" вернет не "вес", а ".вес."
  2. Не работает с языками отличными от русского и английского

понедельник, 17 октября 2022 г.

1СЗаготовка Дублирование значение ячейки Ctrl+D (аналог Excel)

Готовый код для программного добавления кнопки (вызвать СоздатьКомандуДублироватьЗначение() из ПриСозданииНаСервере() формы), команды и сама процедура обработки:


&НаСервере
Функция СоздатьКомандуДублироватьЗначение()
	НоваяКоманда = Команды.Добавить("ДублироватьЗначение");
	НоваяКоманда.Действие= "ДублироватьЗначение";
	НоваяКоманда.Заголовок= "Дублировать значение";
	НоваяКоманда.ИзменяетСохраняемыеДанные = ?(найти(нрег(этаформа.ИмяФормы),"обработка")>0,Ложь,Истина);
	НоваяКоманда.Отображение= ОтображениеКнопки.Картинка;
	НоваяКоманда.Подсказка= "Дублируется значение предыдыущей ячейки, аналогично Excel. В т.ч. и выделенные строки";
	НоваяКоманда.СочетаниеКлавиш= Новый СочетаниеКлавиш(Клавиша.D, , Истина); // Ctrl+D
	
	// Опытным путем выявлено, что вызов команды сочетаниемклавиш не работает без кнопки
	// при этом кнопка должна быть на форме, Видимость=Истина
	// также замечено, что нажатие сочетанийклавишь активизирует кнопку
	// из-за чего ЭтаФорма.ТекущийЭлемент вернет кнопку
	// поэтому убираем кнопку в допМеню, этот фокус позволит получить именно текущий элемент
	НоваяКнопка=Элементы.Добавить("ДублироватьЗначение",Тип("КнопкаФормы"),ЭтаФорма.КоманднаяПанель);
	НоваяКнопка.вид=ВидКнопкиФормы.КнопкаКоманднойПанели;
	НоваяКнопка.ИмяКоманды="ДублироватьЗначение";
	НоваяКнопка.ПоложениеВКоманднойПанели=ПоложениеКнопкиВКоманднойПанели.ВДополнительномПодменю;
КонецФункции

&НаКлиенте
Функция ДублироватьЗначение()
	Если Типзнч(этаформа.ТекущийЭлемент)=Тип("ТаблицаФормы") Тогда     //работаем только с таблицами
		ИмяТФ=этаформа.ТекущийЭлемент.Имя;	// имя ТаблицыФормы
		ИмяТЗ=ПолучитьПутьКДанным(ИмяТФ);	// имя элемента формы может отличатся от имени реквизита
		ТЗнаФорме=Вычислить(ИмяТЗ);	// данные формы коллекция
		ВыделенныеСтроки=Элементы[ИмяТФ].ВыделенныеСтроки;
		// Определяем источник - строка предшествующая первой выделенной 
		// При удалении, перемещении строк, их идентификатор не меняется, и очень похож на номер строки
		// при этом индекс строки меняется
		ИндексСтрокиИсточника=9999999;
		Для каждого ИдСтроки ИЗ ВыделенныеСтроки Цикл
			ИндексСтроки=ТЗнаФорме.Индекс(ТЗнаФорме.НайтиПоИдентификатору(ИдСтроки));
			ИндексСтрокиИсточника=?(ИндексСтрокиИсточника>ИндексСтроки,ИндексСтроки,ИндексСтрокиИсточника);
		КонецЦикла;                                 
		ИндексСтрокиИсточника=ИндексСтрокиИсточника-1;
		
		Если ИндексСтрокиИсточника>=0 Тогда
			// опять же, имя элемента формы может отличатся от имени реквизита
			// при этом ПутьКДанным колонки содержит и ПутьКДанным таблицы, исключаем
			// обращатся к колонке по индексу также не стоит, т.к. порядок на форме может отличаться от порядка реквизитов
			ИмяТекКолонки=стрзаменить(ПолучитьПутьКДанным(Элементы[ИмяТФ].ТекущийЭлемент.Имя),""+ИмяТЗ+".","");

			ЗначениеИсточника=ТЗнаФорме[ИндексСтрокиИсточника][ИмяТекКолонки];
			
			Для Каждого СтрТЧ ИЗ ВыделенныеСтроки Цикл
				// ВыделенныеСтроки хранит не индекс строк, а их иденитфикаторы
				// при удалении строк из середины обновляется индекс, но не номер строки
				ТЗнаФорме.НайтиПоИдентификатору(СтрТЧ)[ИмяТекКолонки]=ЗначениеИсточника;
			КонецЦикла;
			Элементы[ИмяТФ].Обновить();  //обновляем данные в ТаблицеФормы
		КонецЕсли;
	КонецЕсли;		
КонецФункции

&НаСервере
Функция ПолучитьПутьКДанным(ИмяЭлемента)
	Возврат(Элементы[ИмяЭлемента].ПутьКДанным);
КонецФункции

Собственно комментарии рассказывают все подводные камни.

В теории можно подключить интерактивную запись и тем самым вызывать событие ПриИзменении(), но:

  1. Мне лень
  2. Нужно немного переделать код
  3. Для поддержки выделенных строк не забыть предварительно скопировать массив со строками
Собственно решение доработано и переделано уже несколько раз здесь

суббота, 15 октября 2022 г.

1СЗаготовка Строка значение и единица измерения в число

 Простенькая функция приводит "480 г" в "0,48", "10 см" в "0,1" и т.д. 

Описание: при помощи регулярного выражения строка на входе делится на две переменные - значение (удаляются буквы) и название единицы измерения (оставляются только буквы). Потом по названию подбирается коэффициент умножения для приведения вес к кг, а размера к см


// Функция - Значение в единицу измерения
//
// Параметры:
//  мНаименование	 - Строка - Наименование характеристики - может содержать единицу измерения Пример (Длина в упаковке, кг:)
//  мЗначение		 - Строка - Само значение, может содержать единцу измерения пример (10 грамм)
// 
// Возвращаемое значение:
//  Число - Коэффициент умножения для привидения к КГ или СМ
//
&НаСервере
Функция ЗначениеВЕдиницуИзмерения(мНаименование,мЗначение)
	ЗначЕдИзм=РегЭксп(мЗначение,"ОставитьЦифры",Истина,"");
	МножителиЕдиницИзмерения=Новый Соответствие;
	МножителиЕдиницИзмерения.Вставить("г","0,001");
	МножителиЕдиницИзмерения.Вставить("грамм","0,001");
	МножителиЕдиницИзмерения.Вставить("g","0,001");
	МножителиЕдиницИзмерения.Вставить("gram","0,001");
	МножителиЕдиницИзмерения.Вставить("кг","1");
	МножителиЕдиницИзмерения.Вставить("килограмм","1");
	МножителиЕдиницИзмерения.Вставить("kg","1");
	МножителиЕдиницИзмерения.Вставить("kilogram","1");
	МножителиЕдиницИзмерения.Вставить("т","1000");
	МножителиЕдиницИзмерения.Вставить("тонна","1000");
	МножителиЕдиницИзмерения.Вставить("ton","1000");
	МножителиЕдиницИзмерения.Вставить("мм","0,1");
	МножителиЕдиницИзмерения.Вставить("миллиметр","0,1");
	МножителиЕдиницИзмерения.Вставить("mm","0,1");
	МножителиЕдиницИзмерения.Вставить("milimetre","0,1");
	МножителиЕдиницИзмерения.Вставить("см","1");
	МножителиЕдиницИзмерения.Вставить("сантиметр","1");
	МножителиЕдиницИзмерения.Вставить("cm","1");
	МножителиЕдиницИзмерения.Вставить("centimetre","1");
	МножителиЕдиницИзмерения.Вставить("дм","10");
	МножителиЕдиницИзмерения.Вставить("дециметр","10");
	МножителиЕдиницИзмерения.Вставить("м","100");
	МножителиЕдиницИзмерения.Вставить("метр","100");
	МножителиЕдиницИзмерения.Вставить("m","100");
	МножителиЕдиницИзмерения.Вставить("metre","100");
	МножителиЕдиницИзмерения.Вставить("км","1000");
	МножителиЕдиницИзмерения.Вставить("километр","1000");
	МножителиЕдиницИзмерения.Вставить("km","1000");
	МножителиЕдиницИзмерения.Вставить("kilometre","1000");
	
	НаимЕдИзм="";
	НаимЕдИзмНайдено=Ложь;
	
	РегЭксп= Новый COMОбъект("VBScript.RegExp");
	РегЭксп.IgnoreCase = "True"; //Игнорировать регистр 
	РегЭксп.Global = Истина; //Поиск всех вхождений шаблона 
	РегЭксп.MultiLine = Ложь; //Многострочный режим 

	Для каждого ЭлементСоответствия Из МножителиЕдиницИзмерения Цикл
		РегЭксп.Pattern = строка("(^|[^А-Яа-яA-Za-z])("+ЭлементСоответствия.Ключ+")([^А-Яа-яA-Za-z]|$)");
		НаимЕдИзмНайдено=РегЭксп.Test(мЗначение);
		Если НЕ НаимЕдИзмНайдено Тогда
			НаимЕдИзмНайдено=РегЭксп.Test(мНаименование);
		КонецЕсли;
		Если НаимЕдИзмНайдено Тогда 
			НаимЕдИзм=ЭлементСоответствия.Ключ;
			Прервать;
		КонецЕсли;		
	КонецЦикла;

	Если МножителиЕдиницИзмерения.Получить(НаимЕдИзм)=Неопределено Тогда
		ЗначениеЕдИзм=мЗначение;
	Иначе
		ЗначениеЕдИзм=ЗначЕдИзм*МножителиЕдиницИзмерения[НаимЕдИзм];
	КонецЕсли;
	
	Сообщить(СтрШаблон("Значение ""%1"" => ""%2"" (%3)",мЗначение,ЗначениеЕдИзм,НаимЕдИзм));
	Возврат(ЗначениеЕдИзм);
	 
КонецФункции


вторник, 20 сентября 2022 г.

1C ПолучитьИмяЗначенияПеречисления(ЗначениеПеречисления)


Функция ПолучитьИмяЗначенияПеречисления(ЗначениеПеречисления)
	ИмяПеречисления = ЗначениеПеречисления.Метаданные().Имя;
	ИндексЗначенияПеречисления = Перечисления[ИмяПеречисления].Индекс(ЗначениеПеречисления);
	ИмяЗначенияПеречисления = Метаданные.Перечисления[ИмяПеречисления].ЗначенияПеречисления[ИндексЗначенияПеречисления].Имя;
	Возврат(ИмяЗначенияПеречисления);	
КонецФункции

суббота, 18 июня 2022 г.

1С Изменить представление документа

 В модуле менеджера документа можно переопределить представление документа в процедуре ОбработкаПолученияПредставления. Для чего это нужно?, допустим у вас документ может иметь разный вид операции, но технически это один документ, как выделить заголовок и не отображать на форме ненужные элементы?


Процедура ОбработкаПолученияПредставления(Данные, Представление, СтандартнаяОбработка)
   СтандартнаяОбработка = Ложь;
   ДокСсыл=Данные.Ссылка;
   Брак=?(ЗначениеЗаполнено(ДокСсыл.БракЗаданияВРаботу),"Брак: ","");
   Представление = СтрШаблон("%1%2 №%3 от %4 (%5)",
                              Брак,ДокСсыл.Метаданные().Синоним,
                              Число(ДокСсыл.Номер),
                              формат(ДокСсыл.Дата,"ДФ=dd.MM.yyyy"),
                              ДокСсыл.ВидОперации);
КонецПроцедуры




понедельник, 23 мая 2022 г.

1C УдалитьПустыеСтрокиТЧ()

Удивительно, но некоторые пользователи не обращают внимание на пустую строку, оставленную в документе. Для одного документа процедура решается перебором перед записью, но у меня народ косячит исправно в большинстве ТЧ, поэтому было решено описать более-менее универсальную процедуру:

1С ПустаяСсылка() по типу значения

Мне нужно было получить пустую ссылку не знаю заранее тип значения реквизита колонки ТЗ. Нам известно что есть строки в ТЧ (ТЧДок),  имя реквизита передано строкой в переменной ИмяРеквизита.

МояПустаяСсылка=Новый(типзнч(ТЧДок[0][ИмяРеквизита]))

четверг, 14 апреля 2022 г.

1С функция ДобавитьКоманду()

Простенькая функция создающая команду формы и кнопку к ней на форме. Когда описываешь одну, это максимум 10 строчек, а когда таких 5 и более, получается гигантская котлета из почти одинаковых строк:



//Пример использвания в расширении (форма документа ПТиУ)
&НаСервере
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
эГруппаКоманд=ЭтаФорма.Элементы.вставить("ЭГруппаКоманд",тип("ГруппаФормы"), Элементы.Товары.КоманднаяПанель, Элементы.ТоварыКнопкиКоманднойПанели);
	эГруппаКоманд.Вид=ВидГруппыФормы.ГруппаКнопок;
	эГруппаКоманд.Отображение=ОтображениеГруппыКнопок.Компактное;
    
ДобавитьКоманду("ОтобразитьШтрихкоды",
					ОтображениеКнопки.Картинка,БиблиотекаКартинок.ТабличныйДокументТолькоПросмотр,
					Ложь,
					Тип("КнопкаФормы"),
					Элементы.ЭГруппаКоманд,
					Объект.ШтрихкодыУпаковок.Количество()>0);  

КонецПроцедуры    
// Функция - Добавить команду
//
// Параметры:
//  ИмяКомандыПроцедуры		 - Строка - Имя процедуры и команды будут совпадать, при помощи Синониминайзера, Выдернув "Команда", будет создан заголовок и подсказка
//  Отображение		 - ОтображениеКнопки -  Например: ОтображениеКнопки.Картинка
//  Картинка		 - БиблиотекаКартинок - Например: БиблиотекаКартинок.ТабличныйДокументТолькоПросмотр
//	ИзменяетСохраняемыеДанные - Булево - 
//  ТипЭлемента		 - Тип - Тип элемента ФОРМЫ, например: Тип("КнопкаФормы")
//  РодительЭлемента - ЭлементФормы - Например ГруппаКнопок
//  Доступность		 - Булево -
//
&НаСервере
Функция ДобавитьКоманду(ИмяКомандыПроцедуры, Отображение=Неопределено,Картинка=Неопределено,ИзменяетСохраняемыеДанные=Ложь,ТипЭлемента,РодительЭлемента=Неопределено,Доступность=Истина)
	ИмяКоманды="Команда"+ИмяКомандыПроцедуры;
	ИмяПроцедуры=ИмяКоманды;
	Заголовок=Синонимайзер(ИмяКомандыПроцедуры);
	Подсказка=Заголовок;
	
	Если Команды.Найти(ИмяКоманды) <> Неопределено Тогда
		Сообщить(СтрШаблон("Команда ""%1"" существует, пропуск",ИмяКоманды));
		Возврат
	КонецЕсли;
	
	НоваяКоманда= Команды.Добавить(ИмяКоманды);
	НоваяКоманда.Действие = ИмяПроцедуры;//Имя процедуры
	НоваяКоманда.Заголовок = Заголовок;
	НоваяКоманда.Подсказка = Подсказка;
	НоваяКоманда.ИзменяетСохраняемыеДанные=ИзменяетСохраняемыеДанные;
	Если Отображение<>Неопределено  Тогда
		НоваяКоманда.Отображение=Отображение;
	КонецЕсли;
	Если Картинка<>Неопределено  Тогда
		НоваяКоманда.Картинка=Картинка;
	КонецЕсли;
	
	ИмяЭлемента="Элемент"+ИмяКоманды;
	Если ЭтаФорма.Элементы.Найти(ИмяЭлемента)<>Неопределено Тогда  
		Сообщить(СтрШаблон("Элемент ""%1"" существует, пропуск",ИмяЭлемента));
		Возврат
	КонецЕсли;
	
	эНоваяКоманда = ЭтаФорма.Элементы.вставить(ИмяЭлемента, ТипЭлемента,?(РодительЭлемента=Неопределено,"",РодительЭлемента)); 
	эНоваяКоманда.ИмяКоманды=НоваяКоманда.Имя; 
	эНоваяКоманда.Доступность=Доступность;
КонецФункции

&НаСервере
Функция Синонимайзер(Имя, Ответ = "", Стэйт = 7, б = "")
    Для ё = 1 По СтрДлина(Имя) Цикл
        а = Сред(Имя, ё, 1);
        Стэйт = (НРег(а) <> а) * 4 + Цел(Стэйт / 2);
        Ответ = Ответ + ?(Стэйт = 2, НРег(б), ?(ё = 2, ВРег(б), б)) + ?(Стэйт = 4 ИЛИ Стэйт = 5, " ", "");
        б = а
    КонецЦикла;
    Возврат Ответ + б
КонецФункции


Я ее использую уже на форме, но если нужно добавлять сразу на несколько формах, то можно также добавить передачу формы.

суббота, 5 марта 2022 г.

1С УФ вывод на форму ТЗ с генерацией колонок

 Часто надо вывести таблицу значений не зная заранее состав колонок, просто оставлю здесь.

Обязательное условие - наличии на форме пустой ТЗ. Можно ее создать программно.

	
&НаСервере
Процедура ВывестиСписокДокументовНаФорму(ТзВыборкаИзБД)
    //ТзВыборкаИзБД - например выгрузка из БД запросом 
    ИмяТЗ_НаФорме="ТЗ"; //реквизит формы, а не элемент
    //Сначала очищаем ТЗ
    //если есть реквизиты созданные интерактивно, то вылезет ошибка
	УдаляемыеРеквизиты = Новый Массив;
	
	РеквизитыДляУдаления = ПолучитьРеквизиты(ИмяТЗ_НаФорме);
	Для Каждого РеквизитУдаления Из РеквизитыДляУдаления Цикл
        УдаляемыеРеквизиты.Добавить(ИмяТЗ_НаФорме+"."+РеквизитУдаления.Имя);
        Элементы.Удалить(Элементы[ИмяТЗ_НаФорме+РеквизитУдаления.Имя]);
	КонецЦикла;
	
	ДобавляемыеРеквизиты = Новый Массив;
	Для каждого Колонка Из ТзВыборкаИзБД.Колонки Цикл
		ИмяКолонки=Колонка.Имя;
		// пытаемся определить тип значения, иначе при попытке запихать ТЗ в Запрос получим ошибку "тип не может быть выбран в запросе"
		// Также, побочный эффект - это нормальное представление флажка на форме
		// Также прошу обратиться внимание на тот факт, что в первой строке в какой либо из колонок может быть NULL,
		// что привидет к неправильному определению типа всей колонки, поэтому в запросе необходимо заблаговременно
		//ИмяТипа=Строка(ТипЗнч(ТЗ[0][ИмяКолонки]));
		//Если Метаданные.Справочники.Найти(ИмяТипа)<> Неопределено Тогда
		//	ИмяТипа=""+"СправочникСсылка."+ИмяТипа;
		//ИначеЕсли Метаданные.Документы.Найти(ИмяТипа)<>Неопределено Тогда
		//	ИмяТипа=""+"ДокументСсылка."+ИмяТипа;
		//КонецЕсли;
		//ТипЗначенияКолонки=Новый ОписаниеТипов(ИмяТипа); 
		
		ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(ИмяКолонки, Колонка.ТипЗначения,ИмяТЗ_НаФорме, Синонимайзер(ИмяКолонки)));
	КонецЦикла;
	ИзменитьРеквизиты(ДобавляемыеРеквизиты,УдаляемыеРеквизиты);
	
	// Добавление элементов форму
	Для каждого Колонка Из ТзВыборкаИзБД.Колонки Цикл
		ИмяКолонки=СтрЗаменить(Колонка.Имя,"Скрытый","");
        ЭлементРеквизита = Элементы.Добавить(ИмяТЗ_НаФорме + ИмяКолонки, Тип("ПолеФормы"), Элементы[ИмяТЗ_НаФорме]);
        ЭлементРеквизита.ПутьКДанным = ИмяТЗ_НаФорме + "." + ИмяКолонки;
        ЭлементРеквизита.Вид = ВидПоляФормы.ПолеВвода;
		ЭлементРеквизита.Видимость=Не ЭтоСкрытый;
		ЭлементРеквизита.ТолькоПросмотр=Истина;
		ЭлементРеквизита.Заголовок=Синонимайзер(ИмяКолонки);
		//как раз флажок
        Если Колонка.имя="Загрузить" Тогда
			ЭлементРеквизита.ТолькоПросмотр=Ложь;
			ЭлементРеквизита.ПоложениеЗаголовка=ПоложениеЗаголовкаЭлементаФормы.Нет;				
			ЭлементРеквизита.вид=ВидПоляФормы.ПолеФлажка;
			ЭлементРеквизита.ОграничениеТипа=новый ОписаниеТипов("Булево");
		КонецЕсли;
		Колонка.Имя=ИмяКолонки;
    КонецЦикла;
    
    ЗначениеВРеквизитФормы(ТзВыборкаИзБД, ИмяТЗ_НаФорме);
КонецПроцедуры
// Это интересный Синониминайзер за авторством ildarovich https://infostart.ru/1c/articles/306536/
&НаСервере
Функция Синонимайзер(Имя, Ответ = "", Стэйт = 7, б = "") Экспорт
    Для ё = 1 По СтрДлина(Имя) Цикл
        а = Сред(Имя, ё, 1);
        Стэйт = (НРег(а) <> а) * 4 + Цел(Стэйт / 2);
        Ответ = Ответ + ?(Стэйт = 2, НРег(б), ?(ё = 2, ВРег(б), б)) + ?(Стэйт = 4 ИЛИ Стэйт = 5, " ", "");
        б = а
    КонецЦикла;
    Возврат Ответ + б
КонецФункции

    


Вывод заполненной ТЧ объекта на форму (имя элемента (ИмяТЗ) совпадает с именем реквизита объекта). Создаем элемент и связываем с ТЧ объекта

	
&НаСервере
Процедура ВывестиТЗНаФорму(ИмяТЗ)  
	//этот блок нужен только для скрытия уже подгруженной ТЧ
	Если ЭтаФорма.Элементы.Найти(ИмяТЗ)<>Неопределено тогда 
		ЭтаФорма.Элементы[ИмяТЗ].Видимость=не ЭтаФорма.Элементы[ИмяТЗ].Видимость;
		Возврат
	КонецЕсли;
	нЭлемент = ЭтаФорма.Элементы.вставить(ИмяТЗ, Тип("ТаблицаФормы"),Элементы.ГруппаТовары,); 
	нЭлемент.ПутьКДанным = "Объект."+ИмяТЗ;
	ТаблицаЗнач=Объект[ИмяТЗ].Выгрузить();
	Для каждого Колонка Из ТаблицаЗнач.Колонки Цикл
		ИмяКолонки=Колонка.Имя;
		Если ИмяКолонки="ИсходныйНомерСтроки" Тогда
			Продолжить //иначе получаем ошибку
		КонецЕсли;
        ЭлементРеквизита = Элементы.Добавить(ИмяТЗ + ИмяКолонки, Тип("ПолеФормы"), Элементы[ИмяТЗ]);
        ЭлементРеквизита.ПутьКДанным ="Объект."+ИмяТЗ + "." + ИмяКолонки;
        ЭлементРеквизита.Вид = ВидПоляФормы.ПолеВвода;
		ЭлементРеквизита.Заголовок=Синонимайзер(ИмяКолонки);
    КонецЦикла;
    
КонецПроцедуры

1С Кастомная функция сообщений

 Частенько требуется выводить тот или иной уровень сообщений при работе программы или ее отладке. Обычно добавляется реквизит Отладка (булево) и каждый раз, при необходимости, проверяешь ее и делаешь вывод сообщения. 

Первый раз мне такая мысль пришла в расширении для Фреша, т.к. есть ошибки проявляющиеся только там (например в БП какое-то время не работали твои ПодпискиНаСобытия). А каждое изменение требует аудита, представь, отправлять повторно на аудит из-за отключения вывода?


// Процедура - Сообщения отладки
//
// Параметры:
//  Сообщение	 - Строка - Текст сообщения
//  Отладка		 - Булево - Истина - выводит все сообщения. Ложь - только ошибки 
//  Уровень		 - Число - Числовое определение уровня ошибки
//							0 - INFO	- информационные сообщения - значение по-умолчанию
//                          1 - DEBUG	- сообщения отладки
//                          2 - ERROR	- сообщения ошибки, выводятся всегда
//  Поле		 - ЭлементФормы, строка - используется, если есть связка сообщения и элемента формы
//  ОбъектДанных - Объект - используется для определения объекта, вызвавшего процедуру 
//
&НаСервере
Процедура СообщенияОтладки(Сообщение="",Отладка=Неопределено,Уровень=0,Поле="",ОбъектДанных=Неопределено) Экспорт	
	Если Отладка=Неопределено Тогда
		Если РеквизитФормыВЗначение("Объект").метаданные().Реквизиты.Найти("Отладка") Тогда 
			Отладка=ЭтотОбъект.Отладка;
		Иначе
			Отладка=Ложь;
		КонецЕсли;
	КонецЕсли;
			
	Если Отладка ИЛИ Уровень=2 Или Поле="Отладка" Тогда
		Если Уровень=1 Тогда
			УровеньТекст="INFO";
		ИначеЕсли Уровень=2 Тогда
			УровеньТекст="ERROR";
		Иначе
			УровеньТекст="DEBUG";
		КонецЕсли;
		нСообщение = Новый СообщениеПользователю;
		ЗнакТабуляции=Символы.Таб;
		нСообщение.Текст = СтрШаблон("%1 %2 %3 %4 %5 %6 %7",ТекущаяДата(),ЗнакТабуляции,УровеньТекст,ЗнакТабуляции,Сообщение,ЗнакТабуляции,Поле);
		
		Если Уровень=2 тогда
			нСообщение.Поле = Поле;
		КонецЕсли;
		Если ОбъектДанных <> Неопределено Тогда
			нСообщение.УстановитьДанные(ОбъектДанных);
		КонецЕсли;
		нСообщение.Сообщить(); 		
	КонецЕсли;
КонецПроцедуры




среда, 2 марта 2022 г.

1С Своя формула в подвале ТЧ

Встала задача в строках и подвале вывести рентабельность операции продажи. В тч есть колонки себестоимости и стоимостьПродажи, и есть формула для поля Маржа=(стоимостьПродажи-себестоимость)/стоимостьПродажи. Все хорошо, но общий профит от заказа не ясен, а Итог по полю Маржа выдаст некорректные данные.

В таких случаях можно сделать финтушами, посчитать отдельно и вывести значение в "ТекстПодвала", а так как это число, то и прижать вправо


нЭлемент = ЭтаФорма.Элементы.вставить("МаржаПроцент", Тип("ПолеФормы"), Элементы.Товары); 
нЭлемент.Вид = ВидПоляФормы.ПолеВвода; 
нЭлемент.ПутьКДанным = "Объект.Товары.МаржаПроцент"; 	
нЭлемент.ТолькоПросмотр=Истина;	
нЭлемент.ОтображатьВПодвале=Истина;
нЭлемент.ТекстПодвала=Формат((Объект.Товары.Итог("Сумма")-Объект.Товары.Итог("СуммаЗакупки"))/Объект.Товары.Итог("Сумма"),"ЧЦ=4; ЧДЦ=2");
нЭлемент.ГоризонтальноеПоложениеВПодвале=ГоризонтальноеПоложениеЭлемента.Право;



вторник, 14 июля 2020 г.

1С Удалить до числа включительно в строке

Задача:

В строке "Лот ..12345... Новая облицовка" удалить число и все что перед ним.

Задача слегка расплывчата, т.к. первая реализация столкнулась с тем, что первое число может быть и не в начале и не подпадать под определение. Поэтому решил сделать так:
  1. Функция имеет два параметра: строка и длина предполагаемого числа
  2. Функция ищет первое вхождение числа в обрезанной строке
  3. Если число найдено, то ищет первое вхождение буквы после первого числа в обрезанной строке
  4. Обрезает строку
  5. Проверяет наличие в начале строки не букв (пробелы, точки, запятые и т.д.)
  6. Возвращает новую строку