Как узнать когда был создан документ в 1С v8 без журнала регистрации

Иногда необходимо знать дату создания документа, ошибочно полагать, что поле «Дата» содержит именно дату создания, тут можно проанализировать идентификатор объекта:

Функция ДатаСозданияСсылки(Ссылка)
// любая ссылка хранит в себе уникальный идентификатор
ГУИД = Ссылка.УникальныйИдентификатор();
// идентфикатор хранит в себе дату и время создания
Строка16 = Сред(ГУИД, 16, 3) + Сред(ГУИД, 10, 4) + Сред(ГУИД, 1, 8);
Разрядность = СтрДлина(Строка16);
ЧислоСек = 0;
// время создания хранится в секундах 
// от 15 октября 1582 года 
// в шестнадцатиричной системе
Для Позиция = 1 По Разрядность Цикл
     ЧислоСек = ЧислоСек +
          Найти(«123456789abcdef»,Сред(Строка16Позиция1))*
          Pow(16Разрядность — Позиция);

КонецЦикла;
ЧислоСек = ЧислоСек / 10000000;
// вычисляем смещение от начальной точки
датаТ = Дата(1582, 10, 15, 04, 00, 00) + ЧислоСек;
// составляем только дату 
датаТ = Дата( Год(датаТ), Месяц(датаТ), День(датаТ), 00, 00, 00);
Возврат датаТ;

КонецФункции

Чтение DBF файлов и Memo полей при помощи 1С

Для чтения файлов DBF из 1С можно использовать объект XBase, однако до того момента как понадобится чтение полей Memo. В этом случае XBase использовать бесполезно, требуется другой подход. Для примера возьмем файл «C:\Base\MyFile.dbf» Решение проблемы на клиенте приведено ниже:

ИмяФайла = «MyFile»;
ИмяПапки = «C:\Base»;
// создаем объект доступа к БД
oConn = Новый COMОбъект(«ADODB.Connection»);
// задаем строку-соедние с указанием расположения БД
oConn.ConnectionString = «provider=vfpoledb.1;Data Source =»
+ИмяПапки + «;Mode=ReadWrite»;
// SQL запрос делается прямо по имени файла
Query = «Select * From «+ИмяФайла+«.dbf»;
// создаем объект доступа к набору записей
record = New COMОбъект(«ADODB.Recordset»);
Попытка
     record.Open(Query, oConn); // выполняем запрос к таблице
Исключение
     Сообщить(«Ошибка при обращении к таблице: «+ОписаниеОшибки());
     Возврат Неопределено;
КонецПопытки;
// выводим типы колонок таблицы
// тип это число, например:

// 
11 —Булево, 129 — Строка, 131 — Число, 201 — Мемо
Для каждого Field из record.Fields Цикл
     Сообщить(«Поле «Field.Name + » имеет тип « + Строка(Field.Type));
КонецЦикла;
// выводим строки выборки
Пока Не record.EOF() Цикл
     Сообщить(«Контрагент « + Строка(record.Fields(
«Customer»).Value) + » имеет ИНН «Строка(record.Fields(«Inn«).Value) );
     record.MoveNext();
КонецЦикла;

Для работы этого кода необходимо установить соответствующий драйвер Fox Pro и можно скачать программу для просмотра файлов DBF. Без установки драйвера никакими возможностями 1C мемо поля не прочитать.

Скачать драйвер: Fox pro
Скачать программу просмотра DBF файлов: Редактор DBF

Диалог сохранения файла в интерфейсе Такси

Фирма 1С решила отказаться от модальных форм в интерфейсах web и Такси, это значит, что попытка просто сохранить файл через диалог потребует больше усилий чем раньше. В примере ниже надо НА КЛИЕНТЕ просто создать текстовый файл и сохранить его куда укажет пользователь:

&НаКлиенте
Процедура НажатиеКнопкиСохранитьФайл() 

