Compare commits
3 Commits
41a4518911
...
367dceef31
| Author | SHA1 | Date | |
|---|---|---|---|
| 367dceef31 | |||
| f6153fc10d | |||
| a5f4d32306 |
@ -16,9 +16,9 @@ from app.models.base import MaterialBase
|
||||
|
||||
# 尝试导入用户模型
|
||||
try:
|
||||
from app.models.sys.user import User
|
||||
from app.models.system import SysUser
|
||||
except ImportError:
|
||||
User = None
|
||||
SysUser = None
|
||||
|
||||
# 尝试导入半成品和成品
|
||||
try:
|
||||
@ -590,21 +590,29 @@ def export_stocktake():
|
||||
}
|
||||
|
||||
def get_user_name(user_id):
|
||||
"""获取用户真实姓名或昵称"""
|
||||
if not User or not user_id:
|
||||
"""获取用户真实姓名
|
||||
SysUser.username 存储格式为 "真实姓名/登录账号" (例如: 张三/zhangsan01)
|
||||
"""
|
||||
if not SysUser or not user_id:
|
||||
return str(user_id) if user_id else '-'
|
||||
try:
|
||||
user = None
|
||||
# 尝试通过ID或用户名查找
|
||||
if str(user_id).isdigit():
|
||||
user = User.query.get(int(user_id))
|
||||
user = SysUser.query.get(int(user_id))
|
||||
if not user:
|
||||
user = User.query.filter_by(username=str(user_id)).first()
|
||||
user = SysUser.query.filter(SysUser.username.like(f"%/{user_id}")).first()
|
||||
if not user:
|
||||
user = User.query.filter_by(username=str(user_id).upper()).first()
|
||||
user = SysUser.query.filter_by(username=str(user_id)).first()
|
||||
|
||||
if not user:
|
||||
user = User.query.filter_by(username=str(user_id).lower()).first()
|
||||
return getattr(user, 'real_name', None) or getattr(user, 'nickname', None) or str(user_id)
|
||||
return str(user_id)
|
||||
|
||||
# 解析 username 格式: "张三/zhangsan01" -> 取前面的真实姓名
|
||||
raw_username = getattr(user, 'username', None) or str(user_id)
|
||||
if '/' in raw_username:
|
||||
return raw_username.split('/')[0]
|
||||
return raw_username
|
||||
except:
|
||||
return str(user_id)
|
||||
|
||||
@ -627,32 +635,21 @@ def export_stocktake():
|
||||
cell.alignment = header_alignment
|
||||
cell.border = thin_border
|
||||
|
||||
# ===== Sheet 1: 盘点差异明细 =====
|
||||
ws1 = wb.create_sheet("盘点差异明细")
|
||||
# ===== Sheet 1: 盘点全景汇总表 (放在最前面) =====
|
||||
ws1 = wb.create_sheet("盘点全景汇总表", 0)
|
||||
summary_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点状态", "盘点人", "盘点时间"]
|
||||
set_header_row(ws1, summary_headers)
|
||||
master_row_idx = 2 # 汇总表行计数器
|
||||
|
||||
# ===== Sheet 2: 盘点差异明细 =====
|
||||
ws2 = wb.create_sheet("盘点差异明细")
|
||||
diff_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间"]
|
||||
set_header_row(ws1, diff_headers)
|
||||
set_header_row(ws2, diff_headers)
|
||||
|
||||
diff_drafts = StocktakeDraft.query.filter(StocktakeDraft.diff_qty != 0).all()
|
||||
for row_idx, draft in enumerate(diff_drafts, 2):
|
||||
mat_info = get_material_info(draft.source_table, draft.stock_id)
|
||||
ws1.cell(row=row_idx, column=1, value=mat_info['name']).border = thin_border
|
||||
ws1.cell(row=row_idx, column=2, value=mat_info['sku']).border = thin_border
|
||||
ws1.cell(row=row_idx, column=3, value=mat_info['spec']).border = thin_border
|
||||
ws1.cell(row=row_idx, column=4, value=mat_info['location']).border = thin_border
|
||||
ws1.cell(row=row_idx, column=5, value=float(draft.stock_qty or 0)).border = thin_border
|
||||
ws1.cell(row=row_idx, column=6, value=float(draft.quantity or 0)).border = thin_border
|
||||
ws1.cell(row=row_idx, column=7, value=float(draft.diff_qty or 0)).border = thin_border
|
||||
ws1.cell(row=row_idx, column=8, value=get_user_name(draft.user_id)).border = thin_border
|
||||
ws1.cell(row=row_idx, column=9, value=to_beijing_time(draft.scan_time)).border = thin_border
|
||||
|
||||
# ===== Sheet 2: 账实相符明细 =====
|
||||
ws2 = wb.create_sheet("账实相符明细")
|
||||
normal_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间"]
|
||||
set_header_row(ws2, normal_headers)
|
||||
|
||||
normal_drafts = StocktakeDraft.query.filter(StocktakeDraft.diff_qty == 0).all()
|
||||
for row_idx, draft in enumerate(normal_drafts, 2):
|
||||
mat_info = get_material_info(draft.source_table, draft.stock_id)
|
||||
# 写入 Sheet 2 (差异明细)
|
||||
ws2.cell(row=row_idx, column=1, value=mat_info['name']).border = thin_border
|
||||
ws2.cell(row=row_idx, column=2, value=mat_info['sku']).border = thin_border
|
||||
ws2.cell(row=row_idx, column=3, value=mat_info['spec']).border = thin_border
|
||||
@ -662,11 +659,54 @@ def export_stocktake():
|
||||
ws2.cell(row=row_idx, column=7, value=float(draft.diff_qty or 0)).border = thin_border
|
||||
ws2.cell(row=row_idx, column=8, value=get_user_name(draft.user_id)).border = thin_border
|
||||
ws2.cell(row=row_idx, column=9, value=to_beijing_time(draft.scan_time)).border = thin_border
|
||||
|
||||
# ===== Sheet 3: 外借在用资产明细 =====
|
||||
ws3 = wb.create_sheet("外借在用资产明细")
|
||||
# 同时写入 Sheet 1 (汇总表)
|
||||
ws1.cell(row=master_row_idx, column=1, value=mat_info['name']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=2, value=mat_info['sku']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=3, value=mat_info['spec']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=4, value=mat_info['location']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=5, value=float(draft.stock_qty or 0)).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=6, value=float(draft.quantity or 0)).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=7, value=float(draft.diff_qty or 0)).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=8, value="有差异").border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=9, value=get_user_name(draft.user_id)).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=10, value=to_beijing_time(draft.scan_time)).border = thin_border
|
||||
master_row_idx += 1
|
||||
|
||||
# ===== Sheet 3: 账实相符明细 =====
|
||||
ws3 = wb.create_sheet("账实相符明细")
|
||||
normal_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间"]
|
||||
set_header_row(ws3, normal_headers)
|
||||
|
||||
normal_drafts = StocktakeDraft.query.filter(StocktakeDraft.diff_qty == 0).all()
|
||||
for row_idx, draft in enumerate(normal_drafts, 2):
|
||||
mat_info = get_material_info(draft.source_table, draft.stock_id)
|
||||
# 写入 Sheet 3 (账实相符)
|
||||
ws3.cell(row=row_idx, column=1, value=mat_info['name']).border = thin_border
|
||||
ws3.cell(row=row_idx, column=2, value=mat_info['sku']).border = thin_border
|
||||
ws3.cell(row=row_idx, column=3, value=mat_info['spec']).border = thin_border
|
||||
ws3.cell(row=row_idx, column=4, value=mat_info['location']).border = thin_border
|
||||
ws3.cell(row=row_idx, column=5, value=float(draft.stock_qty or 0)).border = thin_border
|
||||
ws3.cell(row=row_idx, column=6, value=float(draft.quantity or 0)).border = thin_border
|
||||
ws3.cell(row=row_idx, column=7, value=float(draft.diff_qty or 0)).border = thin_border
|
||||
ws3.cell(row=row_idx, column=8, value=get_user_name(draft.user_id)).border = thin_border
|
||||
ws3.cell(row=row_idx, column=9, value=to_beijing_time(draft.scan_time)).border = thin_border
|
||||
# 同时写入 Sheet 1 (汇总表)
|
||||
ws1.cell(row=master_row_idx, column=1, value=mat_info['name']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=2, value=mat_info['sku']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=3, value=mat_info['spec']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=4, value=mat_info['location']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=5, value=float(draft.stock_qty or 0)).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=6, value=float(draft.quantity or 0)).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=7, value=float(draft.diff_qty or 0)).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=8, value="正常").border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=9, value=get_user_name(draft.user_id)).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=10, value=to_beijing_time(draft.scan_time)).border = thin_border
|
||||
master_row_idx += 1
|
||||
|
||||
# ===== Sheet 4: 外借在用资产明细 =====
|
||||
ws4 = wb.create_sheet("外借在用资产明细")
|
||||
borrow_headers = ["借出单号", "借用人", "物料名称", "SKU", "规格型号", "借出总数", "已还数量", "待还数量", "借出时间", "预计归还时间"]
|
||||
set_header_row(ws3, borrow_headers)
|
||||
set_header_row(ws4, borrow_headers)
|
||||
|
||||
# 查询未归还的借出记录
|
||||
unreturned_borrows = TransBorrow.query.filter(TransBorrow.is_returned == False).all()
|
||||
@ -686,9 +726,130 @@ def export_stocktake():
|
||||
ws3.cell(row=row_idx, column=8, value=pending_qty).border = thin_border
|
||||
ws3.cell(row=row_idx, column=9, value=to_beijing_time(borrow.borrow_time)).border = thin_border
|
||||
ws3.cell(row=row_idx, column=10, value='无限期' if not borrow.expected_return_time else to_beijing_time(borrow.expected_return_time)).border = thin_border
|
||||
|
||||
|
||||
# ===== Sheet 5: 未盘点明细(疑似漏盘) =====
|
||||
# 逻辑:获取已盘点的集合,遍历库存表,找出未盘点且有库存的物资
|
||||
ws5 = wb.create_sheet("未盘点明细(疑似漏盘)")
|
||||
unscanned_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "状态"]
|
||||
set_header_row(ws5, unscanned_headers)
|
||||
|
||||
# 获取已盘点的 (source_table, stock_id) 集合
|
||||
all_drafts = StocktakeDraft.query.all()
|
||||
scanned_set = {(d.source_table, d.stock_id) for d in all_drafts}
|
||||
|
||||
def get_borrowed_qty(source_table, stock_id):
|
||||
"""获取某库存的借出未还数量"""
|
||||
try:
|
||||
borrowed = TransBorrow.query.filter(
|
||||
TransBorrow.source_table == source_table,
|
||||
TransBorrow.stock_id == stock_id,
|
||||
TransBorrow.is_returned == False
|
||||
).all()
|
||||
total = sum((b.quantity or 0) - (b.returned_quantity or 0) for b in borrowed)
|
||||
return total
|
||||
except:
|
||||
return 0
|
||||
|
||||
unscanned_items = []
|
||||
|
||||
# 遍历 StockBuy
|
||||
for stock in StockBuy.query.all():
|
||||
key = ('stock_buy', stock.id)
|
||||
if key in scanned_set:
|
||||
continue
|
||||
stock_qty = float(stock.stock_quantity or 0)
|
||||
if stock_qty <= 0:
|
||||
continue
|
||||
# 扣除外借数量
|
||||
borrowed_qty = get_borrowed_qty('stock_buy', stock.id)
|
||||
expected_qty = stock_qty - borrowed_qty
|
||||
if expected_qty > 0:
|
||||
mat_info = get_material_info('stock_buy', stock.id)
|
||||
unscanned_items.append({
|
||||
'name': mat_info['name'],
|
||||
'sku': mat_info['sku'],
|
||||
'spec': mat_info['spec'],
|
||||
'location': mat_info['location'],
|
||||
'stock_qty': expected_qty,
|
||||
'actual_qty': 0,
|
||||
'diff_qty': -expected_qty,
|
||||
'status': '未盘点'
|
||||
})
|
||||
|
||||
# 遍历 StockSemi
|
||||
if StockSemi:
|
||||
for stock in StockSemi.query.all():
|
||||
key = ('stock_semi', stock.id)
|
||||
if key in scanned_set:
|
||||
continue
|
||||
stock_qty = float(stock.stock_quantity or 0)
|
||||
if stock_qty <= 0:
|
||||
continue
|
||||
borrowed_qty = get_borrowed_qty('stock_semi', stock.id)
|
||||
expected_qty = stock_qty - borrowed_qty
|
||||
if expected_qty > 0:
|
||||
mat_info = get_material_info('stock_semi', stock.id)
|
||||
unscanned_items.append({
|
||||
'name': mat_info['name'],
|
||||
'sku': mat_info['sku'],
|
||||
'spec': mat_info['spec'],
|
||||
'location': mat_info['location'],
|
||||
'stock_qty': expected_qty,
|
||||
'actual_qty': 0,
|
||||
'diff_qty': -expected_qty,
|
||||
'status': '未盘点'
|
||||
})
|
||||
|
||||
# 遍历 StockProduct
|
||||
if StockProduct:
|
||||
for stock in StockProduct.query.all():
|
||||
key = ('stock_product', stock.id)
|
||||
if key in scanned_set:
|
||||
continue
|
||||
stock_qty = float(stock.stock_quantity or 0)
|
||||
if stock_qty <= 0:
|
||||
continue
|
||||
borrowed_qty = get_borrowed_qty('stock_product', stock.id)
|
||||
expected_qty = stock_qty - borrowed_qty
|
||||
if expected_qty > 0:
|
||||
mat_info = get_material_info('stock_product', stock.id)
|
||||
unscanned_items.append({
|
||||
'name': mat_info['name'],
|
||||
'sku': mat_info['sku'],
|
||||
'spec': mat_info['spec'],
|
||||
'location': mat_info['location'],
|
||||
'stock_qty': expected_qty,
|
||||
'actual_qty': 0,
|
||||
'diff_qty': -expected_qty,
|
||||
'status': '未盘点'
|
||||
})
|
||||
|
||||
# 写入未盘点明细
|
||||
for row_idx, item in enumerate(unscanned_items, 2):
|
||||
# 写入 Sheet 5 (未盘点明细)
|
||||
ws5.cell(row=row_idx, column=1, value=item['name']).border = thin_border
|
||||
ws5.cell(row=row_idx, column=2, value=item['sku']).border = thin_border
|
||||
ws5.cell(row=row_idx, column=3, value=item['spec']).border = thin_border
|
||||
ws5.cell(row=row_idx, column=4, value=item['location']).border = thin_border
|
||||
ws5.cell(row=row_idx, column=5, value=item['stock_qty']).border = thin_border
|
||||
ws5.cell(row=row_idx, column=6, value=item['actual_qty']).border = thin_border
|
||||
ws5.cell(row=row_idx, column=7, value=item['diff_qty']).border = thin_border
|
||||
ws5.cell(row=row_idx, column=8, value=item['status']).border = thin_border
|
||||
# 同时写入 Sheet 1 (汇总表) - 盘点人和时间留空
|
||||
ws1.cell(row=master_row_idx, column=1, value=item['name']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=2, value=item['sku']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=3, value=item['spec']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=4, value=item['location']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=5, value=item['stock_qty']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=6, value=item['actual_qty']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=7, value=item['diff_qty']).border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=8, value="未盘点").border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=9, value="").border = thin_border
|
||||
ws1.cell(row=master_row_idx, column=10, value="").border = thin_border
|
||||
master_row_idx += 1
|
||||
|
||||
# 调整列宽
|
||||
for ws in [ws1, ws2, ws3]:
|
||||
for ws in [ws1, ws2, ws3, ws4, ws5]:
|
||||
for col in ws.columns:
|
||||
max_length = 0
|
||||
col_letter = col[0].column_letter
|
||||
|
||||
Reference in New Issue
Block a user