Compare commits

5 Commits

3 changed files with 57 additions and 44 deletions

View File

@ -196,7 +196,7 @@ def add_draft():
# 计算借出未还数量 (quantity - returned_quantity)
borrowed_result = db.session.query(
db.func.sum(TransBorrow.quantity - TransBorrow.returned_quantity)
db.func.sum(db.func.coalesce(TransBorrow.quantity, 0) - db.func.coalesce(TransBorrow.returned_quantity, 0))
).filter(
TransBorrow.source_table == source_table,
TransBorrow.stock_id == stock_id,
@ -644,13 +644,13 @@ def export_stocktake():
# ===== Sheet 1: 盘点全景汇总表 (放在最前面) =====
ws1 = wb.create_sheet("盘点全景汇总表", 0)
summary_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点状态", "盘点人", "盘点时间"]
summary_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点状态", "盘点人", "盘点时间", "备注"]
set_header_row(ws1, summary_headers)
master_row_idx = 2 # 汇总表行计数器
# ===== Sheet 2: 盘点差异明细 =====
ws2 = wb.create_sheet("盘点差异明细")
diff_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间"]
diff_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间", "备注"]
set_header_row(ws2, diff_headers)
diff_drafts = StocktakeDraft.query.filter(StocktakeDraft.diff_qty != 0).all()
@ -666,6 +666,7 @@ 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
ws2.cell(row=row_idx, column=10, value=draft.remark or '').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
@ -677,11 +678,12 @@ def export_stocktake():
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
ws1.cell(row=master_row_idx, column=11, value=draft.remark or '').border = thin_border
master_row_idx += 1
# ===== Sheet 3: 账实相符明细 =====
ws3 = wb.create_sheet("账实相符明细")
normal_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间"]
normal_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间", "备注"]
set_header_row(ws3, normal_headers)
normal_drafts = StocktakeDraft.query.filter(StocktakeDraft.diff_qty == 0).all()
@ -697,6 +699,7 @@ def export_stocktake():
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
ws3.cell(row=row_idx, column=10, value=draft.remark or '').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
@ -708,13 +711,14 @@ def export_stocktake():
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
ws1.cell(row=master_row_idx, column=11, value=draft.remark or '').border = thin_border
master_row_idx += 1
# ===== Sheet 4: 外借在用资产明细 =====
ws4 = wb.create_sheet("外借在用资产明细")
borrow_headers = ["借出单号", "借用人", "物料名称", "SKU", "规格型号", "借出总数", "已还数量", "待还数量", "借出时间", "预计归还时间"]
set_header_row(ws4, borrow_headers)
# 查询未归还的借出记录
unreturned_borrows = TransBorrow.query.filter(TransBorrow.is_returned == False).all()
for row_idx, borrow in enumerate(unreturned_borrows, 2):
@ -723,16 +727,16 @@ def export_stocktake():
returned_qty = float(borrow.returned_quantity or 0)
pending_qty = total_qty - returned_qty
ws3.cell(row=row_idx, column=1, value=borrow.borrow_no or '').border = thin_border
ws3.cell(row=row_idx, column=2, value=borrow.borrower_name or '').border = thin_border
ws3.cell(row=row_idx, column=3, value=mat_info['name']).border = thin_border
ws3.cell(row=row_idx, column=4, value=mat_info['sku']).border = thin_border
ws3.cell(row=row_idx, column=5, value=mat_info['spec']).border = thin_border
ws3.cell(row=row_idx, column=6, value=total_qty).border = thin_border
ws3.cell(row=row_idx, column=7, value=returned_qty).border = thin_border
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
ws4.cell(row=row_idx, column=1, value=borrow.borrow_no or '').border = thin_border
ws4.cell(row=row_idx, column=2, value=borrow.borrower_name or '').border = thin_border
ws4.cell(row=row_idx, column=3, value=mat_info['name']).border = thin_border
ws4.cell(row=row_idx, column=4, value=mat_info['sku']).border = thin_border
ws4.cell(row=row_idx, column=5, value=mat_info['spec']).border = thin_border
ws4.cell(row=row_idx, column=6, value=total_qty).border = thin_border
ws4.cell(row=row_idx, column=7, value=returned_qty).border = thin_border
ws4.cell(row=row_idx, column=8, value=pending_qty).border = thin_border
ws4.cell(row=row_idx, column=9, value=to_beijing_time(borrow.borrow_time)).border = thin_border
ws4.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: 未盘点明细(疑似漏盘) =====
# 逻辑:获取已盘点的集合,遍历库存表,找出未盘点且有库存的物资
@ -752,7 +756,7 @@ def export_stocktake():
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)
total = sum(float(b.quantity or 0) - float(b.returned_quantity or 0) for b in borrowed)
return total
except:
return 0
@ -838,18 +842,18 @@ def export_stocktake():
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=5, value=float(item['stock_qty'])).border = thin_border
ws5.cell(row=row_idx, column=6, value=float(item['actual_qty'])).border = thin_border
ws5.cell(row=row_idx, column=7, value=float(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=5, value=float(item['stock_qty'])).border = thin_border
ws1.cell(row=master_row_idx, column=6, value=float(item['actual_qty'])).border = thin_border
ws1.cell(row=master_row_idx, column=7, value=float(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

View File

@ -5,7 +5,7 @@ from app.models.transaction import TransBorrow
from app.models.inbound.buy import StockBuy
from app.models.inbound.semi import StockSemi
from app.models.inbound.product import StockProduct
from sqlalchemy import desc, func
from sqlalchemy import desc, func, nullslast, asc
class TransService:
@ -199,7 +199,7 @@ class TransService:
TransBorrow.sku.ilike(f'%{keyword}%') |
TransBorrow.borrow_no.ilike(f'%{keyword}%'))
q = q.order_by(desc(TransBorrow.borrow_time))
q = q.order_by(nullslast(asc(TransBorrow.expected_return_time)))
pagination = q.paginate(page=page, per_page=limit, error_out=False)
return {

View File

@ -52,7 +52,7 @@
<div v-else>
<el-tag type="info" size="small">预计</el-tag>
{{ formatExpectedTime(row.expected_return_time).text }}
<span :class="formatExpectedTime(row.expected_return_time).cssClass">
<span :style="formatExpectedTime(row.expected_return_time).style">
{{ formatExpectedTime(row.expected_return_time).diffText }}
</span>
</div>
@ -164,9 +164,9 @@ const handlePage = (val: number) => {
fetchData()
}
// ★ 新增:格式化预计归还时间及倒计时逻辑
// ★ 新增:格式化预计归还时间及倒计时逻辑(交通灯预警系统)
const formatExpectedTime = (timeStr: string) => {
if (!timeStr) return { text: '无限期', diffText: '', cssClass: '' }
if (!timeStr) return { text: '无限期', diffText: '', style: {} }
// 后端返回的可能是 YYYY-MM-DD HH:mm:ss我们只取日期部分比较
const expected = dayjs(timeStr).startOf('day')
@ -174,30 +174,33 @@ const formatExpectedTime = (timeStr: string) => {
const diffDays = expected.diff(today, 'day')
let diffText = ''
let cssClass = ''
let style: Record<string, string> = {}
// 这里的 timeStr 只展示前10位 (日期),或者展示完整
// 需求说单号规则是日期,预计归还也主要看日期
const displayTime = timeStr.substring(0, 10)
if (diffDays < 0) {
// 逾期
diffText = ` (逾期 ${Math.abs(diffDays)} 天)`
cssClass = 'text-danger'
} else if (diffDays === 0) {
// 今天到期
diffText = ` (今天到期)`
cssClass = 'text-warning'
} else {
// 剩余
// 逾期 - 红色加粗
diffText = ` (逾期 ${Math.abs(diffDays)} 天)`
style = { color: '#F56C6C', fontWeight: 'bold' }
} else if (diffDays <= 1) {
// 极其紧急(剩 0-1 天)- 警告黄加粗
diffText = diffDays === 0 ? ` (今天到期)` : ` (剩 ${diffDays})`
style = { color: '#E6A23C', fontWeight: 'bold' }
} else if (diffDays <= 7) {
// 即将到期(剩 2-7 天)- 安全绿
diffText = ` (剩 ${diffDays} 天)`
cssClass = 'text-normal' // 正常,或者灰色
style = { color: '#67C23A' }
} else {
// 安全/无期限 - 默认颜色
diffText = ` (剩 ${diffDays} 天)`
style = {}
}
return { text: displayTime, diffText, cssClass }
return { text: displayTime, diffText, style }
}
// ★ 新增:表格行样式逻辑
// ★ 新增:表格行样式逻辑(交通灯预警)
const tableRowClassName = ({ row }: { row: any }) => {
// 如果已归还,不标颜色
if (row.status === 'returned') return ''
@ -208,9 +211,11 @@ const tableRowClassName = ({ row }: { row: any }) => {
const diffDays = expected.diff(today, 'day')
if (diffDays < 0) {
return 'danger-row' // 逾期标红
} else if (diffDays === 0) {
return 'warning-row' // 当天标黄
return 'danger-row' // 逾期 - 红色
} else if (diffDays <= 1) {
return 'warning-row' // 极其紧急0-1天- 黄色
} else if (diffDays <= 7) {
return 'success-row' // 即将到期2-7天- 绿色
}
return ''
}
@ -227,6 +232,10 @@ onMounted(fetchData)
--el-table-tr-bg-color: #fef0f0 !important; /* 浅红色 */
color: #F56C6C; /* 文字变红增强警示 */
}
.el-table .success-row {
--el-table-tr-bg-color: #f0f9eb !important; /* 浅绿色 */
color: #67C23A; /* 文字变绿增强提示 */
}
/* 文字颜色辅助类 */
.text-danger {