feat: add borrowed quantity column and update stocktake export formulas
Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
This commit is contained in:
@ -115,6 +115,22 @@ def clear_draft():
|
|||||||
return jsonify({"message": "Cleared"}), 200
|
return jsonify({"message": "Cleared"}), 200
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/borrowed-quantities', methods=['POST'])
|
||||||
|
@permission_required('inventory_stocktake')
|
||||||
|
def get_borrowed_quantities():
|
||||||
|
"""批量获取借出未还数量"""
|
||||||
|
from app.models.transaction import TransBorrow
|
||||||
|
data = request.json.get('items', [])
|
||||||
|
result = {}
|
||||||
|
for item in data:
|
||||||
|
source = item.get('source_table')
|
||||||
|
stock_id = item.get('stock_id')
|
||||||
|
if source and stock_id is not None:
|
||||||
|
qty = TransBorrow.get_borrowed_quantity(source, stock_id)
|
||||||
|
result[f"{source}_{stock_id}"] = qty
|
||||||
|
return jsonify(result), 200
|
||||||
|
|
||||||
|
|
||||||
# --- 打印接口 ---
|
# --- 打印接口 ---
|
||||||
|
|
||||||
@bp.route('/print/selection', methods=['POST'])
|
@bp.route('/print/selection', methods=['POST'])
|
||||||
|
|||||||
@ -277,6 +277,8 @@ interface StockItem {
|
|||||||
type?: string
|
type?: string
|
||||||
category?: string
|
category?: string
|
||||||
price?: number
|
price?: number
|
||||||
|
source_table?: string
|
||||||
|
stock_id?: number
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +295,7 @@ const showQtyDialog = ref(false)
|
|||||||
|
|
||||||
const allData = ref<StockItem[]>([])
|
const allData = ref<StockItem[]>([])
|
||||||
const scannedMap = ref<Map<string, number>>(new Map())
|
const scannedMap = ref<Map<string, number>>(new Map())
|
||||||
|
const borrowedQuantities = ref<Record<string, number>>({})
|
||||||
|
|
||||||
const filterType = ref('all')
|
const filterType = ref('all')
|
||||||
const searchKeyword = ref('')
|
const searchKeyword = ref('')
|
||||||
@ -307,6 +310,34 @@ const api = {
|
|||||||
clearDraft: () => request({ url: '/v1/inbound/stock/draft/clear', method: 'post', data: { user_id: currentUser } })
|
clearDraft: () => request({ url: '/v1/inbound/stock/draft/clear', method: 'post', data: { user_id: currentUser } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const typeToSourceTable = (type: string): string => {
|
||||||
|
switch (type) {
|
||||||
|
case 'material': return 'stock_buy'
|
||||||
|
case 'semi': return 'stock_semi'
|
||||||
|
case 'product': return 'stock_product'
|
||||||
|
default: return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchBorrowedQuantities(items: StockItem[]): Promise<void> {
|
||||||
|
const payload = items.filter(i => i.source_table && i.stock_id).map(i => ({
|
||||||
|
source_table: i.source_table,
|
||||||
|
stock_id: i.stock_id
|
||||||
|
}))
|
||||||
|
if (payload.length === 0) return
|
||||||
|
try {
|
||||||
|
const res = await request({
|
||||||
|
url: '/v1/inbound/stock/borrowed-quantities',
|
||||||
|
method: 'post',
|
||||||
|
data: { items: payload }
|
||||||
|
})
|
||||||
|
// res is map of key->qty
|
||||||
|
borrowedQuantities.value = { ...borrowedQuantities.value, ...res }
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取借出数量失败', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await checkServerDraft()
|
await checkServerDraft()
|
||||||
})
|
})
|
||||||
@ -381,7 +412,9 @@ const loadData = async () => {
|
|||||||
qty_stock: stock,
|
qty_stock: stock,
|
||||||
qty_actual: isScanned ? scannedMap.value.get(uuid)! : 0,
|
qty_actual: isScanned ? scannedMap.value.get(uuid)! : 0,
|
||||||
scanned: isScanned,
|
scanned: isScanned,
|
||||||
uniqueKey: `${type}_${item.id}`
|
uniqueKey: `${type}_${item.id}`,
|
||||||
|
source_table: typeToSourceTable(type),
|
||||||
|
stock_id: item.id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,6 +423,7 @@ const loadData = async () => {
|
|||||||
if (res.products) res.products.forEach((i: any) => processItem(i, 'product'))
|
if (res.products) res.products.forEach((i: any) => processItem(i, 'product'))
|
||||||
|
|
||||||
allData.value = list
|
allData.value = list
|
||||||
|
await fetchBorrowedQuantities(list)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ElMessage.error('数据加载失败')
|
ElMessage.error('数据加载失败')
|
||||||
} finally { loading.value = false }
|
} finally { loading.value = false }
|
||||||
@ -471,34 +505,47 @@ const closeOverlays = () => {
|
|||||||
const exportToExcel = () => {
|
const exportToExcel = () => {
|
||||||
try {
|
try {
|
||||||
// 1. 已盘点 Sheet
|
// 1. 已盘点 Sheet
|
||||||
const scannedData = allData.value.filter(i => i.scanned).map(item => ({
|
const scannedData = allData.value.filter(i => i.scanned).map(item => {
|
||||||
'物品名称': item.name,
|
const key = item.source_table && item.stock_id ? `${item.source_table}_${item.stock_id}` : ''
|
||||||
'类型': item.type || item.material_type || '-',
|
const borrowedQty = borrowedQuantities.value[key] || 0
|
||||||
'类别': item.category || '-',
|
const actualTotal = item.qty_actual + borrowedQty
|
||||||
'规格型号': item.spec_model || item.standard || '-', // ★ 双重保险
|
const diff = actualTotal - item.qty_stock
|
||||||
'SKU': item.sku,
|
const result = diff === 0 ? '正常' : diff < 0 ? '盘亏/差异' : '盘盈'
|
||||||
'批次/SN': item.serial_number || item.batch_no || '-',
|
return {
|
||||||
'单位': item.unit || '个',
|
'物品名称': item.name,
|
||||||
'单价': item.price || item.unit_price || 0,
|
'类型': item.type || item.material_type || '-',
|
||||||
'账面库存': parseFloat(item.qty_stock as any),
|
'类别': item.category || '-',
|
||||||
'实盘数量': item.qty_actual,
|
'规格型号': item.spec_model || item.standard || '-', // ★ 双重保险
|
||||||
'盘点结果': item.qty_stock === item.qty_actual ? '相符' : '差异',
|
'SKU': item.sku,
|
||||||
'差异数': item.qty_actual - item.qty_stock
|
'批次/SN': item.serial_number || item.batch_no || '-',
|
||||||
}))
|
'单位': item.unit || '个',
|
||||||
|
'单价': item.price || item.unit_price || 0,
|
||||||
|
'账面库存': parseFloat(item.qty_stock as any),
|
||||||
|
'实盘数量': item.qty_actual,
|
||||||
|
'借出未还数量': borrowedQty,
|
||||||
|
'盘点结果': result,
|
||||||
|
'差异数': diff
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 2. 未盘点 Sheet
|
// 2. 未盘点 Sheet
|
||||||
const missingData = allData.value.filter(i => !i.scanned).map(item => ({
|
const missingData = allData.value.filter(i => !i.scanned).map(item => {
|
||||||
'物品名称': item.name,
|
const key = item.source_table && item.stock_id ? `${item.source_table}_${item.stock_id}` : ''
|
||||||
'类型': item.type || item.material_type || '-',
|
const borrowedQty = borrowedQuantities.value[key] || 0
|
||||||
'类别': item.category || '-',
|
return {
|
||||||
'规格型号': item.spec_model || item.standard || '-', // ★ 双重保险
|
'物品名称': item.name,
|
||||||
'SKU': item.sku,
|
'类型': item.type || item.material_type || '-',
|
||||||
'批次/SN': item.serial_number || item.batch_no || '-',
|
'类别': item.category || '-',
|
||||||
'单位': item.unit || '个',
|
'规格型号': item.spec_model || item.standard || '-', // ★ 双重保险
|
||||||
'单价': item.price || item.unit_price || 0,
|
'SKU': item.sku,
|
||||||
'账面库存': parseFloat(item.qty_stock as any),
|
'批次/SN': item.serial_number || item.batch_no || '-',
|
||||||
'状态': '未盘点'
|
'单位': item.unit || '个',
|
||||||
}))
|
'单价': item.price || item.unit_price || 0,
|
||||||
|
'账面库存': parseFloat(item.qty_stock as any),
|
||||||
|
'借出未还数量': borrowedQty,
|
||||||
|
'状态': '未盘点'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const wb = XLSX.utils.book_new()
|
const wb = XLSX.utils.book_new()
|
||||||
const ws1 = XLSX.utils.json_to_sheet(scannedData)
|
const ws1 = XLSX.utils.json_to_sheet(scannedData)
|
||||||
@ -507,7 +554,7 @@ const exportToExcel = () => {
|
|||||||
const wscols = [
|
const wscols = [
|
||||||
{wch: 20}, {wch: 10}, {wch: 10}, {wch: 15},
|
{wch: 20}, {wch: 10}, {wch: 10}, {wch: 15},
|
||||||
{wch: 15}, {wch: 15}, {wch: 5}, {wch: 8},
|
{wch: 15}, {wch: 15}, {wch: 5}, {wch: 8},
|
||||||
{wch: 8}, {wch: 8}, {wch: 8}, {wch: 8}
|
{wch: 8}, {wch: 8}, {wch: 8}, {wch: 8}, {wch: 8}
|
||||||
]
|
]
|
||||||
ws1['!cols'] = wscols
|
ws1['!cols'] = wscols
|
||||||
ws2['!cols'] = wscols
|
ws2['!cols'] = wscols
|
||||||
|
|||||||
Reference in New Issue
Block a user