Untitled
unknown
plain_text
8 months ago
25 kB
11
Indexable
import logging
import sqlite3
import pandas as pd
import os
from datetime import datetime, timedelta
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, MessageHandler, filters, ContextTypes, ConversationHandler
# Настройка логирования
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
logger = logging.getLogger(__name__)
# 🔑 ЗАМЕНИТЕ НА ВАШ ТОКЕН
BOT_TOKEN = "ВАШ_ТОКЕН_БОТА"
# 👥 Добавьте ваш ID
ALLOWED_USERS = [6947728011]
# Состояния для ConversationHandler
AMOUNT, DESCRIPTION = range(2)
# Категории по умолчанию
EXPENSE_CATEGORIES = ["Закупка", "Аренда", "Зарплаты", "Реклама", "Налоги", "Прочее"]
INCOME_CATEGORIES = ["Продажи", "Услуги", "Инвестиции", "Прочее"]
class Database:
def __init__(self):
self.init_database()
def init_database(self):
"""Инициализация базы данных для бухгалтерии"""
conn = sqlite3.connect('accounting.db')
c = conn.cursor()
# Таблица транзакций
c.execute('''CREATE TABLE IF NOT EXISTS transactions
(id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL,
amount REAL NOT NULL,
category TEXT NOT NULL,
description TEXT NOT NULL,
date TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
# Таблица сотрудников
c.execute('''CREATE TABLE IF NOT EXISTS employees
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
salary REAL NOT NULL)''')
# Таблица налоговых настроек
c.execute('''CREATE TABLE IF NOT EXISTS tax_settings
(id INTEGER PRIMARY KEY AUTOINCREMENT,
tax_rate REAL NOT NULL)''')
# Начальные настройки
c.execute("SELECT COUNT(*) FROM tax_settings")
if c.fetchone()[0] == 0:
c.execute("INSERT INTO tax_settings (tax_rate) VALUES (?)", (15.0,))
conn.commit()
conn.close()
logger.info("✅ База данных бухгалтерии инициализирована")
def execute_query(self, query, params=()):
"""Выполнить запрос и вернуть результат"""
try:
conn = sqlite3.connect('accounting.db')
c = conn.cursor()
c.execute(query, params)
result = c.fetchall()
conn.commit()
conn.close()
return result
except Exception as e:
logger.error(f"Ошибка запроса: {e}")
return None
def add_transaction(self, transaction_type, amount, category, description):
"""Добавить транзакцию"""
return self.execute_query(
"INSERT INTO transactions (type, amount, category, description, date) VALUES (?, ?, ?, ?, ?)",
(transaction_type, amount, category, description, datetime.now().strftime("%Y-%m-%d"))
)
def get_monthly_transactions(self, year, month):
"""Получить транзакции за месяц"""
return self.execute_query(
"SELECT type, amount, category, description, date FROM transactions WHERE strftime('%Y', date) = ? AND strftime('%m', date) = ? ORDER BY date",
(str(year), str(month).zfill(2))
)
def get_employees(self):
"""Получить список сотрудников"""
return self.execute_query("SELECT name, salary FROM employees")
def add_employee(self, name, salary):
"""Добавить сотрудника"""
return self.execute_query(
"INSERT INTO employees (name, salary) VALUES (?, ?)",
(name, salary)
)
def delete_employee(self, name):
"""Удалить сотрудника"""
return self.execute_query("DELETE FROM employees WHERE name = ?", (name,))
def get_tax_rate(self):
"""Получить текущую ставку налога"""
result = self.execute_query("SELECT tax_rate FROM tax_settings WHERE id = 1")
return result[0][0] if result else 15.0
def update_tax_rate(self, tax_rate):
"""Обновить ставку налога"""
return self.execute_query("UPDATE tax_settings SET tax_rate = ? WHERE id = 1", (tax_rate,))
# Инициализация базы данных
db = Database()
def check_access(user_id):
"""Проверка доступа пользователя"""
return user_id in ALLOWED_USERS
# ===== ОСНОВНЫЕ ОБРАБОТЧИКИ =====
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Главное меню бухгалтерии"""
if not check_access(update.effective_user.id):
await update.message.reply_text("❌ Доступ запрещен")
return
keyboard = [
[InlineKeyboardButton("💸 Добавить расход", callback_data="add_expense")],
[InlineKeyboardButton("💰 Добавить доход", callback_data="add_income")],
[InlineKeyboardButton("📊 Отчет за месяц", callback_data="monthly_report")],
[InlineKeyboardButton("👥 Сотрудники", callback_data="employees")],
[InlineKeyboardButton("⚙️ Настройки", callback_data="settings")]
]
reply_markup = InlineKeyboardMarkup(keyboard)
if update.message:
await update.message.reply_text("📊 Бухгалтерский бот - Главное меню:", reply_markup=reply_markup)
else:
await update.callback_query.edit_message_text("📊 Бухгалтерский бот - Главное меню:", reply_markup=reply_markup)
async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик всех callback'ов"""
query = update.callback_query
await query.answer()
if not check_access(query.from_user.id):
await query.edit_message_text("❌ Доступ запрещен")
return
data = query.data
if data == "main_menu":
await show_main_menu(update, context)
elif data == "add_expense":
await add_expense_start(update, context)
elif data == "add_income":
await add_income_start(update, context)
elif data == "monthly_report":
await show_monthly_report_menu(update, context)
elif data == "employees":
await show_employees_menu(update, context)
elif data == "settings":
await show_settings_menu(update, context)
elif data.startswith("report_"):
year_month = data.split("_")[1]
year, month = year_month.split("-")
await generate_excel_report(update, context, int(year), int(month))
elif data == "add_employee":
context.user_data['waiting_for'] = 'employee_name'
await query.edit_message_text("👤 Введите имя сотрудника:")
elif data.startswith("delete_employee_"):
employee_name = data.split("_")[2]
db.delete_employee(employee_name)
await query.edit_message_text(f"✅ Сотрудник {employee_name} удален!")
await show_employees_menu(update, context)
elif data == "change_tax_rate":
context.user_data['waiting_for'] = 'tax_rate'
await query.edit_message_text("📊 Введите новую ставку налога в процентах:")
elif data.startswith("expense_cat_"):
category = data.replace("expense_cat_", "")
context.user_data['transaction_type'] = 'expense'
context.user_data['category'] = category
context.user_data['waiting_for'] = 'amount'
await query.edit_message_text(f"💵 Введите сумму в SEK для категории '{category}':")
elif data.startswith("income_cat_"):
category = data.replace("income_cat_", "")
context.user_data['transaction_type'] = 'income'
context.user_data['category'] = category
context.user_data['waiting_for'] = 'amount'
await query.edit_message_text(f"💵 Введите сумму в SEK для категории '{category}':")
elif data == "back_from_report":
await show_main_menu(update, context)
else:
await query.edit_message_text(f"Неизвестная команда: {data}")
async def show_main_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показать главное меню"""
keyboard = [
[InlineKeyboardButton("💸 Добавить расход", callback_data="add_expense")],
[InlineKeyboardButton("💰 Добавить доход", callback_data="add_income")],
[InlineKeyboardButton("📊 Отчет за месяц", callback_data="monthly_report")],
[InlineKeyboardButton("👥 Сотрудники", callback_data="employees")],
[InlineKeyboardButton("⚙️ Настройки", callback_data="settings")]
]
reply_markup = InlineKeyboardMarkup(keyboard)
if hasattr(update, 'callback_query') and update.callback_query:
await update.callback_query.edit_message_text("📊 Бухгалтерский бот - Главное меню:", reply_markup=reply_markup)
else:
await update.message.reply_text("📊 Бухгалтерский бот - Главное меню:", reply_markup=reply_markup)
# ===== ОБРАБОТЧИКИ ТРАНЗАКЦИЙ =====
async def add_expense_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Начало добавления расхода"""
query = update.callback_query
await query.answer()
keyboard = [[InlineKeyboardButton(category, callback_data=f"expense_cat_{category}")] for category in EXPENSE_CATEGORIES]
keyboard.append([InlineKeyboardButton("🔙 Назад", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("💸 Выберите категорию расхода:", reply_markup=reply_markup)
async def add_income_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Начало добавления дохода"""
query = update.callback_query
await query.answer()
keyboard = [[InlineKeyboardButton(category, callback_data=f"income_cat_{category}")] for category in INCOME_CATEGORIES]
keyboard.append([InlineKeyboardButton("🔙 Назад", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("💰 Выберите категорию дохода:", reply_markup=reply_markup)
# ===== ОБРАБОТКА ТЕКСТОВЫХ СООБЩЕНИЙ =====
async def handle_text_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик текстовых сообщений"""
user_id = update.effective_user.id
if not check_access(user_id):
return
text = update.message.text
waiting_for = context.user_data.get('waiting_for')
if waiting_for == 'amount':
# Обработка суммы
try:
amount = float(text.replace(",", "."))
context.user_data['amount'] = amount
context.user_data['waiting_for'] = 'description'
await update.message.reply_text("📝 Введите описание транзакции (источник дохода или цель расхода):")
except ValueError:
await update.message.reply_text("❌ Неверный формат суммы. Введите число:")
elif waiting_for == 'description':
# Обработка описания
description = text
# Получаем данные из контекста
transaction_type = context.user_data.get('transaction_type')
amount = context.user_data.get('amount')
category = context.user_data.get('category')
# Сохраняем транзакцию
db.add_transaction(transaction_type, amount, category, description)
# Очищаем состояние
context.user_data.clear()
emoji = "💸" if transaction_type == 'expense' else "💰"
await update.message.reply_text(f"{emoji} Транзакция успешно добавлена!")
# Возвращаем в главное меню
await show_main_menu(update, context)
elif waiting_for == 'employee_name':
context.user_data['employee_name'] = text
context.user_data['waiting_for'] = 'employee_salary'
await update.message.reply_text("💵 Введите зарплату сотрудника в SEK:")
elif waiting_for == 'employee_salary':
try:
salary = float(text.replace(",", "."))
db.add_employee(context.user_data['employee_name'], salary)
await update.message.reply_text(f"✅ Сотрудник {context.user_data['employee_name']} добавлен с зарплатой {salary} SEK!")
context.user_data.clear()
await show_employees_menu_direct(update.effective_chat.id, context)
except ValueError:
await update.message.reply_text("❌ Неверный формат зарплаты. Введите число:")
except sqlite3.IntegrityError:
await update.message.reply_text("❌ Сотрудник с таким именем уже существует!")
context.user_data.clear()
elif waiting_for == 'tax_rate':
try:
tax_rate = float(text.replace(",", "."))
db.update_tax_rate(tax_rate)
await update.message.reply_text(f"✅ Ставка налога изменена на {tax_rate}%!")
context.user_data.clear()
await show_settings_menu_direct(update.effective_chat.id, context)
except ValueError:
await update.message.reply_text("❌ Неверный формат. Введите число:")
else:
await update.message.reply_text("🤔 Не понял ваше сообщение. Используйте кнопки меню:")
await show_main_menu(update, context)
# ===== ОТЧЕТЫ =====
async def show_monthly_report_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показать меню отчетов по месяцам"""
query = update.callback_query
await query.answer()
# Генерируем список последних 6 месяцев
keyboard = []
today = datetime.now()
for i in range(6):
report_date = today - timedelta(days=30*i)
year = report_date.year
month = report_date.month
button_text = f"{report_date.strftime('%B %Y')}"
keyboard.append([InlineKeyboardButton(button_text, callback_data=f"report_{year}-{month}")])
keyboard.append([InlineKeyboardButton("🔙 Назад", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("📅 Выберите месяц для отчета:", reply_markup=reply_markup)
async def generate_excel_report(update: Update, context: ContextTypes.DEFAULT_TYPE, year: int, month: int):
"""Генерация Excel отчета за месяц"""
query = update.callback_query
await query.answer()
try:
transactions = db.get_monthly_transactions(year, month)
employees = db.get_employees()
tax_rate = db.get_tax_rate()
if not transactions:
await query.edit_message_text(f"❌ Нет данных за {month}/{year}")
return
# Подготовка данных для Excel
data = []
total_income = 0
total_expenses = 0
for trans_type, amount, category, description, date in transactions:
data.append({
'Дата': date,
'Тип': 'Доход' if trans_type == 'income' else 'Расход',
'Категория': category,
'Сумма (SEK)': amount,
'Описание': description
})
if trans_type == 'income':
total_income += amount
else:
total_expenses += amount
# Расчет налогов и прибыли
total_salary = sum(emp[1] for emp in employees) if employees else 0
net_profit_before_tax = total_income - total_expenses
tax_amount = net_profit_before_tax * (tax_rate / 100)
net_profit_after_tax = net_profit_before_tax - tax_amount
# Создаем DataFrame
df = pd.DataFrame(data)
# Создаем Excel файл
filename = f"accounting_report_{year}_{month:02d}.xlsx"
with pd.ExcelWriter(filename, engine='openpyxl') as writer:
# Лист с транзакциями
df.to_excel(writer, sheet_name='Транзакции', index=False)
# Сводный отчет
summary_data = {
'Показатель': [
'Общий доход (SEK)',
'Общие расходы (SEK)',
'Зарплаты сотрудников (SEK)',
'Прибыль до налогов (SEK)',
f'Налог ({tax_rate}%) (SEK)',
'Чистая прибыль (SEK)'
],
'Сумма': [
total_income,
total_expenses,
total_salary,
net_profit_before_tax,
tax_amount,
net_profit_after_tax
]
}
summary_df = pd.DataFrame(summary_data)
summary_df.to_excel(writer, sheet_name='Сводка', index=False)
# Лист с сотрудниками
if employees:
employees_df = pd.DataFrame(employees, columns=['Имя', 'Зарплата (SEK)'])
employees_df.to_excel(writer, sheet_name='Сотрудники', index=False)
# Отправляем файл
await query.message.reply_document(
document=open(filename, 'rb'),
filename=filename,
caption=f"📊 Отчет за {month:02d}.{year}"
)
# Удаляем временный файл
os.remove(filename)
# Добавляем кнопку "Назад" после отправки отчета
keyboard = [[InlineKeyboardButton("🔙 Назад в меню", callback_data="back_from_report")]]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("✅ Excel отчет сгенерирован и отправлен!", reply_markup=reply_markup)
except Exception as e:
logger.error(f"Ошибка генерации отчета: {e}")
await query.edit_message_text("❌ Ошибка при генерации отчета")
# ===== СОТРУДНИКИ =====
async def show_employees_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показать меню сотрудников"""
query = update.callback_query
await query.answer()
employees = db.get_employees()
text = "👥 Управление сотрудниками:\n\n"
total_salary = 0
if employees:
for name, salary in employees:
text += f"• {name}: {salary:.2f} SEK\n"
total_salary += salary
text += f"\n💵 Общий фонд зарплат: {total_salary:.2f} SEK"
else:
text += "Сотрудники не добавлены"
keyboard = [
[InlineKeyboardButton("➕ Добавить сотрудника", callback_data="add_employee")]
]
# Кнопки удаления для каждого сотрудника
for name, salary in employees:
keyboard.append([InlineKeyboardButton(f"🗑️ Удалить {name}", callback_data=f"delete_employee_{name}")])
keyboard.append([InlineKeyboardButton("🔙 Назад", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(text, reply_markup=reply_markup)
async def show_employees_menu_direct(chat_id: int, context: ContextTypes.DEFAULT_TYPE):
"""Показать меню сотрудников напрямую"""
employees = db.get_employees()
text = "👥 Управление сотрудниками:\n\n"
total_salary = 0
if employees:
for name, salary in employees:
text += f"• {name}: {salary:.2f} SEK\n"
total_salary += salary
text += f"\n💵 Общий фонд зарплат: {total_salary:.2f} SEK"
else:
text += "Сотрудники не добавлены"
keyboard = [
[InlineKeyboardButton("➕ Добавить сотрудника", callback_data="add_employee")]
]
for name, salary in employees:
keyboard.append([InlineKeyboardButton(f"🗑️ Удалить {name}", callback_data=f"delete_employee_{name}")])
keyboard.append([InlineKeyboardButton("🔙 Назад", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard)
await context.bot.send_message(chat_id=chat_id, text=text, reply_markup=reply_markup)
# ===== НАСТРОЙКИ =====
async def show_settings_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показать меню настроек"""
query = update.callback_query
await query.answer()
tax_rate = db.get_tax_rate()
text = f"⚙️ Настройки:\n\n"
text += f"📊 Текущая ставка налога: {tax_rate}%\n"
keyboard = [
[InlineKeyboardButton("📊 Изменить ставку налога", callback_data="change_tax_rate")],
[InlineKeyboardButton("🔙 Назад", callback_data="main_menu")]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(text, reply_markup=reply_markup)
async def show_settings_menu_direct(chat_id: int, context: ContextTypes.DEFAULT_TYPE):
"""Показать меню настроек напрямую"""
tax_rate = db.get_tax_rate()
text = f"⚙️ Настройки:\n\n"
text += f"📊 Текущая ставка налога: {tax_rate}%\n"
keyboard = [
[InlineKeyboardButton("📊 Изменить ставку налога", callback_data="change_tax_rate")],
[InlineKeyboardButton("🔙 Назад", callback_data="main_menu")]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await context.bot.send_message(chat_id=chat_id, text=text, reply_markup=reply_markup)
def main():
"""Основная функция запуска бота"""
try:
application = Application.builder().token(BOT_TOKEN).build()
# Обработчики команд
application.add_handler(CommandHandler("start", start))
# Обработчики callback'ов
application.add_handler(CallbackQueryHandler(handle_callback))
# Обработчик текстовых сообщений
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_text_message))
logger.info("🤖 Бухгалтерский бот запускается...")
print("=" * 50)
print("📊 БУХГАЛТЕРСКИЙ БОТ ЗАПУЩЕН!")
print("➡️ Напишите /start в Telegram")
print("=" * 50)
application.run_polling()
except Exception as e:
logger.error(f"❌ Ошибка запуска бота: {e}")
if __name__ == "__main__":
main()Editor is loading...
Leave a Comment