Заметки

Позднее Ctrl + ↑

Карьерные самосвалы

Автомобили

Коллега проворчал, что если продолжать эту логическую цепочку, то конфигуратор будет «Жигулями», а EDT — «Кама1» (это такой электрокар, который много лет пилят где-то в недрах КАМАЗа и всё никак не могут собраться с силами и, наконец, допилить).

Ну а я стараюсь смотреть на вещи с оптимизмом. Думаю, платформа и её IDE — это такие карьерные самосвалы. Никто в здравом уме на них по домашним делам не катает, но на разрезе эти звери незаменимы!

2023-02-20

Про странный Битрикс

Чем дальше лезу в REST-интерфейс Битрикс24, тем сильнее поражаюсь тому, насколько необычно устроено мышление у его разработчиков. Проявляется это по-разному.

Взять, например, интерфейс сделок и товаров по ним. В таблице последних нет поля суммы: мол, нужна тебе сумма по строке — сам и считай. Зато сумма есть на уровне документа. Угадаете, как называется поле?

AMOUNT? DEAL_AMOUNT? DOCUMENT_AMOUNT? AMOUNT_TOTAL?

Не угадали, правильный ответ — OPPORTUNITY.

Чего, блядь?

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

Логично-то логично, но нет, хрен там плавал. Для номенклатуры и правда хранится ID, а вот для ставки НДС — значение ставки, и для единицы измерения — код измерения по ОКЕИ ¯\_(ツ)_/¯

Нормализация базы данных? Чего? Какая еще нормализация? Отстань, мужик, работать мешаешь.

2022-10-15 работа Битрикс

Групповая работа в Google Docs

Последний месяц переписываю типовой обмен данными между FirstBit ERP и Битриксом под клиентскую задачу. Коллеги, занимающиеся тем же на стороне Битрикса, подготовили под это дело массивный маппинг: какое поле на стороне 1С в какое поле Битрикса нужно передавать (и наоборот).

Этот маппинг они выложили в виде таблиц Google Docs, в интерфейсе которого можно видеть пользователей, пользующихся документом в этот момент — как залогиненных, так и анонимных. Последних он традиционно отображает в виде животных.

Коллеги в основном предпочитают работать анонимно. В итоге я последнее время крепко ощущаю себя диснеевской принцессой: приступаешь такой утром к работе, и отовсюду выкатываются неопознанные квокки, пингвины и шиншиллы :-)

2022-09-18 работа Битрикс тем временем

Слак советует

Слак, конечно, есть за что ругать. За тормоза, за баги, за оповещения. Но я просто обожаю его заглушки на случай, если новых сообщений нет.

Посмотрите, какая милота:

All done. The future is yours.

Зачем вообще нужны психотерапевты?

You're up to date. Go forth and do great things.

Или вот:

You're all read. Here is a tractor.

Боже, слак, хоть ты не начинай.

2022-08-25 тем временем

Взболтать, но не смешивать

Затрем немного про организацию кода. Если вам нужно где-то описать множество объектов с общими свойствами — подумайте, не стоит ли подробить описание на отдельные методы, каждый из которых будет описывать один конкретный объект?

Посмотрим на пример — метод, который описывает табличные части документов, пригодные для какой-то задачи. Что-то вроде:

SupportedTypes["Document.SupplierPricesEntering"]   = "Prices";
SupportedTypes["Document.OpeningBalancesEntering"]  = "CustomerAccounts,VendorAccounts";
SupportedTypes["Document.Requisition"]              = "InventoryAndServices";

Вроде всё в порядке, так? Описания есть, табличные части перечислены, посплитить их по запятой — считай, бесплатно.

Однако документов в методе описано много. Рано или поздно какому-нибудь коллеге (или вам самим) понадобится добавить ещё одну табличную часть для документа, который уже описан в методе. Его что-то отвлечет, он забудет поискать существующую строку и получится что-то вроде:

