Compare commits

2 Commits

8 changed files with 25 additions and 9 deletions

View File

@ -28,9 +28,9 @@ ssh -t $SERVER "sudo mkdir -p $REMOTE_BACKUP_DIR && \
if [ $? -ne 0 ]; then echo "❌ 远端备份失败,为保护数据已终止部署!"; exit 1; fi if [ $? -ne 0 ]; then echo "❌ 远端备份失败,为保护数据已终止部署!"; exit 1; fi
# 2. 导出本地数据库 # 2. 导出本地数据库 (修复点:使用 pg_dump 仅导出指定数据库,并忽略所有者和权限)
echo "[2/5] 正在导出本地数据库..." echo "[2/5] 正在导出本地数据库..."
docker exec inventory_db pg_dumpall -c -U test | gzip > db_sync.sql.gz docker exec inventory_db pg_dump -c -O -x -U test inventory_system | gzip > db_sync.sql.gz
# 3. 本地打包 (新增了精准拦截本地图片、数据库文件和各类缓存) # 3. 本地打包 (新增了精准拦截本地图片、数据库文件和各类缓存)
echo "[3/5] 正在本地打包代码和数据库..." echo "[3/5] 正在本地打包代码和数据库..."

View File

@ -213,6 +213,11 @@ def submit():
if perm_code and perm_code not in user_permissions: if perm_code and perm_code not in user_permissions:
data.pop(field, None) data.pop(field, None)
# 库位必填校验(安全兜底)
location = data.get('warehouse_location', '').strip()
if not location:
return jsonify({"code": 400, "msg": "入库失败:库位为必填项,不能为空!"}), 400
new_stock = BuyInboundService.handle_inbound(data) new_stock = BuyInboundService.handle_inbound(data)
return jsonify({ return jsonify({

View File

@ -133,6 +133,12 @@ def submit():
try: try:
data = request.get_json() data = request.get_json()
if not data: return jsonify({"code": 400, "msg": "No data"}), 400 if not data: return jsonify({"code": 400, "msg": "No data"}), 400
# 库位必填校验(安全兜底)
location = data.get('warehouse_location', '').strip()
if not location:
return jsonify({"code": 400, "msg": "入库失败:库位为必填项,不能为空!"}), 400
user_permissions = get_current_user_permissions() user_permissions = get_current_user_permissions()
if 'inbound_product:*' not in user_permissions: if 'inbound_product:*' not in user_permissions:
field_to_perm = {'id': 'inbound_product:id', 'base_id': 'inbound_product:base_id', 'company_name': 'inbound_product:company_name', 'material_name': 'inbound_product:material_name', 'category': 'inbound_product:category', 'material_type': 'inbound_product:material_type', 'spec_model': 'inbound_product:spec_model', 'unit': 'inbound_product:unit', 'sku': 'inbound_product:sku', 'inbound_date': 'inbound_product:inbound_date', 'barcode': 'inbound_product:barcode', 'serial_number': 'inbound_product:serial_number', 'status': 'inbound_product:status', 'quality_status': 'inbound_product:quality_status', 'in_quantity': 'inbound_product:in_quantity', 'stock_quantity': 'inbound_product:stock_quantity', 'available_quantity': 'inbound_product:available_quantity', 'warehouse_location': 'inbound_product:warehouse_location', 'bom_code': 'inbound_product:bom_code', 'bom_version': 'inbound_product:bom_version', 'work_order_code': 'inbound_product:work_order_code', 'order_id': 'inbound_product:order_id', 'production_manager': 'inbound_product:production_manager', 'production_start_time': 'inbound_product:production_start_time', 'production_end_time': 'inbound_product:production_end_time', 'raw_material_cost': 'inbound_product:raw_material_cost', 'manual_cost': 'inbound_product:manual_cost', 'sale_price': 'inbound_product:sale_price', 'product_photo': 'inbound_product:product_photo', 'quality_report_link': 'inbound_product:quality_report_link', 'inspection_report_link': 'inbound_product:inspection_report_link', 'detail_link': 'inbound_product:detail_link'} field_to_perm = {'id': 'inbound_product:id', 'base_id': 'inbound_product:base_id', 'company_name': 'inbound_product:company_name', 'material_name': 'inbound_product:material_name', 'category': 'inbound_product:category', 'material_type': 'inbound_product:material_type', 'spec_model': 'inbound_product:spec_model', 'unit': 'inbound_product:unit', 'sku': 'inbound_product:sku', 'inbound_date': 'inbound_product:inbound_date', 'barcode': 'inbound_product:barcode', 'serial_number': 'inbound_product:serial_number', 'status': 'inbound_product:status', 'quality_status': 'inbound_product:quality_status', 'in_quantity': 'inbound_product:in_quantity', 'stock_quantity': 'inbound_product:stock_quantity', 'available_quantity': 'inbound_product:available_quantity', 'warehouse_location': 'inbound_product:warehouse_location', 'bom_code': 'inbound_product:bom_code', 'bom_version': 'inbound_product:bom_version', 'work_order_code': 'inbound_product:work_order_code', 'order_id': 'inbound_product:order_id', 'production_manager': 'inbound_product:production_manager', 'production_start_time': 'inbound_product:production_start_time', 'production_end_time': 'inbound_product:production_end_time', 'raw_material_cost': 'inbound_product:raw_material_cost', 'manual_cost': 'inbound_product:manual_cost', 'sale_price': 'inbound_product:sale_price', 'product_photo': 'inbound_product:product_photo', 'quality_report_link': 'inbound_product:quality_report_link', 'inspection_report_link': 'inbound_product:inspection_report_link', 'detail_link': 'inbound_product:detail_link'}

View File

@ -128,6 +128,12 @@ def submit():
try: try:
data = request.get_json() data = request.get_json()
if not data: return jsonify({"code": 400, "msg": "No data"}), 400 if not data: return jsonify({"code": 400, "msg": "No data"}), 400
# 库位必填校验(安全兜底)
location = data.get('warehouse_location', '').strip()
if not location:
return jsonify({"code": 400, "msg": "入库失败:库位为必填项,不能为空!"}), 400
user_permissions = get_current_user_permissions() user_permissions = get_current_user_permissions()
if 'inbound_semi:*' not in user_permissions: if 'inbound_semi:*' not in user_permissions:
field_to_perm = {'id': 'inbound_semi:id', 'base_id': 'inbound_semi:base_id', 'company_name': 'inbound_semi:company_name', 'material_name': 'inbound_semi:material_name', 'category': 'inbound_semi:category', 'material_type': 'inbound_semi:material_type', 'spec_model': 'inbound_semi:spec_model', 'unit': 'inbound_semi:unit', 'sku': 'inbound_semi:sku', 'inbound_date': 'inbound_semi:inbound_date', 'barcode': 'inbound_semi:barcode', 'serial_number': 'inbound_semi:serial_number', 'batch_number': 'inbound_semi:batch_number', 'status': 'inbound_semi:status', 'quality_status': 'inbound_semi:quality_status', 'in_quantity': 'inbound_semi:in_quantity', 'stock_quantity': 'inbound_semi:stock_quantity', 'available_quantity': 'inbound_semi:available_quantity', 'warehouse_location': 'inbound_semi:warehouse_location', 'bom_code': 'inbound_semi:bom_code', 'bom_version': 'inbound_semi:bom_version', 'work_order_code': 'inbound_semi:work_order_code', 'raw_material_cost': 'inbound_semi:raw_material_cost', 'manual_cost': 'inbound_semi:manual_cost', 'unit_total_cost': 'inbound_semi:unit_total_cost', 'production_manager': 'inbound_semi:production_manager', 'production_start_time': 'inbound_semi:production_start_time', 'production_end_time': 'inbound_semi:production_end_time', 'arrival_photo': 'inbound_semi:arrival_photo', 'quality_report_link': 'inbound_semi:quality_report_link', 'detail_link': 'inbound_semi:detail_link'} field_to_perm = {'id': 'inbound_semi:id', 'base_id': 'inbound_semi:base_id', 'company_name': 'inbound_semi:company_name', 'material_name': 'inbound_semi:material_name', 'category': 'inbound_semi:category', 'material_type': 'inbound_semi:material_type', 'spec_model': 'inbound_semi:spec_model', 'unit': 'inbound_semi:unit', 'sku': 'inbound_semi:sku', 'inbound_date': 'inbound_semi:inbound_date', 'barcode': 'inbound_semi:barcode', 'serial_number': 'inbound_semi:serial_number', 'batch_number': 'inbound_semi:batch_number', 'status': 'inbound_semi:status', 'quality_status': 'inbound_semi:quality_status', 'in_quantity': 'inbound_semi:in_quantity', 'stock_quantity': 'inbound_semi:stock_quantity', 'available_quantity': 'inbound_semi:available_quantity', 'warehouse_location': 'inbound_semi:warehouse_location', 'bom_code': 'inbound_semi:bom_code', 'bom_version': 'inbound_semi:bom_version', 'work_order_code': 'inbound_semi:work_order_code', 'raw_material_cost': 'inbound_semi:raw_material_cost', 'manual_cost': 'inbound_semi:manual_cost', 'unit_total_cost': 'inbound_semi:unit_total_cost', 'production_manager': 'inbound_semi:production_manager', 'production_start_time': 'inbound_semi:production_start_time', 'production_end_time': 'inbound_semi:production_end_time', 'arrival_photo': 'inbound_semi:arrival_photo', 'quality_report_link': 'inbound_semi:quality_report_link', 'detail_link': 'inbound_semi:detail_link'}

View File

@ -229,17 +229,13 @@
<el-table-column v-if="columns.inventory.visible" prop="inventoryCount" label="库存数" min-width="100" align="center" sortable="custom"> <el-table-column v-if="columns.inventory.visible" prop="inventoryCount" label="库存数" min-width="100" align="center" sortable="custom">
<template #default="{ row }"> <template #default="{ row }">
<span :style="{ fontWeight: 'bold', color: row.inventoryCount > 0 ? '#67C23A' : '#909399' }"> <span>{{ row.inventoryCount }}</span>
{{ row.inventoryCount }}
</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column v-if="columns.available.visible" prop="availableCount" label="可用数" min-width="100" align="center" sortable="custom"> <el-table-column v-if="columns.available.visible" prop="availableCount" label="可用数" min-width="100" align="center" sortable="custom">
<template #default="{ row }"> <template #default="{ row }">
<span :style="{ fontWeight: 'bold', color: row.availableCount > 0 ? '#409EFF' : '#909399' }"> <span>{{ row.availableCount }}</span>
{{ row.availableCount }}
</span>
</template> </template>
</el-table-column> </el-table-column>

View File

@ -1180,6 +1180,7 @@ const rules = computed(() => {
const baseRules = { const baseRules = {
base_id: [{required: true, message: '请选择物料', trigger: 'change'}], base_id: [{required: true, message: '请选择物料', trigger: 'change'}],
in_quantity: [{required: true, message: '请输入数量', trigger: 'blur'}], in_quantity: [{required: true, message: '请输入数量', trigger: 'blur'}],
warehouse_location: [{required: true, message: '库位不能为空,请填写或选择', trigger: ['blur', 'change']}],
serial_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}], serial_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}],
batch_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}] batch_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}]
} }

