casessalon-budget-bot

rudiscuss your taskmenuclose

Small beauty salon · 

A salon’s income and expense tracking, done by Telegram message

An expense or income is written to the bot in your own words: “rent 20000 for October.” The bot shows how it understood the message and records it after a button tap. The yearly summary opens on a phone from a link, and the familiar Excel file is put together at the tap of a button.

The yearly summary on a phone screen: income, expenses, profit, and profit margin, with a table by month below. Demo data

The task

A small salon kept its income and expenses in one Excel spreadsheet: date, category, amount, a total at the bottom, and profit by month and two charts off to the side. The spreadsheet itself is clear. The trouble is elsewhere: to enter an expense, you have to open it. And an expense happens on the go, when all you have at hand is a phone.

So entries appeared later, whenever someone got around to the spreadsheet. By then the day of the purchase could no longer be recalled, so the first of the month went into the spreadsheet. And some expenses were left without even a name: the spreadsheet has rows labeled “Expense with no explanation.” The money went out, but what it was spent on is not in the records.

What was needed was tracking that does not require sitting down at a computer: you record it right where you chat and forget about it. It had to come with no new program to learn, and two people had to be able to make entries. And the familiar spreadsheet with totals and charts had to stay.

What it delivered

An expense or income is recorded with a Telegram message, in your own words and on the day it happened, not at the end of the month from memory. A yearly summary with profit by month and charts opens on a phone at the tap of a button. The familiar Excel file is still there: the bot puts it together from the same numbers.

Where I started

The 2026 spreadsheet is all there was before the bot. It shows where tracking by hand loses accuracy.

The salon’s yearly totals are not shown in this case study. The screenshots of the summary page use demo data: the numbers are made up, while the page is built by the same code that runs for the salon. The screenshots of the chat with the bot are real, from the working bot.

The salon’s tracking spreadsheet, January to early October 2026
MetricHow many
Transactions in the spreadsheet for the year109
Of those, dated the first of the month instead of the actual day105
Purchases in rubles where the amount and exchange rate are written into the category name5
Spellings of one and the same category2

The first of the month in place of the day is not carelessness but a consequence of the method: the spreadsheet gets filled in when someone gets around to it. A spreadsheet like that shows how much went out in a month but not when, and by then it is hard to check an entry against a receipt.

The same goes for purchases in rubles. The column held the amount after conversion, while the rubles and the exchange rate were written out in words in the category name: you can read them, but you cannot calculate with them.

What I did

One transaction, one message

You write to the bot the way you would say it to a person: “rent 20000 for October,” “income 4800 today,” “coffee 1200 yesterday.” There are no forms and no required word order. From the message the bot pulls the date, the type (income or expense), the category, and the amount.

If no date is given, today’s date is used. If only a month is given, the bot marks that there is no exact day and does not make one up. Several transactions can be sent in one message, one line each.

The bot’s help message in Telegram with example messages: “purchase lebel 14244,” “rent 16000 for October,” “income 5400 today,” “July in rubles purchase 37700”
The bot’s own help message: this is everything you need to know to keep the records. Four example messages and not a single form.

It shows first, then records

The bot answers every message with its reading of it: what it understood, for what date, in what category, and for what amount. Under that reading are two buttons, “Record” («Записать») and “Cancel” («Отмена»). A transaction goes into the records only after “Record.”

A chat with the bot: the message “Coffee purchase for 2000,” the reply “Check and confirm: expense, coffee, 2,000, 07.10.2026,” and the “Record” and “Cancel” buttons
All the input is one message. The bot replies with how it understood it: expense, category, amount, and today’s date, because no day was given. After that, one button.

If the text does not make clear whether this is income or an expense, the bot does not guess; it asks with two buttons. If it could not make sense of the message, it says so and records nothing.

AI reads, a program does the math

AI parses the free-form text, and that is where its job ends. It returns the date, type, category, and amount as the person wrote them. It is not trusted to add, convert currency, or calculate percentages: all the arithmetic is done by an ordinary program that always gives the same answer on the same data.

For income and expense tracking, this is the key decision. A mistake in understanding is visible on the screen before anything is recorded. A mistake in addition would be visible to no one.

One category, one name

Along with the message, the AI gets the list of categories already in the records. So a name written in lowercase, with a capital letter, or in Latin letters goes into one and the same category, not three different ones.

In the old spreadsheet, one category was written two ways; during the migration they were merged into one.

Rubles convert on their own

The salon pays for some purchases in rubles but keeps its records in another currency. It is enough to write “July in rubles purchase 30000”: the bot takes the Bank of Russia exchange rate and converts. For a month already closed, it uses the average rate for the month; for the current month, the rate on the day of the transaction. The entry keeps the rubles, the rate, and the result, so you can see where the amount came from.

If the Bank of Russia does not respond, no rate is filled in by guesswork: the transaction is not recorded, the message is saved, and a “Retry” button appears under it.

