Compare commits

...

5 Commits

Author SHA1 Message Date
26397a83dd 1 2025-12-29 00:36:40 +08:00
d33216f9b9 添加库存不足检查功能
在记录消耗到库存功能中增加了库存充足性验证,确保只有在所有原料库存充足时才允许记录消耗。

主要改进:
- 在记录消耗前先检查所有原料的当前库存
- 如果任何原料库存不足,显示详细的库存不足信息并中止操作
- 提供清晰的提示信息,显示每种原料所需数量和实际库存数量
- 只有所有原料库存都充足时才执行记录操作
- 优化成功提示信息,显示记录的原料种类数量

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 00:33:51 +08:00
5cf27c8cfe 优化原料管理界面功能
1. 类目选项标准化
   - 新增和编辑界面的类目固定为"面料"、"辅料"、"其他"三个选项
   - 新增原料时默认选中"面料"

2. 必填字段验证
   - 新增必填验证:类目、类型、幅宽、克重
   - 保存时验证所有必填字段,提供明确的错误提示

3. 已删除原料恢复功能
   - 允许新增已被删除的型号,自动恢复并更新原料信息
   - 无需用户确认,直接恢复已删除原料

4. 供应商字段优化
   - 新增原料时供应商默认为空
   - 用户可选择输入新供应商或从列表选择

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:41:39 +08:00
803127062e 修复新建数据库缺少is_deleted列的问题
在CREATE TABLE语句中直接添加is_deleted字段,确保新建数据库时就包含该列。
修改了三个表的建表语句:
- fabrics表
- fabric_stock_in表
- fabric_consumption表

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:14:31 +08:00
83609aaa20 款号管理列表设置为只读模式
将衣服款号管理界面的表格所有列(款号、类目数量、款式图预览)设置为只读,防止用户直接在表格中编辑内容,保持与原料库列表的一致性。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:10:43 +08:00
4 changed files with 161 additions and 60 deletions

View File

@@ -42,7 +42,8 @@ class DatabaseManager:
unit TEXT DEFAULT '',
timestamp TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_deleted INTEGER DEFAULT 0
)
''')
@@ -194,6 +195,7 @@ class DatabaseManager:
note TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_deleted INTEGER DEFAULT 0,
FOREIGN KEY (model) REFERENCES fabrics(model)
)
''')
@@ -216,6 +218,7 @@ class DatabaseManager:
unit TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_deleted INTEGER DEFAULT 0,
FOREIGN KEY (style_number) REFERENCES garments(style_number),
FOREIGN KEY (model) REFERENCES fabrics(model)
)

View File

@@ -142,17 +142,24 @@ class GarmentLibraryDialog(QDialog):
self.garment_table.setRowHeight(i, 140)
for row_idx, (style_number, image_path) in enumerate(rows):
self.garment_table.setItem(row_idx, 0, QTableWidgetItem(style_number))
# 款号列 - 设置为只读
style_item = QTableWidgetItem(style_number)
style_item.setFlags(style_item.flags() & ~Qt.ItemIsEditable)
self.garment_table.setItem(row_idx, 0, style_item)
# 查询材料数量
with self.get_conn() as conn:
cursor2 = conn.execute("SELECT COUNT(*) FROM garment_materials WHERE style_number = ?", (style_number,))
count = cursor2.fetchone()[0]
self.garment_table.setItem(row_idx, 1, QTableWidgetItem(str(count)))
# 类目数量列 - 设置为只读
count_item = QTableWidgetItem(str(count))
count_item.setFlags(count_item.flags() & ~Qt.ItemIsEditable)
self.garment_table.setItem(row_idx, 1, count_item)
# 显示图片预览
image_item = QTableWidgetItem()
image_item.setTextAlignment(Qt.AlignCenter)
image_item.setFlags(image_item.flags() & ~Qt.ItemIsEditable)
if image_path and os.path.exists(image_path):
try:

69
main.py
View File

