fix:发货管理(指定发货)
parent
954be37aa4
commit
9ee56a83a7
|
|
@ -89,3 +89,13 @@ export function importSnData(formData) {
|
|||
data: formData
|
||||
})
|
||||
}
|
||||
export function assignDeliverySnData(formData) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue/assignDeliveryData',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: formData
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,16 +121,16 @@
|
|||
<dict-tag :options="dict.type.execution_outer_status" :value="scope.row.outerStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="签收状态" align="center" prop="signStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.execution_sign_status" :value="scope.row.signStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发货状态" align="center" prop="deliveryStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.execution_delivery_status" :value="scope.row.deliveryStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="签收状态" align="center" prop="signStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.execution_sign_status" :value="scope.row.signStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单生效时间" align="center" prop="approveTime" sortable="custom" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.approveTime, '{y}-{m}-{d}') }}</span>
|
||||
|
|
|
|||
|
|
@ -73,6 +73,9 @@
|
|||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleSelectPurchaseBeforeImport">导入</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="el-icon-upload2" size="mini" @click="handleSelectPurchaseBeforeAssign">指定发货</el-button>
|
||||
</el-col>
|
||||
<el-col :span="21">
|
||||
<div style="text-align: right;">已选: {{ selectedSnList.length }} (台)</div>
|
||||
</el-col>
|
||||
|
|
@ -92,6 +95,7 @@
|
|||
<purchase-order-select-dialog :visible.sync="purchaseOrderSelectVisible" :productCode="queryParams.productCode" :quantity="queryParams.pageSize" :productTypeList="queryProductType" @select="handlePurchaseOrderSelect"/>
|
||||
|
||||
<input type="file" ref="uploadInput" @change="onFileChange" accept=".xls,.xlsx" style="display: none" />
|
||||
<input type="file" ref="assignUploadInput" @change="onAssignFileChange" accept=".xls,.xlsx" style="display: none" />
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
|
|
@ -102,7 +106,7 @@
|
|||
|
||||
<script>
|
||||
import { addDelivery, listProductSn } from '@/api/inventory/delivery';
|
||||
import {importSnData,exportDownloadTemplate} from '@/api/inventory/outer'
|
||||
import {importSnData,exportDownloadTemplate,assignDeliverySnData} from '@/api/inventory/outer'
|
||||
import PurchaseOrderSelectDialog from '../../../purchaseorder/components/PurchaseOrderSelectDialog.vue';
|
||||
import {handleTree} from "@/utils/ruoyi";
|
||||
import { productMatchBindList } from '@/api/project/order';
|
||||
|
|
@ -153,6 +157,7 @@ export default {
|
|||
warehouseName: '',
|
||||
purchaseOrderList: [],
|
||||
activePurchaseNo: '',
|
||||
isAssignMode: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -273,23 +278,46 @@ export default {
|
|||
},
|
||||
handleSelectPurchaseBeforeImport() {
|
||||
// if (((this.productData.orderType || '1') === '1' ) && !this.outerData.vendorName.startsWith('新华三') ) {
|
||||
this.isAssignMode = false;
|
||||
this.purchaseOrderSelectVisible = true;
|
||||
// }else{
|
||||
// this.handleImport()
|
||||
// }
|
||||
},
|
||||
handleSelectPurchaseBeforeAssign() {
|
||||
this.isAssignMode = true;
|
||||
this.purchaseOrderSelectVisible = true;
|
||||
},
|
||||
handlePurchaseOrderSelect(order) {
|
||||
// 如果是指定发货模式,需要验证选择的采购单是否在已绑定列表中
|
||||
if (this.isAssignMode) {
|
||||
const isInFilteredList = this.filteredPurchaseOrderList.some(
|
||||
purchase => purchase.purchaseNo === order.purchaseNo
|
||||
);
|
||||
if (!isInFilteredList) {
|
||||
this.$message.error('指定发货只能选择已绑定的采购单');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.warehouseId = order.warehouseId;
|
||||
this.warehouseName = order.warehouseName;
|
||||
this.price = order.price;
|
||||
this.taxRate = order.taxRate;
|
||||
this.form.purchaseNo = order.purchaseNo;
|
||||
this.form.itemId = order.itemId;
|
||||
this.handleImport();
|
||||
if (this.isAssignMode) {
|
||||
this.handleAssign();
|
||||
} else {
|
||||
this.handleImport();
|
||||
}
|
||||
},
|
||||
handleImport() {
|
||||
this.$refs.uploadInput.click();
|
||||
},
|
||||
handleAssign() {
|
||||
this.$refs.assignUploadInput.click();
|
||||
},
|
||||
onFileChange(event) {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
|
@ -328,6 +356,49 @@ export default {
|
|||
});
|
||||
this.$refs.uploadInput.value = ''; // Reset file input
|
||||
},
|
||||
onAssignFileChange(event) {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('productCode', this.productData.productCode);
|
||||
formData.append('orderType', this.productData.orderType);
|
||||
formData.append('purchaseNo', this.form.purchaseNo);
|
||||
formData.append('itemId', this.form.itemId);
|
||||
|
||||
assignDeliverySnData(formData).then(response => {
|
||||
this.$message.success('指定发货导入成功');
|
||||
this.snList = response.data.productSnDataList;
|
||||
if (this.warehouseName) {
|
||||
this.snList.forEach(item => {
|
||||
item.warehouseName = this.warehouseName;
|
||||
item.warehouseId = this.warehouseId;
|
||||
item.innerPrice = this.price;
|
||||
});
|
||||
}
|
||||
this.total = this.snList.length;
|
||||
this.queryParams.pageNum = 1;
|
||||
this.queryParams.pageSize = this.total;
|
||||
|
||||
// 切换到选择的采购单标签页
|
||||
if (this.form.purchaseNo) {
|
||||
this.activePurchaseNo = this.form.purchaseNo;
|
||||
this.queryParams.purchaseNo = this.form.purchaseNo;
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.snTable) {
|
||||
this.$refs.snTable.clearSelection();
|
||||
this.$refs.snTable.toggleAllSelection();
|
||||
this.selectedSnList = this.snList;
|
||||
}
|
||||
});
|
||||
}).catch(() => {
|
||||
this.$message.error('指定发货导入失败');
|
||||
});
|
||||
this.$refs.assignUploadInput.value = ''; // Reset file input
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
|
|
@ -392,6 +463,7 @@ export default {
|
|||
this.warehouseName = '';
|
||||
this.purchaseOrderList = [];
|
||||
this.activePurchaseNo = '';
|
||||
this.isAssignMode = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -133,6 +133,34 @@ public class VueInventoryOuterController extends BaseController
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定发货导入数据
|
||||
*/
|
||||
@PostMapping("/assignDeliveryData")
|
||||
@ResponseBody
|
||||
public AjaxResult assignDeliveryData(@RequestPart("file") MultipartFile file,
|
||||
@RequestParam(value = "productCode") String productCode,
|
||||
@RequestParam(value = "orderType", defaultValue = "1") String orderType,
|
||||
@RequestParam(value = "purchaseNo") String purchaseNo,
|
||||
@RequestParam(value = "itemId") Long itemId) {
|
||||
ExcelUtil<InventoryInfoExcelDto> util = new ExcelUtil<InventoryInfoExcelDto>(InventoryInfoExcelDto.class);
|
||||
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
List<InventoryInfoExcelDto> inventoryInfoExcelDtoList = util.importExcel(inputStream);
|
||||
if (CollUtil.isEmpty(inventoryInfoExcelDtoList)) {
|
||||
return AjaxResult.error("导入数据为空");
|
||||
}
|
||||
return AjaxResult.success(innerService.validateAndGetAssignDeliveryList(
|
||||
inventoryInfoExcelDtoList, productCode, orderType, purchaseNo, itemId));
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("读取excel错误,请联系管理员");
|
||||
} catch (ServiceException e) {
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出出库单列表
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -66,4 +66,5 @@ public interface IOmsInventoryInnerService
|
|||
|
||||
void importByOuter(List<InventoryInfo> inventoryInfoList, String productCode, String purchaseNo,Long itemId);
|
||||
Map<String,Object> getInventoryInfoList(List<InventoryInfoExcelDto> inventoryInfoExcelDtoList, String productCode, String orderType);
|
||||
Map<String,Object> validateAndGetAssignDeliveryList(List<InventoryInfoExcelDto> inventoryInfoExcelDtoList, String productCode, String orderType, String purchaseNo, Long itemId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -372,5 +372,118 @@ public class OmsInventoryInnerServiceImpl implements IOmsInventoryInnerService {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> validateAndGetAssignDeliveryList(List<InventoryInfoExcelDto> inventoryInfoExcelDtoList,
|
||||
String productCode, String orderType, String purchaseNo, Long itemId) {
|
||||
|
||||
// 1. 判断文件中所有数据的产品编码是否相同
|
||||
long distinctProductCodeCount = inventoryInfoExcelDtoList.stream()
|
||||
.map(InventoryInfoExcelDto::getProductCode)
|
||||
.distinct()
|
||||
.count();
|
||||
if (distinctProductCodeCount > 1) {
|
||||
throw new ServiceException("文件中产品编码不一致,只能包含一个产品编码");
|
||||
}
|
||||
|
||||
String fileProductCode = inventoryInfoExcelDtoList.get(0).getProductCode();
|
||||
if (!fileProductCode.equals(productCode)) {
|
||||
throw new ServiceException("文件中的产品编码与当前选择的产品编码不一致");
|
||||
}
|
||||
|
||||
// 2. 判断文件中入库价是否相同
|
||||
long distinctPriceCount = inventoryInfoExcelDtoList.stream()
|
||||
.map(InventoryInfoExcelDto::getInnerPrice)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.count();
|
||||
if (distinctPriceCount > 1) {
|
||||
throw new ServiceException("文件中入库价不一致,所有产品的入库价必须相同");
|
||||
}
|
||||
|
||||
BigDecimal fileInnerPrice = inventoryInfoExcelDtoList.stream()
|
||||
.map(InventoryInfoExcelDto::getInnerPrice)
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new ServiceException("文件中未找到入库价"));
|
||||
|
||||
// 3. 判断当前选择的采购单中是否存在这个产品编码
|
||||
List<OmsPurchaseOrderItem> purchaseOrderItems = purchaseOrderService.listByItemId(itemId);
|
||||
if (CollUtil.isEmpty(purchaseOrderItems)) {
|
||||
throw new ServiceException("未找到采购单明细信息");
|
||||
}
|
||||
|
||||
OmsPurchaseOrderItem matchedItem = purchaseOrderItems.stream()
|
||||
.filter(item -> item.getProductCode().equals(productCode))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new ServiceException("采购单中不存在产品编码:" + productCode));
|
||||
|
||||
// 4. 判断采购单中对应的产品价格是否和文件中的入库价相同
|
||||
BigDecimal purchasePrice = matchedItem.getPrice();
|
||||
if (purchasePrice == null) {
|
||||
throw new ServiceException("采购单中未设置产品价格");
|
||||
}
|
||||
|
||||
if (fileInnerPrice.compareTo(purchasePrice) != 0) {
|
||||
throw new ServiceException(String.format("文件中的入库价[%s]与采购单价格[%s]不一致",
|
||||
fileInnerPrice.toString(), purchasePrice.toString()));
|
||||
}
|
||||
|
||||
// 5. 判断文件中的SN码是否未发货未绑定订单(outer_code和order_code都为空)
|
||||
List<String> snList = inventoryInfoExcelDtoList.stream()
|
||||
.map(InventoryInfoExcelDto::getProductSn)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<InventoryInfo> existingInventoryList = inventoryInfoService.listByProductSnList(snList);
|
||||
|
||||
if (CollUtil.isNotEmpty(existingInventoryList)) {
|
||||
// 检查是否有已发货或已绑定订单的SN码
|
||||
List<String> invalidSnList = existingInventoryList.stream()
|
||||
.filter(item -> StringUtils.isNotEmpty(item.getOuterCode()) || StringUtils.isNotEmpty(item.getOrderCode()))
|
||||
.map(InventoryInfo::getProductSn)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (CollUtil.isNotEmpty(invalidSnList)) {
|
||||
// 最多显示10条SN码,超过的用省略号表示
|
||||
String snDisplay;
|
||||
if (invalidSnList.size() <= 10) {
|
||||
snDisplay = String.join(", ", invalidSnList);
|
||||
} else {
|
||||
snDisplay = String.join(", ", invalidSnList.subList(0, 10)) + "...等" + invalidSnList.size() + "条";
|
||||
}
|
||||
throw new ServiceException(String.format("以下SN码已发货或已绑定订单,不能用于指定发货:%s", snDisplay));
|
||||
}
|
||||
} else {
|
||||
throw new ServiceException("文件中的SN码在库存中不存在");
|
||||
}
|
||||
|
||||
// 验证通过,返回库存信息列表
|
||||
List<ProductInfo> productInfos = productInfoService.selectProductInfoByCodeList(Collections.singletonList(productCode));
|
||||
if (CollUtil.isEmpty(productInfos)) {
|
||||
throw new ServiceException("产品编码对应产品未找到");
|
||||
}
|
||||
|
||||
Map<String, InventoryInfo> existingInventoryMap = existingInventoryList.stream()
|
||||
.collect(Collectors.toMap(InventoryInfo::getProductSn, Function.identity()));
|
||||
|
||||
List<InventoryInfo> inventoryInfoList = inventoryInfoExcelDtoList.stream().map(item -> {
|
||||
InventoryInfo info = existingInventoryMap.get(item.getProductSn());
|
||||
if (info == null) {
|
||||
info = new InventoryInfo();
|
||||
info.setProductSn(item.getProductSn());
|
||||
info.setProductCode(productCode);
|
||||
info.setInventoryStatus(InventoryInfo.InventoryStatusEnum.INNER.getCode());
|
||||
}
|
||||
info.setLicenseKey(item.getLicenseKey());
|
||||
info.setModel(productInfos.get(0).getModel());
|
||||
info.setProductDesc(productInfos.get(0).getDescription());
|
||||
info.setInnerPrice(item.getInnerPrice());
|
||||
return info;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("productSnDataList", inventoryInfoList);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue