
【Python】在庫管理システムを自作する方法
公開: 更新:Pythonで在庫管理システムを自作する方法
Pythonを使用して在庫管理システムを構築する場合、クラスベースの設計が効率的です。商品IDや名称、価格、在庫数、カテゴリといった基本的な属性を持つ商品クラスを定義することで、システムの枠組みを作ることが可能です。データの永続化には、SQLiteやCSVファイルなどの軽量なデータストレージが適しています。
操作性を高めるインターフェースとして、コマンドラインインターフェース(CLI)から始め、後にTkinterやPyQtなどのライブラリを利用してグラフィカルユーザーインターフェース(GUI)へ拡張することも可能です。
import json
import os
from datetime import datetime
class InventorySystem:
def __init__(self, storage_file="inventory.json"):
self.storage_file = storage_file
self.inventory = {}
self.load_inventory()
def load_inventory(self):
if os.path.exists(self.storage_file):
with open(self.storage_file, 'r', encoding='utf-8') as f:
self.inventory = json.load(f)
else:
self.inventory = {}
def save_inventory(self):
with open(self.storage_file, 'w', encoding='utf-8') as f:
json.dump(self.inventory, f, ensure_ascii=False, indent=4)
def add_item(self, item_id, name, price, quantity, category):
self.inventory[item_id] = {
'name': name,
'price': price,
'quantity': quantity,
'category': category,
'last_updated': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
self.save_inventory()
return True
def display_inventory(self):
print("\n現在の在庫状況:")
print("ID\t商品名\t\t価格\t数量\tカテゴリ")
print("-" * 60)
for item_id, details in self.inventory.items():
print(f"{item_id}\t{details['name']}\t\t{details['price']}\t{details['quantity']}\t{details['category']}")
# 在庫管理システムのテスト
if __name__ == "__main__":
inv_sys = InventorySystem()
inv_sys.add_item("A001", "ノートPC", 80000, 5, "電子機器")
inv_sys.add_item("A002", "プリンター", 25000, 3, "電子機器")
inv_sys.add_item("B001", "事務用椅子", 12000, 10, "家具")
inv_sys.display_inventory()
行番号 | 詳細説明 |
---|---|
1-3行目 | 必要なモジュール(json、os、datetime)をインポート |
5行目 | 在庫管理システムのクラスを定義 |
6-9行目 | 初期化メソッド:ストレージファイルの設定と在庫データの読み込み |
11-16行目 | inventory.jsonファイルから在庫データを読み込むメソッド |
18-20行目 | 在庫データをJSON形式でファイルに保存するメソッド |
22-30行目 | 商品を在庫に追加するメソッド:ID、名前、価格、数量、カテゴリを指定 |
32-37行目 | 現在の在庫状況を表示するメソッド |
40-44行目 | メインプログラム:在庫システムの初期化と商品追加、表示のテスト |
【実行結果】
現在の在庫状況:
ID 商品名 価格 数量 カテゴリ
------------------------------------------------------------
A001 ノートPC 80000 5 電子機器
A002 プリンター 25000 3 電子機器
B001 事務用椅子 12000 10 家具
【PR】『Python』を学べる企業・個人向けのプログラミングコース
在庫管理に必要なデータ構造の設計
在庫管理システムのデータ構造設計では、商品情報の相互関係を考慮したモデリングが重要です。商品クラス(Item)と在庫管理クラス(Inventory)を分離し、それぞれの責任を明確にすることで、将来的な機能拡張や保守性が向上します。
class Item:
def __init__(self, item_id, name, price, category):
self.item_id = item_id
self.name = name
self.price = price
self.category = category
self.created_at = datetime.now()
class Inventory:
def __init__(self, db_file="inventory.db"):
self.db_file = db_file
self.items = {}
self.transactions = []
self.setup_database()
def setup_database(self):
import sqlite3
conn = sqlite3.connect(self.db_file)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS items (
item_id TEXT PRIMARY KEY,
name TEXT NOT NULL,
price REAL,
category TEXT,
created_at TEXT
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS stock (
item_id TEXT,
quantity INTEGER,
last_updated TEXT,
FOREIGN KEY (item_id) REFERENCES items (item_id)
)
''')
conn.commit()
conn.close()
行番号 | 詳細説明 |
---|---|
1-6行目 | 商品情報を管理するItemクラスの定義 |
8-12行目 | 在庫管理を行うInventoryクラスの初期化 |
14-30行目 | SQLiteデータベースの初期設定メソッド |
16-23行目 | 商品情報を格納するitemsテーブルの作成 |
24-29行目 | 在庫数量を管理するstockテーブルの作成 |
在庫の追加・削除・更新機能の作成
在庫管理システムの核となる機能は、商品の追加・削除・更新処理です。入力値のバリデーションを徹底し、データの整合性を保ちながらエラーを防止することが重要です。
def add_item(self, item_id, name, price, quantity, category):
try:
# 入力値のバリデーション
if not item_id or not name:
raise ValueError("商品IDと商品名は必須です")
if price < 0 or quantity < 0:
raise ValueError("価格と数量は0以上である必要があります")
# データベースに商品を追加
import sqlite3
conn = sqlite3.connect(self.db_file)
cursor = conn.cursor()
# トランザクション開始
cursor.execute("BEGIN TRANSACTION")
# 商品情報の登録
cursor.execute(
"INSERT INTO items (item_id, name, price, category, created_at) VALUES (?, ?, ?, ?, ?)",
(item_id, name, price, category, datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
)
# 在庫情報の登録
cursor.execute(
"INSERT INTO stock (item_id, quantity, last_updated) VALUES (?, ?, ?)",
(item_id, quantity, datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
)
# トランザクションのコミット
conn.commit()
conn.close()
return True
except Exception as e:
# エラー発生時はロールバック
if 'conn' in locals() and conn:
conn.rollback()
conn.close()
print(f"エラー発生: {e}")
return False
def update_stock(self, item_id, new_quantity):
try:
if new_quantity < 0:
raise ValueError("在庫数量は0以上である必要があります")
import sqlite3
conn = sqlite3.connect(self.db_file)
cursor = conn.cursor()
# 商品の存在確認
cursor.execute("SELECT * FROM items WHERE item_id = ?", (item_id,))
if not cursor.fetchone():
raise ValueError(f"商品ID {item_id} は存在しません")
# 在庫数量の更新
cursor.execute(
"UPDATE stock SET quantity = ?, last_updated = ? WHERE item_id = ?",
(new_quantity, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), item_id)
)
conn.commit()
conn.close()
return True
except Exception as e:
if 'conn' in locals() and conn:
conn.rollback()
conn.close()
print(f"エラー発生: {e}")
return False
行番号 | 詳細説明 |
---|---|
1-31行目 | 商品を在庫に追加するメソッド |
3-7行目 | 入力値のバリデーション処理 |
14行目 | データベーストランザクションの開始 |
17-20行目 | 商品情報をitemsテーブルに登録 |
23-26行目 | 在庫情報をstockテーブルに登録 |
29行目 | トランザクションのコミット |
32-37行目 | エラー処理とロールバック |
39-63行目 | 在庫数量を更新するメソッド |
47-50行目 | 商品の存在確認 |
53-56行目 | 在庫数量の更新処理 |
【PR】『Python』を学べる個人・中高生向けのプログラミングコース
検索・フィルタリング機能の追加
大量の商品データから必要な情報を素早く抽出するには、検索・フィルタリング機能が不可欠です。カテゴリ、価格帯、在庫状況などの複数条件による絞り込みができると便利なので、実装することをおすすめします。
def search_items(self, keyword=None, category=None, min_price=None, max_price=None, min_stock=None):
import sqlite3
conn = sqlite3.connect(self.db_file)
cursor = conn.cursor()
query = """
SELECT i.item_id, i.name, i.price, i.category, s.quantity
FROM items i
JOIN stock s ON i.item_id = s.item_id
WHERE 1=1
"""
params = []
# 検索条件の追加
if keyword:
query += " AND (i.name LIKE ? OR i.item_id LIKE ?)"
keyword_param = f"%{keyword}%"
params.extend([keyword_param, keyword_param])
if category:
query += " AND i.category = ?"
params.append(category)
if min_price is not None:
query += " AND i.price >= ?"
params.append(min_price)
if max_price is not None:
query += " AND i.price <= ?"
params.append(max_price)
if min_stock is not None:
query += " AND s.quantity >= ?"
params.append(min_stock)
cursor.execute(query, params)
results = cursor.fetchall()
conn.close()
# 結果をリスト形式で返す
items = []
for row in results:
items.append({
"item_id": row[0],
"name": row[1],
"price": row[2],
"category": row[3],
"quantity": row[4]
})
return items
def display_search_results(self, items, page=1, items_per_page=10):
if not items:
print("検索結果がありません")
return
total_pages = (len(items) + items_per_page - 1) // items_per_page
if page < 1:
page = 1
elif page > total_pages:
page = total_pages
start_idx = (page - 1) * items_per_page
end_idx = min(start_idx + items_per_page, len(items))
print(f"\n検索結果 ({len(items)}件中 {start_idx+1}-{end_idx}件表示, {page}/{total_pages}ページ)")
print("ID\t商品名\t\t価格\t数量\tカテゴリ")
print("-" * 60)
for item in items[start_idx:end_idx]:
print(f"{item['item_id']}\t{item['name']}\t\t{item['price']}\t{item['quantity']}\t{item['category']}")
print(f"\nページ: {page}/{total_pages}")
if total_pages > 1:
print("前後のページを表示するには 'prev'/'next' と入力してください")
行番号 | 詳細説明 |
---|---|
1-42行目 | 複数条件で商品を検索するメソッド |
7-12行目 | SQLクエリの基本部分を定義 |
15-34行目 | 各検索条件に応じてクエリを動的に構築 |
36-37行目 | 構築したクエリを実行して結果を取得 |
42-51行目 | SQLの結果を辞書形式に変換して返す |
53-80行目 | 検索結果を表示するメソッド(ページネーション機能付き) |
60-66行目 | 表示するページの範囲計算 |
68-74行目 | 検索結果の表形式での表示 |
76-79行目 | ページ情報の表示と操作ガイド |
在庫アラートと通知システムの設計
在庫量が特定のしきい値を下回った際に自動通知を行うアラートシステムを実装することで、在庫切れリスクを事前に把握できます。通知方法はメールやSMS、デスクトップ通知など、状況や重要度に応じた適切な手段を選択できるようにすると良いでしょう。
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
class InventoryAlert:
def __init__(self, inventory_system, low_stock_threshold=5):
self.inventory = inventory_system
self.threshold = low_stock_threshold
self.email_config = {
"smtp_server": "smtp.example.com",
"port": 587,
"username": "username@example.com",
"password": "your_password",
"from_email": "inventory@example.com"
}
self.notification_targets = {
"email": ["manager@example.com", "stock@example.com"],
"sms": ["+1234567890"]
}
def check_low_stock(self):
import sqlite3
conn = sqlite3.connect(self.inventory.db_file)
cursor = conn.cursor()
cursor.execute("""
SELECT i.item_id, i.name, i.category, s.quantity
FROM items i
JOIN stock s ON i.item_id = s.item_id
WHERE s.quantity <= ?
ORDER BY s.quantity ASC
""", (self.threshold,))
low_stock_items = cursor.fetchall()
conn.close()
if low_stock_items:
self.send_alerts(low_stock_items)
return low_stock_items
def send_email_alert(self, low_stock_items):
message = MIMEMultipart()
message["Subject"] = f"在庫アラート: {len(low_stock_items)}件の商品が在庫不足"
message["From"] = self.email_config["from_email"]
message["To"] = ", ".join(self.notification_targets["email"])
# メール本文の作成
body = "以下の商品が在庫不足しています:\n\n"
body += "商品ID\t商品名\t\tカテゴリ\t在庫数\n"
body += "-" * 60 + "\n"
for item in low_stock_items:
body += f"{item[0]}\t{item[1]}\t\t{item[2]}\t{item[3]}\n"
body += "\n\n早急に発注してください。"
message.attach(MIMEText(body, "plain"))
try:
server = smtplib.SMTP(self.email_config["smtp_server"], self.email_config["port"])
server.starttls()
server.login(self.email_config["username"], self.email_config["password"])
server.send_message(message)
server.quit()
print("在庫アラートメールを送信しました")
return True
except Exception as e:
print(f"メール送信エラー: {e}")
return False
def send_alerts(self, low_stock_items):
# メールアラートの送信
self.send_email_alert(low_stock_items)
# その他の通知方法も実装可能
# self.send_sms_alert(low_stock_items)
# self.send_desktop_notification(low_stock_items)
def generate_inventory_report(self, report_type="daily"):
import sqlite3
from datetime import datetime, timedelta
conn = sqlite3.connect(self.inventory.db_file)
cursor = conn.cursor()
today = datetime.now()
if report_type == "daily":
title = f"日次在庫レポート ({today.strftime('%Y-%m-%d')})"
elif report_type == "weekly":
start_date = (today - timedelta(days=today.weekday())).strftime('%Y-%m-%d')
end_date = today.strftime('%Y-%m-%d')
title = f"週次在庫レポート ({start_date} から {end_date})"
elif report_type == "monthly":
title = f"月次在庫レポート ({today.strftime('%Y年%m月')})"
# 在庫状況のサマリを取得
cursor.execute("""
SELECT
COUNT(*) as total_items,
SUM(s.quantity) as total_stock,
COUNT(CASE WHEN s.quantity <= ? THEN 1 ELSE NULL END) as low_stock_count
FROM items i
JOIN stock s ON i.item_id = s.item_id
""", (self.threshold,))
summary = cursor.fetchone()
# カテゴリ別の集計
cursor.execute("""
SELECT
i.category,
COUNT(*) as item_count,
SUM(s.quantity) as total_quantity,
AVG(i.price) as avg_price
FROM items i
JOIN stock s ON i.item_id = s.item_id
GROUP BY i.category
ORDER BY total_quantity DESC
""")
category_stats = cursor.fetchall()
conn.close()
# レポートの生成と出力(ここではコンソール出力のみ)
print("\n" + "=" * 60)
print(f"{title}")
print("=" * 60)
print(f"総商品数: {summary[0]}")
print(f"総在庫数: {summary[1]}")
print(f"在庫不足商品数: {summary[2]}")
print("\nカテゴリ別統計:")
print("カテゴリ\t\t商品数\t総在庫\t平均価格")
print("-" * 60)
for stat in category_stats:
print(f"{stat[0]}\t\t{stat[1]}\t{stat[2]}\t{stat[3]:.2f}")
print("\n※このレポートは自動生成されています")
# レポートをファイルに保存したりメールで送信したりする機能も追加可能
return True
行番号 | 詳細説明 |
---|---|
1-4行目 | メール送信に必要なモジュールをインポート |
6-17行目 | 在庫アラートクラスの初期化とメール設定 |
19-36行目 | 在庫不足商品を検出するメソッド |
38-63行目 | メールによるアラート通知を送信するメソッド |
65-71行目 | 複数の通知方法を統合する送信メソッド |
73-129行目 | 在庫レポートを生成するメソッド |
82-89行目 | レポートタイプ(日次/週次/月次)に応じたタイトル設定 |
92-100行目 | 在庫状況の概要情報取得SQL |
105-115行目 | カテゴリ別の集計データ取得SQL |
119-128行目 | レポート情報のコンソール出力 |
※上記コンテンツの内容やソースコードはAIで確認・デバッグしておりますが、間違いやエラー、脆弱性などがある場合は、コメントよりご報告いただけますと幸いです。
ITやプログラミングに関するコラム
- 【Python】画像認識で個数カウントをする方法
- 【Python】ChatGPTでコード生成をするプロンプトの作り方
- 【Python】pipenvでバージョン変更を行う方法
- Pythonを使ったAndroidアプリの作り方(開発方法)