Notes
Later Ctrl + ↑
Screenshot With Sound
Recently, we came up with the idea of dividing the internal ERP into several independent parts and organizing data exchange between them. We discussed the outlines of the task, the exchange model, transport, and roughly agreed on deadlines — in general, we did what we usually do.
I created a task for the stuff. We give them names in English, so I wrote in the first wording that came to mind.
The title came out with sound. Okay, this one is funny, but what should I call it then? Well, let it be Distributed Internal ERP. Abbreviated... DIE?
I let the first one be. Long live Papa Roach :)
Timesheet for Obsidian
I made another Obsidian plugin, this time for daily notes. Draws a nice report: what tasks I worked on, what I did, and how much time I spent. I tried to describe how it works in the repository; will be glad if it is useful to someone else!
Funny thing: for the examples in the README, I used issue numbers FBI-1, FBI-2, and so on. This isn't a reference to the X-Files or Twin Peaks — it's just the first thing that came to mind. The fact is that our internal project for the development of FirstBit ERP is called First Bit Internal, abbreviated as FBI. The main pool of tasks we work on lives in it.
We’re already used to it, but our colleagues outside the company always find our screenshots from JIRA or SonarQube amusing. Did you imagine that you were Agent Cooper? Well, I almost don’t even need to :)
12 May 2024 done TypeScript Obsidian work
One Query More, One Query Less
One query more, one query less — it doesn’t matter, people often say. Like, the main thing is that the query must be cheap: it doesn’t read too much, uses index, and so on.
This point of view makes sense, but mindlessly machine-gunning queries is a dangerous idea. Even if everything looks good at the moment, the system may change slightly in the future. And then a seemingly harmless patch will burn your production server to the ground right on Friday.
Let me tell you about one example from recent practice. There is an ERP that contains a table with payment stages for customer orders. One of these stages is prepayment; until it is received, you cannot create an order for the supplier.
Technically, the purchase order simply stores the customer's order ID; if the field is filled (that is, if the purchase order is created by the customer's order), ERP needs to read the payment stages of the customer's order and understand whether the purchase can be made.
It sounds elementary, but monitoring shows that the operation is slow as hell and eats up memory as if it were the last time.
Welp, let's go find out. We saw something like this:
What do we have here? Instead of taking two or three stages of payment for an order, ERP reads almost a million! How this could be possible?
It turned out that the problem was with those purchase orders that were not related to customer orders at all. The developer considered that the logic for them could not be changed: the customer’s order ID is empty and the query will not find payment stages for this ID. This means that the same result will be obtained as if there were no query at all. And an extra query — well... One query more, one query less... Not a big deal.
The deal turned out to be big. The table of payment stages contained data not only for customer orders, but also for other types of documents. Their customer order ID field was empty. As a result, ERP, when trying to find payment stages using an empty customer order ID, unexpectedly found them.
The query read about a gigabyte of data and wrote it to a temporary table. A gigabyte was read, then a gigabyte was written... History hit the disk, the DBMS buffer cache, and other components of the system (even the network, which had to drive this gigabyte back and forth without any benefit).
Do you know what I think? If the result of a query is known, there is probably no need to do it after all.
5 May 2024 1С optimization
Foodiary for Obsidian
Following my first plugin for Obsidian, I released the second one a couple of weeks ago. Counts calories, proteins, fats, and carbohydrates in food. It helps not to overeat out of nowhere — it’s rather difficult to judge by eye how much you’ve eaten today and whether you can afford that donut.
In short, a useful thing if you:
- Fatty (like me)
- Want to stop being fatty (like me)
- You take notes in Obsidian (like me) 🙂
In fact, there is a lot of software for this task. I tried some of them but was dissatisfied: it either has terrible design, is bugged, or is constantly trying to sell me a monthly subscription. In short, it's more annoying than helpful. I wanted something native, built into the usual routine; so, if routine settles in Obsidian, then the solution seems to suggest itself.
You can install the plugin directly from the program — the developers have already approved it. Otherwise, everything is simple: you write in a daily note what you ate and how much it weighed, and you receive a simple table sorted by calories with numbers by proteins, fats, and carbohydrates.
There are examples in the repository at the link above.
14 April 2024 done TypeScript Obsidian
Fastimer's Look
I just implemented Fastimer's rendering through callouts: this is an Obsidian mechanic that allows you to turn an ordinary quote into a designed block of text that attracts the reader's attention. You've probably seen blocks like “advice” and “pay attention” — these are callouts.
You can read more in Obsidian Help.
As a result, the timer now takes on a different color depending on the state: blue for an active fast, green for completed, and red for a failed one.
In addition, I made the text more compact and worked on styling:
It turned out to be way nicer than the block of preformatted text as it was before.
9 Marth 2024 Obsidian TypeScript
Small Pleasures
I was wasting time at the supermarket checkout: it’s evening, there aren’t many customers anymore, but the elderly cashier is clearly tired and isn’t in too much of a hurry. Standing in front a tall, gray-haired man with a luxurious beard whiles away the time studying the rack of chocolates next to the cash register.
Finally, he takes a Snickers, twirls it in his hands thoughtfully. Then he pushes two more towards himself and, broadly, with visible pleasure, smiles into his mustache :)
Not Only Everything
This commentary in the documentation for the WriteJSON() method of XDTOSerializer is enviably deep, I would say:
Well, yes. The method dumps data into JSON, not XML. So it’s hard to argue that not all value types can be packed into XML using it (to be precise, none). Such a pity that there is an obvious copy-paste from the help for WriteXML() further in the text! I almost decided that it was an Easter egg from the developers :)
25 February 2024 1С
Fastimer for Obsidian
The Obsidian developers recently approved one of my TypeScript pet plugins — Fastimer. It is an intermittent fasting tracker that adds a new code block to your vault: you enter the start date of the fasting interval and get the date of its finish, the time until this moment, and a list of the zones to be passed.
The block shows an up-to-date picture every time Obsidian renders it, which means you can monitor your progress in real time. When a fasting window ends, you can enter the end date, and the code block will show the result: whether you managed to achieve the goal, how much time you fasted beyond the plan, and so on.
I'm thinking of improving the visual part a bit (currently, everything is displayed as plain text without any design). In addition, I want to add functions for calculating statistics so that you can draw cute graphs like Charts and show achievements. I already made the same in implementing the same application in Python, but I’m unlikely to return to it — it’s easier to solve the task in the Obsidian vault than to roll out an additional utility.
In short, check out the plugin! :) You can find it by name (Fastimer) in the Obsidian library, or install it manually from the repository.
13 February 2024 done TypeScript Obsidian
Do? Do Not?
Among our projects, we have one where two systems are communicating with each other: ERP and CRM. Data exchange is done well: a push'n'pull server has been set up, subscriptions to events have been registered, a REST API has been implemented, and so on. There are many other fascinating technical details, but I'm not talking about that now.
The exchange has various logic chains inside. For instance, if a new company appears in CRM, it sends the data to ERP. The other day, a problem appeared: a company was not sent from the CRM, no matter how many times you tried to write it. So we went to investigate, suspecting the worst: CRM is written in PHP (nothing personal; it’s just not our technical stack), and there’s a lot of different legacy stuff there. It's easier to shoot yourself in the foot than to blow your nose.
However, it didn't take much digging. We opened the company’s page in CRM and saw that he had the “Do Not Export To ERP” checkbox, which, in fact, blocked the sending. A manager made an obvious mistake.
Should we uncheck the box and close the ticket?
This will solve the problem with that particular company, but not the reason it appeared. It is actually in the interface, specifically in the name of the option: “do not” is used, which is advisable to avoid due to the fact that it is more difficult for users to read the wording correctly. By the way, this also applies to a simple “do”.
It is often difficult for programmers to understand why this is so: we are used to instantly calculating Boolean expressions in our heads, and variations like “not (not true)” are commonplace for us. But people with a different background can get confused. Just a little, but sometimes this is enough for them to perceive “do not export” as “do export” in the heat of the day, click the option, and move on.
To sum up, the solution is to rename the checkbox. “Disable Export” or “Stop Export” are both fine, for example. “Prohibit Export” also comes to mind, but it’s more about interpersonal relationships, and in general, a ban on doing something does not mean that it won’t be done :)
Last Meth
I'm digging into the code of an external component for 1C platform, published by its developers as an example. The good thing is: well, it can be compiled and if you tweak it a little — it does the job.
As for other things, there are a lot of bruh moments. For example, the project can't be opened in modern Visual Studio (you need to specify CMake manually). The code is quite sloppy; there is no documentation, comments, or formatting. Long story short: I believe, it can be difficult for a developer without solid experience in C++ to get the hang of this.
Was a bit amused by the naming in the code below:
long CAddInNative::FindMethod(const WCHAR_T* wsMethodName)
{
long plMethodNum = -1;
wchar_t* name = 0;
::convFromShortWchar(&name, wsMethodName);
plMethodNum = findName(g_MethodNames, name, eMethLast);
if (plMethodNum == -1)
plMethodNum = findName(g_MethodNamesRu, name, eMethLast);
delete[] name;
return plMethodNum;
}
I see here the inexplicable love for abbreviations. What made the author name the variable “eMethLast”, not “eMethodLast”? They already created "wsMethodName" and "plMethodNum", after all.
Perhaps this is an Easter egg with a reference to Breaking Bad. Then I like it for sure :)
17 December 2023 1С
Wrong Freeway Entrance
Have you ever taken the wrong freeway entrance? You need to drive to the next exit to turn around, but you hate every inch of travel because you're going away from your goal.
― Andy Weir, "The Martian"
Programmers have exactly the same emotions when they spend a long time working on something. They suddenly realize that part of it should be designed differently. Moreover, this is exactly what you have to do since it solves several problems at once. This is where technical debt is born.
However, right now you don’t change anything but continue to work with the part of the code that already exists. After all, you are professional, and you have a release date! You have to make it on time and then pay the debt, but you hate every inch of code you write because you're going away from your goal.
14 November 2023 code smell
Everyday Heroism
Some time ago, I was setting up Swagger for the internal API. While I was fiddling around, it became clear that some functionality did not need to be included in the documentation. I was looking for a way to do this without crutches and came across a funny question on GitHub.
What's funny, you ask? Well, I involuntarily remembered Mista. Among 1C developers, this is synonymous with the word “toxicity”: if you ask anything there, you get a bucket of slop by the collar instead of an answer. Here, of course, everything is not so neglected, but holy crap! These persistent guys who referring to the 14-page manual made me laugh a lot.
One thing is good: by the end of the thread, there appeared a brave rebel who just answered the question. They, like, named the required parameter for a FastAPI method's decorator, which is not supposed to be shown in the documentation. No links — could you imagine?
Not all heroes wear capes, I would say.
Romania's Feature
Making a password recover function via SMS for our customer portal. Got to the Twilio documentation related to alphanumeric sender ID support in different countries; this feature allows you to send messages so that the recipient sees not the sender's number but something meaningful (a company name, for example).
The feature is regulated differently everywhere: in some countries it just works, but in others registration is possible or even required.
Well, let's take a look:
🤔
- Portugal: yes
- Puerto Rico: no
- Qatar: yes (with registration)
- Reunion: yes
- Romania: yes (with registration) (but be afraid of Dracula)
I don’t know how else I can explain this cemetery.
UPD: Found the answer. Grave crosses mean you have to pay $700 to register.
To be frank, I like the explanation about Dracula much more.
New Fastimer
Released a new version of my console timer for intermittent fasting. I wrote this app about a year ago, when I was once again upset by the Zero application for Android: some very primitive functions (like viewing a specific interval) did not work. Oh my gosh, guys, you had one job!
The main difference between 1.3.1 and 1.2.3 is that the console menu has been cut to hell in favor of the good old arguments and options. The menu concept looks convenient only if an application has few features. However, as soon as you take a step forward, the need to answer a list of questions each time you need something starts to irritate you.
Do you speak English?
The repository login form of the current platform (8.3.22.1923, to be precise) launched with the English interface:
You had one job, literally.
I heard some rumors that development of the Designer was stopped largely due to the monstrous amount of technical debt that slows down any new features. But here are interface glitches right on one of the first application windows! Curious how it got through the build tests.
Maybe they don't exist at all.
14 May 2023 1С
Going Postal
Recently, I was working with vocabulary in an attempt to describe my latest adventures to an English-speaking friend and got to know about the colorful idiom “to go postal”. It means something like “go crazy with anger”; appeared somewhere between the 80s and 90s in the USA after a series of rather insane incidents in which postal workers went crazy and attacked people around, including colleagues and visitors.
The expression sounds funny at first glance, but the story behind the scenes is painfully gloomy. I think I will continue to use the good old “to go ballistic”. Literally, “get angry that strong so you become a rocket which lost control”. Or, to simplify, “explode with anger”.
By the way, there are similar rocket-like connotations in Russian, but for some reason they are about a more manageable cases. Mostly, they implies something like "the fire in the ass was so damn strong that the poor guy left Earth and successfully landed on Mars". Sounds a bit better than a simple explosion — you didn't completely waste the precious resource, at least :-)
8 May 2023 English
Pause()
An important detail: the CallPause method is not available in a client-server call; when a client calls a server method in which CallPause is called, the exception "Cannot call the CallPause method in a client-server call" will be thrown.
CallPause Method (RU)
What a strange restriction, to be frank. On the one hand, an experienced developer will not make an intentional pause in a client-server call anyway; on the other hand, whoever wants to make it makes it anyway (by checking time in a cycle, for example). Does security by obscurity worth the efforts?
At best, some junior will catch this exception, think “welp, it looks like I'm doing something wrong” and move in the right direction. However, putting the restriction on the platform for the sake of this case is like firing a cannon at sparrows. You know what? Let's implement a number limit as well — like, no more than 1000 pauses per session, otherwise users will suddenly think that the program is too slow :-)
30 April 2023 1С
In a Pedantic Way
The daily award for the most philosophical code goes to the author of this elegant way to check that two boolean variables are not equal to each other:
If DataStructure.Property("AmountVATIn")
And ((DataStructure.AmountVATIn And NOT SearchPriceIncludesVAT)
OR (NOT DataStructure.AmountVATIn And SearchPriceIncludesVAT)) Then
Price = RecalculateAmountOnVATFlagsChange(Price, DataStructure.AmountVATIn, TabSectionLine.VATRate);
EndIf;
I'm thinking of adding something like “And Not (DataStructure.AmountVATIn = SearchPriceIncludesVAT)” here to spice it up with a subtle note of insanity.
21 Marth 2023 1С work code smell
Environmental Storytelling
I like to notice things in the world around me that clearly have a story behind them. In video games, this is called “environmental storytelling”: they don’t tell you the story directly, but if you look around, you can guess which gun was hanging on the wall and who fired from it.
For example, I recently celebrated the company's 10th anniversary with colleagues at a local golf club. Balls had to be sent flying from the second floor; there are no railings for obvious reasons, but a net is stretched in case someone loses their balance.
Why are you talking about this, you ask? Well, there are warnings on the walls: jump into the net of your own free will and pay ten thousand dirhams. Recording this heroic leap of faith on camera is fine as well, just prepare five thousand more.
Do you feel the smell of a good history?
Another example: once flew to Turkey to rest and decided, just in case, to look through the rules of the airline (what can be taken on board, what can not). Among the list of items prohibited from being carried on board, I found “steel spear” and “steel flail” 😬
UPD: Another great example from somewhere on the Internet.
Slow down, I'm recording
Usually the idea of developing is simple: the faster it works, the better. For example, the more requests an application manages to execute per unit of time, the faster the task for which these requests are needed will be solved.
However, it also happens the other way around: you need to reduce the number of operations that a program is able to perform. Let's imagine we exchange data with an external service and it bans if we hit it with requests too often. For example: the cloud version of Bitrix24 requires sending requests to it no more than two per second.
Here is an implementation of such a slowdown, which I wrote last week. There is no queue support; the main solved problem is to execute as many requests as possible without going beyond the limit (taking into account the fact that requests can be made from different sessions).
The problem is solved through a constant that stores the date for the current second and the number of requests that have already been sent. Clients who run into a limitation are waiting. This approach is not suitable for high-loaded systems, but otherwise it can come in handy.
Earlier Ctrl + ↓