随着企业规模的扩大和业务的复杂化,仓库管理变得越来越重要。为了提高仓库管理的效率和准确性,开发一个通用的仓库管理系统显得尤为重要。该系统将帮助企业实现库存的自动化管理,减少人为错误,提高工作效率。
本项目旨在开发一个基于PySide6和SQLite3的通用仓库管理系统,主要功能包括:
id
(INTEGER, PRIMARY KEY, AUTOINCREMENT)name
(TEXT, NOT NULL)description
(TEXT)price
(REAL, NOT NULL)quantity
(INTEGER, NOT NULL)id
(INTEGER, PRIMARY KEY, AUTOINCREMENT)name
(TEXT, NOT NULL)contact
(TEXT)phone
(TEXT)address
(TEXT)id
(INTEGER, PRIMARY KEY, AUTOINCREMENT)name
(TEXT, NOT NULL)contact
(TEXT)phone
(TEXT)address
(TEXT)id
(INTEGER, PRIMARY KEY, AUTOINCREMENT)product_id
(INTEGER, FOREIGN KEY REFERENCES Products(id))supplier_id
(INTEGER, FOREIGN KEY REFERENCES Suppliers(id))quantity
(INTEGER, NOT NULL)date
(TEXT, NOT NULL)id
(INTEGER, PRIMARY KEY, AUTOINCREMENT)product_id
(INTEGER, FOREIGN KEY REFERENCES Products(id))customer_id
(INTEGER, FOREIGN KEY REFERENCES Customers(id))quantity
(INTEGER, NOT NULL)date
(TEXT, NOT NULL)id
(INTEGER, PRIMARY KEY, AUTOINCREMENT)username
(TEXT, NOT NULL, UNIQUE)password
(TEXT, NOT NULL)role
(TEXT, NOT NULL)pip install PySide6
import sqlite3
def create_database():
conn = sqlite3.connect('warehouse.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS Products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
quantity INTEGER NOT NULL
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS Suppliers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
contact TEXT,
phone TEXT,
address TEXT
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS Customers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
contact TEXT,
phone TEXT,
address TEXT
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS StockIn (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER NOT NULL,
supplier_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
date TEXT NOT NULL,
FOREIGN KEY (product_id) REFERENCES Products(id),
FOREIGN KEY (supplier_id) REFERENCES Suppliers(id)
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS StockOut (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER NOT NULL,
customer_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
date TEXT NOT NULL,
FOREIGN KEY (product_id) REFERENCES Products(id),
FOREIGN KEY (customer_id) REFERENCES Customers(id)
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS Users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
role TEXT NOT NULL
)
''')
conn.commit()
conn.close()
create_database()
QMainWindow
作为主窗口,包含菜单栏、工具栏和状态栏。QTableWidget
显示商品列表。QTableWidget
显示库存信息。QTableWidget
显示入库历史。QTableWidget
显示出库历史。QTableWidget
显示供应商列表。QTableWidget
显示客户列表。QTextEdit
显示报表内容。QTableWidget
显示用户列表。Products
表中。Products
表中的记录。Products
表中删除记录。Products
表中的记录。Products
表中的库存信息。Products
表中的库存数量。StockIn
表中,并更新Products
表中的库存数量。StockIn
表中的记录。StockOut
表中,并更新Products
表中的库存数量。StockOut
表中的记录。Suppliers
表中。Suppliers
表中的记录。Suppliers
表中删除记录。Suppliers
表中的记录。Customers
表中。Customers
表中的记录。Customers
表中删除记录。Customers
表中的记录。Products
表中的库存信息并生成报表。StockIn
和StockOut
表中的记录并生成报表。Users
表中。本项目通过使用PySide6和SQLite3,成功开发了一个通用的仓库管理系统。该系统功能完善,操作简便,能够有效提高仓库管理的效率和准确性。未来可以根据实际需求进一步扩展系统功能,如增加多仓库管理、支持更多报表类型等。
以下是 通用仓库管理系统 的开发文件目录结构,基于 PySide6 和 SQLite3 实现。该目录结构清晰划分了前端、后端、数据库、资源文件等模块,便于开发和维护。
warehouse_management_system/
├── main.py # 程序入口文件
├── requirements.txt # 项目依赖文件
├── README.md # 项目说明文档
├── database/ # 数据库相关文件
│ ├── db.py # 数据库连接与初始化
│ ├── models.py # 数据库表结构定义
│ └── queries.py # 数据库查询语句封装
├── ui/ # 前端界面文件
│ ├── main_window.py # 主窗口界面
│ ├── product_ui.py # 商品管理界面
│ ├── stock_ui.py # 库存管理界面
│ ├── supplier_ui.py # 供应商管理界面
│ ├── customer_ui.py # 客户管理界面
│ ├── report_ui.py # 报表生成界面
│ └── user_ui.py # 用户管理界面
├── logic/ # 后端业务逻辑
│ ├── product_logic.py # 商品管理逻辑
│ ├── stock_logic.py # 库存管理逻辑
│ ├── supplier_logic.py # 供应商管理逻辑
│ ├── customer_logic.py # 客户管理逻辑
│ ├── report_logic.py # 报表生成逻辑
│ └── user_logic.py # 用户管理逻辑
├── resources/ # 资源文件
│ ├── icons/ # 图标资源
│ └── styles/ # 样式表文件
├── tests/ # 测试文件
│ ├── test_product.py # 商品管理测试
│ ├── test_stock.py # 库存管理测试
│ ├── test_supplier.py # 供应商管理测试
│ ├── test_customer.py # 客户管理测试
│ ├── test_report.py # 报表生成测试
│ └── test_user.py # 用户管理测试
└── utils/ # 工具类
├── logger.py # 日志工具
└── helpers.py # 通用工具函数
main.py
requirements.txt
PySide6==6.5.0
sqlite3
database/
db.py
: 数据库连接与初始化,包括创建数据库和表的逻辑。models.py
: 定义数据库表结构(如商品、供应商、客户等)。queries.py
: 封装常用的 SQL 查询语句,便于复用。ui/
QMainWindow
、QDialog
、QWidget
等组件构建界面。logic/
resources/
tests/
utils/
main.py
import sys
from PySide6.QtWidgets import QApplication
from ui.main_window import MainWindow
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
database/db.py
import sqlite3
def create_database():
conn = sqlite3.connect('warehouse.db')
cursor = conn.cursor()
# 创建商品表
cursor.execute('''
CREATE TABLE IF NOT EXISTS Products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
quantity INTEGER NOT NULL
)
''')
# 创建供应商表
cursor.execute('''
CREATE TABLE IF NOT EXISTS Suppliers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
contact TEXT,
phone TEXT,
address TEXT
)
''')
conn.commit()
conn.close()
if __name__ == "__main__":
create_database()
ui/main_window.py
from PySide6.QtWidgets import QMainWindow, QTabWidget
from ui.product_ui import ProductUI
from ui.stock_ui import StockUI
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("仓库管理系统")
self.setGeometry(100, 100, 800, 600)
# 创建选项卡
self.tabs = QTabWidget()
self.setCentralWidget(self.tabs)
# 添加商品管理界面
self.product_ui = ProductUI()
self.tabs.addTab(self.product_ui, "商品管理")
# 添加库存管理界面
self.stock_ui = StockUI()
self.tabs.addTab(self.stock_ui, "库存管理")
以上目录结构清晰划分了项目的各个模块,便于团队协作开发和维护。通过模块化的设计,可以快速扩展功能(如添加新的管理模块)或修复问题。
为了支持通过 CSS 文档 来动态调整 UI 样式,我们可以将 PySide6 的界面设计与 CSS 样式分离。PySide6 支持使用 QSS(Qt Style Sheets),这是一种类似于 CSS 的样式表语言,可以通过加载外部 CSS 文件或直接在代码中设置样式来实现 UI 样式的动态调整。
以下是实现方案和目录结构调整:
warehouse_management_system/
├── main.py # 程序入口文件
├── requirements.txt # 项目依赖文件
├── README.md # 项目说明文档
├── database/ # 数据库相关文件
│ ├── db.py # 数据库连接与初始化
│ ├── models.py # 数据库表结构定义
│ └── queries.py # 数据库查询语句封装
├── ui/ # 前端界面文件
│ ├── main_window.py # 主窗口界面
│ ├── product_ui.py # 商品管理界面
│ ├── stock_ui.py # 库存管理界面
│ ├── supplier_ui.py # 供应商管理界面
│ ├── customer_ui.py # 客户管理界面
│ ├── report_ui.py # 报表生成界面
│ └── user_ui.py # 用户管理界面
├── logic/ # 后端业务逻辑
│ ├── product_logic.py # 商品管理逻辑
│ ├── stock_logic.py # 库存管理逻辑
│ ├── supplier_logic.py # 供应商管理逻辑
│ ├── customer_logic.py # 客户管理逻辑
│ ├── report_logic.py # 报表生成逻辑
│ └── user_logic.py # 用户管理逻辑
├── resources/ # 资源文件
│ ├── icons/ # 图标资源
│ └── styles/ # 样式表文件
│ ├── main_style.qss # 主窗口样式表
│ ├── product_style.qss # 商品管理界面样式表
│ └── button_style.qss # 按钮通用样式表
├── tests/ # 测试文件
│ ├── test_product.py # 商品管理测试
│ ├── test_stock.py # 库存管理测试
│ ├── test_supplier.py # 供应商管理测试
│ ├── test_customer.py # 客户管理测试
│ ├── test_report.py # 报表生成测试
│ └── test_user.py # 用户管理测试
└── utils/ # 工具类
├── logger.py # 日志工具
├── helpers.py # 通用工具函数
└── style_loader.py # 样式加载工具
在 resources/styles/
目录下创建 .qss
文件,例如:
main_style.qss
/* 主窗口样式 */
QMainWindow {
background-color: #f0f0f0;
font-family: "Arial";
font-size: 14px;
}
/* 选项卡样式 */
QTabWidget::pane {
border: 1px solid #ccc;
background-color: #fff;
}
QTabBar::tab {
padding: 10px;
background-color: #e0e0e0;
border: 1px solid #ccc;
}
QTabBar::tab:selected {
background-color: #fff;
border-bottom-color: #fff;
}
button_style.qss
/* 按钮通用样式 */
QPushButton {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px;
border-radius: 5px;
}
QPushButton:hover {
background-color: #45a049;
}
QPushButton:pressed {
background-color: #3d8b40;
}
在 utils/style_loader.py
中编写样式加载工具:
from PySide6.QtWidgets import QApplication
def load_style(file_path):
"""加载指定的 QSS 样式表文件"""
try:
with open(file_path, "r", encoding="utf-8") as f:
style = f.read()
QApplication.instance().setStyleSheet(style)
except Exception as e:
print(f"加载样式表失败: {e}")
在 main.py
中加载全局样式:
import sys
from PySide6.QtWidgets import QApplication
from ui.main_window import MainWindow
from utils.style_loader import load_style
if __name__ == "__main__":
app = QApplication(sys.argv)
# 加载全局样式
load_style("resources/styles/main_style.qss")
window = MainWindow()
window.show()
sys.exit(app.exec())
在具体的界面文件中,可以加载局部样式。例如,在 ui/product_ui.py
中:
from PySide6.QtWidgets import QWidget, QVBoxLayout, QPushButton
from utils.style_loader import load_style
class ProductUI(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
self.load_styles()
def init_ui(self):
self.layout = QVBoxLayout()
self.button = QPushButton("添加商品")
self.layout.addWidget(self.button)
self.setLayout(self.layout)
def load_styles(self):
# 加载按钮样式
load_style("resources/styles/button_style.qss")
可以通过配置文件或用户设置动态切换样式。例如:
def change_theme(theme_name):
if theme_name == "light":
load_style("resources/styles/light_theme.qss")
elif theme_name == "dark":
load_style("resources/styles/dark_theme.qss")
dark_theme.qss
/* 深色主题 */
QMainWindow {
background-color: #333;
color: #fff;
}
QPushButton {
background-color: #555;
color: #fff;
border: 1px solid #777;
}
QPushButton:hover {
background-color: #666;
}
QPushButton:pressed {
background-color: #444;
}
通过将样式表与界面逻辑分离,可以实现以下优势:
.qss
文件,实现主题切换。resources/styles/
目录下,便于管理。这种方法非常适合需要频繁调整 UI 样式的项目,同时也能提高开发效率。
为了支持将打印功能改为基于 HTML 模板,并且允许用户通过修改外部 HTML 文件来自定义打印内容,我们可以将 HTML 模板文件与代码分离。通过加载外部 HTML 文件并动态填充数据,可以实现灵活的打印功能。
以下是实现方案和目录结构调整:
warehouse_management_system/
├── main.py # 程序入口文件
├── requirements.txt # 项目依赖文件
├── README.md # 项目说明文档
├── database/ # 数据库相关文件
│ ├── db.py # 数据库连接与初始化
│ ├── models.py # 数据库表结构定义
│ └── queries.py # 数据库查询语句封装
├── ui/ # 前端界面文件
│ ├── main_window.py # 主窗口界面
│ ├── product_ui.py # 商品管理界面
│ ├── stock_ui.py # 库存管理界面
│ ├── supplier_ui.py # 供应商管理界面
│ ├── customer_ui.py # 客户管理界面
│ ├── report_ui.py # 报表生成界面
│ └── user_ui.py # 用户管理界面
├── logic/ # 后端业务逻辑
│ ├── product_logic.py # 商品管理逻辑
│ ├── stock_logic.py # 库存管理逻辑
│ ├── supplier_logic.py # 供应商管理逻辑
│ ├── customer_logic.py # 客户管理逻辑
│ ├── report_logic.py # 报表生成逻辑
│ └── user_logic.py # 用户管理逻辑
├── resources/ # 资源文件
│ ├── icons/ # 图标资源
│ ├── styles/ # 样式表文件
│ └── templates/ # HTML 模板文件
│ ├── product_report.html # 商品报表模板
│ ├── stock_report.html # 库存报表模板
│ └── invoice_template.html# 发票模板
├── tests/ # 测试文件
│ ├── test_product.py # 商品管理测试
│ ├── test_stock.py # 库存管理测试
│ ├── test_supplier.py # 供应商管理测试
│ ├── test_customer.py # 客户管理测试
│ ├── test_report.py # 报表生成测试
│ └── test_user.py # 用户管理测试
└── utils/ # 工具类
├── logger.py # 日志工具
├── helpers.py # 通用工具函数
├── style_loader.py # 样式加载工具
└── template_loader.py # HTML 模板加载工具
在 resources/templates/
目录下创建 HTML 模板文件,例如:
product_report.html
商品报表
商品报表
商品名称
描述
价格
库存数量
{{ products }}
在 utils/template_loader.py
中编写 HTML 模板加载工具:
def load_template(file_path, data):
"""
加载 HTML 模板并填充数据
:param file_path: 模板文件路径
:param data: 填充的数据(字典格式)
:return: 渲染后的 HTML 内容
"""
try:
with open(file_path, "r", encoding="utf-8") as f:
template = f.read()
for key, value in data.items():
template = template.replace("{{ " + key + " }}", value)
return template
except Exception as e:
print(f"加载模板失败: {e}")
return None
在 logic/report_logic.py
中编写报表生成逻辑:
from utils.template_loader import load_template
from database.queries import get_all_products
def generate_product_report():
"""
生成商品报表
:return: 渲染后的 HTML 内容
"""
# 从数据库获取商品数据
products = get_all_products()
# 将数据格式化为 HTML 表格行
product_rows = ""
for product in products:
product_rows += f"""
{product['name']}
{product['description']}
{product['price']}
{product['quantity']}
"""
# 加载模板并填充数据
template_data = {
"products": product_rows
}
html_content = load_template("resources/templates/product_report.html", template_data)
return html_content
在 ui/report_ui.py
中实现打印功能:
from PySide6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QTextEdit, QPrintDialog, QPrinter
from PySide6.QtPrintSupport import QPrintDialog
from PySide6.QtWebEngineWidgets import QWebEngineView
from logic.report_logic import generate_product_report
class ReportUI(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.layout = QVBoxLayout()
# 生成报表按钮
self.generate_button = QPushButton("生成商品报表")
self.generate_button.clicked.connect(self.generate_report)
self.layout.addWidget(self.generate_button)
# 显示 HTML 内容
self.web_view = QWebEngineView()
self.layout.addWidget(self.web_view)
# 打印按钮
self.print_button = QPushButton("打印报表")
self.print_button.clicked.connect(self.print_report)
self.layout.addWidget(self.print_button)
self.setLayout(self.layout)
def generate_report(self):
"""生成并显示商品报表"""
html_content = generate_product_report()
if html_content:
self.web_view.setHtml(html_content)
def print_report(self):
"""打印报表"""
printer = QPrinter()
dialog = QPrintDialog(printer, self)
if dialog.exec() == QPrintDialog.Accepted:
self.web_view.page().print(printer, lambda success: print("打印成功" if success else "打印失败"))
用户可以直接修改 resources/templates/
目录下的 HTML 文件,例如调整样式或添加新的字段。系统会在每次生成报表时加载最新的模板。
通过将打印功能改为基于 HTML 模板,并允许用户通过修改外部 HTML 文件来自定义打印内容,可以实现以下优势:
这种方法非常适合需要频繁调整打印格式的项目,同时也能提高开发效率。
为了支持 添加入库记录 时能够记录 多个明细商品,我们需要对数据库设计和表单逻辑进行调整。以下是详细的实现方案:
id
(INTEGER, PRIMARY KEY, AUTOINCREMENT): 入库记录的唯一标识。supplier_id
(INTEGER, FOREIGN KEY REFERENCES Suppliers(id)): 供应商 ID。date
(TEXT, NOT NULL): 入库日期。total_quantity
(INTEGER, NOT NULL): 总入库数量。total_amount
(REAL, NOT NULL): 总入库金额。id
(INTEGER, PRIMARY KEY, AUTOINCREMENT): 明细记录的唯一标识。stock_in_id
(INTEGER, FOREIGN KEY REFERENCES StockIn(id)): 关联的入库记录 ID。product_id
(INTEGER, FOREIGN KEY REFERENCES Products(id)): 商品 ID。quantity
(INTEGER, NOT NULL): 入库数量。price
(REAL, NOT NULL): 商品单价。amount
(REAL, NOT NULL): 明细金额(quantity * price)。在 database/db.py
中更新数据库初始化逻辑:
def create_database():
conn = sqlite3.connect('warehouse.db')
cursor = conn.cursor()
# 创建入库主表
cursor.execute('''
CREATE TABLE IF NOT EXISTS StockIn (
id INTEGER PRIMARY KEY AUTOINCREMENT,
supplier_id INTEGER NOT NULL,
date TEXT NOT NULL,
total_quantity INTEGER NOT NULL,
total_amount REAL NOT NULL,
FOREIGN KEY (supplier_id) REFERENCES Suppliers(id)
)
''')
# 创建入库明细表
cursor.execute('''
CREATE TABLE IF NOT EXISTS StockInDetails (
id INTEGER PRIMARY KEY AUTOINCREMENT,
stock_in_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
price REAL NOT NULL,
amount REAL NOT NULL,
FOREIGN KEY (stock_in_id) REFERENCES StockIn(id),
FOREIGN KEY (product_id) REFERENCES Products(id)
)
''')
conn.commit()
conn.close()
在 ui/stock_ui.py
中设计一个表单,支持添加多个明细商品:
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTableWidget, QTableWidgetItem, QLineEdit, QDateEdit, QComboBox
from PySide6.QtCore import QDate
from database.queries import get_all_suppliers, get_all_products
class StockInUI(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
self.details = [] # 存储明细商品
def init_ui(self):
self.layout = QVBoxLayout()
# 供应商选择
self.supplier_combo = QComboBox()
self.supplier_combo.addItems([s['name'] for s in get_all_suppliers()])
self.layout.addWidget(self.supplier_combo)
# 入库日期
self.date_edit = QDateEdit(QDate.currentDate())
self.layout.addWidget(self.date_edit)
# 明细商品表格
self.details_table = QTableWidget()
self.details_table.setColumnCount(4)
self.details_table.setHorizontalHeaderLabels(["商品", "数量", "单价", "金额"])
self.layout.addWidget(self.details_table)
# 添加明细按钮
self.add_detail_button = QPushButton("添加明细")
self.add_detail_button.clicked.connect(self.add_detail)
self.layout.addWidget(self.add_detail_button)
# 提交按钮
self.submit_button = QPushButton("提交入库")
self.submit_button.clicked.connect(self.submit_stock_in)
self.layout.addWidget(self.submit_button)
self.setLayout(self.layout)
def add_detail(self):
"""添加明细商品"""
# 弹出对话框选择商品和输入数量、单价
dialog = QDialog(self)
dialog.setWindowTitle("添加明细")
layout = QVBoxLayout()
# 商品选择
product_combo = QComboBox()
product_combo.addItems([p['name'] for p in get_all_products()])
layout.addWidget(product_combo)
# 数量输入
quantity_edit = QLineEdit()
quantity_edit.setPlaceholderText("数量")
layout.addWidget(quantity_edit)
# 单价输入
price_edit = QLineEdit()
price_edit.setPlaceholderText("单价")
layout.addWidget(price_edit)
# 确认按钮
confirm_button = QPushButton("确认")
confirm_button.clicked.connect(lambda: self.confirm_detail(
dialog, product_combo.currentText(), quantity_edit.text(), price_edit.text()
))
layout.addWidget(confirm_button)
dialog.setLayout(layout)
dialog.exec()
def confirm_detail(self, dialog, product_name, quantity, price):
"""确认添加明细"""
try:
quantity = int(quantity)
price = float(price)
amount = quantity * price
# 添加到明细列表
self.details.append({
"product_name": product_name,
"quantity": quantity,
"price": price,
"amount": amount
})
# 更新表格
row = self.details_table.rowCount()
self.details_table.insertRow(row)
self.details_table.setItem(row, 0, QTableWidgetItem(product_name))
self.details_table.setItem(row, 1, QTableWidgetItem(str(quantity)))
self.details_table.setItem(row, 2, QTableWidgetItem(str(price)))
self.details_table.setItem(row, 3, QTableWidgetItem(str(amount)))
dialog.close()
except ValueError:
print("请输入有效的数量和单价")
def submit_stock_in(self):
"""提交入库记录"""
if not self.details:
print("请添加至少一个明细商品")
return
# 计算总数量和总金额
total_quantity = sum(d['quantity'] for d in self.details)
total_amount = sum(d['amount'] for d in self.details)
# 获取供应商 ID
supplier_name = self.supplier_combo.currentText()
supplier_id = next(s['id'] for s in get_all_suppliers() if s['name'] == supplier_name)
# 插入入库主表
from database.queries import insert_stock_in
stock_in_id = insert_stock_in(supplier_id, self.date_edit.date().toString("yyyy-MM-dd"), total_quantity, total_amount)
# 插入入库明细表
from database.queries import insert_stock_in_detail
for detail in self.details:
product_id = next(p['id'] for p in get_all_products() if p['name'] == detail['product_name'])
insert_stock_in_detail(stock_in_id, product_id, detail['quantity'], detail['price'], detail['amount'])
print("入库记录提交成功")
self.details.clear()
self.details_table.setRowCount(0)
在 database/queries.py
中编写数据库操作函数:
def insert_stock_in(supplier_id, date, total_quantity, total_amount):
"""插入入库主表记录"""
conn = sqlite3.connect('warehouse.db')
cursor = conn.cursor()
cursor.execute('''
INSERT INTO StockIn (supplier_id, date, total_quantity, total_amount)
VALUES (?, ?, ?, ?)
''', (supplier_id, date, total_quantity, total_amount))
conn.commit()
stock_in_id = cursor.lastrowid
conn.close()
return stock_in_id
def insert_stock_in_detail(stock_in_id, product_id, quantity, price, amount):
"""插入入库明细表记录"""
conn = sqlite3.connect('warehouse.db')
cursor = conn.cursor()
cursor.execute('''
INSERT INTO StockInDetails (stock_in_id, product_id, quantity, price, amount)
VALUES (?, ?, ?, ?, ?)
''', (stock_in_id, product_id, quantity, price, amount))
conn.commit()
conn.close()
通过以上设计,系统可以支持:
这种方法非常适合需要记录复杂入库场景的仓库管理系统。
为了支持 Excel 导出和导入 功能,我们可以使用 Python 的 openpyxl
库来处理 Excel 文件。以下是实现方案,涵盖 商品管理、供应商管理、客户管理 和 入库记录 等模块的 Excel 导出和导入功能。
首先,安装 openpyxl
库:
pip install openpyxl
在 utils/excel_utils.py
中编写导出工具函数:
from openpyxl import Workbook
def export_to_excel(data, headers, file_path):
"""
将数据导出到 Excel 文件
:param data: 数据列表(每行是一个字典)
:param headers: 表头列表(例如 ["ID", "名称", "描述", "价格", "库存"])
:param file_path: 导出的文件路径(例如 "products.xlsx")
"""
wb = Workbook()
ws = wb.active
# 写入表头
ws.append(headers)
# 写入数据
for row in data:
ws.append([row.get(header.lower(), "") for header in headers])
# 保存文件
wb.save(file_path)
print(f"数据已导出到 {file_path}")
在 logic/product_logic.py
中调用导出函数:
from utils.excel_utils import export_to_excel
from database.queries import get_all_products
def export_products_to_excel(file_path):
"""导出商品数据到 Excel"""
products = get_all_products()
headers = ["ID", "名称", "描述", "价格", "库存"]
export_to_excel(products, headers, file_path)
在 ui/product_ui.py
中添加导出按钮:
from PySide6.QtWidgets import QFileDialog
from logic.product_logic import export_products_to_excel
class ProductUI(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.layout = QVBoxLayout()
# 导出按钮
self.export_button = QPushButton("导出到 Excel")
self.export_button.clicked.connect(self.export_products)
self.layout.addWidget(self.export_button)
self.setLayout(self.layout)
def export_products(self):
"""导出商品数据"""
file_path, _ = QFileDialog.getSaveFileName(self, "保存文件", "", "Excel 文件 (*.xlsx)")
if file_path:
export_products_to_excel(file_path)
在 utils/excel_utils.py
中编写导入工具函数:
from openpyxl import load_workbook
def import_from_excel(file_path):
"""
从 Excel 文件导入数据
:param file_path: Excel 文件路径
:return: 数据列表(每行是一个字典)
"""
wb = load_workbook(file_path)
ws = wb.active
# 读取表头
headers = [cell.value for cell in ws[1]]
# 读取数据
data = []
for row in ws.iter_rows(min_row=2, values_only=True):
row_data = {headers[i].lower(): row[i] for i in range(len(headers))}
data.append(row_data)
return data
在 logic/product_logic.py
中调用导入函数:
from utils.excel_utils import import_from_excel
from database.queries import insert_product
def import_products_from_excel(file_path):
"""从 Excel 导入商品数据"""
data = import_from_excel(file_path)
for item in data:
insert_product(
name=item.get("名称", ""),
description=item.get("描述", ""),
price=item.get("价格", 0),
quantity=item.get("库存", 0)
)
print(f"成功导入 {len(data)} 条商品数据")
在 ui/product_ui.py
中添加导入按钮:
from logic.product_logic import import_products_from_excel
class ProductUI(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.layout = QVBoxLayout()
# 导入按钮
self.import_button = QPushButton("从 Excel 导入")
self.import_button.clicked.connect(self.import_products)
self.layout.addWidget(self.import_button)
self.setLayout(self_layout)
def import_products(self):
"""导入商品数据"""
file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "Excel 文件 (*.xlsx)")
if file_path:
import_products_from_excel(file_path)
from database.queries import get_all_suppliers
def export_suppliers_to_excel(file_path):
"""导出供应商数据到 Excel"""
suppliers = get_all_suppliers()
headers = ["ID", "名称", "联系人", "电话", "地址"]
export_to_excel(suppliers, headers, file_path)
from database.queries import insert_supplier
def import_suppliers_from_excel(file_path):
"""从 Excel 导入供应商数据"""
data = import_from_excel(file_path)
for item in data:
insert_supplier(
name=item.get("名称", ""),
contact=item.get("联系人", ""),
phone=item.get("电话", ""),
address=item.get("地址", "")
)
print(f"成功导入 {len(data)} 条供应商数据")
from database.queries import get_all_customers
def export_customers_to_excel(file_path):
"""导出客户数据到 Excel"""
customers = get_all_customers()
headers = ["ID", "名称", "联系人", "电话", "地址"]
export_to_excel(customers, headers, file_path)
from database.queries import insert_customer
def import_customers_from_excel(file_path):
"""从 Excel 导入客户数据"""
data = import_from_excel(file_path)
for item in data:
insert_customer(
name=item.get("名称", ""),
contact=item.get("联系人", ""),
phone=item.get("电话", ""),
address=item.get("地址", "")
)
print(f"成功导入 {len(data)} 条客户数据")
from database.queries import get_all_stock_in
def export_stock_in_to_excel(file_path):
"""导出入库记录到 Excel"""
stock_in_records = get_all_stock_in()
headers = ["ID", "供应商", "日期", "总数量", "总金额"]
export_to_excel(stock_in_records, headers, file_path)
from database.queries import insert_stock_in
def import_stock_in_from_excel(file_path):
"""从 Excel 导入入库记录"""
data = import_from_excel(file_path)
for item in data:
insert_stock_in(
supplier_id=item.get("供应商ID", 0),
date=item.get("日期", ""),
total_quantity=item.get("总数量", 0),
total_amount=item.get("总金额", 0)
)
print(f"成功导入 {len(data)} 条入库记录")
通过以上实现,系统可以支持:
这种方法非常适合需要与 Excel 交互的仓库管理系统,同时也能提高数据管理的效率。
要实现 模糊录入 功能,系统需要能够从用户粘贴的文本中自动识别 产品名称、单价 和 数量。这可以通过以下步骤实现:
import re
def preprocess_text(text):
"""
预处理文本
:param text: 用户输入的文本
:return: 处理后的文本列表(每行一个条目)
"""
# 去除多余的空格和换行符
text = re.sub(r'\s+', ' ', text).strip()
# 按行或分号拆分
lines = re.split(r'[\n;]', text)
return [line.strip() for line in lines if line.strip()]
假设系统中已有商品列表,可以从数据库中获取:
from database.queries import get_all_products
def extract_product_name(text, products):
"""
从文本中提取产品名称
:param text: 单行文本
:param products: 商品列表(从数据库获取)
:return: 匹配到的产品名称,如果没有匹配到则返回 None
"""
for product in products:
if product['name'] in text:
return product['name']
return None
def extract_price_and_quantity(text):
"""
从文本中提取单价和数量
:param text: 单行文本
:return: 单价和数量(如果未提取到则返回 None)
"""
# 匹配数字(支持小数)
numbers = re.findall(r'\d+\.?\d*', text)
if len(numbers) >= 2:
return float(numbers[0]), int(numbers[1]) # 第一个数字是单价,第二个是数量
return None, None
def parse_input_text(text):
"""
解析用户输入的文本
:param text: 用户输入的文本
:return: 解析后的数据列表(每个条目是一个字典)
"""
products = get_all_products()
lines = preprocess_text(text)
result = []
for line in lines:
product_name = extract_product_name(line, products)
price, quantity = extract_price_and_quantity(line)
if product_name and price and quantity:
result.append({
"product_name": product_name,
"price": price,
"quantity": quantity
})
return result
在 ui/stock_ui.py
中添加模糊录入功能:
from PySide6.QtWidgets import QTextEdit, QMessageBox
class StockInUI(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.layout = QVBoxLayout()
# 模糊录入文本框
self.fuzzy_input = QTextEdit()
self.fuzzy_input.setPlaceholderText("粘贴文本内容(例如:苹果 5.5元 3个)")
self.layout.addWidget(self.fuzzy_input)
# 解析按钮
self.parse_button = QPushButton("解析文本")
self.parse_button.clicked.connect(self.parse_input)
self.layout.addWidget(self.parse_button)
self.setLayout(self.layout)
def parse_input(self):
"""解析用户输入的文本"""
text = self.fuzzy_input.toPlainText()
if not text:
QMessageBox.warning(self, "错误", "请输入文本内容")
return
parsed_data = parse_input_text(text)
if not parsed_data:
QMessageBox.warning(self, "错误", "未识别到有效数据")
return
# 将解析结果填充到表单中
for item in parsed_data:
self.add_detail(item['product_name'], item['quantity'], item['price'])
QMessageBox.information(self, "成功", f"成功解析 {len(parsed_data)} 条数据")
def add_detail(self, product_name, quantity, price):
"""将解析结果添加到明细表格"""
row = self.details_table.rowCount()
self.details_table.insertRow(row)
self.details_table.setItem(row, 0, QTableWidgetItem(product_name))
self.details_table.setItem(row, 1, QTableWidgetItem(str(quantity)))
self.details_table.setItem(row, 2, QTableWidgetItem(str(price)))
self.details_table.setItem(row, 3, QTableWidgetItem(str(quantity * price)))
苹果 5.5元 3个
香蕉 3.2元 5个
橙子 4.0元 2个
[
{"product_name": "苹果", "price": 5.5, "quantity": 3},
{"product_name": "香蕉", "price": 3.2, "quantity": 5},
{"product_name": "橙子", "price": 4.0, "quantity": 2}
]
通过以上实现,系统可以:
这种方法非常适合需要快速录入数据的场景,能够显著提高工作效率。
为了实现 每周五自动发送邮件 给采购人员,通知库存低的商品需要补货,我们可以结合以下技术:
schedule
库或操作系统的定时任务(如 cron
)来定期执行脚本。smtplib
库发送邮件。以下是详细实现方案:
安装所需的 Python 库:
pip install schedule
在 database/queries.py
中编写查询库存低商品的函数:
def get_low_stock_products(threshold=10):
"""
获取库存低于阈值的商品
:param threshold: 库存阈值,默认 10
:return: 库存低的商品列表
"""
conn = sqlite3.connect('warehouse.db')
cursor = conn.cursor()
cursor.execute('''
SELECT name, quantity FROM Products WHERE quantity < ?
''', (threshold,))
products = cursor.fetchall()
conn.close()
return products
在 utils/email_utils.py
中编写邮件发送工具:
import smtplib
from email.mime.text import MIMEText
from email.header import Header
def send_email(subject, body, to_emails, smtp_server, smtp_port, sender_email, sender_password):
"""
发送邮件
:param subject: 邮件主题
:param body: 邮件正文
:param to_emails: 收件人邮箱列表
:param smtp_server: SMTP 服务器地址
:param smtp_port: SMTP 服务器端口
:param sender_email: 发件人邮箱
:param sender_password: 发件人邮箱密码或授权码
"""
try:
# 创建邮件内容
msg = MIMEText(body, 'plain', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = sender_email
msg['To'] = ', '.join(to_emails)
# 连接 SMTP 服务器并发送邮件
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls() # 启用 TLS 加密
server.login(sender_email, sender_password)
server.sendmail(sender_email, to_emails, msg.as_string())
print("邮件发送成功")
except Exception as e:
print(f"邮件发送失败: {e}")
在 scripts/low_stock_notification.py
中编写定时任务脚本:
import schedule
import time
from database.queries import get_low_stock_products
from utils.email_utils import send_email
def check_low_stock_and_notify():
"""
检查库存低的商品并发送邮件通知
"""
# 获取库存低的商品
low_stock_products = get_low_stock_products(threshold=10)
if not low_stock_products:
print("没有库存低的商品")
return
# 生成邮件正文
body = "以下商品库存较低,请及时补货:\n\n"
for product in low_stock_products:
body += f"商品名称: {product[0]}, 当前库存: {product[1]}\n"
# 邮件配置
subject = "库存补货通知"
to_emails = ["[email protected]", "[email protected]"] # 采购人员邮箱
smtp_server = "smtp.example.com" # SMTP 服务器地址
smtp_port = 587 # SMTP 服务器端口
sender_email = "[email protected]" # 发件人邮箱
sender_password = "your_password" # 发件人邮箱密码或授权码
# 发送邮件
send_email(subject, body, to_emails, smtp_server, smtp_port, sender_email, sender_password)
# 每周五上午 10:00 执行任务
schedule.every().friday.at("10:00").do(check_low_stock_and_notify)
# 保持脚本运行
while True:
schedule.run_pending()
time.sleep(1)
直接运行 low_stock_notification.py
脚本:
python scripts/low_stock_notification.py
如果希望脚本在后台运行,可以使用操作系统的定时任务工具。
cron
crontab -e
0 10 * * 5 python /path/to/your/scripts/low_stock_notification.py
python
并指定脚本路径。库存补货通知
以下商品库存较低,请及时补货:
商品名称: 苹果, 当前库存: 5
商品名称: 香蕉, 当前库存: 8
商品名称: 橙子, 当前库存: 3
通过以上实现,系统可以:
这种方法非常适合需要定期补货的场景,能够显著提高仓库管理的效率。