View File

@ -923,7 +923,8 @@ const validateUnique = (rule: any, value: string, callback: any) => {
const rules = { const rules = {
base_id: [{ required: true, message: '必选', trigger: 'change' }], base_id: [{ required: true, message: '必选', trigger: 'change' }],
serial_number: [{ required: true, message: '必填', trigger: 'blur' }, { validator: validateUnique, trigger: 'blur' }], serial_number: [{ required: true, message: '必填', trigger: 'blur' }, { validator: validateUnique, trigger: 'blur' }],
in_quantity: [{ required: true, message: '必填', trigger: 'blur' }] in_quantity: [{ required: true, message: '必填', trigger: 'blur' }],
warehouse_location: [{required: true, message: '库位不能为空,请填写或选择', trigger: ['blur', 'change']}]
} }

View File

@ -1050,6 +1050,7 @@ const validateIdentity = (rule: any, value: any, callback: any) => {
const rules = { const rules = {
base_id: [{required: true, message: '请选择物料', trigger: 'change'}], base_id: [{required: true, message: '请选择物料', trigger: 'change'}],
in_quantity: [{required: true, message: '请输入数量', trigger: 'blur'}], in_quantity: [{required: true, message: '请输入数量', trigger: 'blur'}],
warehouse_location: [{required: true, message: '库位不能为空,请填写或选择', trigger: ['blur', 'change']}],
serial_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}], serial_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}],
batch_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}] batch_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}]
} }