본문 바로가기
Python/Flask

[Day 7] WMS(Warehouse Management System) 구성 및 요구 사항 정의 2

by 깨준 2024. 7. 23.

재고 관리

  • 재고 기록: 모든 재고의 위치, 수량, 상태 등을 실시간으로 기록하고 관리합니다.
  • 재고 이동: 재고의 이동 기록을 추적하여 정확한 위치 정보를 제공합니다.
  • 재고 조정: 재고의 손실, 손상, 반품 등의 이유로 인한 재고 조정을 관리합니다.

연습문제 1~2

문제 1 : 필터링

  • 진행상태가 “입고완료”로 되어 있는 레코드 찾기

  • 진행상태가 “입고완료”중 화주와 상품종류가 같은 레코드 찾기

  • 위 첫 번째와 두 번째 조건을 만족하는 레코드를 최근 저장된 레코드 부터 출력하기

 

문제 2 : 재고 저장 관리

  • 다음과 같은 재고 테이블을 만드시오

 

재고 관리 실습예제

1. 재고기록 및 조회

다음은 입고 완료된 정보에 대해 재고 기록(입고완료 후 자동으로 기록)을 하고 조회를 하기 위한 요구사항(모든 재고의 위치, 수량, 상태 )을 반영하여 화면설계 및 기능 설계를 한 예제입니다.

  • 입고 완료된 레코드 중 재고에 반영된 레코드는 입고테이블에서 재고반영완료로 처리
  • 재고 수량의 update는 화주와 상품종류가 같을 때 입고 완료된 입고수량을 재고 수량에 더해서 저장

재고 조회 페이지

2. 재고이동 및 재고조정

다음은 재고 완료된 정보에 대해 재고이동은 “적치장소”를 조정, 재고조정(반품등)은 “진행상태”를 조정 반영하여 화면설계 및 기능설계를 한 예제입니다.

  • 재고이동 및 재고조정을 위해 GNB에서 재고이동과 재고조정 항목을 재고관리로 수정

제고 관리 페이지

 

디렉토리 구조

site2/
├── app.py
└── templates/
    ├── enterRegist.html
    ├── enterView.html
    ├── stockView.html
    └── stockManage.html

 

app.py

from flask import Flask, render_template, request, redirect, url_for
import mysql.connector
from mysql.connector import Error
from datetime import date
from datetime import datetime

app = Flask(__name__)

# 데이터베이스 연결 설정
def create_connection():
    connection = None
    try:
        connection = mysql.connector.connect(
            host='localhost',
            user='joon',
            password='1234',
            database='backend'
        )
    except Error as e:
        print(f"Error: '{e}'")
    return connection

# 메인 페이지 라우트
@app.route('/')
def enterRegist():
    return render_template('enterRegist.html')

# 데이터 저장 라우트
@app.route('/submit', methods=['POST'])
def submit():
    if request.method == 'POST':
        consignor = request.form['consignor']
        product_type = request.form['product_type']
        product_quantity = request.form['product_quantity']
        arrival_date = request.form['arrival_date']
        arrival_manager = request.form['arrival_manager']
        storage_location = request.form['storage_location']
        product_status = request.form['product_status']
        progress_status = request.form['progress_status']

        connection = create_connection()
        cursor = connection.cursor()
        cursor.execute("""
            INSERT INTO incoming (consignor, product_type, product_quantity, arrival_date, arrival_manager, storage_location, product_status, progress_status)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
        """, (consignor, product_type, product_quantity, arrival_date, arrival_manager, storage_location, product_status, progress_status))

        connection.commit()
        cursor.close()
        connection.close()

        return redirect(url_for('enterRegist'))

