|
import pandas as pd
from openpyxl import load_workbook, Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
import datetime
import os
class InventoryManager:
def __init__(self, inventory_file):
"""初始化库存管理系统"""
self.inventory_file = inventory_file
# 如果文件不存在,创建一个新的库存文件
if not os.path.exists(inventory_file):
self._create_new_inventory_file()
# 加载库存数据
self.load_inventory()
def _create_new_inventory_file(self):
"""创建新的库存文件"""
wb = Workbook()
ws = wb.active
ws.title = "库存"
# 设置表头
headers = ['产品ID', '产品名称', '类别', '供应商', '单价', '库存量', '库存价值', '最后更新']
for col_num, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_num)
cell.value = header
cell.font = Font(bold=True)
cell.fill = PatternFill("solid", fgColor="4F81BD")
cell.alignment = Alignment(horizontal="center")
# 设置示例数据
sample_data = [
[1001, '笔记本电脑', '电子产品', 'A供应商', 5999, 10, '=E2*F2', datetime.datetime.now()],
[1002, '办公椅', '办公家具', 'B供应商', 899, 20, '=E3*F3', datetime.datetime.now()],
]
for row_num, row_data in enumerate(sample_data, 2):
for col_num, value in enumerate(row_data, 1):
cell = ws.cell(row=row_num, column=col_num)
cell.value = value
if col_num == 5: # 单价列
cell.number_format = '¥#,##0.00'
elif col_num == 7: # 库存价值列
cell.number_format = '¥#,##0.00'
elif col_num == 8: # 日期列
cell.number_format = 'yyyy-mm-dd hh:mm:ss'
# 创建入库记录工作表
ws_in = wb.create_sheet(title="入库记录")
headers = ['记录ID', '产品ID', '产品名称', '入库数量', '单价', '总价值', '供应商', '入库日期', '操作人']
for col_num, header in enumerate(headers, 1):
cell = ws_in.cell(row=1, column=col_num)
cell.value = header
cell.font = Font(bold=True)
cell.fill = PatternFill("solid", fgColor="4F81BD")
cell.alignment = Alignment(horizontal="center")
# 创建出库记录工作表
ws_out = wb.create_sheet(title="出库记录")
headers = ['记录ID', '产品ID', '产品名称', '出库数量', '单价', '总价值', '客户', '出库日期', '操作人']
for col_num, header in enumerate(headers, 1):
cell = ws_out.cell(row=1, column=col_num)
cell.value = header
cell.font = Font(bold=True)
cell.fill = PatternFill("solid", fgColor="4F81BD")
cell.alignment = Alignment(horizontal="center")
# 调整所有工作表的列宽
for ws in wb.worksheets:
for col in range(1, len(headers) + 1):
ws.column_dimensions[get_column_letter(col)].width = 15
# 保存文件
wb.save(self.inventory_file)
print(f"已创建新的库存文件: {self.inventory_file}")
def load_inventory(self):
"""加载库存数据"""
# 使用pandas读取Excel文件的所有工作表
self.inventory_df = pd.read_excel(self.inventory_file, sheet_name="库存")
self.in_records_df = pd.read_excel(self.inventory_file, sheet_name="入库记录")
self.out_records_df = pd.read_excel(self.inventory_file, sheet_name="出库记录")
print("库存数据已加载")
print(f"当前库存: {len(self.inventory_df)} 种产品")
print(f"入库记录: {len(self.in_records_df)} 条")
print(f"出库记录: {len(self.out_records_df)} 条")
def add_product(self, product_id, name, category, supplier, price, quantity):
"""添加新产品到库存"""
# 检查产品ID是否已存在
if product_id in self.inventory_df['产品ID'].values:
print(f"错误: 产品ID {product_id} 已存在")
return False
# 创建新产品记录
new_product = {
'产品ID': product_id,
'产品名称': name,
'类别': category,
'供应商': supplier,
'单价': price,
'库存量': quantity,
'库存价值': price * quantity,
'最后更新': datetime.datetime.now()
}
# 添加到DataFrame
self.inventory_df = self.inventory_df.append(new_product, ignore_index=True)
# 添加入库记录
in_record = {
'记录ID': len(self.in_records_df) + 1,
'产品ID': product_id,
'产品名称': name,
'入库数量': quantity,
'单价': price,
'总价值': price * quantity,
'供应商': supplier,
'入库日期': datetime.datetime.now(),
'操作人': 'system'
}
self.in_records_df = self.in_records_df.append(in_record, ignore_index=True)
# 保存更改
self._save_to_excel()
print(f"已添加新产品: {name} (ID: {product_id})")
return True
def update_stock(self, product_id, quantity_change, is_incoming=True, customer_or_supplier=None, operator='system'):
"""更新库存"""
# 查找产品
product_mask = self.inventory_df['产品ID'] == product_id
if not any(product_mask):
print(f"错误: 产品ID {product_id} 不存在")
return False
# 获取产品信息
product_idx = product_mask.idxmax()
product = self.inventory_df.loc[product_idx]
# 计算新库存量
new_quantity = product['库存量'] + quantity_change if is_incoming else product['库存量'] - quantity_change
# 检查库存是否足够(出库时)
if not is_incoming and new_quantity < 0:
print(f"错误: 产品 {product['产品名称']} 库存不足,当前库存: {product['库存量']}")
return False
# 更新库存
self.inventory_df.at[product_idx, '库存量'] = new_quantity
self.inventory_df.at[product_idx, '库存价值'] = new_quantity * product['单价']
self.inventory_df.at[product_idx, '最后更新'] = datetime.datetime.now()
# 添加记录
if is_incoming:
# 入库记录
record = {
'记录ID': len(self.in_records_df) + 1,
'产品ID': product_id,
'产品名称': product['产品名称'],
'入库数量': quantity_change,
'单价': product['单价'],
'总价值': quantity_change * product['单价'],
'供应商': customer_or_supplier or product['供应商'],
'入库日期': datetime.datetime.now(),
'操作人': operator
}
self.in_records_df = self.in_records_df.append(record, ignore_index=True)
else:
# 出库记录
record = {
'记录ID': len(self.out_records_df) + 1,
'产品ID': product_id,
'产品名称': product['产品名称'],
'出库数量': quantity_change,
'单价': product['单价'],
'总价值': quantity_change * product['单价'],
'客户': customer_or_supplier or '未指定',
'出库日期': datetime.datetime.now(),
'操作人': operator
}
self.out_records_df = self.out_records_df.append(record, ignore_index=True)
# 保存更改
self._save_to_excel()
action = "入库" if is_incoming else "出库"
print(f"已{action} {product['产品名称']} {quantity_change} 个,当前库存: {new_quantity}")
return True
def generate_inventory_report(self, output_file):
"""生成库存报表"""
# 创建一个新的工作簿
wb = Workbook()
ws = wb.active
ws.title = "库存报表"
# 添加报表标题
ws.merge_cells('A1:H1')
title_cell = ws['A1']
title_cell.value = "库存状况报表"
title_cell.font = Font(size=16, bold=True)
title_cell.alignment = Alignment(horizontal="center")
# 添加报表生成时间
ws.merge_cells('A2:H2')
date_cell = ws['A2']
date_cell.value = f"生成时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
date_cell.alignment = Alignment(horizontal="center")
# 添加表头
headers = ['产品ID', '产品名称', '类别', '供应商', '单价', '库存量', '库存价值', '库存状态']
for col_num, header in enumerate(headers, 1):
cell = ws.cell(row=4, column=col_num)
cell.value = header
cell.font = Font(bold=True)
cell.fill = PatternFill("solid", fgColor="4F81BD")
cell.alignment = Alignment(horizontal="center")
# 添加数据
# 计算库存状态
def get_stock_status(row):
if row['库存量'] <= 0:
return "缺货"
elif row['库存量'] < 5:
return "库存不足"
elif row['库存量'] > 20:
return "库存过多"
else:
return "正常"
# 添加库存状态列
self.inventory_df['库存状态'] = self.inventory_df.apply(get_stock_status, axis=1)
# 按类别和库存状态排序
sorted_df = self.inventory_df.sort_values(['类别', '库存状态'])
# 写入数据
for row_num, (_, row) in enumerate(sorted_df.iterrows(), 5):
for col_num, column in enumerate(headers, 1):
cell = ws.cell(row=row_num, column=col_num)
value = row[column] if column in row else ""
cell.value = value
# 设置格式
if column == '单价':
cell.number_format = '¥#,##0.00'
elif column == '库存价值':
cell.number_format = '¥#,##0.00'
# 设置库存状态的颜色
if column == '库存状态':
if value == "缺货":
cell.fill = PatternFill("solid", fgColor="FF0000")
elif value == "库存不足":
cell.fill = PatternFill("solid", fgColor="FFC000")
elif value == "库存过多":
cell.fill = PatternFill("solid", fgColor="92D050")
# 添加合计行
total_row = len(sorted_df) + 5
ws.cell(row=total_row, column=1).value = "合计"
ws.cell(row=total_row, column=1).font = Font(bold=True)
# 计算总库存量和总价值
ws.cell(row=total_row, column=6).value = sorted_df['库存量'].sum()
ws.cell(row=total_row, column=6).font = Font(bold=True)
ws.cell(row=total_row, column=7).value = sorted_df['库存价值'].sum()
ws.cell(row=total_row, column=7).font = Font(bold=True)
ws.cell(row=total_row, column=7).number_format = '¥#,##0.00'
# 添加类别统计
ws.cell(row=total_row + 2, column=1).value = "类别统计"
ws.cell(row=total_row + 2, column=1).font = Font(bold=True)
category_stats = sorted_df.groupby('类别').agg({
'产品ID': 'count',
'库存量': 'sum',
'库存价值': 'sum'
}).reset_index()
# 写入类别统计表头
category_headers = ['类别', '产品数量', '总库存量', '总库存价值']
for col_num, header in enumerate(category_headers, 1):
cell = ws.cell(row=total_row + 3, column=col_num)
cell.value = header
cell.font = Font(bold=True)
cell.fill = PatternFill("solid", fgColor="A5A5A5")
# 写入类别统计数据
for row_num, (_, row) in enumerate(category_stats.iterrows(), total_row + 4):
ws.cell(row=row_num, column=1).value = row['类别']
ws.cell(row=row_num, column=2).value = row['产品ID']
ws.cell(row=row_num, column=3).value = row['库存量']
ws.cell(row=row_num, column=4).value = row['库存价值']
ws.cell(row=row_num, column=4).number_format = '¥#,##0.00'
# 调整列宽
for col in range(1, len(headers) + 1):
ws.column_dimensions[get_column_letter(col)].width = 15
# 保存报表
wb.save(output_file)
print(f"库存报表已生成: {output_file}")
return output_file
def _save_to_excel(self):
"""保存数据到Excel文件"""
with pd.ExcelWriter(self.inventory_file, engine='openpyxl') as writer:
self.inventory_df.to_excel(writer, sheet_name="库存", index=False)
self.in_records_df.to_excel(writer, sheet_name="入库记录", index=False)
self.out_records_df.to_excel(writer, sheet_name="出库记录", index=False)
# 使用示例
# inventory = InventoryManager("库存管理.xlsx")
# inventory.add_product(1003, "打印机", "办公设备", "C供应商", 1299, 5)
# inventory.update_stock(1001, 5, is_incoming=True, customer_or_supplier="A供应商", operator="张三")
# inventory.update_stock(1002, 2, is_incoming=False, customer_or_supplier="客户A", operator="李四")
# inventory.generate_inventory_report("库存报表.xlsx")
|
所有评论(0)