【Python】在庫管理システムを自作する方法

【Python】在庫管理システムを自作する方法

公開: 更新:



Pythonで在庫管理システムを自作する方法

Pythonを使用して在庫管理システムを構築する場合、クラスベースの設計が効率的です。商品IDや名称、価格、在庫数、カテゴリといった基本的な属性を持つ商品クラスを定義することで、システムの枠組みを作ることが可能です。データの永続化には、SQLiteやCSVファイルなどの軽量なデータストレージが適しています。

操作性を高めるインターフェースとして、コマンドラインインターフェース(CLI)から始め、後にTkinterPyQtなどのライブラリを利用してグラフィカルユーザーインターフェース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やプログラミングに関するコラム


ITやプログラミングに関するニュース

ブログに戻る

コメントを残す

コメントは公開前に承認される必要があることにご注意ください。

コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア 金融業界の業務効率化を加速するニッセイアセットマネジメントの生成AI×GAS活用研修事例 - IT・プログラミングを知って学べるコネクトメディア 【製造業のDX人材育成事例】デジタル人材の即戦力化を実現する、日本ガイシ株式会社の異動者向オンボーディング研修 - ITやプログラミングを知って学べるコネクトメディア フューチャーアーキテクト株式会社が実現した新入社員向けIT研修プログラムでタスクフォース制度が主体的な学びと成長を生み出す - IT・プログラミングを知って学べるコネクトメディア コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【IT新入社員研修】オンラインとオフラインの最適バランスを実現したFutureOneの導入事例 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【新入社員研修】柔軟なハイブリッド型Java研修で実現した新卒20名の成長と成果|サークレイス株式会社 - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/現場により近いところにデジタルを根付かせるDX基礎講座研修|株式会社ブリヂストン - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/業務の効率化・DX推進に向けたIT人材育成への第一歩|株式会社カナエ - ITやプログラミングを知って学べるコネクトメディア 企業・法人向けのIT・プログラミング研修 - ITやプログラミングを知って学べるコネクトメディア

新着記事

対象者別で探す

子供(小学生・中学生・高校生)向け
プログラミング教室検索する

子供(小学生・中学生・高校生)がロボットやプログラミング言語を学ぶことができるオフラインからオンラインスクールを検索、比較することが可能です。

子供(小学生・中学生・高校生)
プログラミング教室検索する

ITやプログラムなどの
最新情報を検索する

日々、新しいITやプログラミング言語の情報が流れていきますが、特定の情報を時系列でニュースやコラムを確認することができます。

ITやプログラムなどの
最新情報を検索する