def update_stock():
    connection = create_connection()
    cursor = connection.cursor(dictionary=True)

    # 입고 테이블에서 입고완료인 경우만 가져오기
    cursor.execute("""
        SELECT * FROM incoming WHERE progress_status = '입고완료'
    """)
    incoming_data = cursor.fetchall()

    for row in incoming_data:
        consignor = row['consignor']
        product_type = row['product_type']
        product_quantity = row['product_quantity']
        arrival_manager = row['arrival_manager']
        storage_location = row['storage_location']
        product_status = row['product_status']
        progress_status = row['progress_status']

        # 재고 테이블에서 동일한 화주와 상품종류가 있는지 확인
        cursor.execute("""
            SELECT * FROM stock WHERE consignor = %s AND product_type = %s AND storage_location = %s ORDER BY stock_update_date DESC LIMIT 1
        """, (consignor, product_type, storage_location))
        stock_entry = cursor.fetchone()

        if stock_entry:
            # 재고 수량을 계산
            new_quantity = stock_entry['stock_quantity'] + product_quantity
            # 재고 수량 업데이트
            cursor.execute("""
                UPDATE stock SET stock_quantity = %s, stock_update_date = %s, stock_manager = %s WHERE id = %s
            """, (new_quantity, datetime.now(), arrival_manager, stock_entry['id']))
        else:
            # 재고 테이블에 새로운 레코드 추가
            progress_status = "재고"
            cursor.execute("""
                INSERT INTO stock (consignor, product_type, stock_quantity, stock_update_date, stock_manager, storage_location, product_status, progress_status)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
            """, (consignor, product_type, product_quantity, datetime.now(), arrival_manager, storage_location, product_status, progress_status))

        # incoming 테이블의 progress_status를 '재고반영완료'로 업데이트
        cursor.execute("""
            UPDATE incoming SET progress_status = '재고반영완료' WHERE id = %s
        """, (row['id'],))

    connection.commit()
    cursor.close()
    connection.close()

# 입고 조회 페이지 라우트
@app.route('/enterView', methods=['GET'])
def enterView():
    page = request.args.get('page', 1, type=int)
    per_page = 5

    consignor = request.args.get('consignor', '')
    product_type = request.args.get('product_type', '')
    arrival_date = request.args.get('arrival_date', '')
    arrival_manager = request.args.get('arrival_manager', '')
    progress_status = request.args.get('progress_status', '')

    connection = create_connection()
    cursor = connection.cursor(dictionary=True)

    query = """
        SELECT * FROM incoming
        WHERE consignor LIKE %s AND product_type LIKE %s AND arrival_date LIKE %s AND arrival_manager LIKE %s AND progress_status LIKE %s
        LIMIT %s OFFSET %s
    """
    cursor.execute(query, ('%' + consignor + '%', '%' + product_type + '%', '%' + arrival_date + '%', '%' + arrival_manager + '%', '%' + progress_status + '%', per_page, (page-1)*per_page))

    results = cursor.fetchall()

    cursor.execute("SELECT COUNT(*) FROM incoming WHERE consignor LIKE %s AND product_type LIKE %s AND arrival_date LIKE %s AND arrival_manager LIKE %s AND progress_status LIKE %s",
                   ('%' + consignor + '%', '%' + product_type + '%', '%' + arrival_date + '%', '%' + arrival_manager + '%', '%' + progress_status + '%'))
    total = cursor.fetchone()['COUNT(*)']
    total_pages = (total + per_page - 1) // per_page

    cursor.close()
    connection.close()

    prev_url = url_for('enterView', page=page-1, consignor=consignor, product_type=product_type, arrival_date=arrival_date, arrival_manager=arrival_manager, progress_status=progress_status) if page > 1 else None
    next_url = url_for('enterView', page=page+1, consignor=consignor, product_type=product_type, arrival_date=arrival_date, arrival_manager=arrival_manager, progress_status=progress_status) if page < total_pages else None

    return render_template('enterView.html', results=results, total_pages=total_pages, current_page=page, prev_url=prev_url, next_url=next_url,
                           consignor=consignor, product_type=product_type, arrival_date=arrival_date, arrival_manager=arrival_manager, progress_status=progress_status)