The summary opens from a link that lasts 15 minutes

When the “Statistics” button is tapped, the bot sends a link. It opens in the phone’s browser, with no app, password, or sign-up, and works for 15 minutes. After that the page stops opening, and you get a new link.

The bot’s reply to a statistics request: “The statistics link is valid for 15 minutes” and the “Open statistics” button
The summary is one button in the chat. The bot says right away how long the link lasts and what to do after that.

The page shows the totals for the year: income, expenses, profit, and profit margin. Below are a table by month with the average profit since the start of the year, two charts on which a month with a loss is marked in red, and a list of all transactions.

A month that is still under way, with expenses already recorded but no revenue yet, is left out of the profit and the average; otherwise every month would start with a “loss.” The page says so directly.

The salon’s name is not on the page. At an expired link or any other address, the server shows nothing, and the page is closed to search engines.

And one detail that is not obvious: the link is not single-use, though that would be stricter. Telegram itself opens the links it is sent in order to show a preview, so it would use up a single-use link before the person did.

Two charts on the summary page: bars of income and expenses by month and a line of net profit with the average since the start of the year; March, a month with a loss, is marked with a red dot
The charts on the summary page as seen on a phone. This is demo data: March was given a loss to show the red mark, and October has no revenue, so it is left out of the profit.

Excel stayed

When the “Export xlsx” button is tapped, the bot sends a file in the familiar form: transactions, the total, profit, profit margin, a table by month, and two charts.

The bot sent the file “Income and expenses 07.10.2026.xlsx” to the chat
The file arrives right in the chat, with the export date in its name.

The totals in the file and on the page cannot differ: both come from one calculation. The cells hold numbers, not formulas, so the file opens the same way in Excel and in a preview on a phone.

A mistake is visible and can be removed

The “Recent transactions” button («Последние операции») shows the ten most recent entries, each with a delete option that asks for confirmation. A deleted transaction disappears from the summary and from the export, but it is not erased from the database; it is marked.

There is deliberately no “Edit” button: correcting an entry means deleting it and writing it again, with the same kind of message.

The bot’s “Recent transactions” message: ten entries with date, type, category, and amount, and under them ten buttons with a trash can and a number
The ten most recent entries in the chat. Under the list is a button for each one: tap a number, confirm, and the entry is out of the records.
The list of transactions on the summary page: date, category, income or expense, newest entries on top
All transactions on the summary page: newest on top, each with its day, category, and amount. A deleted entry disappears from here. Demo data.

The old spreadsheet moved over in full

The year’s records were not started from scratch: on its first run, the bot moved all 109 transactions over from the spreadsheet. The migration counts as successful only if four control totals match afterward: the number of transactions, total income, total expenses, and profit. All four matched.

During the migration, the purchases in rubles were split into rubles, rate, and result. At the same time, the five such transactions were recalculated from scratch at the Bank of Russia rate, and the result matched the spreadsheet to the last unit.

This turned up a small thing of the kind that is never small where money is concerned: with the rate rounded to three decimal places, one transaction was off by two units. So the bot calculates with the unrounded rate and rounds only the result.

The server goes down, and no messages are lost

The bot lives on a small server of its own, not in a data center: the power or the internet can go out. When that happens, messages wait inside Telegram itself and are processed in order when the bot comes back.

The transaction date is taken from the time the message was sent, not the time it was processed: an expense written on the evening of the thirtieth will not slide to the first. The “Record” and “Cancel” buttons under old messages still work after a restart, and a message delivered a second time is not recorded twice. Once back, the bot says in one message that it was unavailable and how many messages it processed.

There is an honest limit to this: Telegram keeps such messages for no more than a day. If the server was silent longer, the earlier ones are lost. The bot warns about this in its help message and advises checking “Recent transactions” after a long downtime.

A copy of the records, every night

All the records are one file on the server. Once a day, at night, the bot sends a copy of it on Telegram to those who keep the records, if anything changed during the day. If the server breaks, the latest copy stays in the chat.

The bot does not answer strangers

Only people on the list can enter transactions and view the summary; right now that is two people. The bot does not answer anyone else at all, neither messages nor buttons, and an outsider does not see the command menu.

In groups the bot stays silent too, so that the amounts are not seen by people who should not see them. Neither message text nor amounts get into the logs on the server.

Does your income and expense tracking also live in a spreadsheet you never get around to?

Message me

Why this case study has no numbers

The bot has been running since October 2, 2026, which is less than a week as of the day of publication. In that time, it is not possible to say honestly either “it saves this many hours” or “tracking got this much more accurate.”

What is known for certain is what was checked before launch and on the first day.

What was checked and how, as of October 7, 2026
What was checkedResult
Migration of the old spreadsheet109 transactions, all four control totals matched
Message parsing by the real AIEight test messages out of eight parsed correctly
Response timeAbout five seconds per message
Downtime and restartAn automated check: three messages from the queue recorded in order, with their sending dates and no repeats
Interruption in the middle of recordingA separate code check: no duplicate entry and no database corruption