SupportedTypes["Document.SupplierPricesEntering"]   = "Prices";
SupportedTypes["Document.OpeningBalancesEntering"]  = "CustomerAccounts,VendorAccounts";
SupportedTypes["Document.Requisition"]              = "InventoryAndServices";

<...>

SupportedTypes["Document.OpeningBalancesEntering"]  = "PayrollDeductions";

В результате пара табличных частей выпадет из описания, и хорошо, если связанный функционал покрыт тестами.

Вывод? Ну, можно написать метод-хелпер, который будет принимать на вход тип документа и имя одной табличной части. Хелпер будет заполнять соответствие SupportedTypes и следить за тем, чтобы уже добавленные в него данные не были потеряны.

А если совсем по фен-шую, лучше поступить так, как я написал в начале заметки: разделить метод на отдельные вспомогательные методы, каждый из которых будет описывать только табличные части по одному документу. Что-то вроде:

Procedure AddOpeningBalancesEnteringDocument(SupportedTypes)

    TabularSections = New Array;

    TabularSections.Add("CustomerAccounts");
    TabularSections.Add("VendorAccounts");
    TabularSections.Add("PayrollDeductions");

    SupportedTypes["Document.Requisition"] = StrConcat(TabularSections, ",");

EndProcedure

Так и описание документа никто случайно не сотрет, и Сонар будет доволен. А вот случае реализации хелпера он, скорее всего, примется ругаться на повторяющиеся литералы с именами табличных частей.

2022-08-07

Не так, а этак!

Про хорватский комикс NOT/BUT мне кто-то рассказал в начале прошлого года, когда я всерьёз учился рисовать. Каждый стрип там — про какую-то травмирующую или просто мрачную мысль, которая приходит в голову художника во время работы. Идея в том, чтобы подтолкнуть читателя в правильном направлении и дать ему посмотреть на ситуацию, в которую он попал, с более практичной стороны.

Рисовать я бросил через год, в конце февраля: стало понятно, что времени на хобби у меня больше нет. Но комикс — совершенно универсальный; полистайте, даже если понятия не имеете, что такое клячка :-)

Да что за херня?

Все эти бесконечные самокопания хорошо знакомы любому профессионалу, а выход из них — не всегда очевиден. Особенно если ты зол, устал, а дедлайн по проекту истёк где-то на прошлой неделе.

2022-07-23 работа тем временем

Окей!

Блин. Посмотрел видео и понял, что я как раз давным-давно просто пишу OK в утренних дейликах в ответ на вопрос «how do you feel today». Мол, со мной всё в норме, жив-здоров, на работу ничто не влияет.

Что обо мне думали коллеги — боюсь предположить. Надеюсь, они тоже не знали про то, что okay в ответе на такой вопрос нифига не значит okay. :-)

2022-07-22 английский работа

Баночка с печеньем

На днях прикручивал работу с куками к одному из своих скриптов. Пока искал оптимальное решение, наткнулся на совершенно очаровательный (при этом, кстати, 100% рабочий) совет со StackOverflow:

You can get a CookieJar object from the session with session.cookies, and use pickle to store it to a file.

Ну то есть, буквально: держите ваше печенье в банке, а чтобы хранить его — маринуйте. Банку с маринованным печеньем, кстати, потом можно поставить на полку.

Вот как после этого не любить пайтон, а?

P.S. Пока писал заметку, разобрало любопытство — почему всё-таки pickle, а нe serialization? Так вот, если вкратце: таков путь.

2022-05-28 Python

Минус встроенные твиты

Не успел я выпилить из блога Google Fonts, как пришлось отправить вдогонку встроенные твиты.

Как это работало раньше? Хотел сослаться на твит — просто вставлял на него ссылку. Скрипт сборки заменял её на HTML-блок, который находил скрипт Твиттера и заменял на текст твита (и всякими полезными ссылками). Вот в этом коммите в целом видно, как это работало.

Как это работает теперь? Ну да, никак. Твиттер в России заблокирован, так что его скрипты работают только со включенным VPN.

Штош