# 재고 조회 페이지 라우트
@app.route('/stockView', methods=['GET'])
def stockView():
    update_stock()

    page = request.args.get('page', 1, type=int)
    per_page = 5

    consignor = request.args.get('consignor', '')
    product_type = request.args.get('product_type', '')
    stock_manager = request.args.get('stock_manager', '')
    storage_location = request.args.get('storage_location', '')
    product_status = request.args.get('product_status', '')
    progress_status = request.args.get('progress_status', '')

    connection = create_connection()
    cursor = connection.cursor(dictionary=True)

    query = """
        SELECT * FROM stock
        WHERE consignor LIKE %s AND product_type LIKE %s AND stock_manager LIKE %s AND storage_location LIKE %s AND product_status LIKE %s AND progress_status LIKE %s
        ORDER BY stock_update_date DESC
        LIMIT %s OFFSET %s
    """
    cursor.execute(query, ('%' + consignor + '%', '%' + product_type + '%', '%' + stock_manager + '%', '%' + storage_location + '%', '%' + product_status + '%', '%' + progress_status + '%', per_page, (page-1)*per_page))

    results = cursor.fetchall()

    cursor.execute("SELECT COUNT(*) FROM stock WHERE consignor LIKE %s AND product_type LIKE %s AND stock_manager LIKE %s AND storage_location LIKE %s AND product_status LIKE %s AND progress_status LIKE %s",
                   ('%' + consignor + '%', '%' + product_type + '%', '%' + stock_manager + '%', '%' + storage_location + '%', '%' + product_status + '%', '%' + progress_status + '%'))
    total = cursor.fetchone()['COUNT(*)']
    total_pages = (total + per_page - 1) // per_page

    cursor.close()
    connection.close()

    prev_url = url_for('stockView', page=page-1, consignor=consignor, product_type=product_type, stock_manager=stock_manager, storage_location=storage_location, product_status=product_status, progress_status=progress_status) if page > 1 else None
    next_url = url_for('stockView', page=page+1, consignor=consignor, product_type=product_type, stock_manager=stock_manager, storage_location=storage_location, product_status=product_status, progress_status=progress_status) if page < total_pages else None

    return render_template('stockView.html', results=results, total_pages=total_pages, current_page=page, prev_url=prev_url, next_url=next_url,
                           consignor=consignor, product_type=product_type, stock_manager=stock_manager, storage_location=storage_location, product_status=product_status, progress_status=progress_status)

# 재고 관리 페이지 라우트
@app.route('/stockManage', methods=['GET', 'POST'])
def stockManage():
    if request.method == 'POST':
        update_ids = request.form.getlist('update_id')
        storage_location = request.form.get('storage_location', '')
        progress_status = request.form.get('progress_status', '')

        connection = create_connection()
        cursor = connection.cursor()

        for update_id in update_ids:
            update_fields = []
            update_values = []

            if storage_location:
                update_fields.append("storage_location = %s")
                update_values.append(storage_location)
            if progress_status:
                update_fields.append("progress_status = %s")
                update_values.append(progress_status)

            update_values.append(update_id)
            if update_fields:
                cursor.execute(f"UPDATE stock SET {', '.join(update_fields)} WHERE id = %s", update_values)

        connection.commit()
        cursor.close()
        connection.close()

        return redirect(url_for('stockManage'))

    page = request.args.get('page', 1, type=int)
    per_page = 5

    consignor = request.args.get('consignor', '')
    product_type = request.args.get('product_type', '')
    stock_manager = request.args.get('stock_manager', '')
    storage_location = request.args.get('storage_location', '')
    product_status = request.args.get('product_status', '')
    progress_status = request.args.get('progress_status', '')

    connection = create_connection()
    cursor = connection.cursor(dictionary=True)

    query = """
        SELECT * FROM stock
        WHERE consignor LIKE %s AND product_type LIKE %s AND stock_manager LIKE %s AND storage_location LIKE %s AND product_status LIKE %s AND progress_status LIKE %s
        ORDER BY stock_update_date DESC
        LIMIT %s OFFSET %s
    """
    cursor.execute(query, ('%' + consignor + '%', '%' + product_type + '%', '%' + stock_manager + '%', '%' + storage_location + '%', '%' + product_status + '%', '%' + progress_status + '%', per_page, (page-1)*per_page))

    results = cursor.fetchall()

    cursor.execute("SELECT COUNT(*) FROM stock WHERE consignor LIKE %s AND product_type LIKE %s AND stock_manager LIKE %s AND storage_location LIKE %s AND product_status LIKE %s AND progress_status LIKE %s",
                   ('%' + consignor + '%', '%' + product_type + '%', '%' + stock_manager + '%', '%' + storage_location + '%', '%' + product_status + '%', '%' + progress_status + '%'))
    total = cursor.fetchone()['COUNT(*)']
    total_pages = (total + per_page - 1) // per_page

    cursor.close()
    connection.close()

    prev_url = url_for('stockManage', page=page-1, consignor=consignor, product_type=product_type, stock_manager=stock_manager, storage_location=storage_location, product_status=product_status, progress_status=progress_status) if page > 1 else None
    next_url = url_for('stockManage', page=page+1, consignor=consignor, product_type=product_type, stock_manager=stock_manager, storage_location=storage_location, product_status=product_status, progress_status=progress_status) if page < total_pages else None

    return render_template('stockManage.html', results=results, total_pages=total_pages, current_page=page, prev_url=prev_url, next_url=next_url,
                           consignor=consignor, product_type=product_type, stock_manager=stock_manager, storage_location=storage_location, product_status=product_status, progress_status=progress_status)

