§ 01О чём проектWhat this is
Пекарня продаёт хлеб, самсу и выпечку оптом — кафе, рестораны, магазины. Заказы принимаются по телефону и в WhatsApp. Менеджер записывает в тетрадку, потом перебивает в Excel. К утру пекарь получает сводку на бумажке. The bakery sells bread, samsa, and pastries wholesale — to cafes, restaurants, and shops. Orders come in via phone calls and WhatsApp. A manager writes them down in a notebook, then re-enters into Excel. By morning the baker gets a summary on a piece of paper.
Проблемы: ошибки в количествах при ручном переносе, потерянные заказы в WhatsApp-чатах, нет истории — клиент спорит «я заказывал 80, а не 50». Биллинг — раз в месяц вручную, цены у каждого клиента свои, и менеджер помнит их по памяти. Problems: quantity errors during manual transfer, lost orders buried in WhatsApp chats, no history — the client argues "I ordered 80, not 50." Billing once a month by hand, each client has custom pricing, and the manager remembers it all from memory.
Задача: Telegram-бот, через который клиенты сами оформляют заказы, а админ видит сводку и управляет статусами. Без сайта, без приложения — всё в мессенджере. Goal: a Telegram bot where clients place orders themselves, and the admin sees a summary and manages statuses. No website, no app — everything in the messenger.
§ 02Как устроеноHow it's built
FSM-подход для обоих потоков. Регистрация клиента — трёхшаговый сценарий: контактные данные → название организации → телефон. Оформление заказа — выбор продуктов, количество, комментарий, тип доставки и дата. Каждый шаг валидируется сразу, ошибка — понятное сообщение и повтор. FSM approach for both flows. Client registration is a three-step scenario: contact details → organization name → phone. Order placement: product selection, quantity, comment, delivery type and date. Each step is validated immediately — error means a clear message and retry.
┌────────────────────────────────────────────────────────┐ │ Telegram Update │ │ │ │ │ ▼ │ │ ┌──────────┐ admin? ┌──────────────────────────┐ │ │ │ Router │────YES──▶│ Admin Router (priority) │ │ │ └──────────┘ └──────────────────────────┘ │ │ │ NO │ │ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │ Client │─▶│ FSM │─▶│ Domain Models │ │ │ │ Router │ │ States │ │ Order/Product/ │ │ │ └──────────┘ └──────────┘ │ ClientPrice │ │ │ └────────┬─────────┘ │ │ │ │ │ ┌────────────────────────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ SQLAlchemy async · SQLite / PostgreSQL │ │ │ └──────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ APScheduler │ │ openpyxl │ │ │ │ 08:00 daily │ │ xlsx export │ │ │ └──────────────┘ └──────────────┘ │ └────────────────────────────────────────────────────────┘
Доменные модели: Client с юридическими полями (ИНН, МФО, расчётный счёт, адрес), Product с базовой ценой, Order с позициями и статусами, ClientPrice — индивидуальная цена продукта для конкретного клиента. Если записи в ClientPrice нет, берётся базовая цена из Product.
Domain models: Client with legal entity fields (INN, MFO, bank account, address), Product with base price, Order with line items and statuses, ClientPrice — per-product pricing for a specific client. If no ClientPrice record exists, the base price from Product is used.
Два потока статусов: доставка (новый → принят → готовится → в пути → доставлен) и самовывоз (новый → принят → готовится → готов → выдан). Каждая смена статуса отправляет уведомление клиенту. Two status workflows: delivery (new → accepted → preparing → on the way → delivered) and pickup (new → accepted → preparing → ready → given). Each status change sends a notification to the client.
§ 03B2B-спецификаB2B specifics
Это не потребительский бот с каталогом и корзиной. Клиенты — юрлица, и бот обязан хранить реквизиты: ИНН, МФО, расчётный счёт, юридический адрес. Эти данные нужны для выставления счетов и сверки. Клиент редактирует реквизиты сам через профиль — не через менеджера. This is not a consumer bot with a catalog and shopping cart. Clients are legal entities, and the bot must store their details: INN (tax ID), MFO (bank code), bank account, legal address. This data is required for invoicing and reconciliation. Clients edit their own details through the profile — not through a manager.
Индивидуальные ценыPer-client pricing
Кафе «Восток» покупает хлеб по 8 000 сум, а ООО «Green Food» — по 7 500, потому что берёт больше. Модель ClientPrice хранит пару (клиент, продукт) → цена. Админ назначает через бот: /set_price @client Хлеб белый 7500. Если для клиента нет записи — применяется базовая цена из каталога.
Cafe "Vostok" buys bread at 8,000 sum, while "Green Food" LLC pays 7,500 because they order larger volumes. The ClientPrice model stores a (client, product) → price pair. Admin sets it via bot: /set_price @client White bread 7500. If no record exists for the client — the base catalog price applies.
Утренние отчётыMorning reports
APScheduler запускает задачу каждый день в 08:00 Asia/Tashkent. Админ получает сводку: сколько заказов на сегодня, общая сумма, список позиций по продуктам. Пекарь видит, что печь, до начала смены. APScheduler fires a task every day at 08:00 Asia/Tashkent. The admin receives a summary: how many orders for today, total amount, product breakdown. The baker sees what to bake before the shift starts.
Что это даёт бизнесу: каждый клиент платит свою согласованную цену автоматически — менеджер не держит прайсы в голове и не ошибается в счёте. А пекарь к 08:00 уже знает, сколько и чего печь, — без утренних звонков и пересчётов. What this means for the business: every client is automatically charged their agreed price — the manager doesn't keep price lists in their head or miscount an invoice. And by 08:00 the baker already knows what and how much to bake — no morning calls or recounts.
§ 04ЦифрыNumbers
§ 05Что я вынесLessons learned
(а) B2B-бот обязан поддерживать юрлица с первого дня. ИНН, МФО, расчётный счёт — это не «потом добавим», а фундамент. Без реквизитов клиент не может получить счёт, а бизнес — провести оплату. (a) A B2B bot must support legal entities from day one. Tax ID, bank code, bank account — this is not "we'll add it later," it's the foundation. Without these details the client can't receive an invoice, and the business can't process payment.
(б) Индивидуальные цены запрашивают всегда. Даже если заказчик говорит «у нас один прайс для всех» — через неделю появляется VIP-клиент с особыми условиями. ClientPrice как отдельная модель с фолбэком на базовую цену — минимальное решение, которое закрывает 100% случаев.
(b) Per-client pricing is always requested. Even when the client says "we have one price for everyone" — a week later a VIP customer with special terms appears. ClientPrice as a separate model with a fallback to the base price is the minimal solution that covers 100% of cases.
(в) Автоматический утренний отчёт в 08:00 экономит час менеджера каждый день. Не нужно собирать заказы вручную, не нужно звонить пекарю — сводка уже в чате. Для команды из трёх человек это 30 часов в месяц. (c) An automated morning report at 08:00 saves the manager an hour every day. No need to collect orders manually, no need to call the baker — the summary is already in the chat. For a three-person team, that's 30 hours a month.