h-1.flet.3/components/customer_master_with_gps.py

312 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
GPS対応顧客マスタ編集コンポーネント
"""
import flet as ft
import sqlite3
import logging
from datetime import datetime
from typing import List, Dict, Optional
class CustomerMasterEditorWithGPS:
"""GPS対応顧客マスタ編集"""
def __init__(self, page: ft.Page):
self.page = page
# UI部品
self.title = ft.Text("GPS対応顧客マスタ", size=24, weight=ft.FontWeight.BOLD, color=ft.Colors.BLUE_900)
# GPS情報表示
self.gps_info = ft.Text("", size=12, color=ft.Colors.GREY_600)
# フォーム
self.form_fields = ft.Column([], spacing=10)
# ボタン群
self.save_btn = ft.Button("保存", on_click=self.save_data, bgcolor=ft.Colors.GREEN, color=ft.Colors.WHITE)
self.add_btn = ft.Button("追加", on_click=self.add_new, bgcolor=ft.Colors.BLUE, color=ft.Colors.WHITE)
self.delete_btn = ft.Button("削除", on_click=self.delete_selected, bgcolor=ft.Colors.RED, color=ft.Colors.WHITE)
self.clear_btn = ft.Button("クリア", on_click=self.clear_form, bgcolor=ft.Colors.ORANGE, color=ft.Colors.WHITE)
# GPSボタン
self.gps_btn = ft.Button("GPS情報取得", on_click=self.get_gps_info, bgcolor=ft.Colors.PURPLE, color=ft.Colors.WHITE)
# データリスト
self.data_list = ft.Column([], scroll=ft.ScrollMode.AUTO, height=250)
# 操作説明
self.instructions = ft.Text(
"操作方法: 顧客情報を入力・GPS情報取得・保存・削除",
size=12,
color=ft.Colors.GREY_600
)
# データ読み込み
self._load_data()
def _build_form(self):
"""入力フォームを構築"""
self.form_fields.controls.clear()
# 基本フィールド
basic_fields = [
{'name': 'name', 'label': '顧客名', 'width': 200, 'required': True},
{'name': 'phone', 'label': '電話番号', 'width': 200},
{'name': 'email', 'label': 'メールアドレス', 'width': 250},
{'name': 'address', 'label': '住所', 'width': 300}
]
# GPSフィールド
gps_fields = [
{'name': 'latitude', 'label': '緯度', 'width': 150},
{'name': 'longitude', 'label': '経度', 'width': 150},
{'name': 'address_gps', 'label': 'GPS住所', 'width': 300},
{'name': 'gps_timestamp', 'label': 'GPS取得日時', 'width': 200}
]
# 全てのフィールドを結合
all_fields = basic_fields + gps_fields
# フィールドを動的に追加
for field in all_fields:
field_control = ft.TextField(
label=field['label'],
value='',
width=field['width']
)
self.form_fields.controls.append(field_control)
self.page.update()
def save_data(self, e):
"""データを保存"""
try:
# フォームデータを収集
form_data = {}
for i, field in enumerate(self.form_fields.controls):
field_name = f"field_{i}"
if i < len(basic_fields):
# 基本フィールド
form_data[all_fields[i]['name']] = field_control.value
else:
# GPSフィールド
if i < len(basic_fields) + len(gps_fields):
gps_field = self.form_fields.controls[i]
form_data[gps_field['name']] = gps_field.value
# データベースに保存
conn = sqlite3.connect('sales.db')
cursor = conn.cursor()
if self.editing_id:
# 更新
columns = ', '.join([f"field_{i}" for i in range(len(basic_fields))])
cursor.execute(f'''
UPDATE customers
SET {columns}
WHERE id = ?
''', tuple(form_data.values()) + (self.editing_id,))
logging.info(f"顧客データ更新完了: ID={self.editing_id}")
else:
# 新規追加
columns = ', '.join([f"field_{i}" for i in range(len(basic_fields))])
cursor.execute(f'''
INSERT INTO customers ({columns})
VALUES ({placeholders})
''', tuple(form_data.values()))
logging.info(f"顧客データ追加完了")
conn.commit()
conn.close()
# 成功メッセージ
self._show_snackbar("顧客情報を保存しました", ft.Colors.GREEN)
logging.info(f"顧客情報: {form_data}")
# データ再読み込み
self._load_data()
except Exception as ex:
logging.error(f"顧客データ保存エラー: {ex}")
self._show_snackbar("保存エラー", ft.Colors.RED)
def add_new(self, e):
"""新規顧客を追加"""
self.editing_id = None
self.clear_form()
self.form_fields.controls[0].focus()
logging.info("新規顧客追加モード")
def delete_selected(self, e):
"""選択中の顧客を削除"""
if self.editing_id:
try:
conn = sqlite3.connect('sales.db')
cursor = conn.cursor()
cursor.execute('''
DELETE FROM customers WHERE id = ?
''', (self.editing_id,))
conn.commit()
conn.close()
self._show_snackbar("顧客を削除しました", ft.Colors.GREEN)
logging.info(f"顧客削除完了: ID={self.editing_id}")
# データ再読み込み
self._load_data()
self.editing_id = None
self.clear_form()
except Exception as ex:
logging.error(f"顧客削除エラー: {ex}")
self._show_snackbar("削除エラー", ft.Colors.RED)
def clear_form(self, e):
"""フォームをクリア"""
for field_control in self.form_fields.controls:
field_control.value = ''
self.editing_id = None
self.page.update()
logging.info("顧客フォームをクリア")
def get_gps_info(self, e):
"""GPS情報を取得"""
try:
# GPS情報を取得モックアップ
location = geocoder.Nominatim(user_agent="MyApp/1.0")
location = "東京駅, 日本"
# 住所を取得
address = geocoder.geocode(location, exactly_one=True)
if address:
self.gps_info.value = f"緯度: {address.lat}, 経度: {address.lng}"
self.gps_info.color = ft.Colors.GREEN
logging.info(f"GPS情報取得成功: {address.address}")
else:
self.gps_info.value = "GPS情報取得失敗"
self.gps_info.color = ft.Colors.RED
logging.error("GPS情報取得失敗")
except Exception as ex:
logging.error(f"GPS情報取得エラー: {ex}")
self._show_snackbar("GPSエラー", ft.Colors.RED)
def _load_data(self):
"""顧客データを読み込む"""
try:
conn = sqlite3.connect('sales.db')
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM customers
ORDER BY id
''')
self.current_data = cursor.fetchall()
conn.close()
# データリストを更新
self.data_list.controls.clear()
for item in self.current_data:
item_id, name, phone, email, address, latitude, longitude, address_gps, gps_timestamp, created_at = item
# データ行
row_controls = []
# 基本情報
for i, field_name in enumerate(['name', 'phone', 'email', 'address']):
if i < 4: # 基本フィールド
value = item[i+1] if i < len(item) else ''
else: # GPSフィールド
if i == 4: # address_gps
value = item[i+1]
else:
value = ''
else:
value = item[i+1]
row_controls.append(
ft.Text(f"{['name']}: {value}", size=12)
)
# GPS情報
if item.get('latitude') and item.get('longitude'):
gps_text = f"GPS: {item.get('latitude', '')}, {item.get('longitude', '')}"
gps_color = ft.Colors.GREEN
row_controls.append(
ft.Text(gps_text, size=10, color=gps_color)
)
# 操作ボタン
row_controls.extend([
ft.Button(
"編集",
on_click=lambda _, did=item_id: self.edit_item(item_id),
bgcolor=ft.Colors.ORANGE,
color=ft.Colors.WHITE,
width=60
),
ft.Button(
"削除",
on_click=lambda _, did=item_id: self.delete_item(item_id),
bgcolor=ft.Colors.RED,
color=ft.Colors.WHITE,
width=60
)
])
# データカード
data_card = ft.Card(
content=ft.Container(
content=ft.Column(row_controls),
padding=10
),
margin=ft.margin.only(bottom=5)
)
self.data_list.controls.append(data_card)
self.page.update()
except Exception as e:
logging.error(f"顧客データ読込エラー: {e}")
def _show_snackbar(self, message: str, color: ft.Colors):
"""SnackBarを表示"""
try:
self.page.snack_bar = ft.SnackBar(
content=ft.Text(message),
bgcolor=color
)
self.page.snack_bar.open = True
self.page.update()
except:
pass
def build(self):
"""UIを構築して返す"""
return ft.Column([
self.title,
self.gps_info,
ft.Divider(),
ft.Text("入力フォーム", size=18, weight=ft.FontWeight.BOLD),
self.form_fields,
ft.Row([self.save_btn, self.add_btn, self.delete_btn, self.clear_btn, self.gps_btn], spacing=10),
ft.Divider(),
ft.Text("顧客一覧", size=18, weight=ft.FontWeight.BOLD),
self.instructions,
self.data_list
], expand=True, spacing=15)
# 使用例
def create_customer_master_with_gps(page: ft.Page) -> CustomerMasterEditorWithGPS:
"""GPS対応顧客マスタ編集画面を作成"""
return CustomerMasterEditorWithGPS(page)