// создаем на клиенте текстовый файл
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(«txt»);
// создаем документ который надо записать
ТД = новый ТекстовыйДокумент;
ТД.ДобавитьСтроку(«Некоторый текст»);
ТД.Записать(ИмяВременногоФайла);
// помещаем файл в хранилище
Адрес
= ПоместитьВоВременноеХранилище(новый ДвоичныеДанные(ИмяВременногоФайла ));
// описание события после выбора файла пользователем
ОписаниеОповещения = Новый ОписаниеОповещения(«ОбработкаСохраненияФайлов», ЭтаФорма);
// место хранения файла в 1С
Файл = Новый ОписаниеПередаваемогоФайла(,Адрес);
// создаем перечень файлов
ПолучаемыеФайлы = Новый Массив;
ПолучаемыеФайлы.Добавить(Файл);
// создаем диалог сохранения файла
ДиалогОткрытияФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
ДиалогОткрытияФайла.МножественныйВыбор = Ложь;
// выводим диалог на экран, после выбора файла 
// вызовется процедура ОбработкаСохраненияФайлов
НачатьПолучениеФайлов(ОписаниеОповещения,ПолучаемыеФайлы, ДиалогОткрытияФайла, Истина);

КонецПроцедуры

&НаКлиенте
Процедура ОбработкаСохраненияФайлов(ПомещенныеФайлы, ДополнительныеПараметры) Экспорт
// какие-то действия
// …

КонецПроцедуры

Работа с Access из 1Cv8.3

В некоторых случаях удобно выгрузить из 1С какие либо данные в Access для дальнейшей работы. Работа осуществляется через ADODB.Connection, причем возможно использование языка запросов Transact SQL. Пример ниже:

adOpenForwardOnly = 0; // код состояния
adLockPessimistic = 2; // режим блокировок
adOpenStatic = 3; // режим открытия соединения
adStateOpen = 1; // код открытия соединения
ИмяФайла = «C:\MyFile.accdb»; // имя файла 

// строка подключения к файлу БД Access
conn_str = «Provider=Microsoft.ACE.OLEDB.12.0; Data Source=» + ИмяФайла + «;»»»;
cn = Новый COMОбъект(«ADODB.Connection»); // объект для соединения
cn.Open(conn_str); // соединяемся с БД Access
Если (cn.State <> adStateOpen ) тогда // проверка открытия соединения
     Сообщить(«Не удалось открыть ADODB.Connection»);
     Возврат;
КонецЕсли;
rs = Новый COMОбъект(«ADODB.Recordset»); // объект для обхода строк
rs_select = Новый COMОбъект(«ADODB.Recordset»);
strSQL = «customers»; // таблица Access, с которой имеем дело
rs.Open(strSQL, cn, adOpenStatic, adLockPessimistic); // открываем таблицу 
strSQL = «select name from customers where id=100»; // sql запрос к таблице
rs_select.Open(strSQL, cn, adOpenStatic, adLockPessimistic); // делаем выборку
Если rs_select.EOF = Ложь тогда // проверка наличия записей в выборке
     Сообщить(«Нет строки с таким идентификатором: «);
     rs_select.Close(); // закрываем выборку
     rs_select = Неопределено; // уничтожаем лишние данные

КонецЕсли;
Попытка
rs.AddNew(); // добавить новую строку в табл  customers
rs.Fields(0) .Value = 250; // имя нового контрагента
rs.Fields(1) .Value = «Новый контрагент»; // имя нового контрагента
rs.Update(); // записать новую строку в таблицу
// вывод на печать строки по колонкам
Для Каждого col Из rs.Fields Цикл
     Сообщить(«Колонка « + col.Name + » = « + Строка(col.Value));
КонецЦикла;
Исключение
// исключение может быть вызвано наличием сроки с таким же 
// идентификатором
Если  rs.ActiveConnection.Errors(0).Number = -2147217887  тогда
     Сообщить(«Такая запись уже есть в таблице»);
Иначе
     Сообщить(ОписаниеОшибки());// другая ошибка
КонецЕсли;
КонецПопытки;
// закрываем выборку, если она открыта
Если (rs.state = adStateOpen) тогда
      rs.Close();
КонецЕсли;
// закрываем соединение с БД, если оно открыто
Если cn.state = adStateOpen тогда
      cn.Close();