Пришлось поскринить все твиты, на которые я когда-то ссылался, и добавить их в заметки в виде картинок со ссылками. Кому нужен оригинал, тот включит VPN и перейдёт в Твиттер, а остальные, по крайней мере, могут прочитать текст.

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

Ничего лишнего. Чистый функционал. Ты ему — твит. Он тебе — картинку:

npx snap-tweet https://twitter.com/PossumEveryHour/status/1506148678461014016

И всё. Захотелось занести автору денег.

Подумал, не прикрутить ли snap-tweet к скрипту сборки (чтобы было, как раньше: вставляешь ссылку на твит, а дальше оно само генерит картинку и кладёт куда надо). Решил, нафиг. Грубое попирание KISS, да и вообще... В мире и так хватает энтропии. Особенно, блин, сейчас.

2022-03-21 блог вебдев Node.js

Смотри не перепутай

Код

Если вид операции — продажа товара или недвижимости, то открой общую форму AdvancesPickFormWithVAT с параметрами, описанными в структуре PickParameters. Колбеком будет метод EditPrepaymentOffsetEnd, описанный в этом же модуле; передай ему структуру AdditionalParameters. Форму нужно открыть так, чтобы она заблокировала весь интерфейс.

А вот если вид операции — возврат поставщику, то открой общую форму AdvancesPickFormWithVAT с параметрами, описанными в структуре PickParameters. Колбеком будет метод EditPrepaymentOffsetEnd, описанный в этом же модуле; передай ему структуру AdditionalParameters. Форму нужно открыть так, чтобы она заблокировала весь интерфейс.

Смотри не перепутай.

2022-03-16 код с запашком

Минус Google Fonts

Твит

Год назад, блин. Ладно, всем привет! С вами Джонни Слоупок! Сегодня мы будем выпиливать из моего блога работу с Google Fonts. Я подгружал с него основной шрифт (PT Sans), но без кросс-доменного кэширования единственный смысл делать это дальше — если сервер, на котором работает сайт, слабоват и с гугла шрифты грузятся шустрее.

В моем случае блог отдают сервера гитхаба, которые на производительность не жалуются, так что теперь PT Sans загружается прямо с них. И знаете что? Разница прямо драматическая. Если раньше при обновлении страницы была явно заметна задержка между загрузкой страницы и загрузкой шрифта, то теперь её невооруженным глазом не разобрать. Если у вас свой блог и хотите попробовать — вот тут есть классный сервис, который решает задачу в несколько кликов.

Не забудьте повесить звезду на репозиторий!

2022-02-19 блог вебдев

Итоги 2021-го

Поздновато я сел итоги подводить, да? Ну, предыдущие пара месяцев вышли, гм, плотными и у меня банально руки не доходили сесть и подумать. А сейчас я как раз в отпуске за 2021-й год — так что самое время. Пишу, типа, из прошлого.

Выписывать каждую ачивку меня совершенно не тянет. Год точно вышел неплохим: я проделал уйму сложной работы над FirstBit ERP (писал новые модули, перепиливал существующие, давил баги, писал функциональные и нагрузочные тесты — да много чего было). Это хорошо сказалось и на самой конфе, и на делах компании. Кроме того, между делом я защитился на эксперта по 1С и на профессионала по PostgreSQL, гип-гип-ура.

Были и фейлы, ну. Перечислять, опять же, лень, но главный провал — я чудовищно разжирел и вместо того, чтобы сбросить килограм десять (таков был изначальный план) — набрал ещё пяток.

Оглядываясь назад, корень большинства проблем за прошлый год вижу так: я слишком сильно фокусировался на потоке задач. Они все были по-своему интересные, от тщательно проработанного ТЗ до челленджей в духе «найди в коде проблему, используя ведьмачье чутьё». Делаешь одну задачу, другую, третью, сотую и сам не замечаешь, что забил почти на всё, кроме работы. В итоге получаешь кучу опыта, но мир усыхает до окна с конфигуратором и сил как-то применять этот опыт нет. Нездоровая фигня.

