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) # 计算借出未还数量 (quantity - returned_quantity)
borrowed_result = db.session.query( 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( ).filter(
TransBorrow.source_table == source_table, TransBorrow.source_table == source_table,
TransBorrow.stock_id == stock_id, TransBorrow.stock_id == stock_id,
@ -644,13 +644,13 @@ def export_stocktake():
# ===== Sheet 1: 盘点全景汇总表 (放在最前面) ===== # ===== Sheet 1: 盘点全景汇总表 (放在最前面) =====
ws1 = wb.create_sheet("盘点全景汇总表", 0) ws1 = wb.create_sheet("盘点全景汇总表", 0)
summary_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点状态", "盘点人", "盘点时间"] summary_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点状态", "盘点人", "盘点时间", "备注"]
set_header_row(ws1, summary_headers) set_header_row(ws1, summary_headers)
master_row_idx = 2 # 汇总表行计数器 master_row_idx = 2 # 汇总表行计数器
# ===== Sheet 2: 盘点差异明细 ===== # ===== Sheet 2: 盘点差异明细 =====
ws2 = wb.create_sheet("盘点差异明细") ws2 = wb.create_sheet("盘点差异明细")
diff_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间"] diff_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间", "备注"]
set_header_row(ws2, diff_headers) set_header_row(ws2, diff_headers)
diff_drafts = StocktakeDraft.query.filter(StocktakeDraft.diff_qty != 0).all() 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=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=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=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 (汇总表) # 同时写入 Sheet 1 (汇总表)
ws1.cell(row=master_row_idx, column=1, value=mat_info['name']).border = thin_border 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=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=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=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=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 master_row_idx += 1
# ===== Sheet 3: 账实相符明细 ===== # ===== Sheet 3: 账实相符明细 =====
ws3 = wb.create_sheet("账实相符明细") ws3 = wb.create_sheet("账实相符明细")
normal_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间"] normal_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间", "备注"]
set_header_row(ws3, normal_headers) set_header_row(ws3, normal_headers)
normal_drafts = StocktakeDraft.query.filter(StocktakeDraft.diff_qty == 0).all() 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=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=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=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 (汇总表) # 同时写入 Sheet 1 (汇总表)
ws1.cell(row=master_row_idx, column=1, value=mat_info['name']).border = thin_border 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=2, value=mat_info['sku']).border = thin_border
@ -708,6 +711,7 @@ def export_stocktake():
ws1.cell(row=master_row_idx, column=8, value="正常").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=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=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 master_row_idx += 1
# ===== Sheet 4: 外借在用资产明细 ===== # ===== Sheet 4: 外借在用资产明细 =====
@ -723,16 +727,16 @@ def export_stocktake():
returned_qty = float(borrow.returned_quantity or 0) returned_qty = float(borrow.returned_quantity or 0)
pending_qty = total_qty - returned_qty pending_qty = total_qty - returned_qty
ws3.cell(row=row_idx, column=1, value=borrow.borrow_no or '').border = thin_border ws4.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 ws4.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 ws4.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 ws4.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 ws4.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 ws4.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 ws4.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 ws4.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 ws4.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=10, value='无限期' if not borrow.expected_return_time else to_beijing_time(borrow.expected_return_time)).border = thin_border
# ===== Sheet 5: 未盘点明细(疑似漏盘) ===== # ===== Sheet 5: 未盘点明细(疑似漏盘) =====
# 逻辑:获取已盘点的集合,遍历库存表,找出未盘点且有库存的物资 # 逻辑:获取已盘点的集合,遍历库存表,找出未盘点且有库存的物资
@ -752,7 +756,7 @@ def export_stocktake():
TransBorrow.stock_id == stock_id, TransBorrow.stock_id == stock_id,
TransBorrow.is_returned == False TransBorrow.is_returned == False
).all() ).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 return total
except: except:
return 0 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=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=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=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=5, value=float(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=6, value=float(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=7, value=float(item['diff_qty'])).border = thin_border
ws5.cell(row=row_idx, column=8, value=item['status']).border = thin_border ws5.cell(row=row_idx, column=8, value=item['status']).border = thin_border
# 同时写入 Sheet 1 (汇总表) - 盘点人和时间留空 # 同时写入 Sheet 1 (汇总表) - 盘点人和时间留空
ws1.cell(row=master_row_idx, column=1, value=item['name']).border = thin_border 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=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=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=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=5, value=float(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=6, value=float(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=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=8, value="未盘点").border = thin_border
ws1.cell(row=master_row_idx, column=9, 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 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.buy import StockBuy
from app.models.inbound.semi import StockSemi from app.models.inbound.semi import StockSemi
from app.models.inbound.product import StockProduct from app.models.inbound.product import StockProduct
from sqlalchemy import desc, func from sqlalchemy import desc, func, nullslast, asc
class TransService: class TransService:
@ -199,7 +199,7 @@ class TransService:
TransBorrow.sku.ilike(f'%{keyword}%') | TransBorrow.sku.ilike(f'%{keyword}%') |
TransBorrow.borrow_no.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) pagination = q.paginate(page=page, per_page=limit, error_out=False)
return { return {

View File

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