КонецЕсли;
// уничтожаем лишние данные
rs = Неопределено;
cn = Неопределено;

Вывод содержимого файлов на HTML форму в 1Сv8

Нынешние тенденции разработок решений на базе платформы 1С стремительно переходят в область  интернет-технологий, часто требуется обеспечить вывод на управляемую форму содержимое того или иного файла. В В большинстве браузеров по умолчанию есть возможность просмотра PDF файлов и на большинстве компьютеров стоит MS Office. Как правило, используются файлы следующих форматов: gif, tif, jpeg, jpg, png, bmp, xls, xlsx, xml, pdf, doc, docx, rtf. Их можно разделить на картинки и документы Office. Напомню, что цель всех функций — преобразовать файл в формат PDF и отобразить его на форме HTML. Итак, пусть на форме есть элемент управления с именем fHtml и есть файл с именем C:\MyFile.pdf, код HTML для вывода файла на страницу будет таким:

<html>
<body>
<object data=»file:///C:/MyFile.pdf» type=»application/pdf» width=»90%» height=»90%»>
</object>
</body>
</html>

Далее нам надо из файла любого формата получить файл pdf.

Текстовый файл, формат txt

_word = New COMОбъект («Word.Application»);// запускаем word
_word.DisplayAlerts = 0// запрещаем сообщения, они блокируют word
doc = _word.Documents.Add(); // добавляем документ
doc.Activate(); // активируем текущий документ
ЧТ = новый ЧтениеТекста(ИмяФайла_txt); // чтение нашего txt файла
_word.Selection.TypeText(ЧТ.Прочитать()); // вставляем текст в ткеущий документ
wdExportFormatPDF = 17; // код экспорт а в PDF
ИмяФайлаPDF = ПолучитьИмяВременногоФайла(«pdf»);
// преобразуем текстовый формат в PDF при сохранении документа
doc.ExportAsFixedFormat (ИмяФайлаPDF, wdExportFormatPDF, 0, false, 0, 1, 1, 0, True, True, 0, True, True, False);
doc.Close( False ); // закрываем документ
_word.Quit(); // закрывам word

Документ MS Word, форматы doc, docx, rtf

doc = _word.Documents.Open(ИмяФайла); // просто открываем файл
wdExportFormatPDF
= 17; // код экспорт а в PDF

ИмяФайлаPDF = ПолучитьИмяВременногоФайла(«pdf»);
// преобразуем в формат PDF при сохранении документа
doc.ExportAsFixedFormat (ИмяФайлаPDF, wdExportFormatPDF, 0, false, 0, 1, 1, 0, True, True, 0, True, True, False);

Картинки, форматы gif, tif, jpeg, jpg, png, bmp

doc = _word.Documents.Add();// добавляем документ
s = doc.InlineShapes.AddPicture(ИмяФайла, False, True); // добавляем картинку в документ
wdExportFormatPDF = 17; // код экспорта в PDF
ИмяФайлаPDF = ПолучитьИмяВременногоФайла(«pdf»);
// преобразуем в формат PDF при сохранении документа

doc.ExportAsFixedFormat (ИмяФайлаPDF , wdExportFormatPDF, 0, false, 0, 1, 1, 0, True, True, 0, True, True, False);

Документ Excel, форматы xls, xlsx, xml

_Excel = New COMОбъект («Excel.Application»); // запускаем Excel
doc = _Excel.Workbooks.Open(ИмяФайла); // открываем файл
ИмяФайлаPDF = ПолучитьИмяВременногоФайла(«pdf»);
doc.ExportAsFixedFormat (0, ИмяФайлаPDF , 0);// сохраняем файл в формате PDF
doc.Close( False );  // закрываем документ
_Excel.Quit(); // закрываем Excel

Управление Excel из 1С без знания команд Excel

Иногда требуется автоматически из 1С обработать некоторый файл Excel используя возможности самого Excel. Кажется, что надо кидаться в Гугл или Яндекс искать описание тех или иных функций Excel, но оказывается, что можно заставить Excel самому писать необходимые программы. Представим, что надо создать книгу Excel, заполнить первый лист номерами от 1 до 20 и сохранить файл. Что делаем:

Записываем макрос Excel

  1. Запускаем Excel, записываем макрос

e_1

2. В появившемся окне нажимаем «Ок» и запись макроса (программы) начата

e_2

3. Заполняем лист нужными нам данными — позиционируем на первую ячейку, в следующую водим формулу (предыдущая ячейка + 1)

e_3

4. Результат вычисления

e_4

5. Хватая за правый нижний угол, автоматически заполняем нижестоящие 18 ячеек

e_5

6. Все, что надо выполнено, останавливаем запись макроса

e_6

7. Для просмотра сгенерированного Excel кода заходим в макросы

 e_7

8. И открываем на редактирование созданный нами макрос

e_11

9.  Программный код макроса

e_8

Его можно напрямую скопировать в 1С, только понадобится немного изменить для совместимости, ниже приведен аналог кода в 1С, а в комментариях приведен код из Excel:

// Range(«A1»).Select
Лист.Range(«A1»).Select();
// ActiveCell.FormulaR1C1 = «1»
Эксель.ActiveCell.FormulaR1C1 = «1»;
//Range(«A2»).Select
Лист.Range(«A2»).Select();
//ActiveCell.FormulaR1C1 = «=1+R[-1]C»
Эксель.ActiveCell.FormulaR1C1 = «=1+R[-1]C»;
//Range(«A2»).Select
Лист.Range(«A2»).Select();
//Selection.AutoFill Destination:=Range(«A2:A20»), Type:=xlFillDefault
Эксель.Selection.AutoFill(Лист.Range(«A2:A20»), 0);
//Range(«A2:A20»).Select
Лист.Range(«A2:A20»).Select();

В качестве примечания следует добавить, что перед обращением к Excel следует создать соответствующий СОМ объект, книгу и лист:

Попытка
     Эксель = новый COMОбъект(«Excel.Application»);
Исключение
     Сообщить(ОписаниеОшибки());
Возврат;
КонецПопытки;
// добавить книгу
Книга = Эксель.WorkBooks.Add();
// добавить лист на книгу
Лист = Книга.WorkSheets.Add();

Получение и открытие формы элемента справочника или документа 1с 8.3

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

ПараметрыФормы = Новый Структура(«Ключ», СсылкаНаНоменклатуру);
Форма = ПолучитьФорму(«Справочник.Номенклатура.ФормаОбъекта», ПараметрыФормы);
Форма.Открыть();

Работа с Excel в 1С 8.3

Excel — это сторонняя программа, ее можно запускать из 1С в невидимом и видимом режиме и пользоваться всеми возможностями. Excel должен быть установлен на компьютере. Для примера приведено чтение таблицы в из файла Excel на клиенте, передача на сервер и создание на сервере таблицы значений с данными из файла. Такую задачку любят давать на собеседованиях на должность программиста 1С.

&НаКлиенте
Процедура НажатиеКноки()
// обращаемся к Excel
Попытка

     Эксель = новый COMОбъект(«Excel.Application»);
Исключение

Сообщить(ОписаниеОшибки());
     Возврат;
КонецПопытки;
ПутьКФайлу = «C:\Некоторый файл.xlsx»;
// открываем ексель файл
Книга = Эксель.WorkBooks.Open(ПутьКФайлу);
// переходим на первую страницу
Лист = Книга.WorkSheets(1);
// для количества колонок и строк в экселе есть специальные ячейки
ВсегоКолонок = Лист.Cells(1,1).SpecialCells(11).Column;
ВсегоСтрок = Лист.Cells(1,1).SpecialCells(11).Row;

// сохраняем файл в массив строк, где строка это так же массив (на клиенте
// нельзя создавать таблицу значений
НаборСтрокФайла = новый массив;
для Строка = 1 по ВсегоСтрок цикл
     МассивСтрока = новый массив;
     для Колонка = 1 по ВсегоКолонок цикл
// получаем значение из ячейки excel

          Значение = СокрЛП(Лист.Cells(Строка,Колонка).Value); 
          МассивСтрока.Добавить(Значение);
         КонецЦикла;
     НаборСтрокФайла.Добавить(МассивСтрока);
