Заметки

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

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

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

AMOUNT? DEAL_AMOUNT? DOCUMENT_AMOUNT? AMOUNT_TOTAL?

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

Чего, блядь?

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

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

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

15 октября 2022 работа Битрикс

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

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

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

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

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

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

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

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

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.

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

25 августа 2022 тем временем

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

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

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

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

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

7 августа 2022

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

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

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

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

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

23 июля 2022 работа тем временем

Окей!

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

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

22 июля 2022 английский работа

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

На днях прикручивал работу с куками к одному из своих скриптов. Пока искал оптимальное решение, наткнулся на совершенно очаровательный (при этом, кстати, 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? Так вот, если вкратце: таков путь.

28 мая 2022 Python

Изменения

Твит

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

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

Мира всем.

2 мая 2022 тем временем

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

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

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

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

Штош

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

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

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

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

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

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

21 марта 2022 блог вебдев Node.js

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

Код

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

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

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

16 марта 2022 код с запашком

Минус Google Fonts

Твит

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

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

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

19 февраля 2022 блог вебдев

Итоги 2021-го

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

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

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

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

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

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

Твит

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

8 февраля 2022 работа

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

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

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

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

Твит

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

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

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

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

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

Твит

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

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

8 февраля 2022 работа

Infostart Event 2021

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

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

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

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

16 ноября 2021 готово PostgreSQL

Матрёшка

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

Error.pdf

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

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

30 октября 2021 работа

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

У меня стоят два монитора. Второй нужен во время встреч в 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

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

16 октября 2021 рабочее место

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

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

Ссылка

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

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

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

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

2 октября 2021

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

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

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

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

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

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

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

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

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

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

29 сентября 2021 блог готово

Diablo

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

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

— Доктор Кот

22 сентября 2021 работа видеоигры

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

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

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

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

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

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

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

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

15 сентября 2021 работа код с запашком

Ранее Ctrl + ↓