@@ -234,7 +234,7 @@ class FabricManager(QMainWindow):
return
quantity = self.quantity_input.value()
loss_rate = self.loss_input.value() / 100
try:
with self.get_conn() as conn:
cursor = conn.execute('''
@@ -243,21 +243,24 @@ class FabricManager(QMainWindow):
WHERE style_number = ?
''', (style_number,))
rows = cursor.fetchall()
inserted = 0
# 第一步:检查所有原料库存是否充足
insufficient_materials = []
materials_to_record = []
for category, fabric_type, usage_per_piece, unit in rows:
if usage_per_piece == 0:
continue
# 获取该原料在入库时使用的单位
stock_cursor = conn.execute('''
SELECT unit FROM fabric_stock_in
WHERE model = ?
ORDER BY purchase_date DESC
SELECT unit FROM fabric_stock_in
WHERE model = ?
ORDER BY purchase_date DESC
LIMIT 1
''', (category,))
stock_unit_row = stock_cursor.fetchone()
# 如果有入库记录,使用入库单位;否则使用原来的单位
if stock_unit_row:
stock_unit = stock_unit_row[0]
@@ -272,17 +275,59 @@ class FabricManager(QMainWindow):
# 没有入库记录,使用原单位
consume_qty = usage_per_piece * quantity * (1 + loss_rate)
final_unit = unit
# 检查当前库存
stock_info = conn.execute('''
SELECT
COALESCE(SUM(CASE WHEN si.is_deleted = 0 THEN si.quantity ELSE 0 END), 0) AS total_in,
COALESCE(SUM(CASE WHEN c.is_deleted = 0 THEN c.consume_quantity ELSE 0 END), 0) AS total_out
FROM fabrics f
LEFT JOIN fabric_stock_in si ON f.model = si.model
LEFT JOIN fabric_consumption c ON f.model = c.model
WHERE f.model = ?
GROUP BY f.model
''', (category,)).fetchone()
if stock_info:
total_in, total_out = stock_info
current_stock = total_in - total_out
else:
current_stock = 0
# 如果库存不足,记录下来
if current_stock < consume_qty:
material_name = category or "未命名材料"
insufficient_materials.append(
f"{material_name}: 需要 {consume_qty:.3f} {final_unit},库存仅剩 {current_stock:.3f} {final_unit}"
)
else:
# 库存充足,加入待记录列表
materials_to_record.append((
style_number, category, usage_per_piece, quantity,
loss_rate, consume_qty, final_unit
))
# 如果有库存不足的材料,提示用户并中止操作
if insufficient_materials:
message = "以下原料库存不足,无法记录消耗:\n\n" + "\n".join(insufficient_materials)
QMessageBox.warning(self, "库存不足", message)
return
# 第二步:所有库存都充足,执行记录
inserted = 0
for material_data in materials_to_record:
style_num, model, single_usage, qty, loss, consume_qty, final_unit = material_data
conn.execute('''
INSERT INTO fabric_consumption
(style_number, model, single_usage, quantity_made, loss_rate, consume_quantity, consume_date, unit)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (style_number, category, usage_per_piece, quantity, loss_rate, consume_qty, datetime.now().strftime('%Y-%m-%d'), final_unit))
''', (style_num, model, single_usage, qty, loss, consume_qty, datetime.now().strftime('%Y-%m-%d'), final_unit))
inserted += 1
conn.commit()
if inserted > 0:
QMessageBox.information(self, "成功", "本次生产消耗已记录到库存!")
QMessageBox.information(self, "成功", f"本次生产消耗已记录到库存!共记录 {inserted} 种原料。")
else:
QMessageBox.information(self, "提示", "本次没有可记录的原料消耗")
except Exception as e:

View File