# 데이터 수정 및 삭제 라우트
@app.route('/save_changes', methods=['POST'])
def save_changes():
    update_ids = request.form.getlist('update_id')
    delete_ids = request.form.getlist('delete_id')
    consignor = request.form.get('consignor', '')
    product_type = request.form.get('product_type', '')
    product_quantity = request.form.get('product_quantity', '')
    arrival_date = request.form.get('arrival_date', '')
    arrival_manager = request.form.get('arrival_manager', '')
    storage_location = request.form.get('storage_location', '')
    product_status = request.form.get('product_status', '')
    progress_status = request.form.get('progress_status', '')

    connection = create_connection()
    cursor = connection.cursor()

    for update_id in update_ids:
        update_fields = []
        update_values = []

        if consignor:
            update_fields.append("consignor = %s")
            update_values.append(consignor)
        if product_type:
            update_fields.append("product_type = %s")
            update_values.append(product_type)
        if product_quantity:
            update_fields.append("product_quantity = %s")
            update_values.append(product_quantity)
        if arrival_date:
            update_fields.append("arrival_date = %s")
            update_values.append(arrival_date)
        if arrival_manager:
            update_fields.append("arrival_manager = %s")
            update_values.append(arrival_manager)
        if storage_location:
            update_fields.append("storage_location = %s")
            update_values.append(storage_location)
        if product_status:
            update_fields.append("product_status = %s")
            update_values.append(product_status)
        if progress_status:
            update_fields.append("progress_status = %s")
            update_values.append(progress_status)

        update_values.append(update_id)
        if update_fields:
            cursor.execute(f"UPDATE incoming SET {', '.join(update_fields)} WHERE id = %s", update_values)

    for delete_id in delete_ids:
        cursor.execute("DELETE FROM incoming WHERE id = %s", (delete_id,))

    connection.commit()
    cursor.close()
    connection.close()

    return redirect(url_for('enterView'))

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5001, debug=True)

오늘 WMS(Warehouse Management System) 기능 중 재고 조회 및 관리 기능을 구현해 봤다. 재고 조회, 관리 페이지를 제작해 보니 sql 언어가 더 어려워졌다. 예전에 다 배웠던 것인데 1도 기억이 안 난다... 다시 공부를 해야 될 것 같다. 그리고 "재고 수량의 update는 화주와 상품종류가 같을 때 입고 완료된 입고수량을 재고 수량에 더해서 저장" 하는 로직을 구현하는데 많이 시간을 할애 했는데 확실히 시간을 많이 할애해서 코드를 짜는 것이 코드를 이해하는데 좋은 것 같다. 아직 100%는 이해 못 했지만 flask 코드에 좀 더 익숙해지는 기회가 되었다.