Короче, если представить меня как персонажа какой-нибудь видеоигры, выглядит так: атака и интеллект за год заметно выросли, но здоровье, подвижность, харизма и вообще почти всё остальное — поползло вниз. Теперь надо с этим всем что-то делать.

Пойду для начала пробегусь километров десять, вот что!

Твит

Возможно, птичка, возможно.

2022-02-08 работа

Переиспользуйте с осторожностью

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

Начинаю расследовать. Первым делом смотрю на скрины в Аллюре: о'кей, причина очевидна — в одном из документов сдохло условное оформление для поля с суммой НДС. Тест ожидал, что оно будет недоступно, если ставка НДС равна нулю, а оно оказалось доступным.

Непорядок, надо чинить. Смотрю на условие оформления в коде: ну, поле блокируется, если ссылка на ставку НДС есть в списке «нулевых» ставок (т.е. ставка в которых равна нулю). Всё просто и логично. Что тут, блин, могло сломаться?

Твит

Ладно, лезу в документ ручками. А там внезапно всё тип-топ: оформление работает как надо. Плавающий баг, что ли? Запускаю автотест снова, в нужный момент вклиниваюсь с отладчиком и обнаруживаю какую-то откровенную фигню: в списке «нулевых» ставок, кроме них самих — пачка пустых ссылок!

Откровенно чешу в затылке. Этот список документ получает из общего модуля, где лежит примерно такой код. Пустую ссылку отсюда даже теоретически получить нельзя. Причем у модуля включено переиспользование возвращаемых значений и функция по факту выполняется один раз где-то в начале теста, до всех сложных манипуляций с данными. То есть тест, по идее, повлиять на неё никак не может.

Тупик? Ну, опытные коллеги уже, наверное, обо всём догадались, но мне пришлось порядком потанцевать вокруг бага и даже залезть в стандарт, пока не дошло: кэш возвращаемых значений в 1С можно изменить. В смысле, не просто вызвать ОбновитьПовторноИспользуемыеЗначения(), а прямо вот ручками взять и поменять конкретные данные.

Как? Ну, если вы тянете из общего модуля со включенным кэшированием какие-то значения и они не примитивного типа (строка, число и т.п.) — вы получите не само значение, а указатель на него где-то в памяти. Запишете этот указатель в переменную и попробуете поменять — поменяете кэш.

Вот так просто, да. Это и произошло в моем случае: метод, формирующий список «нулевых» ставок, вызывала форма другого документа. Получив список значений, она непринужденно дописывала к нему пустую ссылку и использовала в своей логике. Таким образом, при каждом открытии этой формы кэш списка прирастал всё новыми и новыми пустыми ссылками, что в конце концов сломало документ на другом конце конфы.

Твит

По-хорошему, платформе стоило бы швыряться исключениями при попытке поменять кэш, но пока этого не происходит — надо бить себя по рукам самостоятельно. Например, при разработке кэшируемых модулей возвращать из них неизменяемые типы данных (ФиксированнаяСтруктура вместо Структура, ФиксированныйМассив вместо Массив и так далее). Это, правда, не стопроцентная защита: во-первых, фиксированные типы не везде применимы, а во-вторых — даже в последних версиях БСП это делается далеко не везде. Много конфигураций сейчас пишется не на БСП?

Сонар тоже пока не умеет ловить проблему, а про менее популярные средства я вообще молчу. Никакой серебрянной пули, короче — следим за своим кодом, поглядываем в код коллег и стараемся не забывать об ещё одном изящном способе бахнуть себе в ногу.

2022-02-08 работа

Infostart Event 2021

На прошлой неделе сгонял в Москву на Infostart Event 2021:

  • Послушать дюжину докладов о разработке и близких темах — check;
  • Развиртуализоваться с несколькими классными людьми — check;
  • Поболтать с друзьями — check!

По итогу неожиданно почуствовал себя отдохнувшим — и это несмотря на перелёты, нервы и суету. Думаю, дело в том, что я удалёнщик; было по-настоящему здорово посмотреть на такое количество коллег вживую, а не в виде потока электронов.

