Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X
This commit is contained in:
@@ -148,6 +148,27 @@ SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA =
|
||||
SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan ADD COLUMN arrived_count INT NOT NULL DEFAULT 0 COMMENT ''已到货卷数'' AFTER plan_qty', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 2026-06-29 大改:采购计划→采购合同;要求改为 规格+总重量+厂商;到货区分已到/在途
|
||||
SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_purchase_plan_item' AND COLUMN_NAME = 'spec');
|
||||
SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan_item ADD COLUMN spec VARCHAR(128) DEFAULT NULL COMMENT ''规格'' AFTER plan_id', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_purchase_plan_item' AND COLUMN_NAME = 'manufacturer');
|
||||
SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan_item ADD COLUMN manufacturer VARCHAR(128) DEFAULT NULL COMMENT ''厂商(手填,历史记忆)'' AFTER spec', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_purchase_plan_delivery' AND COLUMN_NAME = 'in_transit');
|
||||
SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan_delivery ADD COLUMN in_transit TINYINT NOT NULL DEFAULT 0 COMMENT ''是否在途:1是0否(钢卷表存在且data_type=10)'' AFTER arrived', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_purchase_plan' AND COLUMN_NAME = 'in_transit_count');
|
||||
SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan ADD COLUMN in_transit_count INT NOT NULL DEFAULT 0 COMMENT ''在途卷数'' AFTER arrived_count', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_purchase_plan' AND COLUMN_NAME = 'in_transit_weight');
|
||||
SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan ADD COLUMN in_transit_weight DECIMAL(14,3) NOT NULL DEFAULT 0 COMMENT ''在途重量(T)'' AFTER in_transit_count', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
|
||||
-- ============ 菜单 ============
|
||||
-- 采购目录挂在「生产辅助」(path=helper) 下;父 id 按 path 动态解析,兼容不同环境的不同 menu_id。
|
||||
@@ -170,11 +191,11 @@ SET @purchase_id = (SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE path
|
||||
|
||||
-- 5) 采购计划 + 按钮(REPLACE 可重复执行)
|
||||
REPLACE INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) VALUES
|
||||
(2100000000000000001, '采购计划', @purchase_id, 2, 'purchasePlan', 'erp/purchasePlan/index', '', 1, 0, 'C', '0', '0', 'erp:purchasePlan:list', 'form', 'admin', sysdate(), '采购计划菜单'),
|
||||
(2100000000000000002, '采购计划查询', 2100000000000000001, 1, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:query', '#', 'admin', sysdate(), ''),
|
||||
(2100000000000000003, '采购计划新增', 2100000000000000001, 2, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:add', '#', 'admin', sysdate(), ''),
|
||||
(2100000000000000004, '采购计划修改', 2100000000000000001, 3, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:edit', '#', 'admin', sysdate(), ''),
|
||||
(2100000000000000005, '采购计划删除', 2100000000000000001, 4, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:remove', '#', 'admin', sysdate(), ''),
|
||||
(2100000000000000001, '采购合同', @purchase_id, 2, 'purchasePlan', 'erp/purchasePlan/index', '', 1, 0, 'C', '0', '0', 'erp:purchasePlan:list', 'form', 'admin', sysdate(), '采购合同菜单'),
|
||||
(2100000000000000002, '采购合同查询', 2100000000000000001, 1, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:query', '#', 'admin', sysdate(), ''),
|
||||
(2100000000000000003, '采购合同新增', 2100000000000000001, 2, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:add', '#', 'admin', sysdate(), ''),
|
||||
(2100000000000000004, '采购合同修改', 2100000000000000001, 3, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:edit', '#', 'admin', sysdate(), ''),
|
||||
(2100000000000000005, '采购合同删除', 2100000000000000001, 4, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:remove', '#', 'admin', sysdate(), ''),
|
||||
(2100000000000000006, '到货导入', 2100000000000000001, 5, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:import', '#', 'admin', sysdate(), '');
|
||||
|
||||
-- 6) 采购审核 + 按钮(REPLACE 可重复执行)
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.klp.common.utils.poi.ExcelUtil;
|
||||
import com.klp.erp.domain.bo.ErpPurchasePlanAuditBo;
|
||||
import com.klp.erp.domain.bo.ErpPurchasePlanBo;
|
||||
import com.klp.erp.domain.vo.ErpContractOptionVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryBatchVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanItemVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanVo;
|
||||
@@ -149,19 +148,13 @@ public class ErpPurchasePlanController extends BaseController {
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/** 某计划的到货上传批次(每次上传一条,可回看) */
|
||||
@GetMapping("/{planId}/deliveryBatches")
|
||||
public R<List<ErpPurchasePlanDeliveryBatchVo>> deliveryBatches(@PathVariable Long planId) {
|
||||
return R.ok(iErpPurchasePlanService.queryDeliveryBatches(planId));
|
||||
/** 厂商历史记忆(手填自动补全) */
|
||||
@GetMapping("/manufacturers")
|
||||
public R<List<String>> manufacturers(@RequestParam(value = "keyword", required = false) String keyword) {
|
||||
return R.ok(iErpPurchasePlanService.queryManufacturers(keyword));
|
||||
}
|
||||
|
||||
/** 某批次的到货明细 */
|
||||
@GetMapping("/deliveryBatch/{batchId}")
|
||||
public R<List<ErpPurchasePlanDeliveryVo>> deliveryByBatch(@PathVariable Long batchId) {
|
||||
return R.ok(iErpPurchasePlanService.queryDeliveryListByBatch(batchId));
|
||||
}
|
||||
|
||||
/** 到货记录页左侧:审核通过的计划分页 */
|
||||
/** 到货记录页左侧:审核通过的合同分页 */
|
||||
@GetMapping("/deliveryPlans")
|
||||
public TableDataInfo<ErpPurchasePlanVo> deliveryPlans(@RequestParam(value = "keyword", required = false) String keyword,
|
||||
PageQuery pageQuery) {
|
||||
|
||||
@@ -57,12 +57,18 @@ public class ErpPurchasePlan extends BaseEntity {
|
||||
/** 已到货重量(T) = Σ到货卷(WMS已确认)单卷重量 */
|
||||
private BigDecimal arrivedWeight;
|
||||
|
||||
/** 计划要求总数量(卷/件) = Σ明细数量 */
|
||||
/** 要求项数 = 明细行数 */
|
||||
private Integer planQty;
|
||||
|
||||
/** 已到货卷数 = WMS确认到货的上传卷数 */
|
||||
private Integer arrivedCount;
|
||||
|
||||
/** 在途卷数 = 钢卷表存在但 data_type=10 的上传卷数 */
|
||||
private Integer inTransitCount;
|
||||
|
||||
/** 在途重量(T) */
|
||||
private BigDecimal inTransitWeight;
|
||||
|
||||
/** 删除标志 */
|
||||
@TableLogic
|
||||
private String delFlag;
|
||||
|
||||
@@ -33,9 +33,12 @@ public class ErpPurchasePlanDelivery extends BaseEntity {
|
||||
/** 关联上传批次ID */
|
||||
private Long batchId;
|
||||
|
||||
/** 是否WMS已确认到货: 1-是 0-否(卷号在钢卷表查不到或为占位类型) */
|
||||
/** 是否WMS已确认到货: 1-是 0-否(卷号在钢卷表存在且 data_type<>10) */
|
||||
private Integer arrived;
|
||||
|
||||
/** 是否在途: 1-是(卷号在钢卷表存在但 data_type=10) 0-否 */
|
||||
private Integer inTransit;
|
||||
|
||||
/** 日期 */
|
||||
private Date arrivalDate;
|
||||
|
||||
|
||||
@@ -29,6 +29,12 @@ public class ErpPurchasePlanItem extends BaseEntity {
|
||||
/** 关联计划ID */
|
||||
private Long planId;
|
||||
|
||||
/** 规格 */
|
||||
private String spec;
|
||||
|
||||
/** 厂商(手填,带历史记忆) */
|
||||
private String manufacturer;
|
||||
|
||||
/** 产品(如热轧卷板) */
|
||||
private String productType;
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ public class ErpPurchasePlanItemBo extends BaseEntity {
|
||||
/** 关联计划ID */
|
||||
private Long planId;
|
||||
|
||||
/** 规格 */
|
||||
private String spec;
|
||||
|
||||
/** 厂商(手填,带历史记忆) */
|
||||
private String manufacturer;
|
||||
|
||||
/** 产品(如热轧卷板) */
|
||||
private String productType;
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ public class ErpPurchasePlanDeliveryVo implements Serializable {
|
||||
/** 是否WMS已确认到货: 1-是 0-否 */
|
||||
private Integer arrived;
|
||||
|
||||
/** 是否在途: 1-是 0-否 */
|
||||
private Integer inTransit;
|
||||
|
||||
@ExcelProperty(value = "日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date arrivalDate;
|
||||
|
||||
@@ -24,6 +24,12 @@ public class ErpPurchasePlanItemVo implements Serializable {
|
||||
|
||||
private Long planId;
|
||||
|
||||
@ExcelProperty(value = "规格")
|
||||
private String spec;
|
||||
|
||||
@ExcelProperty(value = "厂商")
|
||||
private String manufacturer;
|
||||
|
||||
@ExcelProperty(value = "产品")
|
||||
private String productType;
|
||||
|
||||
|
||||
@@ -59,21 +59,24 @@ public class ErpPurchasePlanVo implements Serializable {
|
||||
@ExcelProperty(value = "已到货重量(T)")
|
||||
private BigDecimal arrivedWeight;
|
||||
|
||||
/** 计划要求总数量(卷/件) */
|
||||
/** 要求项数(明细行数) */
|
||||
private Integer planQty;
|
||||
|
||||
/** 已到货卷数 */
|
||||
private Integer arrivedCount;
|
||||
|
||||
/** 在途卷数 */
|
||||
private Integer inTransitCount;
|
||||
|
||||
/** 在途重量(T) */
|
||||
private BigDecimal inTransitWeight;
|
||||
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
/** 到货进度-按重量(0-100) = 已到货重量/计划要求总重量 */
|
||||
/** 到货进度-按重量(0-100) = 已到货重量/要求总重量 */
|
||||
private BigDecimal progress;
|
||||
|
||||
/** 到货进度-按卷数(0-100) = 已到货卷数/计划要求总数量 */
|
||||
private BigDecimal progressQty;
|
||||
|
||||
/** 明细行 */
|
||||
private List<ErpPurchasePlanItemVo> items;
|
||||
|
||||
|
||||
@@ -51,4 +51,14 @@ public interface ErpPurchasePlanMapper extends BaseMapperPlus<ErpPurchasePlanMap
|
||||
* 用于判定上传到货行的卷号是否已实际到货入库。
|
||||
*/
|
||||
List<String> selectExistingSupplierCoilNos(@Param("coilNos") Collection<String> coilNos);
|
||||
|
||||
/**
|
||||
* 在途卷号:WMS 钢卷表中存在但 data_type=10(在途)的厂家卷号集合。
|
||||
*/
|
||||
List<String> selectInTransitSupplierCoilNos(@Param("coilNos") Collection<String> coilNos);
|
||||
|
||||
/**
|
||||
* 厂商历史记忆:已保存过的厂商去重(可按关键字过滤),用于手填自动补全。
|
||||
*/
|
||||
List<String> selectDistinctManufacturers(@Param("kw") String kw);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.klp.erp.domain.bo.ErpPurchasePlanAuditBo;
|
||||
import com.klp.erp.domain.bo.ErpPurchasePlanBo;
|
||||
import com.klp.erp.domain.vo.ErpContractOptionVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanAuditLogVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryBatchVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanItemVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanVo;
|
||||
@@ -66,16 +65,13 @@ public interface IErpPurchasePlanService {
|
||||
*/
|
||||
Map<String, Object> importDelivery(Long planId, InputStream inputStream, String fileName, String operator);
|
||||
|
||||
/** 查询某计划的到货明细(matched 标记是否与明细卷号匹配) */
|
||||
/** 查询某计划的到货明细(仅最新一次上传,含到货/在途标记) */
|
||||
List<ErpPurchasePlanDeliveryVo> queryDeliveryList(Long planId);
|
||||
|
||||
/** 某计划下指定批次的到货明细 */
|
||||
List<ErpPurchasePlanDeliveryVo> queryDeliveryListByBatch(Long batchId);
|
||||
/** 厂商历史记忆(去重,可按关键字过滤),用于手填自动补全 */
|
||||
List<String> queryManufacturers(String keyword);
|
||||
|
||||
/** 某计划的到货上传批次(最新在前) */
|
||||
List<ErpPurchasePlanDeliveryBatchVo> queryDeliveryBatches(Long planId);
|
||||
|
||||
/** 到货记录页左侧:审核通过的计划分页(含合同号、明细数、进度) */
|
||||
/** 到货记录页左侧:审核通过的合同分页(含合同号、出卖方、进度) */
|
||||
TableDataInfo<ErpPurchasePlanVo> queryDeliveryPlanPage(String keyword, PageQuery pageQuery);
|
||||
|
||||
/** 删除到货明细,并刷新进度 */
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.klp.common.utils.StringUtils;
|
||||
import com.klp.erp.domain.ErpPurchasePlan;
|
||||
import com.klp.erp.domain.ErpPurchasePlanContractRel;
|
||||
import com.klp.erp.domain.ErpPurchasePlanDelivery;
|
||||
import com.klp.erp.domain.ErpPurchasePlanDeliveryBatch;
|
||||
import com.klp.erp.domain.ErpPurchasePlanItem;
|
||||
import com.klp.erp.domain.ErpPurchasePlanAuditLog;
|
||||
import com.klp.erp.domain.bo.ErpPurchasePlanAuditBo;
|
||||
@@ -21,13 +20,11 @@ import com.klp.erp.domain.bo.ErpPurchasePlanBo;
|
||||
import com.klp.erp.domain.bo.ErpPurchasePlanItemBo;
|
||||
import com.klp.erp.domain.vo.ErpContractOptionVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanAuditLogVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryBatchVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryImportVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanItemVo;
|
||||
import com.klp.erp.domain.vo.ErpPurchasePlanVo;
|
||||
import com.klp.erp.mapper.ErpPurchasePlanContractRelMapper;
|
||||
import com.klp.erp.mapper.ErpPurchasePlanDeliveryBatchMapper;
|
||||
import com.klp.erp.mapper.ErpPurchasePlanDeliveryMapper;
|
||||
import com.klp.erp.mapper.ErpPurchasePlanItemMapper;
|
||||
import com.klp.erp.mapper.ErpPurchasePlanAuditLogMapper;
|
||||
@@ -46,7 +43,6 @@ import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -73,7 +69,6 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
private final ErpPurchasePlanItemMapper itemMapper;
|
||||
private final ErpPurchasePlanContractRelMapper relMapper;
|
||||
private final ErpPurchasePlanDeliveryMapper deliveryMapper;
|
||||
private final ErpPurchasePlanDeliveryBatchMapper deliveryBatchMapper;
|
||||
private final ErpPurchasePlanAuditLogMapper auditLogMapper;
|
||||
|
||||
@Override
|
||||
@@ -167,7 +162,7 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
add.setAuditStatus(AUDIT_DRAFT); // 新建为「待送审」,需手动送审后才进入审核池
|
||||
add.setArrivedWeight(BigDecimal.ZERO);
|
||||
add.setPlanWeight(sumItemWeight(bo.getItems()));
|
||||
add.setPlanQty(sumItemQty(bo.getItems()));
|
||||
add.setPlanQty(bo.getItems() == null ? 0 : bo.getItems().size());
|
||||
add.setArrivedCount(0);
|
||||
if (baseMapper.insert(add) <= 0) {
|
||||
return false;
|
||||
@@ -192,7 +187,7 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
update.setPlanNo(bo.getPlanNo());
|
||||
}
|
||||
update.setPlanWeight(sumItemWeight(bo.getItems()));
|
||||
update.setPlanQty(sumItemQty(bo.getItems()));
|
||||
update.setPlanQty(bo.getItems() == null ? 0 : bo.getItems().size());
|
||||
baseMapper.updateById(update);
|
||||
// 覆盖式重写明细与合同关联
|
||||
itemMapper.delete(Wrappers.lambdaQuery(ErpPurchasePlanItem.class)
|
||||
@@ -242,13 +237,6 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
private int sumItemQty(List<ErpPurchasePlanItemBo> items) {
|
||||
if (items == null) {
|
||||
return 0;
|
||||
}
|
||||
return items.stream().mapToInt(i -> i.getQuantity() == null ? 0 : i.getQuantity()).sum();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
@@ -341,31 +329,7 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
if (valid.isEmpty()) {
|
||||
throw new ServiceException("未解析到有效到货数据,请检查文件内容或列名是否与模板一致");
|
||||
}
|
||||
// 2) 牌号校验:到货表里的牌号必须都在本计划「采购要求」的牌号范围内
|
||||
List<ErpPurchasePlanItem> planItems = itemMapper.selectList(Wrappers.lambdaQuery(ErpPurchasePlanItem.class)
|
||||
.eq(ErpPurchasePlanItem::getPlanId, planId));
|
||||
Set<String> reqGrades = planItems.stream()
|
||||
.map(it -> normCoil(it.getGrade()))
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.collect(Collectors.toSet());
|
||||
if (!reqGrades.isEmpty()) {
|
||||
Set<String> badGrades = new LinkedHashSet<>();
|
||||
for (ErpPurchasePlanDeliveryImportVo r : valid) {
|
||||
String g = normCoil(r.getGrade());
|
||||
if (StringUtils.isBlank(g)) {
|
||||
badGrades.add("(空牌号)");
|
||||
} else if (!reqGrades.contains(g)) {
|
||||
badGrades.add(r.getGrade().trim());
|
||||
}
|
||||
}
|
||||
if (!badGrades.isEmpty()) {
|
||||
String allow = planItems.stream().map(ErpPurchasePlanItem::getGrade)
|
||||
.filter(StringUtils::isNotBlank).map(String::trim).distinct().collect(Collectors.joining("、"));
|
||||
throw new ServiceException("到货表中的牌号 [" + String.join("、", badGrades)
|
||||
+ "] 不在本计划采购要求的牌号范围内(仅允许:" + allow + "),请核对后重新上传");
|
||||
}
|
||||
}
|
||||
// 3) kg→t 单位判定(文件级:单卷重量最大值超阈值视为 kg)
|
||||
// 2) kg→t 单位判定(文件级:单卷重量最大值超阈值视为 kg)
|
||||
BigDecimal maxCoil = valid.stream()
|
||||
.map(ErpPurchasePlanDeliveryImportVo::getCoilWeight)
|
||||
.filter(w -> w != null)
|
||||
@@ -373,29 +337,26 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
.orElse(BigDecimal.ZERO);
|
||||
boolean kgConverted = maxCoil.compareTo(KG_THRESHOLD) > 0;
|
||||
|
||||
// 4) 卷号去 WMS 钢卷表校验:存在且 data_type<>10 即视为已实际到货入库
|
||||
// 3) 卷号去 WMS 钢卷表分类:存在且 data_type<>10=已到货;data_type=10=在途;查不到=未到
|
||||
List<String> uploadCoilNos = valid.stream()
|
||||
.map(ErpPurchasePlanDeliveryImportVo::getCoilNo)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.map(String::trim)
|
||||
.filter(StringUtils::isNotBlank).map(String::trim)
|
||||
.collect(Collectors.toList());
|
||||
Set<String> wmsArrivedSet = uploadCoilNos.isEmpty() ? new HashSet<>()
|
||||
: baseMapper.selectExistingSupplierCoilNos(uploadCoilNos).stream()
|
||||
.map(this::normCoil).collect(Collectors.toSet());
|
||||
Set<String> arrivedSet = uploadCoilNos.isEmpty() ? new HashSet<>()
|
||||
: baseMapper.selectExistingSupplierCoilNos(uploadCoilNos).stream().map(this::normCoil).collect(Collectors.toSet());
|
||||
Set<String> inTransitSet = uploadCoilNos.isEmpty() ? new HashSet<>()
|
||||
: baseMapper.selectInTransitSupplierCoilNos(uploadCoilNos).stream().map(this::normCoil).collect(Collectors.toSet());
|
||||
|
||||
// 5) 先建批次(每次上传存档,可随时回看)
|
||||
ErpPurchasePlanDeliveryBatch batch = new ErpPurchasePlanDeliveryBatch();
|
||||
batch.setPlanId(planId);
|
||||
batch.setFileName(StringUtils.isNotBlank(fileName) ? fileName : "到货表");
|
||||
deliveryBatchMapper.insert(batch);
|
||||
// 4) 覆盖式:每次上传只用最新文件,先清掉该计划旧到货记录
|
||||
deliveryMapper.delete(Wrappers.lambdaQuery(ErpPurchasePlanDelivery.class).eq(ErpPurchasePlanDelivery::getPlanId, planId));
|
||||
|
||||
// 6) 落库 + 单位换算 + WMS到货标记
|
||||
// 5) 落库 + 单位换算 + 到货/在途标记
|
||||
// 合并单元格:车号(F)几卷共用一车 → 向下填充让每卷都带车号;
|
||||
// 数量(G=整车总重)/件数(H=整车件数) 是「整车合计」,只记在该车首行(其余行留空),
|
||||
// 避免按行重复计数(用户:一共3件,不是每行3件)。
|
||||
// 数量(G=整车总重)/件数(H=整车件数) 是整车合计,只记在该车首行,不向下填充。
|
||||
String lastTruckNo = null;
|
||||
int count = 0;
|
||||
int matched = 0;
|
||||
int arrivedNum = 0;
|
||||
int transitNum = 0;
|
||||
for (ErpPurchasePlanDeliveryImportVo row : rows) {
|
||||
if (StringUtils.isNotBlank(row.getTruckNo())) {
|
||||
lastTruckNo = row.getTruckNo();
|
||||
@@ -403,20 +364,19 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
if (StringUtils.isBlank(row.getCoilNo()) && row.getCoilWeight() == null) {
|
||||
continue;
|
||||
}
|
||||
boolean arrived = StringUtils.isNotBlank(row.getCoilNo()) && wmsArrivedSet.contains(normCoil(row.getCoilNo()));
|
||||
String coil = normCoil(row.getCoilNo());
|
||||
boolean arrived = !coil.isEmpty() && arrivedSet.contains(coil);
|
||||
boolean transit = !arrived && !coil.isEmpty() && inTransitSet.contains(coil);
|
||||
ErpPurchasePlanDelivery d = new ErpPurchasePlanDelivery();
|
||||
d.setPlanId(planId);
|
||||
d.setBatchId(batch.getBatchId());
|
||||
d.setArrived(arrived ? 1 : 0);
|
||||
d.setInTransit(transit ? 1 : 0);
|
||||
d.setArrivalDate(parseDate(row.getArrivalDate()));
|
||||
d.setGrade(row.getGrade());
|
||||
d.setSpec(row.getSpec());
|
||||
d.setCoilNo(row.getCoilNo());
|
||||
d.setCoilWeight(convertWeight(row.getCoilWeight(), kgConverted));
|
||||
// 车号:每卷归属的车(向下填充)
|
||||
d.setTruckNo(StringUtils.isNotBlank(row.getTruckNo()) ? row.getTruckNo() : lastTruckNo);
|
||||
// 整车总重 / 件数:整车合计,仅该车首行有值,不向下填充
|
||||
// 数量/件数 作为 String 读取以兼容合并单元格的空白/点号等占位符,在此 parse
|
||||
d.setTruckWeight(convertWeight(parseDecimal(row.getTruckWeight()), kgConverted));
|
||||
d.setPieceCount(parseInteger(row.getPieceCount()));
|
||||
d.setSalesCode(row.getSalesCode());
|
||||
@@ -424,20 +384,16 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
deliveryMapper.insert(d);
|
||||
count++;
|
||||
if (arrived) {
|
||||
matched++;
|
||||
arrivedNum++;
|
||||
} else if (transit) {
|
||||
transitNum++;
|
||||
}
|
||||
}
|
||||
// 7) 汇总刷新进度
|
||||
// 6) 汇总刷新进度
|
||||
refreshProgress(planId);
|
||||
|
||||
// 8) 回填批次统计(行数、WMS确认到货卷数、上传后进度快照)
|
||||
ErpPurchasePlan after = baseMapper.selectById(planId);
|
||||
batch.setRowCount(count);
|
||||
batch.setMatchedCount(matched);
|
||||
batch.setArrivedPercent(calcPct(after.getArrivedWeight(), after.getPlanWeight()));
|
||||
deliveryBatchMapper.updateById(batch);
|
||||
|
||||
StringBuilder msg = new StringBuilder("成功导入 " + count + " 条到货记录,其中 " + matched + " 卷在钢卷表确认到货");
|
||||
int notArrived = count - arrivedNum - transitNum;
|
||||
StringBuilder msg = new StringBuilder("导入 " + count + " 条:已到 " + arrivedNum + " 卷,在途 " + transitNum + " 卷,未到 " + notArrived + " 卷");
|
||||
if (kgConverted) {
|
||||
msg.append(";检测到重量疑似按 kg 录入(最大单卷 ")
|
||||
.append(maxCoil.stripTrailingZeros().toPlainString())
|
||||
@@ -445,7 +401,8 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
}
|
||||
Map<String, Object> result = new HashMap<>(6);
|
||||
result.put("count", count);
|
||||
result.put("matched", matched);
|
||||
result.put("arrived", arrivedNum);
|
||||
result.put("inTransit", transitNum);
|
||||
result.put("kgConverted", kgConverted);
|
||||
result.put("message", msg.toString());
|
||||
return result;
|
||||
@@ -508,17 +465,8 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ErpPurchasePlanDeliveryVo> queryDeliveryListByBatch(Long batchId) {
|
||||
return deliveryMapper.selectVoList(Wrappers.lambdaQuery(ErpPurchasePlanDelivery.class)
|
||||
.eq(ErpPurchasePlanDelivery::getBatchId, batchId)
|
||||
.orderByDesc(ErpPurchasePlanDelivery::getDeliveryId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ErpPurchasePlanDeliveryBatchVo> queryDeliveryBatches(Long planId) {
|
||||
return deliveryBatchMapper.selectVoList(Wrappers.lambdaQuery(ErpPurchasePlanDeliveryBatch.class)
|
||||
.eq(ErpPurchasePlanDeliveryBatch::getPlanId, planId)
|
||||
.orderByDesc(ErpPurchasePlanDeliveryBatch::getBatchId));
|
||||
public List<String> queryManufacturers(String keyword) {
|
||||
return baseMapper.selectDistinctManufacturers(keyword);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -568,27 +516,39 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.map(String::trim)
|
||||
.collect(Collectors.toList());
|
||||
Set<String> wmsArrivedSet = coilNos.isEmpty() ? new HashSet<>()
|
||||
: baseMapper.selectExistingSupplierCoilNos(coilNos).stream()
|
||||
.map(this::normCoil).collect(Collectors.toSet());
|
||||
Set<String> arrivedSet = coilNos.isEmpty() ? new HashSet<>()
|
||||
: baseMapper.selectExistingSupplierCoilNos(coilNos).stream().map(this::normCoil).collect(Collectors.toSet());
|
||||
Set<String> inTransitSet = coilNos.isEmpty() ? new HashSet<>()
|
||||
: baseMapper.selectInTransitSupplierCoilNos(coilNos).stream().map(this::normCoil).collect(Collectors.toSet());
|
||||
|
||||
int arrivedCount = 0;
|
||||
int transitCount = 0;
|
||||
BigDecimal arrivedWeight = BigDecimal.ZERO;
|
||||
BigDecimal transitWeight = BigDecimal.ZERO;
|
||||
for (ErpPurchasePlanDelivery d : deliveries) {
|
||||
int nowArrived = StringUtils.isNotBlank(d.getCoilNo()) && wmsArrivedSet.contains(normCoil(d.getCoilNo())) ? 1 : 0;
|
||||
// 状态有变化才回写,减少无谓更新
|
||||
if (d.getArrived() == null || d.getArrived() != nowArrived) {
|
||||
String coil = normCoil(d.getCoilNo());
|
||||
int nowArrived = !coil.isEmpty() && arrivedSet.contains(coil) ? 1 : 0;
|
||||
int nowTransit = nowArrived == 0 && !coil.isEmpty() && inTransitSet.contains(coil) ? 1 : 0;
|
||||
if (d.getArrived() == null || d.getArrived() != nowArrived
|
||||
|| d.getInTransit() == null || d.getInTransit() != nowTransit) {
|
||||
d.setArrived(nowArrived);
|
||||
d.setInTransit(nowTransit);
|
||||
deliveryMapper.updateById(d);
|
||||
}
|
||||
BigDecimal w = d.getCoilWeight() == null ? BigDecimal.ZERO : d.getCoilWeight();
|
||||
if (nowArrived == 1) {
|
||||
arrivedCount++;
|
||||
arrivedWeight = arrivedWeight.add(d.getCoilWeight() == null ? BigDecimal.ZERO : d.getCoilWeight());
|
||||
arrivedWeight = arrivedWeight.add(w);
|
||||
} else if (nowTransit == 1) {
|
||||
transitCount++;
|
||||
transitWeight = transitWeight.add(w);
|
||||
}
|
||||
}
|
||||
plan.setArrivedCount(arrivedCount);
|
||||
plan.setArrivedWeight(arrivedWeight);
|
||||
// 到货重量达到计划要求总重量 → 自动归档
|
||||
plan.setInTransitCount(transitCount);
|
||||
plan.setInTransitWeight(transitWeight);
|
||||
// 到货重量达到要求总重量 → 自动归档
|
||||
BigDecimal planWeight = plan.getPlanWeight() == null ? BigDecimal.ZERO : plan.getPlanWeight();
|
||||
boolean done = planWeight.compareTo(BigDecimal.ZERO) > 0 && arrivedWeight.compareTo(planWeight) >= 0;
|
||||
plan.setPlanStatus(done ? PLAN_STATUS_ARCHIVED : PLAN_STATUS_ONGOING);
|
||||
@@ -612,12 +572,9 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
return map;
|
||||
}
|
||||
|
||||
/** 同时填充按重量、按卷数两个到货百分比 */
|
||||
/** 到货百分比按重量 = 已到货重量 / 要求总重量 */
|
||||
private void fillProgress(ErpPurchasePlanVo v) {
|
||||
v.setProgress(calcPct(v.getArrivedWeight(), v.getPlanWeight()));
|
||||
v.setProgressQty(calcPct(
|
||||
v.getArrivedCount() == null ? null : BigDecimal.valueOf(v.getArrivedCount()),
|
||||
v.getPlanQty() == null ? null : BigDecimal.valueOf(v.getPlanQty())));
|
||||
}
|
||||
|
||||
/** 百分比(0-100,保留2位) = num/den,封顶100 */
|
||||
@@ -630,13 +587,13 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService {
|
||||
return pct.min(BigDecimal.valueOf(100));
|
||||
}
|
||||
|
||||
/** 自动生成计划号:CG + yyyyMMdd + 4位流水 */
|
||||
/** 自动生成合同号:yyyyMMdd-当日序号(如 20260629-1) */
|
||||
private String generatePlanNo() {
|
||||
String prefix = "CG" + DateUtil.format(new Date(), "yyyyMMdd");
|
||||
String prefix = DateUtil.format(new Date(), "yyyyMMdd");
|
||||
Long todayCount = baseMapper.selectCount(Wrappers.lambdaQuery(ErpPurchasePlan.class)
|
||||
.likeRight(ErpPurchasePlan::getPlanNo, prefix));
|
||||
.likeRight(ErpPurchasePlan::getPlanNo, prefix + "-"));
|
||||
long seq = (todayCount == null ? 0L : todayCount) + 1L;
|
||||
return prefix + String.format("%04d", seq);
|
||||
return prefix + "-" + seq;
|
||||
}
|
||||
|
||||
private Date parseDate(String text) {
|
||||
|
||||
@@ -77,6 +77,31 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<!-- 在途卷号:钢卷表存在且 data_type=10 -->
|
||||
<select id="selectInTransitSupplierCoilNos" resultType="java.lang.String">
|
||||
SELECT DISTINCT supplier_coil_no
|
||||
FROM wms_material_coil
|
||||
WHERE del_flag = 0 AND data_type = 10
|
||||
AND supplier_coil_no IN
|
||||
<foreach collection="coilNos" item="c" open="(" separator="," close=")">
|
||||
#{c}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<!-- 厂商历史记忆(去重,可按关键字过滤) -->
|
||||
<select id="selectDistinctManufacturers" resultType="java.lang.String">
|
||||
SELECT manufacturer FROM (
|
||||
SELECT DISTINCT manufacturer
|
||||
FROM erp_purchase_plan_item
|
||||
WHERE del_flag = '0' AND manufacturer IS NOT NULL AND TRIM(manufacturer) <> ''
|
||||
<if test="kw != null and kw != ''">
|
||||
AND manufacturer LIKE CONCAT('%', #{kw}, '%')
|
||||
</if>
|
||||
) t
|
||||
ORDER BY manufacturer
|
||||
LIMIT 20
|
||||
</select>
|
||||
|
||||
<!-- 按合同关键字查关联的采购计划ID(订单编号/合同号/合同名称) -->
|
||||
<select id="selectPlanIdsByContractKeyword" resultType="java.lang.Long">
|
||||
SELECT DISTINCT r.plan_id
|
||||
|
||||
@@ -132,19 +132,12 @@ export function refreshArrival(planId) {
|
||||
})
|
||||
}
|
||||
|
||||
// 某计划的到货上传批次(每次上传一条)
|
||||
export function listDeliveryBatches(planId) {
|
||||
// 厂商历史记忆(手填自动补全)
|
||||
export function listManufacturers(keyword) {
|
||||
return request({
|
||||
url: `/erp/purchasePlan/${planId}/deliveryBatches`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 某批次的到货明细
|
||||
export function listDeliveryByBatch(batchId) {
|
||||
return request({
|
||||
url: `/erp/purchasePlan/deliveryBatch/${batchId}`,
|
||||
method: 'get'
|
||||
url: '/erp/purchasePlan/manufacturers',
|
||||
method: 'get',
|
||||
params: { keyword }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<section class="pa-detail">
|
||||
<div v-if="!current.planId" class="pa-placeholder">
|
||||
<i class="el-icon-s-check"></i>
|
||||
<p>从左侧选择一条采购计划进行审核</p>
|
||||
<p>从左侧选择一条采购合同进行审核</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="pa-view">
|
||||
@@ -80,29 +80,21 @@
|
||||
</div>
|
||||
|
||||
<div class="pa-meta">
|
||||
<div class="pa-meta-i"><label>供货商</label><span>{{ current.supplier || '—' }}</span></div>
|
||||
<div class="pa-meta-i"><label>出卖方</label><span>{{ current.supplier || '—' }}</span></div>
|
||||
<div class="pa-meta-i"><label>采购日期</label><span>{{ current.purchaseDate || '—' }}</span></div>
|
||||
<div class="pa-meta-i"><label>计划重量</label><span>{{ current.planWeight }} T</span></div>
|
||||
<div class="pa-meta-i"><label>要求总重量</label><span>{{ current.planWeight }} T</span></div>
|
||||
<div class="pa-meta-i"><label>审核人</label><span>{{ current.auditor || '—' }}</span></div>
|
||||
<div class="pa-meta-i"><label>审核时间</label><span>{{ current.auditTime || '—' }}</span></div>
|
||||
<div class="pa-meta-i"><label>关联合同</label><span>{{ (current.contractCodes || []).join('、') || '—' }}</span></div>
|
||||
<div class="pa-meta-i"><label>关联销售合同</label><span>{{ (current.contractCodes || []).join('、') || '—' }}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="pa-section">采购明细<span class="pa-section-hint" v-if="current.items && current.items.length">共 {{ current.items.length }} 行</span></div>
|
||||
<div class="pa-section">采购要求<span class="pa-section-hint" v-if="current.items && current.items.length">共 {{ current.items.length }} 项</span></div>
|
||||
<el-table :data="current.items || []" border size="mini" max-height="320" class="pa-table">
|
||||
<el-table-column label="#" type="index" width="40" align="center" />
|
||||
<el-table-column label="产品" prop="productType" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="材质" prop="material" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="牌号" prop="grade" width="90" />
|
||||
<el-table-column label="卷号" prop="coilNo" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="宽度" prop="width" width="90" />
|
||||
<el-table-column label="厚度" prop="thickness" width="90" />
|
||||
<el-table-column label="宽公差" prop="widthTolerance" width="80" />
|
||||
<el-table-column label="厚公差" prop="thicknessTolerance" width="80" />
|
||||
<el-table-column label="重量(T)" prop="weight" width="90" align="right" />
|
||||
<el-table-column label="数量" prop="quantity" width="64" align="right" />
|
||||
<el-table-column label="供货商" prop="supplier" width="100" show-overflow-tooltip />
|
||||
<template slot="empty"><span>无明细</span></template>
|
||||
<el-table-column label="#" type="index" width="44" align="center" />
|
||||
<el-table-column label="规格" prop="spec" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="总重量(T)" prop="weight" min-width="110" align="right" />
|
||||
<el-table-column label="厂商" prop="manufacturer" min-width="180" show-overflow-tooltip />
|
||||
<template slot="empty"><span>无采购要求</span></template>
|
||||
</el-table>
|
||||
|
||||
<div class="pa-section">申请 / 审核意见</div>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<div class="pd-wb">
|
||||
<div class="pd-main">
|
||||
<!-- 左:审核通过的计划 -->
|
||||
<!-- 左:审核通过的采购合同 -->
|
||||
<aside class="pd-col pd-plans">
|
||||
<div class="pd-col-tool">
|
||||
<el-input
|
||||
v-model="queryParams.keyword"
|
||||
size="small"
|
||||
clearable
|
||||
placeholder="搜索计划号 / 合同号 / 供货商"
|
||||
placeholder="搜索合同号 / 出卖方"
|
||||
prefix-icon="el-icon-search"
|
||||
@keyup.enter.native="handleQuery"
|
||||
@clear="handleQuery"
|
||||
@@ -26,14 +26,14 @@
|
||||
<span class="pd-no">{{ p.planNo }}</span>
|
||||
<span class="pd-pct" :class="{ done: Number(p.progress) >= 100 }">{{ (Number(p.progress) || 0).toFixed(0) }}%</span>
|
||||
</div>
|
||||
<div class="pd-li-r2">{{ (p.contractCodes || []).join('、') || '无合同号' }}</div>
|
||||
<div class="pd-li-r2">{{ p.supplier || '—' }}</div>
|
||||
<el-progress :percentage="Number(p.progress) || 0" :stroke-width="4" :show-text="false" :color="progressColor" />
|
||||
<div class="pd-li-r3">
|
||||
<span>{{ p.supplier || '—' }}</span>
|
||||
<span>{{ p.arrivedCount || 0 }}/{{ p.planQty || 0 }} 卷<i v-if="p.planStatus === '1'" class="pd-arch">已齐</i></span>
|
||||
<span>已到 {{ p.arrivedCount || 0 }} · 在途 {{ p.inTransitCount || 0 }}</span>
|
||||
<span v-if="p.planStatus === '1'" class="pd-arch">已到齐</span>
|
||||
</div>
|
||||
</li>
|
||||
<li v-if="!loading && !planList.length" class="pd-empty">暂无审核通过的计划</li>
|
||||
<li v-if="!loading && !planList.length" class="pd-empty">暂无审核通过的合同</li>
|
||||
</ul>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
@@ -50,7 +50,7 @@
|
||||
<section class="pd-col pd-detail">
|
||||
<div v-if="!current.planId" class="pd-placeholder">
|
||||
<i class="el-icon-truck"></i>
|
||||
<p>从左侧选择一条审核通过的计划,上传到货表格按卷号比对</p>
|
||||
<p>从左侧选择一条采购合同</p>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
@@ -75,89 +75,51 @@
|
||||
</div>
|
||||
|
||||
<div class="pd-meta">
|
||||
<div class="pd-meta-i"><label>合同号</label><span>{{ (current.contractCodes || []).join('、') || '—' }}</span></div>
|
||||
<div class="pd-meta-i"><label>供货商</label><span>{{ current.supplier || '—' }}</span></div>
|
||||
<div class="pd-meta-i"><label>已到货 / 要求</label><span>{{ current.arrivedCount || 0 }} / {{ current.planQty || 0 }} 卷 · {{ fmt(current.arrivedWeight) }} / {{ fmt(current.planWeight) }} T</span></div>
|
||||
<div class="pd-meta-i"><label>出卖方</label><span>{{ current.supplier || '—' }}</span></div>
|
||||
<div class="pd-meta-i"><label>关联销售合同</label><span>{{ (current.contractCodes || []).join('、') || '—' }}</span></div>
|
||||
<div class="pd-meta-i"><label>要求总重量</label><span>{{ fmt(current.planWeight) }} T</span></div>
|
||||
<div class="pd-meta-i"><label>已到 / 在途</label><span>{{ current.arrivedCount || 0 }} 卷({{ fmt(current.arrivedWeight) }} T) / {{ current.inTransitCount || 0 }} 卷</span></div>
|
||||
<div class="pd-meta-i wide">
|
||||
<label>到货百分比(按重量)</label>
|
||||
<label>到货进度(按重量)</label>
|
||||
<el-progress
|
||||
:percentage="Number(current.progress) || 0"
|
||||
:stroke-width="14" :text-inside="true" :color="progressColor"
|
||||
:format="p => p.toFixed(1) + '%'"
|
||||
/>
|
||||
</div>
|
||||
<div class="pd-meta-i wide">
|
||||
<label>到货百分比(按卷数)</label>
|
||||
<el-progress
|
||||
:percentage="Number(current.progressQty) || 0"
|
||||
:stroke-width="14" :text-inside="true" color="#3a8a4d"
|
||||
:format="p => p.toFixed(1) + '%'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-tabs v-model="activeTab" class="pd-tabs">
|
||||
<el-tab-pane name="items">
|
||||
<span slot="label">采购要求(到货 重量{{ (Number(current.progress) || 0).toFixed(0) }}% / 卷数{{ (Number(current.progressQty) || 0).toFixed(0) }}%)</span>
|
||||
<p class="pd-req-tip">采购要求只说明买什么、买多少;到货按上传表格的卷号在钢卷表确认,进度见上方与「到货记录」。</p>
|
||||
<el-tab-pane label="采购要求" name="items">
|
||||
<el-table :data="current.items" border size="mini" max-height="420">
|
||||
<el-table-column label="#" type="index" width="40" align="center" />
|
||||
<el-table-column label="产品" prop="productType" min-width="70" show-overflow-tooltip />
|
||||
<el-table-column label="材质" prop="material" min-width="78" show-overflow-tooltip />
|
||||
<el-table-column label="牌号" prop="grade" min-width="72" />
|
||||
<el-table-column label="宽度" prop="width" min-width="68" />
|
||||
<el-table-column label="厚度" prop="thickness" min-width="68" />
|
||||
<el-table-column label="宽公差" prop="widthTolerance" min-width="70" />
|
||||
<el-table-column label="厚公差" prop="thicknessTolerance" min-width="70" />
|
||||
<el-table-column label="重量(T)" prop="weight" min-width="76" align="right" />
|
||||
<el-table-column label="数量" prop="quantity" min-width="58" align="right" />
|
||||
<el-table-column label="供货商" prop="supplier" min-width="88" show-overflow-tooltip />
|
||||
<template slot="empty"><span>无采购明细</span></template>
|
||||
<el-table-column label="#" type="index" width="44" align="center" />
|
||||
<el-table-column label="规格" prop="spec" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="总重量(T)" prop="weight" min-width="110" align="right" />
|
||||
<el-table-column label="厂商" prop="manufacturer" min-width="180" show-overflow-tooltip />
|
||||
<template slot="empty"><span>无采购要求</span></template>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane name="batches">
|
||||
<span slot="label">到货记录({{ batches.length }} 次上传)</span>
|
||||
<div class="pd-batch-wrap">
|
||||
<el-table
|
||||
:data="batches"
|
||||
border size="mini" max-height="200" v-loading="batchLoading"
|
||||
highlight-current-row @row-click="viewBatch"
|
||||
>
|
||||
<el-table-column label="上传时间" prop="createTime" width="150" align="center" />
|
||||
<el-table-column label="文件" prop="fileName" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="行数" prop="rowCount" width="64" align="center" />
|
||||
<el-table-column label="匹配卷数" prop="matchedCount" width="84" align="center" />
|
||||
<el-table-column label="上传后到货%" width="110" align="center">
|
||||
<template slot-scope="s">{{ (Number(s.row.arrivedPercent) || 0).toFixed(0) }}%</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="录入人" prop="createBy" width="90" align="center" />
|
||||
<el-table-column label="操作" width="64" align="center">
|
||||
<template slot-scope="s"><el-button type="text" size="mini" @click.stop="viewBatch(s.row)">查看</el-button></template>
|
||||
</el-table-column>
|
||||
<template slot="empty"><span>暂无上传记录,点右上角「上传到货表格」</span></template>
|
||||
</el-table>
|
||||
|
||||
<div class="pd-batch-rows" v-if="currentBatch.batchId">
|
||||
<div class="pd-sub-title">「{{ currentBatch.fileName }}」到货明细(卷号在钢卷表确认到货的高亮为已到货)</div>
|
||||
<el-table :data="batchRows" border stripe size="mini" max-height="300" v-loading="batchRowsLoading"
|
||||
:row-class-name="rowClass">
|
||||
<el-table-column label="日期" prop="arrivalDate" width="100" align="center" />
|
||||
<el-table-column label="牌号" prop="grade" width="78" align="center" />
|
||||
<el-table-column label="规格" prop="spec" width="105" align="center" />
|
||||
<el-table-column label="卷号" prop="coilNo" width="120" align="center" />
|
||||
<el-table-column label="单卷(T)" prop="coilWeight" width="78" align="right" />
|
||||
<el-table-column label="车号" prop="truckNo" width="95" align="center" />
|
||||
<el-table-column label="销售" prop="salesCode" width="80" align="center" />
|
||||
<el-table-column label="到货" width="72" align="center">
|
||||
<template slot-scope="s">
|
||||
<span class="pd-mtag" :class="s.row.arrived === 1 ? 'yes' : 'no'">{{ s.row.arrived === 1 ? '已到货' : '未到' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template slot="empty"><span>该批次无明细</span></template>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<el-tab-pane :label="'到货明细(' + deliveryList.length + ')'" name="delivery">
|
||||
<el-table
|
||||
:data="deliveryList" border stripe size="mini" max-height="420"
|
||||
v-loading="deliveryLoading" :row-class-name="rowClass"
|
||||
>
|
||||
<el-table-column label="日期" prop="arrivalDate" width="100" align="center" />
|
||||
<el-table-column label="牌号" prop="grade" width="78" align="center" />
|
||||
<el-table-column label="规格" prop="spec" width="105" align="center" />
|
||||
<el-table-column label="卷号" prop="coilNo" width="120" align="center" />
|
||||
<el-table-column label="单卷(T)" prop="coilWeight" width="78" align="right" />
|
||||
<el-table-column label="车号" prop="truckNo" width="95" align="center" />
|
||||
<el-table-column label="件数" prop="pieceCount" width="56" align="center" />
|
||||
<el-table-column label="销售" prop="salesCode" width="80" align="center" />
|
||||
<el-table-column label="状态" width="76" align="center">
|
||||
<template slot-scope="s">
|
||||
<span class="pd-mtag" :class="statusClass(s.row)">{{ statusText(s.row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template slot="empty"><span>暂无到货记录,点右上角「上传到货表格」</span></template>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -170,8 +132,7 @@
|
||||
import {
|
||||
getPurchasePlan,
|
||||
listDeliveryPlans,
|
||||
listDeliveryBatches,
|
||||
listDeliveryByBatch,
|
||||
listDelivery,
|
||||
refreshArrival
|
||||
} from '@/api/erp/purchasePlan'
|
||||
import { getToken } from '@/utils/auth'
|
||||
@@ -186,11 +147,8 @@ export default {
|
||||
queryParams: { pageNum: 1, pageSize: 20, keyword: undefined },
|
||||
current: {},
|
||||
activeTab: 'items',
|
||||
batches: [],
|
||||
batchLoading: false,
|
||||
currentBatch: {},
|
||||
batchRows: [],
|
||||
batchRowsLoading: false,
|
||||
deliveryList: [],
|
||||
deliveryLoading: false,
|
||||
refreshing: false,
|
||||
upload: { headers: { Authorization: 'Bearer ' + getToken() } },
|
||||
progressColor: '#5b8db8'
|
||||
@@ -224,9 +182,8 @@ export default {
|
||||
selectPlan(p) {
|
||||
this.activeTab = 'items'
|
||||
this.current = { ...p }
|
||||
this.currentBatch = {}
|
||||
this.batchRows = []
|
||||
// 打开即静默复核一次 WMS 到货状态,保证看到的是最新
|
||||
this.deliveryList = []
|
||||
// 打开即静默按钢卷表复核一次到货状态
|
||||
const planId = p.planId
|
||||
refreshArrival(planId).catch(() => {}).finally(() => {
|
||||
if (this.current.planId === planId) this.refreshDetail()
|
||||
@@ -236,7 +193,8 @@ export default {
|
||||
const planId = this.current.planId
|
||||
if (!planId) return
|
||||
getPurchasePlan(planId).then(res => { this.current = { ...this.current, ...(res.data || {}) } })
|
||||
this.loadBatches()
|
||||
this.deliveryLoading = true
|
||||
listDelivery(planId).then(res => { this.deliveryList = res.data || [] }).finally(() => { this.deliveryLoading = false })
|
||||
},
|
||||
doRefreshArrival() {
|
||||
if (!this.current.planId) return
|
||||
@@ -244,23 +202,23 @@ export default {
|
||||
refreshArrival(this.current.planId).then(() => {
|
||||
this.$modal.msgSuccess('已按钢卷表刷新到货状态')
|
||||
this.refreshDetail()
|
||||
if (this.currentBatch.batchId) this.viewBatch(this.currentBatch)
|
||||
this.getList(true)
|
||||
}).finally(() => { this.refreshing = false })
|
||||
},
|
||||
loadBatches() {
|
||||
this.batchLoading = true
|
||||
listDeliveryBatches(this.current.planId).then(res => {
|
||||
this.batches = res.data || []
|
||||
}).finally(() => { this.batchLoading = false })
|
||||
},
|
||||
viewBatch(b) {
|
||||
this.currentBatch = { ...b }
|
||||
this.batchRowsLoading = true
|
||||
listDeliveryByBatch(b.batchId).then(res => { this.batchRows = res.data || [] }).finally(() => { this.batchRowsLoading = false })
|
||||
},
|
||||
rowClass({ row }) {
|
||||
return row.arrived === 1 ? 'pd-row-matched' : ''
|
||||
if (row.arrived === 1) return 'pd-row-arrived'
|
||||
if (row.inTransit === 1) return 'pd-row-transit'
|
||||
return ''
|
||||
},
|
||||
statusText(row) {
|
||||
if (row.arrived === 1) return '已到货'
|
||||
if (row.inTransit === 1) return '在途'
|
||||
return '未到'
|
||||
},
|
||||
statusClass(row) {
|
||||
if (row.arrived === 1) return 'yes'
|
||||
if (row.inTransit === 1) return 'transit'
|
||||
return 'no'
|
||||
},
|
||||
handleUploadSuccess(res) {
|
||||
if (res.code === 200) {
|
||||
@@ -270,7 +228,7 @@ export default {
|
||||
} else {
|
||||
this.$modal.msgSuccess(res.msg || '导入成功')
|
||||
}
|
||||
this.activeTab = 'batches'
|
||||
this.activeTab = 'delivery'
|
||||
this.refreshDetail()
|
||||
this.getList(true)
|
||||
} else {
|
||||
@@ -311,7 +269,7 @@ $sub: #909399;
|
||||
.pd-pct { font-size: 12px; font-weight: 600; color: $sub; &.done { color: #3a8a4d; } }
|
||||
.pd-li-r2 { font-size: 12px; color: $sub; margin: 4px 0 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.pd-li-r3 { display: flex; justify-content: space-between; font-size: 11px; color: $sub; margin-top: 4px; }
|
||||
.pd-arch { font-style: normal; color: #3a8a4d; margin-left: 6px; }
|
||||
.pd-arch { font-style: normal; color: #3a8a4d; }
|
||||
.pd-empty { text-align: center; color: $sub; padding: 36px 12px; font-size: 13px; }
|
||||
|
||||
.pd-placeholder {
|
||||
@@ -326,26 +284,20 @@ $sub: #909399;
|
||||
font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 2px; border: 1px solid #dcdfe6; color: $sub; background: #fafafa;
|
||||
&.p1 { color: #3a8a4d; border-color: #b7d9bf; background: #f0f9f1; }
|
||||
}
|
||||
.pd-meta { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px 24px; padding: 16px 18px; }
|
||||
.pd-meta { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px 24px; padding: 16px 18px; }
|
||||
.pd-meta-i {
|
||||
display: flex; flex-direction: column; font-size: 13px;
|
||||
label { color: $sub; font-size: 12px; margin-bottom: 3px; }
|
||||
span { color: $ink; }
|
||||
&.wide { grid-column: span 3; }
|
||||
&.wide { grid-column: span 2; }
|
||||
}
|
||||
.pd-tabs { padding: 0 18px 18px; }
|
||||
.pd-nocoil { color: #c0c4cc; }
|
||||
.pd-req-tip { font-size: 12px; color: #909399; margin: 0 0 10px; line-height: 1.6; }
|
||||
.pd-istat {
|
||||
font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 2px; border: 1px solid #dcdfe6; color: $sub;
|
||||
&.s2 { color: #3a8a4d; border-color: #b7d9bf; background: #f0f9f1; }
|
||||
}
|
||||
.pd-batch-rows { margin-top: 14px; }
|
||||
.pd-sub-title { font-size: 13px; font-weight: 600; color: $ink; border-left: 3px solid $accent; padding-left: 8px; margin: 6px 0 10px; }
|
||||
.pd-mtag {
|
||||
font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 2px; border: 1px solid #dcdfe6; color: $sub;
|
||||
&.yes { color: #3a8a4d; border-color: #b7d9bf; background: #f0f9f1; }
|
||||
&.transit { color: #d6a256; border-color: #ecd4a6; background: #fdf6ec; }
|
||||
&.no { color: $sub; }
|
||||
}
|
||||
::v-deep .pd-row-matched { background: #f3f9f4; }
|
||||
::v-deep .pd-row-arrived { background: #f3f9f4; }
|
||||
::v-deep .pd-row-transit { background: #fdf9f0; }
|
||||
</style>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<span>{{ (Number(p.progress) || 0).toFixed(0) }}%<i v-if="p.planStatus === '1'" class="pp-arch">已归档</i></span>
|
||||
</div>
|
||||
</li>
|
||||
<li v-if="!loading && !planList.length" class="pp-empty">暂无采购计划</li>
|
||||
<li v-if="!loading && !planList.length" class="pp-empty">暂无采购合同</li>
|
||||
</ul>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
@@ -63,13 +63,13 @@
|
||||
<section class="pp-col pp-detail">
|
||||
<div v-if="mode === 'empty'" class="pp-placeholder">
|
||||
<i class="el-icon-tickets"></i>
|
||||
<p>从左侧选择一条采购计划,或点击「新增」按销售合同创建</p>
|
||||
<p>从左侧选择一条采购合同,或点击「新增」</p>
|
||||
</div>
|
||||
|
||||
<!-- 编辑 / 新增 -->
|
||||
<div v-else-if="mode === 'edit'" class="pp-edit">
|
||||
<div class="pp-d-head">
|
||||
<span class="pp-d-title">{{ form.planId ? '编辑采购计划' : '新增采购计划' }}</span>
|
||||
<span class="pp-d-title">{{ form.planId ? '编辑采购合同' : '新增采购合同' }}</span>
|
||||
<div>
|
||||
<el-button size="small" @click="cancelEdit">取消</el-button>
|
||||
<el-button size="small" type="primary" :loading="buttonLoading" @click="submitForm">保存</el-button>
|
||||
@@ -78,12 +78,20 @@
|
||||
<el-form :model="form" :rules="rules" ref="form" label-width="80px" class="pp-form" size="small">
|
||||
<div class="pp-section">基本信息</div>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="计划号" prop="planNo">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出卖方" prop="supplier">
|
||||
<div class="pp-inline-pick">
|
||||
<el-input v-model="form.supplier" placeholder="选择供应商" />
|
||||
<el-button icon="el-icon-search" @click="openSupplierPicker">选择</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="合同号" prop="planNo">
|
||||
<el-input v-model="form.planNo" placeholder="留空自动生成" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="采购日期" prop="purchaseDate">
|
||||
<el-date-picker v-model="form.purchaseDate" type="date" value-format="yyyy-MM-dd" style="width:100%" placeholder="选择日期" />
|
||||
</el-form-item>
|
||||
@@ -110,42 +118,28 @@
|
||||
</div>
|
||||
|
||||
<div class="pp-section">
|
||||
采购明细
|
||||
采购要求
|
||||
<el-button type="text" size="mini" icon="el-icon-plus" class="pp-section-act" @click="addItem">加行</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-magic-stick" class="pp-section-act" @click="openBatchFill">批量填充</el-button>
|
||||
<span class="pp-section-hint" v-if="form.items.length">共 {{ form.items.length }} 行 · 计 {{ itemsWeight }} T</span>
|
||||
<span class="pp-section-hint" v-if="form.items.length">共 {{ form.items.length }} 项 · 计 {{ itemsWeight }} T</span>
|
||||
</div>
|
||||
<el-table :data="form.items" border size="mini" max-height="300">
|
||||
<el-table-column label="#" type="index" width="40" align="center" />
|
||||
<el-table-column label="产品" min-width="70">
|
||||
<template slot-scope="s"><el-input v-model="s.row.productType" size="mini" placeholder="热轧卷板" /></template>
|
||||
<el-table :data="form.items" border size="mini" max-height="340">
|
||||
<el-table-column label="#" type="index" width="44" align="center" />
|
||||
<el-table-column label="规格" min-width="220">
|
||||
<template slot-scope="s"><el-input v-model="s.row.spec" size="mini" placeholder="如 3.00×1230" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="材质" min-width="80">
|
||||
<template slot-scope="s"><el-input v-model="s.row.material" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="牌号" min-width="75">
|
||||
<template slot-scope="s"><el-input v-model="s.row.grade" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="宽度" min-width="75">
|
||||
<template slot-scope="s"><el-input v-model="s.row.width" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="厚度" min-width="75">
|
||||
<template slot-scope="s"><el-input v-model="s.row.thickness" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="宽公差" min-width="75">
|
||||
<template slot-scope="s"><el-input v-model="s.row.widthTolerance" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="厚公差" min-width="75">
|
||||
<template slot-scope="s"><el-input v-model="s.row.thicknessTolerance" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="重量(T)" min-width="80">
|
||||
<el-table-column label="总重量(T)" min-width="120">
|
||||
<template slot-scope="s"><el-input v-model="s.row.weight" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" min-width="65">
|
||||
<template slot-scope="s"><el-input v-model="s.row.quantity" size="mini" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="供货商" min-width="90">
|
||||
<template slot-scope="s"><el-input v-model="s.row.supplier" size="mini" /></template>
|
||||
<el-table-column label="厂商" min-width="200">
|
||||
<template slot-scope="s">
|
||||
<el-autocomplete
|
||||
v-model="s.row.manufacturer"
|
||||
:fetch-suggestions="queryManufacturer"
|
||||
size="mini"
|
||||
placeholder="手填厂商"
|
||||
style="width:100%"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="64" align="center" fixed="right">
|
||||
<template slot-scope="s">
|
||||
@@ -153,7 +147,7 @@
|
||||
<i class="el-icon-delete pp-del" title="删除" @click="removeItem(s.$index)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template slot="empty"><span>点「加行」添加,或在上方选择销售合同载入明细</span></template>
|
||||
<template slot="empty"><span>点「加行」添加采购要求</span></template>
|
||||
</el-table>
|
||||
|
||||
<el-form-item label="备注" prop="remark" style="margin-top:14px">
|
||||
@@ -183,10 +177,10 @@
|
||||
</div>
|
||||
|
||||
<div class="pp-meta">
|
||||
<div class="pp-meta-i"><label>供货商</label><span>{{ current.supplier || '—' }}</span></div>
|
||||
<div class="pp-meta-i"><label>出卖方</label><span>{{ current.supplier || '—' }}</span></div>
|
||||
<div class="pp-meta-i"><label>采购日期</label><span>{{ current.purchaseDate || '—' }}</span></div>
|
||||
<div class="pp-meta-i"><label>关联合同</label><span>{{ (current.contractCodes || []).join('、') || '—' }}</span></div>
|
||||
<div class="pp-meta-i"><label>计划重量</label><span>{{ current.planWeight }} T</span></div>
|
||||
<div class="pp-meta-i"><label>关联销售合同</label><span>{{ (current.contractCodes || []).join('、') || '—' }}</span></div>
|
||||
<div class="pp-meta-i"><label>要求总重量</label><span>{{ current.planWeight }} T</span></div>
|
||||
<div class="pp-meta-i"><label>已到货</label><span>{{ current.arrivedWeight }} T</span></div>
|
||||
<div class="pp-meta-i wide">
|
||||
<label>到货进度</label>
|
||||
@@ -215,22 +209,14 @@
|
||||
</div>
|
||||
|
||||
<el-tabs v-model="activeTab" class="pp-tabs">
|
||||
<el-tab-pane label="采购明细" name="items">
|
||||
<el-tab-pane label="采购要求" name="items">
|
||||
<el-table :data="current.items" border size="mini" max-height="340">
|
||||
<el-table-column label="#" type="index" width="40" align="center" />
|
||||
<el-table-column label="产品" prop="productType" min-width="70" show-overflow-tooltip />
|
||||
<el-table-column label="材质" prop="material" min-width="80" show-overflow-tooltip />
|
||||
<el-table-column label="牌号" prop="grade" min-width="75" />
|
||||
<el-table-column label="宽度" prop="width" min-width="70" />
|
||||
<el-table-column label="厚度" prop="thickness" min-width="70" />
|
||||
<el-table-column label="宽公差" prop="widthTolerance" min-width="72" />
|
||||
<el-table-column label="厚公差" prop="thicknessTolerance" min-width="72" />
|
||||
<el-table-column label="重量(T)" prop="weight" min-width="78" align="right" />
|
||||
<el-table-column label="数量" prop="quantity" min-width="60" align="right" />
|
||||
<el-table-column label="供货商" prop="supplier" min-width="90" show-overflow-tooltip />
|
||||
<template slot="empty"><span>无明细</span></template>
|
||||
<el-table-column label="#" type="index" width="44" align="center" />
|
||||
<el-table-column label="规格" prop="spec" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="总重量(T)" prop="weight" min-width="110" align="right" />
|
||||
<el-table-column label="厂商" prop="manufacturer" min-width="180" show-overflow-tooltip />
|
||||
<template slot="empty"><span>无采购要求</span></template>
|
||||
</el-table>
|
||||
<p class="pp-deliv-hint" v-if="current.auditStatus === '1'">到货上传与卷号比对请到「到货记录」页面</p>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -321,38 +307,6 @@
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 批量填充明细:设一次,一键填满所有行(留空不覆盖) -->
|
||||
<el-dialog title="批量填充明细" :visible.sync="batchFillOpen" width="560px" append-to-body>
|
||||
<p class="pp-batch-tip">下面填了值的字段会写入<b>全部明细行</b>,留空的不覆盖。常用于整批同产品/材质/规格。</p>
|
||||
<el-form :model="batchFill" label-width="72px" size="small">
|
||||
<el-row :gutter="14">
|
||||
<el-col :span="12"><el-form-item label="产品"><el-input v-model="batchFill.productType" placeholder="如 热轧卷板" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="材质"><el-input v-model="batchFill.material" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="牌号"><el-input v-model="batchFill.grade" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="宽度"><el-input v-model="batchFill.width" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="厚度"><el-input v-model="batchFill.thickness" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="宽公差"><el-input v-model="batchFill.widthTolerance" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="厚公差"><el-input v-model="batchFill.thicknessTolerance" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="重量(T)"><el-input v-model="batchFill.weight" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="数量"><el-input v-model="batchFill.quantity" /></el-form-item></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供货商">
|
||||
<div class="pp-inline-pick">
|
||||
<el-input v-model="batchFill.supplier" placeholder="选择或输入" />
|
||||
<el-button icon="el-icon-search" @click="openSupplierPicker">选择</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer" class="pp-batch-footer">
|
||||
<el-checkbox v-model="batchFillNewRow">设为「加行」默认值</el-checkbox>
|
||||
<span class="pp-batch-gen">生成 <el-input v-model="batchRows" size="mini" style="width:64px" /> 行</span>
|
||||
<el-button @click="batchFillOpen = false">取消</el-button>
|
||||
<el-button @click="applyBatchFill">应用到现有行</el-button>
|
||||
<el-button type="primary" @click="generateRows">生成行并填充</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -361,12 +315,12 @@ import {
|
||||
listPurchasePlan,
|
||||
purchasePlanStatistics,
|
||||
getPurchasePlan,
|
||||
getItemsByOrders,
|
||||
addPurchasePlan,
|
||||
updatePurchasePlan,
|
||||
delPurchasePlan,
|
||||
submitPurchasePlan,
|
||||
listContracts
|
||||
listContracts,
|
||||
listManufacturers
|
||||
} from '@/api/erp/purchasePlan'
|
||||
import { listSupplier } from '@/api/erp/purchase'
|
||||
|
||||
@@ -383,16 +337,12 @@ export default {
|
||||
current: {},
|
||||
activeTab: 'items',
|
||||
form: {},
|
||||
rules: {},
|
||||
rules: {
|
||||
supplier: [{ required: true, message: '请选择出卖方', trigger: 'change' }]
|
||||
},
|
||||
buttonLoading: false,
|
||||
submitLoading: false,
|
||||
selectedContracts: [],
|
||||
// 批量填充
|
||||
batchFillOpen: false,
|
||||
batchFillNewRow: true,
|
||||
batchRows: 0,
|
||||
batchFill: { productType: '热轧卷板', material: '', grade: '', width: '', thickness: '', widthTolerance: '', thicknessTolerance: '', weight: '', quantity: '', supplier: '' },
|
||||
newRowDefaults: null,
|
||||
// 合同选择器
|
||||
pickerOpen: false,
|
||||
pickerLoading: false,
|
||||
@@ -450,9 +400,6 @@ export default {
|
||||
resetForm() {
|
||||
this.form = { planId: null, planNo: '', supplier: '', purchaseDate: '', remark: '', items: [], orderIds: [], contractCodes: [] }
|
||||
this.selectedContracts = []
|
||||
this.newRowDefaults = null
|
||||
this.batchFill = { productType: '热轧卷板', material: '', grade: '', width: '', thickness: '', widthTolerance: '', thicknessTolerance: '', weight: '', quantity: '', supplier: '' }
|
||||
this.batchRows = 0
|
||||
},
|
||||
handleAdd() {
|
||||
this.resetForm()
|
||||
@@ -514,12 +461,10 @@ export default {
|
||||
this.form.orderIds = this.pickerSelection.map(c => c.orderId)
|
||||
this.form.contractCodes = []
|
||||
this.pickerOpen = false
|
||||
this.deriveItems(this.form.orderIds)
|
||||
},
|
||||
removeContract(orderId) {
|
||||
this.selectedContracts = this.selectedContracts.filter(c => c.orderId !== orderId)
|
||||
this.form.orderIds = this.form.orderIds.filter(id => id !== orderId)
|
||||
this.deriveItems(this.form.orderIds)
|
||||
},
|
||||
// ---- 供应商选择器 ----
|
||||
openSupplierPicker() {
|
||||
@@ -540,39 +485,23 @@ export default {
|
||||
})
|
||||
},
|
||||
chooseSupplier(row) {
|
||||
// 供应商选择器服务于「批量填充」对话框
|
||||
this.batchFill.supplier = row.name
|
||||
// 出卖方 = 供应商
|
||||
this.form.supplier = row.name
|
||||
this.supplierPickerOpen = false
|
||||
},
|
||||
deriveItems(orderIds) {
|
||||
if (!orderIds || !orderIds.length) { this.form.items = []; return }
|
||||
getItemsByOrders(orderIds).then(res => {
|
||||
this.form.items = (res.data || []).map(it => ({
|
||||
productType: it.productType || '热轧卷板',
|
||||
material: it.material || '',
|
||||
grade: it.grade || '',
|
||||
width: it.width || '',
|
||||
thickness: it.thickness || '',
|
||||
widthTolerance: it.widthTolerance || '0',
|
||||
thicknessTolerance: it.thicknessTolerance || '0',
|
||||
weight: it.weight,
|
||||
quantity: it.quantity,
|
||||
supplier: this.form.supplier || ''
|
||||
}))
|
||||
this.$modal.msgSuccess(`已载入 ${this.form.items.length} 条明细,可继续编辑`)
|
||||
})
|
||||
queryManufacturer(queryString, cb) {
|
||||
listManufacturers(queryString).then(res => {
|
||||
cb((res.data || []).map(v => ({ value: v })))
|
||||
}).catch(() => cb([]))
|
||||
},
|
||||
blankItem() {
|
||||
return { productType: '热轧卷板', material: '', grade: '', width: '', thickness: '', widthTolerance: '0', thicknessTolerance: '0', weight: '', quantity: '', supplier: '' }
|
||||
return { spec: '', weight: '', manufacturer: '' }
|
||||
},
|
||||
addItem() {
|
||||
// 优先用批量默认值,其次继承上一行,最后空行;重量每行不同故清空
|
||||
// 新行继承上一行的厂商(同批多为同厂),规格/重量留空
|
||||
const last = this.form.items[this.form.items.length - 1]
|
||||
let row
|
||||
if (this.newRowDefaults) row = { ...this.newRowDefaults }
|
||||
else if (last) row = { ...last }
|
||||
else row = this.blankItem()
|
||||
row.weight = ''
|
||||
const row = this.blankItem()
|
||||
if (last) row.manufacturer = last.manufacturer || ''
|
||||
this.form.items.push(row)
|
||||
},
|
||||
copyItem(index) {
|
||||
@@ -581,50 +510,10 @@ export default {
|
||||
removeItem(index) {
|
||||
this.form.items.splice(index, 1)
|
||||
},
|
||||
openBatchFill() {
|
||||
this.batchFillOpen = true
|
||||
},
|
||||
batchFillKeys() {
|
||||
const f = this.batchFill
|
||||
const keys = ['productType', 'material', 'grade', 'width', 'thickness', 'widthTolerance', 'thicknessTolerance', 'weight', 'quantity', 'supplier']
|
||||
return keys.filter(k => f[k] !== '' && f[k] != null)
|
||||
},
|
||||
applyBatchFill() {
|
||||
const f = this.batchFill
|
||||
const filled = this.batchFillKeys()
|
||||
if (!filled.length) { this.$modal.msgWarning('请至少填写一个要填充的字段'); return }
|
||||
if (!this.form.items.length) { this.$modal.msgWarning('当前没有明细行,请用右侧「生成行并填充」'); return }
|
||||
this.form.items.forEach(it => { filled.forEach(k => { it[k] = f[k] }) })
|
||||
if (f.supplier) this.form.supplier = f.supplier
|
||||
this.saveNewRowDefaults(filled)
|
||||
this.batchFillOpen = false
|
||||
this.$modal.msgSuccess(`已填充 ${this.form.items.length} 行`)
|
||||
},
|
||||
generateRows() {
|
||||
const n = parseInt(this.batchRows, 10) || 0
|
||||
if (n <= 0) { this.$modal.msgWarning('请填写要生成的行数'); return }
|
||||
const f = this.batchFill
|
||||
const filled = this.batchFillKeys()
|
||||
for (let i = 0; i < n; i++) {
|
||||
const row = this.blankItem()
|
||||
filled.forEach(k => { row[k] = f[k] })
|
||||
this.form.items.push(row)
|
||||
}
|
||||
if (f.supplier) this.form.supplier = f.supplier
|
||||
this.saveNewRowDefaults(filled)
|
||||
this.batchFillOpen = false
|
||||
this.$modal.msgSuccess(`已生成 ${n} 行`)
|
||||
},
|
||||
saveNewRowDefaults(filled) {
|
||||
if (!this.batchFillNewRow) return
|
||||
const def = this.blankItem()
|
||||
filled.forEach(k => { def[k] = this.batchFill[k] })
|
||||
this.newRowDefaults = def
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (!valid) return
|
||||
if (!this.form.orderIds.length) { this.$modal.msgWarning('请先选择销售合同'); return }
|
||||
if (!this.form.items.length) { this.$modal.msgWarning('请至少添加一条采购要求'); return }
|
||||
this.buttonLoading = true
|
||||
const api = this.form.planId ? updatePurchasePlan : addPurchasePlan
|
||||
const editedId = this.form.planId
|
||||
@@ -644,7 +533,7 @@ export default {
|
||||
})
|
||||
},
|
||||
submitForAudit() {
|
||||
const tip = this.current.auditStatus === '2' ? '确认重新送审该计划?将再次进入审核' : '确认送审该计划?送审后将出现在采购审核页'
|
||||
const tip = this.current.auditStatus === '2' ? '确认重新送审该合同?将再次进入审核' : '确认送审该合同?送审后将出现在采购审核页'
|
||||
this.$modal.confirm(tip).then(() => {
|
||||
this.submitLoading = true
|
||||
return submitPurchasePlan(this.current.planId)
|
||||
@@ -655,7 +544,7 @@ export default {
|
||||
}).catch(() => {}).finally(() => { this.submitLoading = false })
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$modal.confirm('确认删除采购计划「' + row.planNo + '」?').then(() => {
|
||||
this.$modal.confirm('确认删除采购合同「' + row.planNo + '」?').then(() => {
|
||||
return delPurchasePlan(row.planId)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
@@ -666,9 +555,6 @@ export default {
|
||||
},
|
||||
auditText(s) {
|
||||
return { '0': '待审核', '1': '已通过', '2': '已驳回', '3': '待送审' }[s] || '—'
|
||||
},
|
||||
itemStatusText(s) {
|
||||
return s === '2' ? '已到货' : '未到货'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,11 +96,6 @@
|
||||
|
||||
<!-- ─── 成品厚度偏差 ─── -->
|
||||
<template v-if="hasGauge">
|
||||
<div class="chart-title">
|
||||
成品厚度偏差
|
||||
<span class="chart-subtitle">产出厚度 - {{ rv('EXIT_THICK') }} [mm]</span>
|
||||
</div>
|
||||
<div ref="chartThick1" class="rpt-chart" />
|
||||
<div class="chart-title">末架出口厚度 [mm]</div>
|
||||
<div ref="chartThick4" class="rpt-chart" />
|
||||
</template>
|
||||
@@ -186,15 +181,24 @@ function calcYRange(vals) {
|
||||
return { min: parseFloat((min - pad).toFixed(4)), max: parseFloat((max + pad).toFixed(4)) }
|
||||
}
|
||||
|
||||
function lineOpt(title, xData, series, yName) {
|
||||
const allVals = series.flatMap(s => s.data)
|
||||
const range = calcYRange(allVals)
|
||||
function lineOpt(title, xData, series, yName, decimals, xStep) {
|
||||
const isValueX = xStep != null
|
||||
const yVals = isValueX
|
||||
? series.flatMap(s => s.data.map(d => Array.isArray(d) ? d[1] : d))
|
||||
: series.flatMap(s => s.data)
|
||||
const range = calcYRange(yVals)
|
||||
const tooltipExtra = decimals != null
|
||||
? { valueFormatter: v => v == null ? '—' : Number(v).toFixed(decimals) }
|
||||
: {}
|
||||
const xAxisCfg = isValueX
|
||||
? { type: 'value', name: '带钢长度[m]', min: 0, interval: xStep, nameTextStyle: { fontSize: 9 }, axisLabel: { fontSize: 9 } }
|
||||
: { type: 'category', data: xData, name: '带钢长度[m]', nameTextStyle: { fontSize: 9 }, axisLabel: { fontSize: 9, interval: 'auto' } }
|
||||
return {
|
||||
title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal', color: '#1a365d' }, top: 4, left: 8 },
|
||||
legend: { top: 4, right: 6, textStyle: { fontSize: 9 }, itemWidth: 12, itemHeight: 8 },
|
||||
tooltip: { trigger: 'axis', confine: true },
|
||||
tooltip: { trigger: 'axis', confine: true, ...tooltipExtra },
|
||||
grid: { top: 38, bottom: 28, left: 10, right: 10, containLabel: true },
|
||||
xAxis: { type: 'category', data: xData, name: '带钢长度[m]', nameTextStyle: { fontSize: 9 }, axisLabel: { fontSize: 9, interval: 'auto' } },
|
||||
xAxis: xAxisCfg,
|
||||
yAxis: { type: 'value', name: yName, nameTextStyle: { fontSize: 9 }, axisLabel: { fontSize: 9 }, min: range.min, max: range.max },
|
||||
series: series.map((s, i) => ({
|
||||
name: s.name, type: 'line', smooth: false, symbol: 'none',
|
||||
@@ -386,39 +390,21 @@ export default {
|
||||
const posLim = parseFloat(getV(this.row, 'EXIT_POS_DEV')) || 0.03
|
||||
const negLim = Math.abs(parseFloat(getV(this.row, 'EXIT_NEG_DEV'))) || 0.03
|
||||
|
||||
// THICK1 偏差图 (1架出口 vs 目标)
|
||||
const t1 = colData(rows, 'THICK1')
|
||||
const t1Ref = colData(rows, 'THICK1REF')
|
||||
if (t1.some(v => v != null)) {
|
||||
const c = this.initChart('chartThick1', 180)
|
||||
if (c) {
|
||||
const series = [{ name: '1架出口厚度', data: t1, color: PALETTE[0] }]
|
||||
if (t1Ref.some(v => v != null)) series.push({ name: '目标', data: t1Ref, color: '#909399', dash: true })
|
||||
if (target) {
|
||||
const up = rows.map(() => parseFloat((target + posLim).toFixed(4)))
|
||||
const lo = rows.map(() => parseFloat((target - negLim).toFixed(4)))
|
||||
series.push({ name: '上限', data: up, color: '#F56C6C', dash: true })
|
||||
series.push({ name: '下限', data: lo, color: '#67C23A', dash: true })
|
||||
}
|
||||
c.setOption(lineOpt('1架出口厚度 [mm]', x, series, 'mm'))
|
||||
}
|
||||
}
|
||||
|
||||
// THICK5 末架出口
|
||||
// THICK5 末架出口(value 轴,横坐标步长 500)
|
||||
const xNum = rows.map(r => { const v = getRowVal(r, 'XLOCATION'); return v == null ? null : parseFloat(v) })
|
||||
const t4 = colData(rows, 'THICK5')
|
||||
const t4Ref = colData(rows, 'THICK5REF')
|
||||
const t4Pairs = t4.map((y, i) => [xNum[i], y])
|
||||
if (t4.some(v => v != null)) {
|
||||
const c = this.initChart('chartThick4', 180)
|
||||
if (c) {
|
||||
const series = [{ name: '末架出口厚度', data: t4, color: PALETTE[0] }]
|
||||
if (t4Ref.some(v => v != null)) series.push({ name: '目标', data: t4Ref, color: '#909399', dash: true })
|
||||
const series = [{ name: '末架出口厚度', data: t4Pairs, color: PALETTE[0] }]
|
||||
if (target) {
|
||||
const up = rows.map(() => parseFloat((target + posLim).toFixed(4)))
|
||||
const lo = rows.map(() => parseFloat((target - negLim).toFixed(4)))
|
||||
series.push({ name: '上限', data: up, color: '#F56C6C', dash: true })
|
||||
series.push({ name: '下限', data: lo, color: '#67C23A', dash: true })
|
||||
const upPairs = t4Pairs.map(([xi]) => [xi, parseFloat((target + posLim).toFixed(4))])
|
||||
const loPairs = t4Pairs.map(([xi]) => [xi, parseFloat((target - negLim).toFixed(4))])
|
||||
series.push({ name: '上限', data: upPairs, color: '#F56C6C', dash: true })
|
||||
series.push({ name: '下限', data: loPairs, color: '#67C23A', dash: true })
|
||||
}
|
||||
c.setOption(lineOpt('末架出口厚度 [mm]', x, series, 'mm'))
|
||||
c.setOption(lineOpt('末架出口厚度 [mm]', null, series, 'mm', 3, 500))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user