import flet as ft import sqlite3 import signal import sys import logging from datetime import datetime def init_db(): conn = sqlite3.connect('sales.db') cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS sales ( id INTEGER PRIMARY KEY AUTOINCREMENT, customer TEXT NOT NULL, product TEXT NOT NULL, amount REAL NOT NULL, date TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') conn.commit() conn.close() return True def load_sales(): conn = sqlite3.connect('sales.db') cursor = conn.cursor() cursor.execute(''' SELECT customer, product, amount, date FROM sales ORDER BY created_at DESC LIMIT 20 ''') sales = cursor.fetchall() conn.close() return sales def add_sale(customer, product, amount): conn = sqlite3.connect('sales.db') cursor = conn.cursor() cursor.execute(''' INSERT INTO sales (customer, product, amount, date) VALUES (?, ?, ?, ?) ''', (customer, product, float(amount), datetime.now().strftime("%Y-%m-%d"))) conn.commit() conn.close() return True def cleanup_resources(): """リソースをクリーンアップ""" try: logging.info("アプリケーション終了処理開始") print("✅ 正常終了処理完了") logging.info("アプリケーション正常終了") except Exception as e: logging.error(f"クリーンアップエラー: {e}") print(f"❌ クリーンアップエラー: {e}") def signal_handler(signum, frame): """シグナルハンドラ""" print(f"\nシグナル {signum} を受信しました") cleanup_resources() sys.exit(0) def main(page: ft.Page): # ログ設定 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('app.log'), logging.StreamHandler() ] ) # シグナルハンドラ設定 signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) logging.info("アプリケーション起動") # データベース初期化 try: init_db() logging.info("データベース初期化完了") except Exception as e: logging.error(f"データベース初期化エラー: {e}") print(f"❌ データベース初期化エラー: {e}") page.title = "販売アシスト" page.window_width = 400 page.window_height = 600 # ウィンドウクローズイベント def on_window_close(e): logging.info("ウィンドウクローズイベント") cleanup_resources() page.on_window_close = on_window_close # UI要素定義 customer = ft.TextField(label="顧客名") product = ft.TextField(label="商品名") amount = ft.TextField(label="金額") list_view = ft.Column() def add_sale_clicked(e): if customer.value and product.value and amount.value: try: # 保存前に値を取得 customer_val = customer.value product_val = product.value amount_val = amount.value # データベースに保存 add_sale(customer_val, product_val, amount_val) # フィールドをクリア customer.value = "" product.value = "" amount.value = "" # リスト更新 update_list() # 成功メッセージ page.snack_bar = ft.SnackBar( content=ft.Text("保存しました"), bgcolor=ft.Colors.GREEN ) page.snack_bar.open = True page.update() logging.info(f"売上データ追加: {customer_val} {product_val} {amount_val}") except Exception as ex: logging.error(f"保存エラー: {ex}") page.snack_bar = ft.SnackBar( content=ft.Text("エラーが発生しました"), bgcolor=ft.Colors.RED ) page.snack_bar.open = True page.update() def update_list(): try: list_view.controls.clear() sales = load_sales() for sale in sales: customer, product, amount, date = sale list_view.controls.append( ft.Text(f"{date}: {customer} - {product}: ¥{amount:,.0f}") ) except Exception as e: logging.error(f"リスト更新エラー: {e}") # ボタン定義(関数定義後) add_btn = ft.Button("追加", on_click=add_sale_clicked) # 初期データ読み込み update_list() logging.info("UI初期化完了") print("🚀 アプリケーション起動完了") page.add( ft.Text("売上管理", size=20), customer, product, amount, add_btn, ft.Divider(), ft.Text("売上一覧", size=16), list_view ) if __name__ == "__main__": ft.app(target=main)