はじめに
今回は、ChatGPTを使って在庫管理システムを作成してみようと思います。
使うのは、python
で扱うことのできるDjango
を使っていきます。
要件定義
まず在庫管理システムにどんな機能を付けるかを明確にしておきます。
ChatGPTに出力してもらいましょう。
1. システム概要
倉庫内の商品や資材の在庫を効率的に管理するためのシステム。入出庫管理、在庫状況の可視化、アラート通知、レポート出力などを通じて、在庫管理の正確性と作業効率の向上を目指す。
2. 主要機能
-
入庫・出庫管理
- 新規入庫・出庫の登録
- 在庫数の自動更新
-
在庫状況の表示
- 商品ごとの在庫数表示
- 最終更新日時の表示
システム開発
では、さっそく作っていきましょう。
まずDjango
のインストール、プロジェクト・アプリケーション作成を行っていきます。
pip install django
django-admin startproject inventory_manager
cd inventory_manager
python manage.py startapp inventory_manager_app
これで、Django
のインストールとプロジェクト・アプリケーションの作成が完了です。
次に作成したアプリケーションを使うために設定を行います。inventory_manager/inventory_manager内のsettings.pyにアプリケーションの登録を行います。
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'inventory_manager_app.apps.InventoryManagerAppConfig',#追加
]
...
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
USE_I18N = True
USE_TZ = True
...
ここまで、できたらいよいよ要件定義で決めた機能の実行を行っていきます。
モデル作成
Django
はモデルとビューを使用してwebアプリケーションを作成していきます。モデル作成では、数値や文字などの値のフィールドを作っていきます。
具体的には、アプリケーションを作成したときに生成されたinventory_manager_app
フォルダの中のmodels.py
を編集することでモデルの実装を行うことができます。
今回は、以下の4つのモデルを作成していこうと思います。
- カテゴリモデル(Category)
- ロケーションモデル(Location)
- 商品モデル(Product)
- 在庫モデル(StockTransaction)
各モデルの細かい要素は、コードを見ながら確認しましょう。
from django.db import models
#カテゴリモデル
class Category(models.Model):
name = models.CharField(max_length=20,verbose_name='カテゴリ名')
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
#ロケーションモデル
class Location(models.Model):
name = models.CharField(max_length=50,verbose_name='保管場所')
description = models.TextField(verbose_name='保管場所住所')
def __str__(self):
return self.name
#商品モデル
class Product(models.Model):
name = models.CharField(max_length=50,verbose_name='商品名')
sku = models.CharField(max_length=50,verbose_name='商品識別用コード')
description = models.TextField(verbose_name='商品説明')
category = models.ForeignKey(Category,on_delete=models.CASCADE,verbose_name='カテゴリ')
location = models.ForeignKey(Location,on_delete=models.CASCADE,verbose_name='保管場所')
stock = models.PositiveIntegerField(default=0,verbose_name='在庫数')
min_stock_level = models.IntegerField(default=0,verbose_name='最小在庫数')
created_at = models.DateTimeField(auto_now_add=True,verbose_name='登録日時')
updated_at = models.DateTimeField(auto_now=True,verbose_name='更新日時')
def __str__(self):
return self.name
#在庫モデル
class StockTransaction(models.Model):
IN = 'IN'
OUT = 'OUT'
TRANSACTION_CHOICES = [
(IN,'入庫'),
(OUT,'出庫')
]
product = models.ForeignKey(Product,on_delete=models.CASCADE,verbose_name='商品')
quantity = models.PositiveIntegerField(verbose_name='入出庫数')
transaction_type = models.CharField(max_length=3,choices=TRANSACTION_CHOICES,verbose_name='取引タイプ(IN or OUT)')
note = models.TextField(blank=True,null=True,verbose_name='備考')
last_updated = models.DateTimeField(auto_now=True,verbose_name='最終更新日')
def __str__(self):
return f"{self.product.name} - {self.quantity} ({self. transaction_type})"
上記のコードのようにIntegerField
やCharField
などの整数値を扱うフィールド、文字列を扱うフィールドを設定していきます。引数値のverbose_name
をうまく使うことでこのフィールドはどんな値を示しているのかがわかりやすくなります。
Field
と記載がないForeignKey
はリレーションを行うフィールドで、リレーションとはデータベースのテーブル同士を関連付けることです。
商品モデルの category = models.ForeignKey(Category,on_delete=models.CASCADE,verbose_name="カテゴリ")
例にすると、商品モデルのcategory
という変数にカテゴリモデルの要素であるname
とdescription
を参照しているということです。
そしたら、記述したモデルをデータベースに適用していきましょう。マイグレーションと呼ばれるデータベース構造に反映させる仕組みを使います。やり方はすごく簡単です。
python manage.py makemigrations #変更点を検出し、マイグレーションファイルを作成
python manage.py migrate #データベースに適用
ビューの作成
まずは簡単に、商品の一覧を表示するページを作成してみましょう。
ビューの作成はアプリケーションを作成したときに生成されたinventory_manager_appフォルダの中のviews.pyを編集することで行うことができます。
from django.shortcuts import render,redirect
from django.views.generic import ListView,View
from .models import Product,StockTransaction
class ProductListView(ListView):
def get(self,request):
product_list = Product.objects.all()
return render(request,'inventory_manager_app/product_list.html',{'product_list':product_list})
product_list = ProductListView.as_view()
product_list
変数で商品モデルのすべてのオブジェクトを取得し、テンプレート(product_list.html
)に渡しています。
では、product_list.html
を作成していきます。
*テンプレートはアプリケーションフォルダ(inventory_manager_app)内にtemplates/inventory_manager_app
フォルダを作成してその中に格納します。
{% extends "base.html" %}
{% block content %}
<h2 class="product_list">商品一覧</h2>
<table>
<thead>
<tr>
<th>商品</th>
<th>カテゴリ</th>
<th>在庫</th>
<th>更新日時</th>
<th>備考</th>
</tr>
</thead>
<tbody>
{% for product in product_list %}
<tr>
<td>{{ product.name }}</td>
<td>{{ product.category }}</td>
<td>{{ product.stock }}</td>
<td>{{ product.updated_at }}</td>
<td>{{ product.note }}</td>
</tr>
{% empty %}
<tr>
<td colspan="5">履歴がありません。</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
上記のコードの最初{% extends "base.html" %}
は、base.html
を読み込むコードであり、テンプレートファイルで、同じ記述をする部分を記述しておくことで、何回も同じ記述をすることを避けることができます。
作成したProductListView
はただオブジェクトを表示するだけのものなので、商品の登録ができません。そこで、admin.py
を編集して、管理者画面から商品登録を行ってみようと思います。
from django.contrib import admin
from .models import Category,Location,Product,StockTransaction
#モデル登録
admin.site.register(Category)
admin.site.register(Location)
admin.site.register(Product)
admin.site.register(StockTransaction)
まずはこれだけ記述しておけば大丈夫でしょう。
サーバーを動かして、アプリケーションを使用するためにプロジェクトファイルのurls.py
にパス設定を行います。
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('inventory_manager_app.urls')), #追加
]
これで、サーバーを動かすと管理者画面にアクセスすることができます。管理者画面はログインが必要なので、スーパーユーザーを作成してからアクセスします。
python manage.py createsuperuser
python manage.py runserver
createsuperuser
でスーパーユーザーを作成し、runserver
でサーバーを起動します。runserver
を行うと
http://127.0.0.1:8000/
というリンクが出てくるので、この後に続けて、admin
と入力すると管理者画面にアクセスでき、商品の登録を行うことができます。
商品の登録を行うと先ほど作成したビューに登録した商品が表示されるはずです。inventory_manager_app/urls.py
を作成して、URL設定を行うとサーバー上でテンプレートファイルを表示することができます。
from django.urls import path
from .views import product_list
urlpatterns = [
path('product_list',product_list,name='product_list'),
]
このように記述することで、サーバーを起動後に、http://127.0.0.1:8000/product_list
とURLを打つと表示されます。
在庫数の自動更新
次に在庫数の自動更新機能を付けます。今のままだと商品の入出庫ができません。そのための在庫の変動を管理することができないため、入出庫に合わせて自動で在庫を更新する機能をつけます。
まずinventory_manager_app
フォルダにforms.py
を作成して、入出庫をフォーム入力できるようにしていきます。
from django import forms
from .models import StockTransaction,Product
class StockInForm(forms.ModelForm):
class Meta:
model = StockTransaction
fields = ['product','quantity','note']
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.fields['quantity'].widget.attrs['min'] = 0 #最小値を0に設定
def save(self,commit=True):
instance = super().save(commit=False)
instance.transaction_type = StockTransaction.IN #transaction_typeをINに固定
#在庫を増やす
product = instance.product
product.stock += instance.quantity
product.save() #在庫情報更新
if commit:
instance.save()
return instance
class StockOutForm(forms.ModelForm):
class Meta:
model = StockTransaction
fields = ['product','quantity','note']
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.fields['quantity'].widget.attrs['min'] = 0 #最小値を0に固定
#フォームバリデーション
def clean_quantity(self):
quantity = self.cleaned_data.get("quantity")
if quantity is None or quantity < 0:
raise forms.ValidationError("在庫数は0以上である必要があります。")
return quantity
def save(self,commit=True):
instance = super().save(commit=False)
instance.transaction_type = StockTransaction.OUT #transaction_typeをOUTに固定
#在庫を減らす
product = instance.product
if product.stock >= instance.quantity:
product.stock -= instance.quantity
product.save() #商品の在庫数を更新
else:
raise ValueError("在庫が不足しています。(現在の在庫:{})".format(product.stock))
if commit:
instance.save() #出庫情報を保存
return instance
StockInForm
とStockOutForm
と入庫と出庫別々のフォームを作成しました。
内容としては、在庫モデルで作成したproduct
,quantity
,note
フィールドを入力するフォームを作成して、save
関数の部分で、在庫の増減を計算して、在庫数を更新しています。
このフォームを表示するためのビューがこちらです。
class StockTrasaction(View):
def get(self,request):
inform = StockInForm()
outform = StockOutForm()
return render(request,'inventory_manager_app/stocktransaction.html',{'inform':inform,'outform':outform})
def post(self,request):
inform = StockInForm(request.POST)
outform = StockOutForm(request.POST)
# フォームの種類を判別するための隠しフィールドを追加することをお勧めします
if 'form_type' in request.POST:
if request.POST['form_type'] == 'in':
if inform.is_valid():
inform.save()
return redirect('stocktransaction_list')
else:
if outform.is_valid():
stock_transaction = outform.save()
product = stock_transaction.product
if product.stock <= product.min_stock_level:
messages.warning(request, f"⚠ {product.name} の在庫が最低レベルを下回りました!")
return redirect('stocktransaction_list')
return render(request, 'inventory_manager_app/stocktransaction.html',
{'inform': inform, 'outform': outform})
stocktransaction = StockTrasaction.as_view()
フォーム入力されるとpost
のほうが動き、フォーム内容の確認を行い、在庫数の更新を行っています。forms.py
のほうでもフォームバリデーションを記述しているので、views.py
のほうで記述する必要はないですが、このような記述もできるということで記述しました。
ビューの作成ができたら、テンプレートを作成していきます。
テンプレートは以下になります。
{% extends "base.html" %}
{% block content %}
<div class="transaction-forms">
<div class="form-section">
<h3>入庫登録</h3>
<form method="post" action="{% url 'stocktransaction' %}">
{% csrf_token %}
<input type="hidden" name="form_type" value="in">
{% for field in inform %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% if field.errors %}
<div class="error-message">
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<button type="submit">入庫を登録</button>
</form>
</div>
<div class="form-section">
<h3>出庫登録</h3>
<form method="post" action="{% url 'stocktransaction' %}">
{% csrf_token %}
<input type="hidden" name="form_type" value="out">
{% for field in outform %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% if field.errors %}
<div class="error-message">
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<button type="submit">出庫を登録</button>
</form>
</div>
</div>
{% endblock %}
{% for field in inform %}
がフォームの部分で、後々css
でUIを整えやすいように各フィールドのをfield
に格納して、表示しています。もしフォームでエラーが起きた場合、エラーメッセージが表示されるように{% if field.errors %}
のようにif
文を使用して、エラーメッセージが表示されるようにしています。
そして、先ほどと同様にURL設定をします。
from django.urls import path
from .views import product_list,stocktransaction
urlpatterns = [
path('product_list',product_list,name='product_list'),
path('stocktransaction',stocktransaction,name='stocktransaction'),
]
以上で、
-
入庫・出庫管理
- 新規入庫・出庫の登録
- 在庫数の自動更新
-
在庫状況の表示
- 商品ごとの在庫数表示
- 最終更新日時の表示
を満たすシステムができました。プログラムはChatGPT
に大まかを作成してもらい、エラー箇所を修正するといった方法で、充分できます。
あとは、デザイン部分であるcss
を丸投げしてみたり、検索機能、csvファイルのダウンロード機能などを付けてみても面白いかなと思います!!