Compare commits
5 Commits
9d972f1120
...
7867fc5e40
| Author | SHA1 | Date | |
|---|---|---|---|
| 7867fc5e40 | |||
| 8db1015f99 | |||
| a32d4f6b65 | |||
| b74464df6b | |||
| a19167e804 |
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user