@@ -111,11 +111,8 @@ class RawMaterialEditDialog(QDialog):
"""加载类目和供应商列表"""
try:
with self.get_conn() as conn:
# 加载类目
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)")
majors = set(row[0] for row in cursor.fetchall() if row[0])
majors.update({"布料", "辅料", "其他"})
self.edit_major_category.addItems(sorted(majors))
# 加载类目 - 设置默认选项为:面料、辅料、其他
self.edit_major_category.addItems(["面料", "辅料", "其他"])
# 加载供应商
cursor = conn.execute("SELECT DISTINCT supplier FROM fabrics WHERE supplier IS NOT NULL AND supplier != '' AND (is_deleted IS NULL OR is_deleted = 0) ORDER BY supplier")
@@ -172,6 +169,26 @@ class RawMaterialEditDialog(QDialog):
major = self.edit_major_category.currentText().strip()
sub = self.edit_sub_category.text().strip()
# 验证必填字段:类目
if not major:
QMessageBox.warning(self, "错误", "请选择类目")
return
# 验证必填字段:类型
if not sub:
QMessageBox.warning(self, "错误", "请输入类型")
return
# 验证必填字段:幅宽
if self.edit_width.value() <= 0:
QMessageBox.warning(self, "错误", "请输入幅宽")
return
# 验证必填字段:克重
if self.edit_gsm.value() <= 0:
QMessageBox.warning(self, "错误", "请输入克重")
return
# 特殊处理胸杯类型
if "胸杯" in sub:
major = "辅料"
@@ -453,17 +470,17 @@ class RawMaterialLibraryDialog(QDialog):
self.load_table()
def load_major_categories(self):
"""加载主类目"""
"""加载主类目 - 只显示数据库中实际存在的类目"""
try:
with self.get_conn() as conn:
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)")
majors = set(row[0] for row in cursor.fetchall() if row[0])
majors.update({"布料", "辅料", "其他"})
self.major_combo.blockSignals(True)
self.major_combo.clear()
self.major_combo.addItem("全部类目")
self.major_combo.addItems(sorted(majors))
if majors:
self.major_combo.addItems(sorted(majors))
self.major_combo.blockSignals(False)
except:
pass
@@ -471,26 +488,12 @@ class RawMaterialLibraryDialog(QDialog):
def load_add_major_categories(self):
"""加载添加界面的主类目"""
try:
with self.get_conn() as conn:
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)")
majors = set(row[0] for row in cursor.fetchall() if row[0])
majors.update({"布料", "", "其他"})
self.add_major_category.blockSignals(True)
current_text = self.add_major_category.currentText()
self.add_major_category.clear()
self.add_major_category.addItems(sorted(majors))
if current_text in majors:
self.add_major_category.setCurrentText(current_text)
else:
self.add_major_category.setCurrentText("布料")
self.add_major_category.blockSignals(False)
except:
self.add_major_category.clear()
self.add_major_category.addItems(["布料", "辅料", "其他"])
self.add_major_category.setCurrentText("布料")
# 设置默认类目选项为:面料、辅料、其他
self.add_major_category.blockSignals(True)
self.add_major_category.clear()
self.add_major_category.addItems(["面料", "辅料", "其他"])
self.add_major_category.setCurrentText("")
self.add_major_category.blockSignals(False)
def load_sub_categories(self):
"""加载子类型"""
@@ -521,16 +524,19 @@ class RawMaterialLibraryDialog(QDialog):
with self.get_conn() as conn:
cursor = conn.execute("SELECT DISTINCT supplier FROM fabrics WHERE supplier IS NOT NULL AND supplier != '' AND (is_deleted IS NULL OR is_deleted = 0) ORDER BY supplier")
suppliers = [row[0] for row in cursor.fetchall()]
self.supplier_combo.blockSignals(True)
self.supplier_combo.clear()
self.supplier_combo.addItem("全部供应商")
self.supplier_combo.addItems(suppliers)
self.supplier_combo.blockSignals(False)
# 新增原料界面的供应商下拉框,添加空选项作为默认值
self.add_supplier.blockSignals(True)
self.add_supplier.clear()
self.add_supplier.addItem("") # 添加空选项
self.add_supplier.addItems(suppliers)
self.add_supplier.setCurrentIndex(0) # 默认选中空选项
self.add_supplier.blockSignals(False)
except:
pass
@@ -647,7 +653,7 @@ class RawMaterialLibraryDialog(QDialog):
def delete_raw(self, model):
"""逻辑删除原料"""
reply = QMessageBox.question(self, "确认", f"确定要删除 '{model}' 吗?\n\n此操作将标记删除该原料,不会影响历史数据。")
reply = QMessageBox.question(self, "确认", f"确定要删除 '{model}' 吗?")
if reply == QMessageBox.Yes:
try:
with self.get_conn() as conn:
@@ -668,6 +674,27 @@ class RawMaterialLibraryDialog(QDialog):
major = self.add_major_category.currentText().strip()
sub = self.add_sub_category.text().strip()
# 验证必填字段:类目
if not major:
QMessageBox.warning(self, "错误", "请选择类目")
return
# 验证必填字段:类型
if not sub:
QMessageBox.warning(self, "错误", "请输入类型")
return
# 验证必填字段:幅宽
if self.add_width.value() <= 0:
QMessageBox.warning(self, "错误", "请输入幅宽")
return
# 验证必填字段:克重
if self.add_gsm.value() <= 0:
QMessageBox.warning(self, "错误", "请输入克重")
return
if "胸杯" in sub:
major = "辅料"
@@ -680,23 +707,42 @@ class RawMaterialLibraryDialog(QDialog):
try:
with self.get_conn() as conn:
# 检查是否已存在
cursor = conn.execute("SELECT model FROM fabrics WHERE model = ?", (model,))
if cursor.fetchone():
QMessageBox.warning(self, "错误", f"型号 '{model}' 已存在,请使用编辑功能修改")
return
# 检查是否已存在该型号
cursor = conn.execute("SELECT model, is_deleted FROM fabrics WHERE model = ?", (model,))
existing = cursor.fetchone()
conn.execute('''
INSERT INTO fabrics
(model, category, fabric_type, supplier, color, width, gsm, retail_price, bulk_price, unit, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (model, category, fabric_type, supplier, color,
self.add_width.value() or None, self.add_gsm.value() or None,
self.add_retail.value() or None, self.add_bulk.value() or None,
unit, datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
conn.commit()
if existing:
_, is_deleted = existing
QMessageBox.information(self, "成功", f"已保存 '{model}'")
# 如果型号存在且未被删除,提示使用编辑功能
if not is_deleted or is_deleted == 0:
QMessageBox.warning(self, "错误", f"型号 '{model}' 已存在,请使用编辑功能修改")
return
# 如果型号已被删除,直接恢复并更新
conn.execute('''
UPDATE fabrics
SET category=?, fabric_type=?, supplier=?, color=?, width=?, gsm=?,
retail_price=?, bulk_price=?, unit=?, timestamp=?, is_deleted=0, updated_at=CURRENT_TIMESTAMP
WHERE model=?
''', (category, fabric_type, supplier, color,
self.add_width.value() or None, self.add_gsm.value() or None,
self.add_retail.value() or None, self.add_bulk.value() or None,
unit, datetime.now().strftime('%Y-%m-%d %H:%M:%S'), model))
conn.commit()
QMessageBox.information(self, "成功", f"已保存 '{model}'")
else:
# 型号不存在,创建新记录
conn.execute('''
INSERT INTO fabrics
(model, category, fabric_type, supplier, color, width, gsm, retail_price, bulk_price, unit, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (model, category, fabric_type, supplier, color,
self.add_width.value() or None, self.add_gsm.value() or None,
self.add_retail.value() or None, self.add_bulk.value() or None,
unit, datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
conn.commit()
QMessageBox.information(self, "成功", f"已保存 '{model}'")
# 清空表单
self.add_model.clear()