Чтобы два раза не вставать летать, сдал в офисе PostgresPro входной тест по администрированию PostgreSQL. Впереди ещё три, а финального пока вообще не существует — но дорогу осилит идущий, так вижу.

2021-11-16 готово PostgreSQL

Матрёшка

На днях в чат команды кинули ссылку на сертификат 1Ci (коллега проходил их курс на джуна). Перехожу по ней — авторизации не требует, ошибки не выдает, браузер сразу загружает PDF. Всё идёт как надо, верно? Ладно, открываю файл:

Error.pdf

Честно? Я даже восхищён. Надо этот UX в наших продуктах внедрить: прожимаешь ты, например, печать документа, а он тебе — PDF! А в нём — ссылка на другой PDF! А в нём — entity is not filled, иди заполняй.

Можно продать это как русский путь вывода ошибок: мол, у нас так исторически сложилось. Всё матрёшки виноваты, проклятые, и Кощей со своей иглой. Она, как вы помните, в яйце, а оно — в утке, которая в зайце, засунутом в сундук, и далее по цепочке вложенности.

2021-10-30 работа

Управление вторым монитором из консоли

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

В чем тут засада: средствами ОС управлять вторым монитором неудобно (несколько кликов, скроллить надо, да ещё всё время путаю, куда лезть — в «Параметры экрана» или «Персонализацию»). Хотелось бы одной командой, а команду — на хоткей. И в идеале из скрипта всем этим рулить.

Скрипта я не нашел, но накопал готовую утилиту — MultiMonitorTool. Бесплатная. Под десятой виндой работает без проблем. Команды ниже включают-выключают 2-й монитор:

MultiMonitorTool.exe /disable 2
MultiMonitorTool.exe /enable 2  
MultiMonitorTool.exe /switch 2

Почему-то при включении монитора через enable или switch он иногда неверно позиционируется (например, до выключения он был справа, а после включения встал слева). Это поправимо. Сначала запишем конфигурацию в тот момент, когда включены оба монитора:

MultiMonitorTool.exe /SaveConfig Monitors.cfg

А потом, когда нужно включить монитор — загрузим сохранённый конфиг:

MultiMonitorTool.exe /LoadConfig Monitors.cfg

Утилита ещё много чего умеет (например, одна из команд перекидывает окна приложений между мониторами). Описание — по ссылке выше.

2021-10-16 рабочее место

Самые большие зарплаты

Есть путь до CSV-файла, надо его открыть, прочитать заголовок (первая строка), найти колонку Salary и вывести топ 10 зарплат.

Ссылка

Вбросил свои пять копеек просто для полноты картины. Если забить на стабильность, читаемость и производительность — можно усечь где-то вдвое. Тут с самого начала было очевидно, что на баше выйдет короче, а на питоне проще — так что просто написал как привык.

Из полезного: в комментариях к сообщению куча примеров на других языках. С некоторыми вообще до сих пор не сталкивался; было реально любопытно посмотреть на синтаксис и попробовать понять подход.

В целом история напомнила хобби Евгения Степанищева — писать вывод американской «песни о пиве» на всех языках подряд. Задача с чтением CSV, кстати, мне тоже кажется скорее фаном — слишком узкий кейс, чтобы на его базе что-то всерьёз сравнивать.

Из смешного: у пары коллег язык 1С вызвал настолько острые проблемы со зрением, что они сочли нужным об этом сообщить :-) Отчасти понимаю желание самоутвердиться на стереотипе «1С — это плохо, понятненько?», но тут момент явно выбран неудачно. Предпочтения в синтаксисе — дело вкуса, а кроме них решение на 1С никак не отличается от решений на любом другом языке, где нет встроенной библиотеки для парсинга CSV.

2021-10-02

Сборка блога на GitHub

Последний год этот сайт работал на простой связке: статика, гитхаб и свой домен. То есть все страницы были заранее сгенерированы и лежали в репе гитхаба, к которой был подключен GitHub Pages.