КонецЦикла;
// запишем файл с другим именем

Эксель.SaveAs(«C:\ФайлСДругимИменем.xlsx»);
// закрываем ексель, иначе он останется висеть в памяти
Эксель.Application.Quit();

КонецПроцедуры

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

КонецПроцедуры

Далее приводятся наиболее частые ситуации, возникающие на

Поиск и замена текста в ячейках файла Excel

//Выбираем область поиска — весь лист
ОбластьПоиска = Лист.Cells;
// Начинаем поиск с левого верхнего угла
НачалоЛиста =Лист.Cells(1, 1);
// Ищем ячейку с текстом «привет»
ОбластьПараметр = ОбластьПоиска.Find(«привет», НачалоЛиста);
// Если нашли вывсодим найденую строку
Если ОбластьПараметр <> неопределено Тогда
     Сообщить(ОбластьПараметр.Value);
КонецЕсли;

Работа со строками в Excel

//Выбираем первую строку
НачальнаяСтрока = Лист.Cells(1, 1);
// Выделяем всю строку
НачальнаяСтрока .EntireRow.Select();
// Копируем выделенное
НачальнаяСтрока .EntireRow.Copy();
// Вставляем (новая строка будет вставлена тут же)
НачальнаяСтрока .EntireRow.Insert();
// Удалим строку
НачальнаяСтрока .EntireRow.Delete();

Прочие полезные методы Excel

Эксель.Visible = Видимость; 0 — Excel не виден, 1 — виден.
Книга = Эксель.WorkBooks.Add(); Создание новой книги (файла) Excel.
Книга.SaveAs(ИмяФайла); Сохранение книги Excel.
Лист = Книга.WorkSheets.Add(); Добавление нового листа в книгу.
Книга = Эксель.WorkBooks.Open(ИмяФайла); Открытие существующей книги (файла) Excel.
Лист = Книга.WorkSheets(НомерЛиста); Установка листа в качестве рабочего с номером НомерЛиста.
Лист.Name = ИмяЛиста; Задание рабочему листу имени ИмяЛиста
Лист.PageSetup.Zoom = Масштаб; Задание параметра страницы «Масштаб» (от 10 до 400).
Лист.PageSetup.Orientation = Ориентация; Ориентация: 1 — книжная, 2 — альбомная.
Лист.PageSetup.LeftMargin = Эксель.CentimetersToPoints(Сантиметры); Задание левой границы (в сантиметрах).
Лист.PageSetup.TopMargin = Эксель.CentimetersToPoints(Сантиметры); Задание верхней границы (в сантиметрах).
Лист.PageSetup.RightMargin = Эксель.CentimetersToPoints(Сантиметры); Задание правой границы (в сантиметрах).
Лист.PageSetup.BottomMargin = Эксель.CentimetersToPoints(Сантиметры); Задание нижней границы (в сантиметрах).
Лист.Columns(НомерКолонки).ColumnWidth = Ширина; Задание ширины колонке.
Лист.Cells(НомерСтроки, НомерКолонки).Value = Значение; Ввод данных в ячейку.
Лист.Cells(НомерСтроки,НомерКолонки).Font.Name = ИмяШрифта; Установка шрифта в ячейке.
Лист.Cells(НомерСтроки,НомерКолонки).Font.Size = РазмерШрифта; Установка размера шрифта в ячейке.
Лист.Cells(НомерСтроки,НомерКолонки).Font.Bold = Жирный; 1 — жирный шрифт, 0 — нормальный.
Лист.Cells(НомерСтроки,НомерКолонки).Font.Italic = Курсив; 1 — наклонный шрифт, 0 — нормальный.
Лист.Cells(НомерСтроки,НомерКолонки).Font.Underline = Подчеркнутый; 2 — подчеркнутый, 1 — нет.
Лист.Cells(НомерСтроки, НомерКолонки).NumberFormat = Формат; Установка формата данных ячейки.
Лист.Cells(НомерСтроки,НомерКолонки).Borders.Linestyle = ТипЛинии; Установка рамок ячейки. 1 — тонкая сплошная.
Лист.Protect(); Установка защиты на лист
Лист.UnProtect(); Снятие защиты с листа
Лист.Cells(Строка, Столбец).Locked=0; Ячейка будет доступной (и после установки защиты на лист)
Эксель.DisplayAlerts = False; Убрать все сообщение при открытии файла. Сообщения останавливают выполнения кода до реакции пользователя

