diff --git a/inventory-backend/app/api/v1/inbound/stock.py b/inventory-backend/app/api/v1/inbound/stock.py index 41ecf28..4270b1e 100644 --- a/inventory-backend/app/api/v1/inbound/stock.py +++ b/inventory-backend/app/api/v1/inbound/stock.py @@ -694,9 +694,117 @@ 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 4: 未盘点明细(疑似漏盘) ===== + # 逻辑:获取已盘点的集合,遍历库存表,找出未盘点且有库存的物资 + ws4 = wb.create_sheet("未盘点明细(疑似漏盘)") + unscanned_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "状态"] + set_header_row(ws4, 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): + ws4.cell(row=row_idx, column=1, value=item['name']).border = thin_border + ws4.cell(row=row_idx, column=2, value=item['sku']).border = thin_border + ws4.cell(row=row_idx, column=3, value=item['spec']).border = thin_border + ws4.cell(row=row_idx, column=4, value=item['location']).border = thin_border + ws4.cell(row=row_idx, column=5, value=item['stock_qty']).border = thin_border + ws4.cell(row=row_idx, column=6, value=item['actual_qty']).border = thin_border + ws4.cell(row=row_idx, column=7, value=item['diff_qty']).border = thin_border + ws4.cell(row=row_idx, column=8, value=item['status']).border = thin_border + # 调整列宽 - for ws in [ws1, ws2, ws3]: + for ws in [ws1, ws2, ws3, ws4]: for col in ws.columns: max_length = 0 col_letter = col[0].column_letter