<?php // Полная загрузка сервисных книжек, создан 2025-01-05 12:44:55
global $wpdb2;
global $failure;
global $file_hist;
///// echo '<H2><b>Старт загрузки</b></H2><br>';
$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
///// echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}
$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
///// echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}
/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
///// echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
///// echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist); ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7]; ////получаем размер файла
$m_mtime_file=$masiv_data_file[9]; ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file
///// echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
///// echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
///// echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);
if ($results)
{ foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));
////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
///// echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
///// echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}
////загружаем данные
$table='vin_history'; // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация // (путь от корня web-сервера)
$delim=';'; // Разделитель полей в CSV файле
$enclosed='"'; // Кавычки для содержимого полей
$escaped='\
Хорошо!
Я по аналогии давно хочу сделать «журналирование изменений», но меня терзает один вопрос. Что будет происходить, если один пользователь записывает данные в базу Sqlite и одновременно второй пользователь также начнет писать свои данные.
Не попадал на такое? Как бы красиво и без потерь разрулить подобные ситуации?
Извиняюсь, толком не посмотрел твой код.
Оказывается, у тебя идет бесконечный цикл попыток записи. Да, это решение проблемы, но какое-то тяжелое (для процессора).
(1) попадал, поэтому и все выполнения запросов обёрнуты в цикл + попытка
А так ошибка будет «database is locked»
И т.к таких ошибок по ЖР было за день не так много (штук 10 всего) при 50 пользователях, то такое решение оправдано..
Щас вроде проблем нет.
+ новая версия ВК и режим «PRAGMA journal_mode=WAL» ускорили процессс записи в базу.
(2) не.. норм.
Я ж проверил на живых пользователях методу сперва … и без цикла с попыткой
🙂
(3)
Этот режим вряд ли ускорил запись в базу, просто теперь читатели и писатели не мешают друг другу.
Вот кусок с офф. сайта:
Хотя это очень маленькая погрешность и ей можно пренебречь.
(5) да один хрен.. тут в табличке 2 поля всего.. быстро обновляет табличку.
я забил на блокировку — ошибок блокировок мало цикл вполне оправдан.
Вот если бы система еще сама стучала по голове блокировальщику…
Добавлю, аналогично можно проверить, кто блокировал объект и при програмной записи..
сделав тот же самый запрос , что и в ОбработкаБлокировкиОбъекта
Можно подумать, но это так, — фантазии, чтобы тому, кто заблокировал объект, выскакивало предупреждение о том, что надо бы объект освободить, так как уже очередь…;-)
(10) когда известен автор проблемы, можно и по телефону ему позвонить, или докладную шефу на стол…
(11) да и, если терминалка (или радмин какой-нить), самому закрыть объект..
или сессию срубить..
класс… твои юзеры наверное катаются в масле, окруженные такой заботой программиста 🙂
(13) по крайней мере, перестали мне звонить по этой ерунде — рулят сами
(13),(14) Я еще хотел сказать, что он просто о себе заботится, чтоб не дергали по ерунде, а не о пользователях, но опередил в 14-м посте;-)
А ежели просто ограничить время, которое юзер может держать объект открытым?
Одним формексом можно обойтись
(16) Как-бы это через чур жестко;-) Еще таймер обратного отсчета большими красными буквами: «До взрыва осталось: 00:00:03»;-) Да и с программной блокировкой тогда как быть?
(16)
«Одним формексом можно обойтись»
— Эту задачу можно решить, вообще, без ВК. 😉
(0)
Хорошее решение проблемы.
Но, думаю, еще имеет смысл устранять не следствие, а причину. Т.е. запретить пользователю открывать объекты БД в режиме редактирования по двойному щелчку в списках, журналах. Т.е. открывать в режиме просмотра объекта. И случайных проблем с «задним числом» будет меньше… Ведь чаще всего пользователи хотят только посмотреть объект БД, а не изменить его одновременно. Первичный документ, чаще всего, существует в одном экземпляре, на одном рабочем месте… Или не так? 😉
(18)
Что-то сомневаюсь, если только прописывать код в каждый объект, а это не комильфо
(19)
Прописывать надо. Но не в каждый объект. Т.к. до появления «формекса», уже давно, прописано во все объекты вызов глобальной функции решающей схожие алгоритмы в схожих местах. Типа:
Процедура ПриОткрытии() хПриОткрытии(Контекст); КонецПроцедуры
Процедура ПриЗакрытии() хПриЗакрытии(Контекст); КонецПроцедуры
Нас самом деле, еще проще — используется общая функция, типа:
хУсё(КодДействия,Контекст).
Ну, а в конкретном случае данной темы, выполняются по смыслу следующий код, но расширенный на всё типы объектов 1С-а. Еще есть обработка показа списка всех заблокированных объектов.
Показать
(20)
Процедура ПриОткрытии() хПриОткрытии(Контекст); КонецПроцедуры
Вот и я про то же. У меня тоже было прописано глПриОткрытии() глПризаписи.
Но ты, по ходу, лет 10 на одной базе сидишь, а у меня их сейчас чуть не по паре новых в день (не считая еще 8х). А прописать формекс у меня сейчас всего две строчки в ГМ, которые не фиг восстановить после обновления. Все остальное работает в ДопГМ, который правь на лету — не хочу
+(21)Вот считай:
Перем Сервис Экспорт; // раз строчка
Процедура ПриНачалеРаботыСистемы()
//………………………………
ОткрытьФорму(«Отчет»,Контекст,КаталогИБ()+»СтартСистемы.ert»); // два строчка
Коне
(21)(22)
Аркадий.
Я же не против «формекса». Естественно, когда много разных конфигураций. Требуется делать обновления. Хочется не заниматься тупой работой. Надо использовать средства, типа, «формекса». А у меня, действительно 10 лет, всего, две конфигурации. Одна в бухгалтерии, в которую я не лезу. И одна, полностью самописная, для основной сферы деятельности конторы. Но эта конфигурация одна на все задачи конторы. От автосервиса до проходной. И база одна на всех. Мне легче, чем тебе, но ооо-чень скучно… 🙁
Ну да.. можно было бы обойтись одним формексом:
ид-объекта получать через ЗначениеВСтрокуУнутрь, а блокировки писать в файло (коть в текстовик, хоть в дбф) и поиск там же..
Вот только без ВК совсем не отловить интерактивное возникновение блокировки объекта. В лучшем случае, во всех формах списка /журналах документа прописать рукописное открытие формы через ОткрытьФорму() + смотреть, что возвращает метод.
ежели нуль — искать в файле блокировок юзверя, открывшего объект.
Но это как-то не очень.
(24)
«Но это как-то не очень.»(с)
Да, «без ВК совсем не отловить интерактивное возникновение блокировки»(с). Надо либо «прописывать»(с) в списках/журналах, либо делать кнопку в списках/журналах, либо делать отдельную обработку с кнопкой в панели инструментов. Но пользователю не важно как это сделано если блокировки, такого рода возникают, один раз в неделю. Т.е., скажу еще раз, надо устранять причину. Открывать, по умолчанию, объекты в режиме «только просмотр». А если пользователям необходимо работать одновременно с одним объектом, то в схеме базы данных этот объект уже не один объект. Простой пример. Шапку документа забивает «бухгалтер», а строки документа «кладовщик». Сделать форму документа с закладками — это самое примитивное решение задачи. На самом деле (в нашем примере) шапка документа и строки — это разные объекты. Это в теории… 😉 И не в 1С… 🙁
P.S. Повторю еще раз. Вы предложили очень хорошее решение проблемы. И использование этих ВК оправдано на 100%. Т.к., думаю, эти ВК используются в вашей системе не только для решения данной задачи. А, тогда, почему бы их не использовать и в этой задаче… 😉
(25) Да, так и есть — вк не только для этой задачи используются.
По-поводу правки, документ может иметь несколько состояний (распечатан/собран/и т.д и т.п) + разные схемы отгрузки.. Все эти доп флаги-реквизиты документа проставляются как правило, обработками. Но документ может быть открыт аналитиком в это время, для правки самого документа. Вот и возникают коллизии.
Не часто, но есть.
Аж три ВК! Для такой рутинной задачи
(27) см. (24)… достаточно одной.. — формекс.
(28) «можно было бы обойтись одним формексом»
Так ведь не обошелся же 😀
(29) мне важна скорость работы и максимальное удобство, тем более, что 1cpp и 1sqlite используются в других местах кода, зачем лишать себя этого — не ясно.
А так, можно и вообще всё без ВК сделать, только оно надо ?
(26)
«доп флаги-реквизиты документа проставляются … обработками»
«документ может быть открыт … в это время … для правки самого документа»
Об этом и говорю. Это и означает, что «доп флаги-реквизиты» не должны являются реквизитами документа. Т.е. наблюдается ошибка в проектировании схемы БД.
(31)Намекаешь, что состояния документов нужно хранить в подчиненном документе ? Или еще где ?
Изменяются не только флаги, но и ключевые реквизиты типа Контрагент/договор/Фирма..
Долго объяснять почему, связано с разными схемами учета.
(32)
Намекаю на, немного, другое. 😉
Проблемы блокировки документов, последовательности, проведение задним числом, закрытие периодов, само понятие «провести», свертка БД, регистры и т.д. возникает при попытки «автоматизировать» предметную область документами и бух.проводками. Но эта предметная область называется — бухгалтерский учет (и все его разновидности). Судя по Вашим текстам из (26)(32) сообщений, делается попытка автоматизировать не бух.учет, а реальную жизнь конторы. И использовать, для этого, структуры схемы БД типа документ — не совсем логично. Думаю, документ должен появляться на конечном этапе всей «хоз.операции» исключительно для взаимодействия с внешним контрагентом, в виде распечатки, а не в БД как единое целое. Но, это, опять не про 1С… 🙁
(33) почитал ваши посты в других обсуждениях, примерно понял, об чем речь..
К сожалению, в данный момент переделывать всё и уходить от логики, заложенной еще самой 1с не представляется возможным..
Да и на снеговик пытаются упорно толкнуть.
Скачал, поставил +. Внедрил — база стала неустойчивой, подвержена вылетам и медленной.
(35) ВК каких версий поставил ?
И на счет медленной.. есть сомнения.
У вас терминал, или чисто файловая ?
+36 скажем так, терминалка, пользователей от 40 до 70 в одной базе, проблем с падением производительности и нагрузкой на проц не наблюдается.
Ну и вылетов, тем более нет.
+37 на счет, чисто файловой не скажу, не тестил под нагрузкой, ибо таких баз не имею.
Да и для «чисто файловой» (вне терминала) базы директива sqlite
бесполезна
(36) Релиз 7.70.266. Прога основательно доработана. Есть центральная и 2 периферийки. Периферийки на терминалах. Вылеты случались на разных периферийных базах. Причем по времени обновил вечером, утром следующего дня начало вылетать. День непоняток, на сл.день глушу в конфе текст доработки и два дня тьфу-тьфу. В принципе нет и нет, не парюсь по этому поводу.
+(39)
Я бы даже добавил, что
можно делать ТОЛЬКО если все юзеры этой базы сидят в ней на одном терминальном сервере. Если
(40) заремь 2 процы ОбработкаБлокировкиОбъекта и ПриНачалеБлокировки, проблема осталась ?
попробуй заремить запросSQLLite.ВыполнитьЗапрос(«PRAGMA journal_mode=WAL»);
если юзвери работают не на 1 сервере терминалов.
(41) у меня так и есть…
+42 .. По-поводу частых блокировок Sqllite базы во время записи, их и не так много за целый день.
Смотрел за 2 дня, тупо выкинув цикл с попыткой в ЖР.. При 50 пользователях, таких записей было ~15 за два дня.
Так что.. загон в цикл с поп
ОбработкаБлокировкиОбъекта не цепляет константы хотя ПриНачалеБлокировкиОбъекта отрабатывает. Это нормально ?
(44) нормально, я не ставил целью обработку констант, если нужно — дописать недолго.
плюс однозначный.
модифицировал малость:
Если МонопольныйРежим()=1 Тогда
запросSQLLite.ВыполнитьЗапрос(«delete from БазаБлокировок»);
Иначе
запросSQLLite.ВыполнитьЗапрос(«delete from БазаБлокировок Where Пользователь = ‘»+Пользователь.Наименование+»‘»);
КонецЕсли;
(45) в чем нормальность, просветите.
«Нормальность»
в том, что лень было константы анализировать еще 🙂
(46) я бы не удалял сведения о пользователе — лишнее это.
Там всегда при начале блокировки — INSERT or REPLACE — всегда эта запись «перезапишется», если была.
(47) нет, «ненормальность» в том, что при попытке вторым юзером зайти в константу процедура ОбработкаБлокировкиОбъекта не отрабатывает вообще и 1С сразу выдаёт ошибку.
Может это только у меня так ?
(49)Я не помню уже, ловит или нет, надо у АЛьФа спросить.
+50, для красоты, можно еще так сделать:
//Сообщить(«» + Объект + » открыт пользователем » + ИмяВредителя,»!»);
ДопТекст =»» + Объект + » открыт пользователем » + ИмяВредителя;
(51) Это я уже сделал. Таки супер! 😀
Вещь классная, тока, у меня почему-то наоборот не получается. Когда один пользователь заходит в справочник к примеру, то у другого выдает сообщение, а если этот другой откроет элемент справочника, а первый зайдет в него, то пишет только «объект открыт пользователем», а имя пользователя не пишет.
В чем причина и как ликвидировать?
(53) Кто куда и откуда ?
Нипонятна..
Тут оказывается дело только в одном пользователе. Система блокировки не хочет определять его,тока не понятно почему. А с остальными все впорядке.
(55) как «определяешь» пользователя ?
Через ИмяПользователя() или через глПользователь ?
+56 да еще, у этого пользователя, ВК загружены ?
(56) глПользователь.
(57) Мы работаем через терминал, база на серваке, т.ч. да.
(58) И ? наименование есть у него ?
Мот он без рабочего каталога входит, как «не авторизован» ?
Тогда и не пишется ему ничего..
+59 у этого пользователя открой табло и напиши глПользователь.Наименование, что возвращает ?
Пишет — Ошибка в вырожении.
Может стоит использовать ИмяПользователя а не глПользователь?
(61) ну вот и ответ.
(62) проще пользователю создать нормальный рабочий каталог и проверить его наименование/код в справочнике Пользователи.
Да нет. Все нормально. Просто тот, кто изначально заводил пользователей, не прописал им полное наименование. Т.ч. все работает, но все равно спасибо.
Не. Все равно проблемы. Что-то со строкой: ИДОбъекта = глМД.ЗначениеВДлиннуюСтрокуБД(Объект);
Вопрос такой — есть ли ограничения по версиям dll и конфигурации?
(65) есть.
1cpp желательно иметь 3-ей версии
А почему это решение не подходит для 1С SQL? Мне для SQL надо…
(67) потому, что на 1sqlite написано, а так, в SQL сделать намного проще — создал табличку для хранения заблокированных объектов в самом скуле + всё тоже самое + синтаксис запроса подправить.
(68) То есть, как я понимаю, это вопрос целесообразности? Я было подумал, что это несовместимо с SQL.
(69) для скуля это просто можно сделать разными способами, (на софтпоинте есть одно из решений, например)
а вот для дбф никто не удосужился, вот я и выложил.
А так, для скуля всё сделать по-аналогии, только заместо базы в sqlite использовать свою табличку в скуле.
проверяешь есть ли она, если нет , то create table и всё собственно, так же в ПриНачалеБлокировки пишешь в неё того кто заблокировал, потом так же обрабатываешь блокировку.
Короче, тот же код, только синтаксис запроса чуть другой.
+70 добавил для скуля.. пробуйте.
На скуле не показывает пользователя, но мне кажется это у тех, у кого права в системе «овощные» или же библиотека не зарегана. Заходил под админом на двух разных тачках — всё путем.
(72) Это, использовал ПолноеИмяПользователя() или глПользователь.Наименование ?
+(73) ну и 1cpp и formex желательно грузить в ПриНачалеРаботыСистемы ( и регистрить их спецом не надо, они уже сто лет в обед в этом не нуждаются)
ЗЫ: ставмит только последних версий желательно 3 и выше для 1cpp и ..101 и выше для формекса.
Судя по всему ПолноеИмяПользователя(), т.к. у того, кем блокировал, как раз его и не было(причём только у одного). ;)))
Рад, что регать не надо, у меня версии свежие, а то я уж способы регания библиотек в домене наскоблил)))
Спасибо!!! Плюсанул.
Могет кому будет полезно и мое творениеhttp://infostart.ru/public/19782/ .
Отличие от этой разбработки:
1) использует 2 компоненты: 1с++ и formex
2) работает _ТОЛЬКО_ на sql-базах
3) чтобы все заработало, нужно прописать в глобальнике только одну строку.
4) в любой момент можно отключить/включить приблудку.
(76) публикация не активна, а так, моя поделка использует 2 ВК для скуля и 3 для ДБФ, хотя можно и 1-ой обойтись — формексом, но не удобно.
(77) Странно. Когда-то была активной…
(78) посмотрел, всё тоже самое, отличие — все действия в доп глобальнике, минус — не проверяется, был ли он(доп глобальник) уже загружен ранее.
🙂
Методе то сто лет в обед
цуцй
После подключения данной обработки при закрытии базы стала выскакивать ошибка отложенной записи Windows. И база стала тормозить. Что с этим делать? База dbf.
(81)
база по сети ?
в терминале ?
ВК каких версий поставили ?
Использую данную схему некоторое время (база DBF, база блокировок в SQLite-файле), все гуд.
Но тут наткнулся на такой момент — при массовой обработке объектов получаем большой поток блокировок объектов
и соответствующее кол-во запросов вставок в базу блокировок.
И что то как то подтормаживает все это некисло..
Пока обошел так — добавил методы начала и фиксации транзакции при записи в SQLite, перед массированной обработкой объектов начинаю транзакцию, после фиксирую..
Ускорение на порядок, но как то некузяво это..
Может какое более красивое решение есть?
(83) база в терминале или по сети ?
У меня, даже при массовых операций тормозов не замечено.
база в терминале, правда.
боевая база в терминале, для разработки локальная — результат одинаковый
по профайлеру получается, что 80% времени выполнения массовой обработки объектов занимает запись в таблицу заблокированных объектов..
если обернуть процедуру в транзакцию SQLite, то время выполнения сокращается на порядок..
попробовал на всякий случай сначала один раз подготовить запрос, а потом вызывать с параметрами — результат тот же..
причем ведь файлик базы SQLite весит то всего 10-20Кб, а скорость вставки записей очень низкая получается..
наверное я просто не умею ее готовить 🙁
(86) странно, у меня в худшем случае, если кто-то ужо пишет туда.. но по логам в ЖР, когда цикла с попыткой выполнения запроса не было, таких случаев в день при 70 юзверей, не так и много..
А с попыткой в цикле и так влёт влетает.
1sqlite какой версии хоть ?
Покажи, как свою транзакцию ставишь.
1sqlite версия: 1.0.2.3
транзакция ставится выполнением запроса «BEGIN TRANSACTION», фиксируется «COMMIT TRANSACTION»
в остальном код по работе с таблицей блокировок почти один в один взят из твоего примера..
только у меня используется класс-обертка над объектом 1sqlite для выполнения запросов
(89) странно..
Ладно, попобую поиграться на досуге..
Ты кстати, отключение журналирования делал ?
(90) а то..
даже спецом добавил в класс метод, который возвращает текущий режим — думал, может не срабатывает..
пробовал даже режим MEMORY ставить..
может конечно, имеет смысл с Сашей Орефковым посоветоваться, может я что то не так делаю при работе с 1sqlite..
Но, думаю, еще имеет смысл устранять не следствие, а причину. Т.е. запретить пользователю открывать объекты БД в режиме редактирования по двойному щелчку в списках, журналах. Т.е. открывать в режиме просмотра объекта. И случайных проблем с «задним числом» будет меньше… Ведь чаще всего пользователи хотят только посмотреть объект БД, а не изменить его одновременно. Первичный документ, чаще всего, существует в одном экземпляре, на одном рабочем месте… Или не так? 😉
тоже считаю что очень правильное решение, плюс настроенные права пользователей, не открывать что не нужно или не редактировать.
Большое спасибо.
Плюсанул.
Не могу понять — Процедура ОбработкаБлокировкиОбъекта() вызывается уже из объекта конфигурации?
Интересная идея спасибо!
(97) ?
что значит «из объекта» ?
это всего лишь предопределенная проца глобальника из формекс
Прикрутил… при групповом проведении на писание в SQLite тратится 1% времени. А как бы сделать так, что при групповых операциях в таблички не писалось?. Т.е. как узнать в ПриНачалеБлокировкиОбъекта(), что это групповой проведение?
(101) Dolly_EV, если я правильно помню подноготную 7.7, то можно в модуле проведения дока в разделе осн. программы установить переменную ЭтоГрупПров=0, а в самой процедуре проведения в самом конец ЭтоГрупПров=1.
тогда по тексту процедуры проведения можно будет опираться на эту переменную, за исключением самого первого дока данного вида, но, думаю, 1 док — это не показатель.
только проверьте сначала эту фишку