h-1.flet.3/main_simple.py

311 lines
10 KiB
Python

import flet as ft
import sqlite3
import datetime
from typing import List, Dict, Optional
async def main(page: ft.Page):
# データベース初期化
init_database()
page.title = "販売アシスト1号"
page.theme_mode = ft.ThemeMode.LIGHT
page.window_width = 450
page.window_height = 800
async def refresh_all():
await list_view.refresh()
# Viewの生成
input_view = get_input_view(page, refresh_all)
list_view = get_list_view(page, input_view)
# ナビゲーション制御
async def nav_change(e):
idx = e.control.selected_index
input_view.visible = (idx == 0)
list_view.visible = (idx == 1)
if list_view.visible:
await list_view.refresh()
page.update()
page.navigation_bar = ft.NavigationBar(
selected_index=1, # 最初はリスト
destinations=[
ft.NavigationBarDestination(icon=ft.Icons.EDIT_NOTE, label="売上起票"),
ft.NavigationBarDestination(icon=ft.Icons.LIST_ALT, label="一覧"),
],
on_change=nav_change
)
# 画面への追加
page.add(ft.Container(
content=ft.Column([input_view, list_view], expand=True),
padding=10, expand=True
))
# 初期表示設定
input_view.visible = False
list_view.visible = True
await list_view.refresh()
page.update()
def init_database():
"""Initialize SQLite database"""
conn = sqlite3.connect('sales.db')
cursor = conn.cursor()
# Create sales table
cursor.execute('''
CREATE TABLE IF NOT EXISTS sales (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
customer TEXT NOT NULL,
product TEXT NOT NULL,
amount REAL NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
conn.commit()
conn.close()
class InputView(ft.Column):
def __init__(self, page: ft.Page, on_success):
super().__init__(expand=True, spacing=15, visible=False)
self.my_page = page
self.on_success = on_success
self.edit_id = None
# UI部品の定義
self.title = ft.Text("売上伝票の起票", size=24, weight="bold", color=ft.Colors.BLUE_900)
self.date_tf = ft.TextField(
label="伝票日付",
value=datetime.datetime.now().strftime("%Y-%m-%d"),
width=200,
border_radius=10
)
self.customer_tf = ft.TextField(label="顧客名", border_radius=10)
self.product_tf = ft.TextField(label="項目/商品名", border_radius=10)
self.amount_tf = ft.TextField(
label="金額 (税込)",
width=200,
keyboard_type=ft.KeyboardType.NUMBER,
prefix_icon=ft.Icons.ATTACH_MONEY,
border_radius=10
)
self.save_btn = ft.ElevatedButton(
"保存",
icon=ft.Icons.SAVE,
on_click=self.save_data,
bgcolor=ft.Colors.BLUE_900,
color=ft.Colors.WHITE
)
self.controls = [
self.title,
ft.Row([self.date_tf, self.customer_tf], spacing=10),
self.product_tf,
self.amount_tf,
self.save_btn
]
async def save_data(self, e):
try:
if not all([self.customer_tf.value, self.product_tf.value, self.amount_tf.value]):
self.my_page.snack_bar = ft.SnackBar(
content=ft.Text("すべての項目を入力してください"),
bgcolor=ft.Colors.RED_500
)
self.my_page.snack_bar.open = True
self.my_page.update()
return
amount = float(self.amount_tf.value)
conn = sqlite3.connect('sales.db')
cursor = conn.cursor()
if self.edit_id:
cursor.execute('''
UPDATE sales
SET date=?, customer=?, product=?, amount=?
WHERE id=?
''', (self.date_tf.value, self.customer_tf.value, self.product_tf.value, amount, self.edit_id))
else:
cursor.execute('''
INSERT INTO sales (date, customer, product, amount)
VALUES (?, ?, ?, ?)
''', (self.date_tf.value, self.customer_tf.value, self.product_tf.value, amount))
conn.commit()
conn.close()
# フォームをクリア
self.clear_form()
# 成功メッセージ
self.my_page.snack_bar = ft.SnackBar(
content=ft.Text("保存しました"),
bgcolor=ft.Colors.GREEN_500
)
self.my_page.snack_bar.open = True
self.my_page.update()
# 一覧を更新
await self.on_success()
except ValueError:
self.my_page.snack_bar = ft.SnackBar(
content=ft.Text("金額は数値で入力してください"),
bgcolor=ft.Colors.RED_500
)
self.my_page.snack_bar.open = True
self.my_page.update()
except Exception as ex:
self.my_page.snack_bar = ft.SnackBar(
content=ft.Text(f"エラー: {str(ex)}"),
bgcolor=ft.Colors.RED_500
)
self.my_page.snack_bar.open = True
self.my_page.update()
def clear_form(self):
self.customer_tf.value = ""
self.product_tf.value = ""
self.amount_tf.value = ""
self.date_tf.value = datetime.datetime.now().strftime("%Y-%m-%d")
self.edit_id = None
self.update()
class ListView(ft.Column):
def __init__(self, page: ft.Page, input_view):
super().__init__(expand=True, visible=True)
self.my_page = page
self.input_view = input_view
self.data_list = ft.ListView(expand=True, spacing=5)
self.title = ft.Text("売上一覧", size=20, weight="bold")
self.controls = [self.title, self.data_list]
async def refresh(self):
try:
conn = sqlite3.connect('sales.db')
cursor = conn.cursor()
cursor.execute('''
SELECT id, date, customer, product, amount
FROM sales
ORDER BY date DESC, created_at DESC
LIMIT 50
''')
self.data_list.controls.clear()
for row in cursor.fetchall():
sale_id, date, customer, product, amount = row
card = ft.Card(
content=ft.Container(
content=ft.ListTile(
title=ft.Text(f"{customer} - {product}"),
subtitle=ft.Text(f"{date} | ¥{amount:,.0f}"),
trailing=ft.Row([
ft.IconButton(
icon=ft.Icons.EDIT,
on_click=lambda e, id=sale_id: self.edit_data(id)
),
ft.IconButton(
icon=ft.Icons.DELETE,
on_click=lambda e, id=sale_id: self.delete_data(id)
)
])
),
padding=10
)
)
self.data_list.controls.append(card)
conn.close()
self.update()
except Exception as ex:
self.my_page.snack_bar = ft.SnackBar(
content=ft.Text(f"データ読み込みエラー: {str(ex)}"),
bgcolor=ft.Colors.RED_500
)
self.my_page.snack_bar.open = True
self.my_page.update()
def edit_data(self, sale_id):
try:
conn = sqlite3.connect('sales.db')
cursor = conn.cursor()
cursor.execute('''
SELECT date, customer, product, amount
FROM sales
WHERE id=?
''', (sale_id,))
row = cursor.fetchone()
conn.close()
if row:
date, customer, product, amount = row
self.input_view.date_tf.value = date
self.input_view.customer_tf.value = customer
self.input_view.product_tf.value = product
self.input_view.amount_tf.value = str(amount)
self.input_view.edit_id = sale_id
# 入力画面を表示
self.input_view.visible = True
self.visible = False
self.my_page.update()
except Exception as ex:
self.my_page.snack_bar = ft.SnackBar(
content=ft.Text(f"データ読み込みエラー: {str(ex)}"),
bgcolor=ft.Colors.RED_500
)
self.my_page.snack_bar.open = True
self.my_page.update()
def delete_data(self, sale_id):
try:
conn = sqlite3.connect('sales.db')
cursor = conn.cursor()
cursor.execute("DELETE FROM sales WHERE id=?", (sale_id,))
conn.commit()
conn.close()
self.my_page.snack_bar = ft.SnackBar(
content=ft.Text("削除しました"),
bgcolor=ft.Colors.GREEN_500
)
self.my_page.snack_bar.open = True
self.my_page.update()
# 一覧を更新
self.refresh()
except Exception as ex:
self.my_page.snack_bar = ft.SnackBar(
content=ft.Text(f"削除エラー: {str(ex)}"),
bgcolor=ft.Colors.RED_500
)
self.my_page.snack_bar.open = True
self.my_page.update()
def get_input_view(page: ft.Page, on_success):
return InputView(page, on_success)
def get_list_view(page: ft.Page, input_view):
return ListView(page, input_view)
if __name__ == "__main__":
ft.app(target=main)