1С 8.3 иерархический фильтр в управляемой форме выбора

Отборы в списках управляемых форм имеют особенность вязанную с иерархией —  если в качестве отбираемого элемента стоит некоторый подчиненный (не корневой) элемент, например, эл 2.1, то в форме выбора не отобразится ничего, т.к. вышестоящие элементы (Эл 2) в отборе не участвуют. Пример — разрешить выбирать статьи затрат только в иерархии эл 2.1:

Корень
* Эл 1
* * Эл 1.1
* * Эл 1.2
* Эл 2
* * Эл 2.1
* * * Эл 2.1.1
* * * Эл 2.1.2
* * Эл 2.2

Неправильно будет просто указать все подчиненные элементы Эл 2.1 в отборе для формы, пример:

&НаКлиенте
Процедура НажаниеКнопкиВыбора()
     Отбор = новый Структура(«Ссылка»,   ПолучитьСписокПодчиненныхСтатейЗатрат(ПредопределенноеЗначение(«Справочник.СтатьиЗатрат.Эл_2_1»)));
     ОткрытьФорму(«Справочник.СтатьиЗатрат.ФормаВыбора», Новый     Структура(«Отбор», Отбор), Элемент);
КонецФункции

&НаСервере
Функция ПолучитьСписокПодчиненныхСтатейЗатрат(СЗ)
Список = новый СписокЗначений;
     Запрос = Новый Запрос;
     Запрос.Текст = «ВЫБРАТЬ
          | СтатьиЗатрат.Ссылка
          |ИЗ
          | Справочник.СтатьиЗатрат КАК СтатьиЗатрат
          |ГДЕ
          | НЕ СтатьиЗатрат.ПометкаУдаления
          | И СтатьиЗатрат.Ссылка В ИЕРАРХИИ(&Ссылка)»;
     Запрос.УстановитьПараметр(«Ссылка»,СЗ);
Список.ЗагрузитьЗначения(Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку(«Ссылка»));
Возврат Список;
КонецФункции

Правильно будет в функции ПолучитьСписокПодчиненныхСтатейЗатрат дополнительно в выбранный список подчиненных элементов добавить родительские:

Родитель = СЗ.Родитель;
пока ЗначениеЗаполнено(Родитель) цикл
     Список.Добавить(Родитель);
     Родитель = Родитель.Родитель;
КонецЦикла;

Таким образом в окне выбора появится кусок дерева с теми элементами, которые нужны.

 

Создание письма в Outlook при помощи 1C

Платформа 1С 8 позволяет создавать сообщения в электронной почте и отображать его на экране, непосредственно отправка осуществляется самим пользователем после просмотра содержимого письма.
Что нужно:
Установленный на компьютере Outlook.

Сформируем электронное письмо двум адресатам и прикрепим к письму картинку:

Outlook = Новый COMОбъект(«Outlook.Application»);
// создаем письмо
ПисьмоOutlook = Outlook.CreateItem(0);
// добавляем адресатов
ПисьмоOutlook.Recipients.Add(«my.friend@mail.ru»);
ПисьмоOutlook.Recipients.Add(«my.girlFriend@mail.ru»);
// тема письма
ПисьмоOutlook.Subject = «Это тема письма»;
// текст письма
ПисьмоOutlook.Body = «Это текст письма»;
// прикрепляем картинку
ПисьмоOutlook.Attachments.Add(«c:\myPicture.jpg»);
// показываем письмо на экране, пользователь отправит его сам
Попытка
     ПисьмоOutlook.Display();
Исключение
     Сообщить(«Не удалось открыть письмо.»);
КонецПопытки;

На экране появится окно Outlook:

Outlook