And here is how the result will be measured. For every transaction, the bot marks whether it was recorded through the bot or moved over from the spreadsheet, and whether it names an exact day. In the old spreadsheet, 4 transactions out of 109 had the actual day. In a month or two, the same share among new entries will show whether the records are now being kept on time.

The numbers about the old spreadsheet come from the spreadsheet itself and from the migration check. Those about parsing and response time come from the project’s work log, the entry of October 2, 2026. The screenshots of the summary page were taken on demo data; the chat screenshots, in the working bot on October 7, 2026. The source code is not published: its tests contain the salon’s real numbers.

What this gives the business going forward

Does anyone need training?

No. Writing to the bot is the same as writing to a person on Telegram: what was bought and for how much. There are no forms and no required word order, and if the bot got it wrong, that is visible before anything is recorded.

What if the AI gets it wrong?

It records nothing by itself: first it shows its reading of the message, and a transaction goes into the records only after a button tap. And it is not trusted with the math at all: amounts, the exchange rate, and percentages are calculated by a program.

Who will see the numbers?

Only the people on the list. The bot does not answer anyone else, the summary link lasts 15 minutes, the business name is not on the page, and the page is closed to search engines.

What costs money after launch?

The server the bot lives on and the AI that parses the messages. Telegram and the Bank of Russia exchange rate are free.

What of this carries over to another business?

Almost everything except the categories themselves. You write it in your own words, check it, and confirm; messages are not lost during downtime; a copy every night; a summary from a link and a file for Excel: that is the framework. What changes is what exactly gets recorded: instead of income and expenses, it can be orders, shifts, vehicle mileage, or stock on hand.

How it works technically

Python 3.14, aiogram 3, aiohttp, openpyxl, SQLite in WAL mode with synchronous=FULL. Parsing is done by the Claude Sonnet model, with the response strictly following a JSON schema: date, type, category, amount, currency. The request carries today’s date and the list of existing categories. The exchange rate comes from the Bank of Russia XML with a cache in the database: for a closed month, the average over the days that have a rate; for the current month, the rate on the date or the nearest earlier one.

Updates come by long polling without dropping the queue, strictly one at a time; marks of processed update_id values are kept for a week. A message is saved in the database before the model is called, one batch per Telegram message (UNIQUE (chat_id, message_id)); writing the entries and changing the batch status happen in a single database transaction, so confirmation is idempotent. The reply with buttons is retried until it goes through: until it is sent, the next update is not taken.

The summary page is a single HTML file with no scripts and no external resources; the charts are inline SVG, with a light and a dark theme. The link token is 32 random bytes and lives in memory for 15 minutes. Without a valid token, the server returns an empty 404 for everything except robots.txt, including requests that are not HTTP. Headers forbid indexing, caching, and embedding; the access log is turned off so that tokens do not end up in it. The server is exposed to the outside through Tailscale Funnel.

A systemd service with autostart; the home directory is read-only for it. The database copy is taken with the SQLite backup mechanism, not by reading the file while it is in use. More than three hundred automated tests; they need neither a network nor secrets: the Bank of Russia responses are recorded in advance. Before release, there was a separate code review, and four defects in reply delivery and the web server were fixed as a result.

Who did the work

It’s just me behind jw-dev.pro, not a studio or an agency. That means you talk directly to the person doing the work: no manager, no brief relayed through third parties, and no passing the project to another contractor partway through.

My time zone is UTC+7, the same as Bangkok and Hanoi. I build websites, Telegram bots, and automation: a task rarely ends with a page, and it usually has to be connected to what you already have running.

As time goes on, the task sounds like “build a website” less and less often. One person needs a price to be changed in one place, not on twenty pages. Another needs the channel to run without them. A third needs the whole price list to sit inside Telegram, where their customers already go every day. That’s why the list of what I do is open-ended: if your task isn’t on it, that doesn’t necessarily mean it can’t be solved.

I write case studies so that they can be checked: what was there, what was done, and how it was measured. If there is analytics data, I give the numbers and the period they cover; if it is too early to measure, I say so. You won’t find “increased sales” here: that is a promise, not a result.

Need something similar?

Whether it’s a website, a bot, or something that is now done by hand, tell me what’s going on, and I’ll tell you whether it can be solved or not.

This kind of work is the service “Automation services for small business.”

I have your request

I’ll read it myself and reply where you told me to.

Send another one

What you need you can pick several

In your own words: what you have now and what you want to get. If you have a website or a bot, paste the link.

Your request will come to me on Telegram, and I’ll reply myself.

If it’s easier, contact me directly: on Telegram, on WhatsApp, or by email.

Not sure what’s wrong with your website? Check it for free on Telegram: @jw_site_check_bot.

Who owns everything afterward and what happens if I disappear: answers on the home page.