Статику я собирал с помощью скрипта, на вход которому подавались педантично разложенные по папкам текстовые файлы с содержимым страниц. Скрипт рылся в них и генерил html-ки, которые я потом вручную перекладывал в репу.

Эта схема, в общем, неплохо работала, но меня раздражало количество кликов. Здесь скрипт дерни, там скрипт дерни, потом ещё с гитом повозиться надо. Хотелось бы попроще.

В какой-то момент до меня дошло, что на плечи гитхаба можно переложить не только деплой статики, но и саму сборку. Я поворочал мозгами и добавил к репозиторию со статикой ещё два:

  1. Репозиторий исходных данных. Сюда я положил контент сайта: те самые текстовые файлы и чуть-чуть метаданных (заголовки страниц, даты их создания, теги для заметок и так далее).
  2. Репозиторий скрипта для генерации статики. Кроме самого скрипта, сюда я засунул разные ассеты (иконки, стили, манифесты — в общем, всё, что не нужно каждый раз генерировать, а можно просто «положить» рядом с получившимися html-ками).

Потом я накатал экшен, который просыпается при каждом пуше в репу с исходными данными. Вкратце, его логика:

  1. Клонировать репу со статикой и репу с генератором;
  2. Обновить репу со статикой с помощью генератора;
  3. Запушить изменения репы со статикой в мейн;
  4. Написать хозяину (мне) в телегу.

Вуаля! Теперь при любом изменении репозитория с исходными данными гитхаб немедленно (ну, как немедленно — в пределах минуты) обновляет репозиторий с готовым сайтом и деплоит его оттуда через GitHub Pages. Бонусом — веб-интерфейс для правки страниц сайта (собственно, сайт гитхаба). Почти чистый No Code :-)

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

2021-09-29 блог готово

Diablo

Хороший выпуск айтишного подкаста «Мы обречены» про выгорание разработчиков. Особых откровений нет, но что-то полезное для себя подчерпнуть можно. Очень понравилась аналогия с видеоиграми где-то ближе к середине:

Была такая игра — Diablo. Бегаешь ты там, RPG, всякие спеллы. У персонажа есть мана и здоровье, и когда у тебя маны на каст… Ха, звучу как задрот! Ну ладно. В общем, когда у тебя маны на каст не хватает, она у тебя начинает из здоровья браться.

— Доктор Кот

2021-09-22 работа видеоигры

Про молоток и гвозди

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

Вот есть у вас документ, да? В нём — несколько табличных частей. В каждой — поле для комментария. Делаете вы для этого документа печатную форму; если хотя бы в одной строке любой ТЧ есть комментарий — нужно использовать один шаблон, если комментариев нет — другой.

Задача примитивная, все мы миллион раз такое делали — ну, смотрим в выборку строк, натравливаем ПустаяСтрока() на нужное поле и грузим подходящий шаблон. Готово, можно пить кофе!

Однако, вместо короткого цикла я увидел это:

Ну то есть да, делаем матрешку из подзапросов, в самом нижнем из которых роемся в ТЧ (которые мы, напомню, только что выгребали для вывода на печатную форму). Ищем в них комментарии, если есть — ставим единичку. Нет? Нолик. Итог несколько раз группируем и возвращаем в скрипт.

Я сейчас даже не про нагрузку на СУБД (рискну предположить, что заметного эффекта этот трюк не дает — в конце концов, после отбора по ссылке выборка будет копеечной). Просто… Ну… Чекнуть выборку строк — пять строк кода. Понятных, простых, коротких, Сонару ругнуться негде. Как можно было родить вот это? Из большой любви к запросам?

А ведь что-то в этом духе и было, скорее всего. Я почти вижу его, этого кодера, который только что более-менее сносно освоил запросы и пребывает в перманентном восторге от новых возможностей. А когда в руках есть клёвый блестящий молоток, всё вокруг кажется гвоздями.

2021-09-15 работа код с запашком

Ранее Ctrl + ↓