| New file |
| | |
| | | package com.zy.asrs.controller; |
| | | |
| | | import com.baomidou.mybatisplus.annotations.TableField; |
| | | import com.baomidou.mybatisplus.mapper.EntityWrapper; |
| | | import com.baomidou.mybatisplus.plugins.Page; |
| | | import com.core.common.Cools; |
| | | import com.core.common.R; |
| | | import com.core.annotations.ManagerAuth; |
| | | import com.zy.asrs.entity.MaterialReceive; |
| | | import com.zy.asrs.service.MaterialReceiveService; |
| | | import com.zy.common.web.BaseController; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import com.core.common.DateUtils; |
| | | import java.lang.reflect.Field; |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @RestController |
| | | public class MaterialReceiveController extends BaseController { |
| | | |
| | | @Autowired |
| | | private MaterialReceiveService materialReceiveService; |
| | | |
| | | @RequestMapping(value = "/materialReceive/{id}/auth") |
| | | @ManagerAuth |
| | | public R get(@PathVariable("id") String id) { |
| | | return R.ok(materialReceiveService.selectById(String.valueOf(id))); |
| | | } |
| | | |
| | | @RequestMapping(value = "/materialReceive/list/auth") |
| | | @ManagerAuth |
| | | public R list(@RequestParam(defaultValue = "1") Integer curr, |
| | | @RequestParam(defaultValue = "1000") Integer limit, |
| | | @RequestParam(required = false) String orderByField, |
| | | @RequestParam(required = false) String orderByType, |
| | | @RequestParam(required = false) String condition, |
| | | @RequestParam(required = false) Boolean pdaQuery, |
| | | @RequestParam Map<String, Object> param) { |
| | | EntityWrapper<MaterialReceive> wrapper = new EntityWrapper<>(); |
| | | excludeTrash(param); |
| | | // 移除pdaQuery参数,因为它不是数据库字段,只是控制参数 |
| | | param.remove("pdaQuery"); |
| | | convert(param, wrapper); |
| | | allLike(MaterialReceive.class, param.keySet(), wrapper, condition); |
| | | |
| | | // 参考其他出库模块,PDA出库查询时过滤掉已全部出库的物料(只显示还有剩余数量的) |
| | | // 通过 pdaQuery 参数判断是否是PDA出库查询 |
| | | if (pdaQuery != null && pdaQuery) { |
| | | // PDA出库查询:只显示剩余数量大于0的记录 |
| | | wrapper.gt("remain_qty", 0); |
| | | } |
| | | |
| | | if (!Cools.isEmpty(orderByField)) { |
| | | wrapper.orderBy(humpToLine(orderByField), "asc".equals(orderByType)); |
| | | } |
| | | return R.ok(materialReceiveService.selectPage(new Page<>(curr, limit), wrapper)); |
| | | } |
| | | |
| | | @RequestMapping(value = "/materialReceive/batchSave/auth", method = RequestMethod.POST) |
| | | @ManagerAuth |
| | | public R batchSave(@RequestBody List<Map<String, Object>> dataList) { |
| | | if (Cools.isEmpty(dataList)) { |
| | | return R.error("数据不能为空"); |
| | | } |
| | | int successCount = 0; |
| | | int updateCount = 0; |
| | | for (Map<String, Object> data : dataList) { |
| | | try { |
| | | // 检查是否已存在(根据soCode和invCode和finterid唯一标识) |
| | | String soCode = data.get("soCode") != null ? String.valueOf(data.get("soCode")) : null; |
| | | String invCode = data.get("invCode") != null ? String.valueOf(data.get("invCode")) : null; |
| | | Integer finterid = null; |
| | | if (data.get("finterid") != null) { |
| | | try { |
| | | finterid = Integer.valueOf(String.valueOf(data.get("finterid"))); |
| | | } catch (Exception e) { |
| | | // 忽略转换错误 |
| | | } |
| | | } |
| | | |
| | | MaterialReceive existRecord = null; |
| | | if (soCode != null && invCode != null && finterid != null) { |
| | | existRecord = materialReceiveService.selectOne(new EntityWrapper<MaterialReceive>() |
| | | .eq("so_code", soCode) |
| | | .eq("inv_code", invCode) |
| | | .eq("finterid", finterid)); |
| | | } |
| | | |
| | | MaterialReceive materialReceive; |
| | | if (existRecord != null) { |
| | | // 已存在,更新数据(不覆盖出库相关字段) |
| | | materialReceive = existRecord; |
| | | updateCount++; |
| | | } else { |
| | | // 不存在,创建新记录 |
| | | materialReceive = new MaterialReceive(); |
| | | materialReceive.setCreateTime(new Date()); |
| | | // 初始化出库相关字段 |
| | | materialReceive.setOutQty(BigDecimal.ZERO); |
| | | materialReceive.setRemainQty(data.get("qty") != null ? |
| | | new BigDecimal(String.valueOf(data.get("qty"))) : BigDecimal.ZERO); |
| | | materialReceive.setIsAllOut(0); |
| | | successCount++; |
| | | } |
| | | |
| | | // 更新/设置字段 |
| | | materialReceive.setSoCode(soCode); |
| | | materialReceive.setFbillno(data.get("fbillno") != null ? String.valueOf(data.get("fbillno")) : null); |
| | | materialReceive.setInvCode(invCode); |
| | | materialReceive.setInvName(data.get("invName") != null ? String.valueOf(data.get("invName")) : null); |
| | | materialReceive.setInvStd(data.get("invStd") != null ? String.valueOf(data.get("invStd")) : null); |
| | | |
| | | // 数量字段处理(优先使用fqty,如果没有则使用qty) |
| | | BigDecimal qty = null; |
| | | if (data.get("fqty") != null) { |
| | | qty = new BigDecimal(String.valueOf(data.get("fqty"))); |
| | | materialReceive.setFqty(qty); |
| | | } else if (data.get("qty") != null) { |
| | | qty = new BigDecimal(String.valueOf(data.get("qty"))); |
| | | materialReceive.setQty(qty); |
| | | } |
| | | |
| | | if (data.get("fauxqty") != null) { |
| | | materialReceive.setFauxqty(new BigDecimal(String.valueOf(data.get("fauxqty")))); |
| | | } |
| | | |
| | | // 如果已存在,更新剩余数量(基于原始数量) |
| | | if (existRecord == null && qty != null) { |
| | | materialReceive.setRemainQty(qty); |
| | | } else if (existRecord != null && qty != null) { |
| | | // 已存在时,如果数量有变化,重新计算剩余数量 |
| | | BigDecimal originalQty = existRecord.getFqty() != null ? existRecord.getFqty() : |
| | | (existRecord.getQty() != null ? existRecord.getQty() : BigDecimal.ZERO); |
| | | BigDecimal outQty = existRecord.getOutQty() != null ? existRecord.getOutQty() : BigDecimal.ZERO; |
| | | // 如果新数量与原始数量不同,说明数量更新了,需要重新计算剩余数量 |
| | | if (qty.compareTo(originalQty) != 0) { |
| | | // 数量变化,剩余数量 = 新数量 - 已出库数量 |
| | | materialReceive.setRemainQty(qty.subtract(outQty)); |
| | | } else { |
| | | // 数量没变化,保持原有剩余数量 |
| | | materialReceive.setRemainQty(existRecord.getRemainQty()); |
| | | } |
| | | } |
| | | |
| | | materialReceive.setUnit(data.get("unit") != null ? String.valueOf(data.get("unit")) : null); |
| | | materialReceive.setDepName(data.get("depName") != null ? String.valueOf(data.get("depName")) : null); |
| | | materialReceive.setDepCode(data.get("depCode") != null ? String.valueOf(data.get("depCode")) : null); |
| | | if (data.get("depId") != null) { |
| | | try { |
| | | materialReceive.setDepId(Integer.valueOf(String.valueOf(data.get("depId")))); |
| | | } catch (Exception e) { |
| | | // 忽略转换错误 |
| | | } |
| | | } |
| | | if (data.get("fworkshop") != null) { |
| | | try { |
| | | materialReceive.setFworkshop(Integer.valueOf(String.valueOf(data.get("fworkshop")))); |
| | | } catch (Exception e) { |
| | | // 忽略转换错误 |
| | | } |
| | | } |
| | | if (data.get("whId") != null) { |
| | | try { |
| | | materialReceive.setWhId(Integer.valueOf(String.valueOf(data.get("whId")))); |
| | | } catch (Exception e) { |
| | | // 忽略转换错误 |
| | | } |
| | | } |
| | | materialReceive.setWhName(data.get("whName") != null ? String.valueOf(data.get("whName")) : null); |
| | | if (data.get("fplancommitdate") != null && !Cools.isEmpty(String.valueOf(data.get("fplancommitdate")))) { |
| | | try { |
| | | String dateStr = String.valueOf(data.get("fplancommitdate")); |
| | | java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); |
| | | materialReceive.setFplancommitdate(sdf.parse(dateStr)); |
| | | } catch (Exception ex) { |
| | | // 忽略日期解析错误 |
| | | } |
| | | } |
| | | if (data.get("fplanfinishdate") != null && !Cools.isEmpty(String.valueOf(data.get("fplanfinishdate")))) { |
| | | try { |
| | | String dateStr = String.valueOf(data.get("fplanfinishdate")); |
| | | java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); |
| | | materialReceive.setFplanfinishdate(sdf.parse(dateStr)); |
| | | } catch (Exception ex) { |
| | | // 忽略日期解析错误 |
| | | } |
| | | } |
| | | if (finterid != null) { |
| | | materialReceive.setFinterid(finterid); |
| | | } |
| | | if (data.get("fitemid") != null) { |
| | | try { |
| | | materialReceive.setFitemid(Integer.valueOf(String.valueOf(data.get("fitemid")))); |
| | | } catch (Exception e) { |
| | | // 忽略转换错误 |
| | | } |
| | | } |
| | | if (data.get("funitid") != null) { |
| | | try { |
| | | materialReceive.setFunitid(Integer.valueOf(String.valueOf(data.get("funitid")))); |
| | | } catch (Exception e) { |
| | | // 忽略转换错误 |
| | | } |
| | | } |
| | | |
| | | // 将其他字段存储到ext_data(JSON格式),用于校验生产领料出库数量 |
| | | Map<String, Object> extDataMap = new HashMap<>(); |
| | | // 已映射到数据库表字段的字段列表(这些字段不需要存储在ext_data中) |
| | | java.util.Set<String> mappedFields = new java.util.HashSet<>(); |
| | | mappedFields.add("soCode"); |
| | | mappedFields.add("fbillno"); |
| | | mappedFields.add("invCode"); |
| | | mappedFields.add("invName"); |
| | | mappedFields.add("invStd"); |
| | | mappedFields.add("qty"); |
| | | mappedFields.add("fqty"); |
| | | mappedFields.add("fauxqty"); |
| | | mappedFields.add("unit"); |
| | | mappedFields.add("depName"); |
| | | mappedFields.add("depCode"); |
| | | mappedFields.add("depId"); |
| | | mappedFields.add("fworkshop"); |
| | | mappedFields.add("whId"); |
| | | mappedFields.add("whName"); |
| | | mappedFields.add("fplancommitdate"); |
| | | mappedFields.add("fplanfinishdate"); |
| | | mappedFields.add("finterid"); |
| | | mappedFields.add("fitemid"); |
| | | mappedFields.add("funitid"); |
| | | |
| | | // 将所有未映射的字段存储到ext_data中(包含所有打印预览和打印相关的字段) |
| | | for (Map.Entry<String, Object> entry : data.entrySet()) { |
| | | String key = entry.getKey(); |
| | | if (!mappedFields.contains(key) && entry.getValue() != null) { |
| | | extDataMap.put(key, entry.getValue()); |
| | | } |
| | | } |
| | | |
| | | // 始终更新ext_data,即使为空也要存储(用于后续扩展) |
| | | materialReceive.setExtData(com.alibaba.fastjson.JSON.toJSONString(extDataMap)); |
| | | |
| | | materialReceive.setSyncTime(new Date()); |
| | | materialReceive.setUpdateTime(new Date()); |
| | | |
| | | // 保存或更新 |
| | | if (existRecord != null) { |
| | | materialReceiveService.updateById(materialReceive); |
| | | } else { |
| | | materialReceiveService.insert(materialReceive); |
| | | } |
| | | } catch (Exception e) { |
| | | // 记录失败的记录,继续处理下一条 |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | return R.ok("成功保存 " + successCount + " 条新数据,更新 " + updateCount + " 条已存在数据"); |
| | | } |
| | | |
| | | @RequestMapping(value = "/materialReceive/update/auth") |
| | | @ManagerAuth |
| | | public R update(MaterialReceive materialReceive) { |
| | | if (Cools.isEmpty(materialReceive) || null == materialReceive.getId()) { |
| | | return R.error(); |
| | | } |
| | | materialReceive.setUpdateTime(new Date()); |
| | | materialReceiveService.updateById(materialReceive); |
| | | return R.ok(); |
| | | } |
| | | |
| | | @RequestMapping(value = "/materialReceive/updateOutQty/auth", method = RequestMethod.POST) |
| | | @ManagerAuth |
| | | public R updateOutQty(@RequestBody Map<String, Object> param) { |
| | | Long id = Long.valueOf(String.valueOf(param.get("id"))); |
| | | BigDecimal addOutQty = new BigDecimal(String.valueOf(param.get("outQty"))); // 本次出库数量 |
| | | |
| | | MaterialReceive materialReceive = materialReceiveService.selectById(id); |
| | | if (materialReceive == null) { |
| | | return R.error("记录不存在"); |
| | | } |
| | | |
| | | // 累加出库数量 |
| | | BigDecimal currentOutQty = materialReceive.getOutQty() != null ? materialReceive.getOutQty() : BigDecimal.ZERO; |
| | | BigDecimal newOutQty = currentOutQty.add(addOutQty); |
| | | materialReceive.setOutQty(newOutQty); |
| | | |
| | | // 计算剩余数量(优先使用fqty,如果没有则使用qty) |
| | | BigDecimal qty = materialReceive.getFqty() != null ? materialReceive.getFqty() : |
| | | (materialReceive.getQty() != null ? materialReceive.getQty() : BigDecimal.ZERO); |
| | | BigDecimal remainQty = qty.subtract(newOutQty); |
| | | materialReceive.setRemainQty(remainQty.compareTo(BigDecimal.ZERO) > 0 ? remainQty : BigDecimal.ZERO); |
| | | |
| | | // 判断是否全部出库完成 |
| | | materialReceive.setIsAllOut(remainQty.compareTo(BigDecimal.ZERO) <= 0 ? 1 : 0); |
| | | materialReceive.setUpdateTime(new Date()); |
| | | |
| | | materialReceiveService.updateById(materialReceive); |
| | | return R.ok(); |
| | | } |
| | | |
| | | @RequestMapping(value = "/materialReceive/checkSyncStatus/auth") |
| | | @ManagerAuth |
| | | public R checkSyncStatus(@RequestParam String soCode, @RequestParam(required = false) String invCode) { |
| | | EntityWrapper<MaterialReceive> wrapper = new EntityWrapper<>(); |
| | | wrapper.eq("so_code", soCode); |
| | | if (!Cools.isEmpty(invCode)) { |
| | | wrapper.eq("inv_code", invCode); |
| | | } |
| | | List<MaterialReceive> list = materialReceiveService.selectList(wrapper); |
| | | |
| | | Map<String, Object> result = new HashMap<>(); |
| | | if (Cools.isEmpty(list)) { |
| | | result.put("synced", false); |
| | | result.put("allOut", false); |
| | | result.put("message", "该生产单号未同步到WMS"); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | // 检查是否全部出库完成 |
| | | boolean allOut = true; |
| | | List<Map<String, Object>> notOutList = new ArrayList<>(); |
| | | for (MaterialReceive mr : list) { |
| | | if (mr.getIsAllOut() == null || mr.getIsAllOut() != 1) { |
| | | allOut = false; |
| | | Map<String, Object> notOutItem = new HashMap<>(); |
| | | notOutItem.put("invCode", mr.getInvCode()); |
| | | notOutItem.put("invName", mr.getInvName()); |
| | | notOutItem.put("remainQty", mr.getRemainQty()); |
| | | notOutList.add(notOutItem); |
| | | } |
| | | } |
| | | |
| | | result.put("synced", true); |
| | | result.put("allOut", allOut); |
| | | result.put("records", list); |
| | | if (!allOut && !notOutList.isEmpty()) { |
| | | result.put("notOutList", notOutList); |
| | | result.put("message", "部分物料未全部出库完成"); |
| | | } else if (allOut) { |
| | | result.put("message", "全部物料已出库完成"); |
| | | } |
| | | return R.ok(result); |
| | | } |
| | | |
| | | private void convert(Map<String, Object> map, EntityWrapper<MaterialReceive> wrapper) { |
| | | for (Map.Entry<String, Object> entry : map.entrySet()) { |
| | | String fieldName = entry.getKey(); |
| | | String columnName = getColumnName(MaterialReceive.class, fieldName); |
| | | String val = String.valueOf(entry.getValue()); |
| | | if (val.contains(RANGE_TIME_LINK)) { |
| | | String[] dates = val.split(RANGE_TIME_LINK); |
| | | wrapper.ge(columnName, DateUtils.convert(dates[0])); |
| | | wrapper.le(columnName, DateUtils.convert(dates[1])); |
| | | } else { |
| | | wrapper.like(columnName, val); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据实体类字段名获取数据库列名 |
| | | * @param cls 实体类 |
| | | * @param fieldName Java字段名(驼峰命名) |
| | | * @return 数据库列名(下划线命名) |
| | | */ |
| | | private String getColumnName(Class<?> cls, String fieldName) { |
| | | for (Field field : Cools.getAllFields(cls)) { |
| | | if (field.getName().equals(fieldName)) { |
| | | if (field.isAnnotationPresent(TableField.class)) { |
| | | TableField annotation = field.getAnnotation(TableField.class); |
| | | if (!annotation.exist()) { |
| | | continue; |
| | | } |
| | | String column = annotation.value(); |
| | | if (!Cools.isEmpty(column)) { |
| | | return column; |
| | | } |
| | | } |
| | | // 如果没有@TableField注解,使用humpToLine转换 |
| | | return humpToLine(fieldName); |
| | | } |
| | | } |
| | | // 如果找不到字段,使用humpToLine转换 |
| | | return humpToLine(fieldName); |
| | | } |
| | | |
| | | @RequestMapping(value = "/materialReceive/delete/auth") |
| | | @ManagerAuth |
| | | public R delete(@RequestParam String param) { |
| | | com.alibaba.fastjson.JSONArray jsonArray = com.alibaba.fastjson.JSONArray.parseArray(param); |
| | | List<MaterialReceive> list = jsonArray.toJavaList(MaterialReceive.class); |
| | | if (Cools.isEmpty(list)) { |
| | | return R.error("请选择要删除的数据"); |
| | | } |
| | | for (MaterialReceive entity : list) { |
| | | materialReceiveService.deleteById(entity.getId()); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotations.TableField; |
| | | import com.baomidou.mybatisplus.annotations.TableId; |
| | | import com.baomidou.mybatisplus.annotations.TableName; |
| | | import com.baomidou.mybatisplus.enums.IdType; |
| | | import com.core.common.Cools; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 生产领料单实体类 |
| | | */ |
| | | @TableName("erp_material_receive") |
| | | @Data |
| | | public class MaterialReceive implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * ID |
| | | */ |
| | | @ApiModelProperty(value = "ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 生产单号 |
| | | */ |
| | | @ApiModelProperty(value = "生产单号") |
| | | @TableField("so_code") |
| | | private String soCode; |
| | | |
| | | /** |
| | | * 物料编码 |
| | | */ |
| | | @ApiModelProperty(value = "物料编码") |
| | | @TableField("inv_code") |
| | | private String invCode; |
| | | |
| | | /** |
| | | * 物料名称 |
| | | */ |
| | | @ApiModelProperty(value = "物料名称") |
| | | @TableField("inv_name") |
| | | private String invName; |
| | | |
| | | /** |
| | | * 规格型号 |
| | | */ |
| | | @ApiModelProperty(value = "规格型号") |
| | | @TableField("inv_std") |
| | | private String invStd; |
| | | |
| | | /** |
| | | * 数量 |
| | | */ |
| | | @ApiModelProperty(value = "数量") |
| | | @TableField("qty") |
| | | private BigDecimal qty; |
| | | |
| | | /** |
| | | * 单位 |
| | | */ |
| | | @ApiModelProperty(value = "单位") |
| | | @TableField("unit") |
| | | private String unit; |
| | | |
| | | /** |
| | | * 部门名称 |
| | | */ |
| | | @ApiModelProperty(value = "部门名称") |
| | | @TableField("dep_name") |
| | | private String depName; |
| | | |
| | | /** |
| | | * 部门编码 |
| | | */ |
| | | @ApiModelProperty(value = "部门编码") |
| | | @TableField("dep_code") |
| | | private String depCode; |
| | | |
| | | /** |
| | | * 部门ID |
| | | */ |
| | | @ApiModelProperty(value = "部门ID") |
| | | @TableField("dep_id") |
| | | private Integer depId; |
| | | |
| | | /** |
| | | * 仓库ID |
| | | */ |
| | | @ApiModelProperty(value = "仓库ID") |
| | | @TableField("wh_id") |
| | | private Integer whId; |
| | | |
| | | /** |
| | | * 仓库名称 |
| | | */ |
| | | @ApiModelProperty(value = "仓库名称") |
| | | @TableField("wh_name") |
| | | private String whName; |
| | | |
| | | /** |
| | | * 计划开工日期 |
| | | */ |
| | | @ApiModelProperty(value = "计划开工日期") |
| | | @TableField("fplancommitdate") |
| | | private Date fplancommitdate; |
| | | |
| | | /** |
| | | * ERP内部ID |
| | | */ |
| | | @ApiModelProperty(value = "ERP内部ID") |
| | | @TableField("finterid") |
| | | private Integer finterid; |
| | | |
| | | /** |
| | | * 出库数量 |
| | | */ |
| | | @ApiModelProperty(value = "出库数量") |
| | | @TableField("out_qty") |
| | | private BigDecimal outQty; |
| | | |
| | | /** |
| | | * 剩余数量 |
| | | */ |
| | | @ApiModelProperty(value = "剩余数量") |
| | | @TableField("remain_qty") |
| | | private BigDecimal remainQty; |
| | | |
| | | /** |
| | | * 是否全部出库完成 0-未完成 1-已完成 |
| | | */ |
| | | @ApiModelProperty(value = "是否全部出库完成") |
| | | @TableField("is_all_out") |
| | | private Integer isAllOut; |
| | | |
| | | /** |
| | | * 同步时间 |
| | | */ |
| | | @ApiModelProperty(value = "同步时间") |
| | | @TableField("sync_time") |
| | | private Date syncTime; |
| | | |
| | | /** |
| | | * 创建时间 |
| | | */ |
| | | @ApiModelProperty(value = "创建时间") |
| | | @TableField("create_time") |
| | | private Date createTime; |
| | | |
| | | /** |
| | | * 更新时间 |
| | | */ |
| | | @ApiModelProperty(value = "更新时间") |
| | | @TableField("update_time") |
| | | private Date updateTime; |
| | | |
| | | /** |
| | | * 任务单号 |
| | | */ |
| | | @ApiModelProperty(value = "任务单号") |
| | | @TableField("fbillno") |
| | | private String fbillno; |
| | | |
| | | /** |
| | | * 物料ID |
| | | */ |
| | | @ApiModelProperty(value = "物料ID") |
| | | @TableField("fitemid") |
| | | private Integer fitemid; |
| | | |
| | | /** |
| | | * 数量(FQty) |
| | | */ |
| | | @ApiModelProperty(value = "数量(FQty)") |
| | | @TableField("fqty") |
| | | private BigDecimal fqty; |
| | | |
| | | /** |
| | | * 辅助数量 |
| | | */ |
| | | @ApiModelProperty(value = "辅助数量") |
| | | @TableField("fauxqty") |
| | | private BigDecimal fauxqty; |
| | | |
| | | /** |
| | | * 单位ID |
| | | */ |
| | | @ApiModelProperty(value = "单位ID") |
| | | @TableField("funitid") |
| | | private Integer funitid; |
| | | |
| | | /** |
| | | * 车间ID |
| | | */ |
| | | @ApiModelProperty(value = "车间ID") |
| | | @TableField("fworkshop") |
| | | private Integer fworkshop; |
| | | |
| | | /** |
| | | * 计划完工日期 |
| | | */ |
| | | @ApiModelProperty(value = "计划完工日期") |
| | | @TableField("fplanfinishdate") |
| | | private Date fplanfinishdate; |
| | | |
| | | /** |
| | | * 扩展数据(JSON格式,存储其他字段) |
| | | */ |
| | | @ApiModelProperty(value = "扩展数据") |
| | | @TableField("ext_data") |
| | | private String extData; |
| | | |
| | | public String getFplancommitdate$() { |
| | | if (Cools.isEmpty(this.fplancommitdate)) { |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd").format(this.fplancommitdate); |
| | | } |
| | | |
| | | public String getSyncTime$() { |
| | | if (Cools.isEmpty(this.syncTime)) { |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.syncTime); |
| | | } |
| | | |
| | | public String getCreateTime$() { |
| | | if (Cools.isEmpty(this.createTime)) { |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime); |
| | | } |
| | | |
| | | public String getUpdateTime$() { |
| | | if (Cools.isEmpty(this.updateTime)) { |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime); |
| | | } |
| | | |
| | | public String getFplanfinishdate$() { |
| | | if (Cools.isEmpty(this.fplanfinishdate)) { |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd").format(this.fplanfinishdate); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.mapper; |
| | | |
| | | import com.zy.asrs.entity.MaterialReceive; |
| | | import com.baomidou.mybatisplus.mapper.BaseMapper; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | @Mapper |
| | | @Repository |
| | | public interface MaterialReceiveMapper extends BaseMapper<MaterialReceive> { |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.service; |
| | | |
| | | import com.zy.asrs.entity.MaterialReceive; |
| | | import com.baomidou.mybatisplus.service.IService; |
| | | |
| | | public interface MaterialReceiveService extends IService<MaterialReceive> { |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.service.impl.ServiceImpl; |
| | | import com.zy.asrs.entity.MaterialReceive; |
| | | import com.zy.asrs.mapper.MaterialReceiveMapper; |
| | | import com.zy.asrs.service.MaterialReceiveService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | @Service("materialReceiveService") |
| | | public class MaterialReceiveServiceImpl extends ServiceImpl<MaterialReceiveMapper, MaterialReceive> implements MaterialReceiveService { |
| | | |
| | | } |
| New file |
| | |
| | | -- 生产领料单表(扩展字段以支持完整数据) |
| | | CREATE TABLE [dbo].[erp_material_receive]( |
| | | [id] [bigint] IDENTITY(1,1) NOT NULL, |
| | | -- 基础字段 |
| | | [so_code] [nvarchar](100) NULL, |
| | | [fbillno] [nvarchar](100) NULL, |
| | | [inv_code] [nvarchar](100) NULL, |
| | | [inv_name] [nvarchar](200) NULL, |
| | | [inv_std] [nvarchar](200) NULL, |
| | | [qty] [decimal](18, 2) NULL, |
| | | [fqty] [decimal](18, 2) NULL, |
| | | [fauxqty] [decimal](18, 2) NULL, |
| | | [unit] [nvarchar](50) NULL, |
| | | [funitid] [int] NULL, |
| | | [dep_name] [nvarchar](100) NULL, |
| | | [dep_code] [nvarchar](50) NULL, |
| | | [dep_id] [int] NULL, |
| | | [fworkshop] [int] NULL, |
| | | [wh_id] [int] NULL, |
| | | [wh_name] [nvarchar](100) NULL, |
| | | [fplancommitdate] [datetime] NULL, |
| | | [fplanfinishdate] [datetime] NULL, |
| | | [finterid] [int] NULL, |
| | | [fitemid] [int] NULL, |
| | | -- 出库相关字段 |
| | | [out_qty] [decimal](18, 2) NULL DEFAULT 0, |
| | | [remain_qty] [decimal](18, 2) NULL DEFAULT 0, |
| | | [is_all_out] [int] NULL DEFAULT 0, |
| | | -- 同步相关字段 |
| | | [sync_time] [datetime] NULL, |
| | | [create_time] [datetime] NULL DEFAULT GETDATE(), |
| | | [update_time] [datetime] NULL DEFAULT GETDATE(), |
| | | -- 扩展字段(用于存储更多ERP字段,JSON格式) |
| | | [ext_data] [nvarchar](max) NULL, |
| | | CONSTRAINT [PK_erp_material_receive] PRIMARY KEY CLUSTERED ([id] ASC) |
| | | ) |
| | | |
| | | -- 创建索引 |
| | | CREATE INDEX [IX_erp_material_receive_so_code] ON [dbo].[erp_material_receive] ([so_code]) |
| | | CREATE INDEX [IX_erp_material_receive_inv_code] ON [dbo].[erp_material_receive] ([inv_code]) |
| | | CREATE INDEX [IX_erp_material_receive_finterid] ON [dbo].[erp_material_receive] ([finterid]) |
| | | CREATE INDEX [IX_erp_material_receive_sync] ON [dbo].[erp_material_receive] ([so_code], [inv_code], [finterid]) |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.zy.asrs.mapper.MaterialReceiveMapper"> |
| | | |
| | | <!-- 通用查询映射结果 --> |
| | | <resultMap id="BaseResultMap" type="com.zy.asrs.entity.MaterialReceive"> |
| | | <id column="id" property="id" /> |
| | | <result column="so_code" property="soCode" /> |
| | | <result column="inv_code" property="invCode" /> |
| | | <result column="inv_name" property="invName" /> |
| | | <result column="inv_std" property="invStd" /> |
| | | <result column="qty" property="qty" /> |
| | | <result column="unit" property="unit" /> |
| | | <result column="dep_name" property="depName" /> |
| | | <result column="dep_code" property="depCode" /> |
| | | <result column="dep_id" property="depId" /> |
| | | <result column="wh_id" property="whId" /> |
| | | <result column="wh_name" property="whName" /> |
| | | <result column="fplancommitdate" property="fplancommitdate" /> |
| | | <result column="finterid" property="finterid" /> |
| | | <result column="out_qty" property="outQty" /> |
| | | <result column="remain_qty" property="remainQty" /> |
| | | <result column="is_all_out" property="isAllOut" /> |
| | | <result column="sync_time" property="syncTime" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="fbillno" property="fbillno" /> |
| | | <result column="fitemid" property="fitemid" /> |
| | | <result column="fqty" property="fqty" /> |
| | | <result column="fauxqty" property="fauxqty" /> |
| | | <result column="funitid" property="funitid" /> |
| | | <result column="fworkshop" property="fworkshop" /> |
| | | <result column="fplanfinishdate" property="fplanfinishdate" /> |
| | | <result column="ext_data" property="extData" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="en"> |
| | | |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <title>生成领料单管理</title> |
| | | <meta name="renderer" content="webkit"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> |
| | | <link rel="stylesheet" href="../../static/evn/index.css"> |
| | | </head> |
| | | |
| | | <body> |
| | | <div id="app"> |
| | | <el-card> |
| | | <!-- 搜索表单 --> |
| | | <el-form :inline="true"> |
| | | <el-form-item label="生产单号"> |
| | | <el-input v-model="tableSearchParam.soCode" placeholder="请输入生产单号"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="任务单号"> |
| | | <el-input v-model="tableSearchParam.fbillno" placeholder="请输入任务单号"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="物料编码"> |
| | | <el-input v-model="tableSearchParam.invCode" placeholder="请输入物料编码"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="物料名称"> |
| | | <el-input v-model="tableSearchParam.invName" placeholder="请输入物料名称"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="部门名称"> |
| | | <el-input v-model="tableSearchParam.depName" placeholder="请输入部门名称"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label=""> |
| | | <el-button type="primary" @click="search" circle> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | </el-button> |
| | | <el-button @click="reset" circle style="margin-left: 10px"> |
| | | <el-icon> |
| | | <Refresh /> |
| | | </el-icon> |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- 工具栏 --> |
| | | <el-form :inline="true" style="display: none;"> |
| | | <el-form-item label=""> |
| | | <el-button type="danger" @click="selectDelete">删除</el-button> |
| | | <el-button type="success" @click="exportData">导出</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- 数据表格 --> |
| | | <el-table :data="tableData" border style="width: 100%" @selection-change="handleSelectionChange" |
| | | max-height="550"> |
| | | <el-table-column type="selection"></el-table-column> |
| | | <el-table-column type="index" width="50"></el-table-column> |
| | | <el-table-column prop="soCode" label="生产单号" width="120" |
| | | :show-overflow-tooltip="true"></el-table-column> |
| | | <el-table-column prop="invCode" label="物料编码" width="120" |
| | | :show-overflow-tooltip="true"></el-table-column> |
| | | <el-table-column prop="invName" label="物料名称" width="200" |
| | | :show-overflow-tooltip="true"></el-table-column> |
| | | <el-table-column prop="invStd" label="规格型号" width="150" |
| | | :show-overflow-tooltip="true"></el-table-column> |
| | | <el-table-column prop="fbillno" label="任务单号" width="120" |
| | | :show-overflow-tooltip="true"></el-table-column> |
| | | <el-table-column prop="qty" label="总数量" width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.fqty || scope.row.qty || 0 }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="unit" label="单位" width="80"></el-table-column> |
| | | <el-table-column prop="depName" label="部门名称" width="120"></el-table-column> |
| | | <el-table-column prop="depCode" label="部门编码" width="120"></el-table-column> |
| | | <el-table-column prop="whName" label="仓库名称" width="120"></el-table-column> |
| | | <el-table-column prop="fplancommitdate$" label="计划开工日期" width="120"></el-table-column> |
| | | <el-table-column prop="fplanfinishdate$" label="计划完工日期" width="120"></el-table-column> |
| | | <el-table-column prop="outQty" label="已出库数量" width="110"> |
| | | <template #default="scope"> |
| | | <span :style="{ color: scope.row.outQty > 0 ? 'green' : 'gray' }"> |
| | | {{ scope.row.outQty || 0 }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="remainQty" label="剩余数量" width="110"> |
| | | <template #default="scope"> |
| | | <span :style="{ color: scope.row.remainQty > 0 ? 'orange' : 'green' }"> |
| | | {{ scope.row.remainQty || 0 }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="isAllOut" label="出库状态" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.isAllOut == 1" type="success">已完成</el-tag> |
| | | <el-tag v-else type="warning">未完成</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="syncTime$" label="同步时间" width="160"></el-table-column> |
| | | <el-table-column prop="createTime$" label="创建时间" width="160"></el-table-column> |
| | | <el-table-column prop="updateTime$" label="更新时间" width="160"></el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <div style="margin-top: 10px"> |
| | | <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" |
| | | :current-page="currentPage" :page-sizes="pageSizes" :page-size="pageSize" |
| | | layout="total, sizes, prev, pager, next, jumper" :total="pageTotal"> |
| | | </el-pagination> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script> |
| | | <script src="../../static/evn/vue.global.js"></script> |
| | | <script src="../../static/evn/element-plus.js"></script> |
| | | <script src="../../static/evn/icons-vue.js"></script> |
| | | <script type="module"> |
| | | // 导入中文语言包 |
| | | import zhCn from '../../static/evn/zh-cn.js'; |
| | | |
| | | const { createApp, ref } = Vue; |
| | | |
| | | const app = createApp({ |
| | | setup() { |
| | | const currentPage = ref(1) |
| | | const pageSizes = ref([15, 20, 30, 50, 100, 200]) |
| | | const pageSize = ref(15) |
| | | const pageTotal = ref(0) |
| | | const tableSearchParam = ref({ |
| | | soCode: null, |
| | | fbillno: null, |
| | | invCode: null, |
| | | invName: null, |
| | | depName: null |
| | | }) |
| | | const tableData = ref([]) |
| | | const selectList = ref([]) |
| | | |
| | | function handleSelectionChange(selection) { |
| | | selectList.value = selection |
| | | } |
| | | |
| | | function handleSizeChange(val) { |
| | | pageSize.value = val |
| | | search() |
| | | } |
| | | |
| | | function handleCurrentChange(val) { |
| | | currentPage.value = val |
| | | search() |
| | | } |
| | | |
| | | function search() { |
| | | const loading = ElementPlus.ElLoading.service({ |
| | | lock: true, |
| | | text: 'Loading', |
| | | background: 'rgba(0, 0, 0, 0.7)', |
| | | }) |
| | | let data = JSON.parse(JSON.stringify(tableSearchParam.value)) |
| | | data.curr = currentPage.value |
| | | data.limit = pageSize.value |
| | | // 移除空值 |
| | | Object.keys(data).forEach(key => { |
| | | if (data[key] === null || data[key] === '' || data[key] === undefined) { |
| | | delete data[key] |
| | | } |
| | | }) |
| | | $.ajax({ |
| | | url: baseUrl + "/materialReceive/list/auth", |
| | | headers: { 'token': localStorage.getItem('token') }, |
| | | data: data, |
| | | dataType: 'json', |
| | | method: 'GET', |
| | | success: function (res) { |
| | | if (res.code == 200) { |
| | | tableData.value = res.data.records || [] |
| | | pageTotal.value = res.data.total || 0 |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | } else { |
| | | ElementPlus.ElMessage({ message: res.msg, type: 'error' }); |
| | | } |
| | | }, |
| | | complete: function () { |
| | | loading.close() |
| | | } |
| | | }); |
| | | } |
| | | |
| | | function selectDelete() { |
| | | if (selectList.value.length === 0) { |
| | | ElementPlus.ElMessage({ |
| | | message: "请先选择要删除的数据", |
| | | type: 'warning' |
| | | }); |
| | | return; |
| | | } |
| | | ElementPlus.ElMessageBox.confirm('确定要删除选中的数据吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | const loading = ElementPlus.ElLoading.service({ |
| | | lock: true, |
| | | text: '删除中...', |
| | | background: 'rgba(0, 0, 0, 0.7)', |
| | | }) |
| | | $.ajax({ |
| | | url: baseUrl + "/materialReceive/delete/auth", |
| | | headers: { 'token': localStorage.getItem('token') }, |
| | | data: { |
| | | param: JSON.stringify(selectList.value) |
| | | }, |
| | | dataType: 'json', |
| | | method: 'POST', |
| | | success: function (res) { |
| | | if (res.code == 200) { |
| | | ElementPlus.ElMessage({ |
| | | message: "删除成功", |
| | | type: 'success' |
| | | }); |
| | | search() |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | } else { |
| | | ElementPlus.ElMessage({ message: res.msg, type: 'error' }); |
| | | } |
| | | }, |
| | | complete: function () { |
| | | loading.close() |
| | | } |
| | | }); |
| | | }).catch(() => { |
| | | // 取消 |
| | | }); |
| | | } |
| | | |
| | | function reset() { |
| | | tableSearchParam.value = { |
| | | soCode: null, |
| | | fbillno: null, |
| | | invCode: null, |
| | | invName: null, |
| | | depName: null |
| | | } |
| | | currentPage.value = 1 |
| | | search() |
| | | } |
| | | |
| | | function exportData() { |
| | | if (selectList.value.length === 0) { |
| | | ElementPlus.ElMessage({ |
| | | message: "请先选择要导出的数据", |
| | | type: 'warning' |
| | | }); |
| | | return; |
| | | } |
| | | // 构建导出数据 |
| | | const exportData = selectList.value.map(item => ({ |
| | | soCode: item.soCode || '', |
| | | invCode: item.invCode || '', |
| | | invName: item.invName || '', |
| | | invStd: item.invStd || '', |
| | | qty: item.qty || 0, |
| | | unit: item.unit || '', |
| | | depName: item.depName || '', |
| | | depCode: item.depCode || '', |
| | | whName: item.whName || '', |
| | | fplancommitdate: item.fplancommitdate$ || '', |
| | | outQty: item.outQty || 0, |
| | | remainQty: item.remainQty || 0, |
| | | isAllOut: item.isAllOut == 1 ? '已完成' : '未完成', |
| | | syncTime: item.syncTime$ || '', |
| | | createTime: item.createTime$ || '', |
| | | updateTime: item.updateTime$ || '' |
| | | })); |
| | | |
| | | // 转换为CSV格式 |
| | | const headers = ['生产单号', '物料编码', '物料名称', '规格型号', '总数量', '单位', '部门名称', '部门编码', '仓库名称', '计划开工日期', '已出库数量', '剩余数量', '出库状态', '同步时间', '创建时间', '更新时间']; |
| | | const csvContent = [ |
| | | headers.join(','), |
| | | ...exportData.map(row => [ |
| | | row.soCode, |
| | | row.invCode, |
| | | row.invName, |
| | | row.invStd, |
| | | row.qty, |
| | | row.unit, |
| | | row.depName, |
| | | row.depCode, |
| | | row.whName, |
| | | row.fplancommitdate, |
| | | row.outQty, |
| | | row.remainQty, |
| | | row.isAllOut, |
| | | row.syncTime, |
| | | row.createTime, |
| | | row.updateTime |
| | | ].map(cell => `"${cell}"`).join(',')) |
| | | ].join('\n'); |
| | | |
| | | // 下载文件 |
| | | const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' }); |
| | | const link = document.createElement('a'); |
| | | const url = URL.createObjectURL(blob); |
| | | link.setAttribute('href', url); |
| | | link.setAttribute('download', '生成领料单数据_' + new Date().getTime() + '.csv'); |
| | | link.style.visibility = 'hidden'; |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | |
| | | ElementPlus.ElMessage({ |
| | | message: "导出成功", |
| | | type: 'success' |
| | | }); |
| | | } |
| | | |
| | | // 初始化加载 |
| | | search() |
| | | |
| | | return { |
| | | currentPage, |
| | | pageSizes, |
| | | pageSize, |
| | | pageTotal, |
| | | tableSearchParam, |
| | | tableData, |
| | | selectList, |
| | | handleSelectionChange, |
| | | handleSizeChange, |
| | | handleCurrentChange, |
| | | search, |
| | | reset, |
| | | selectDelete, |
| | | exportData |
| | | } |
| | | } |
| | | }) |
| | | |
| | | // 配置Element Plus中文语言包 |
| | | app.use(ElementPlus, { |
| | | locale: zhCn |
| | | }) |
| | | |
| | | // 注册所有图标组件 |
| | | for (const [key, component] of Object.entries(ElementPlusIconsVue)) { |
| | | app.component(key, component) |
| | | } |
| | | |
| | | app.mount('#app') |
| | | </script> |
| | | </body> |
| | | |
| | | </html> |
| New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="en"> |
| | | <head> |
| | | <meta charset="UTF-8"> |
| | | <title>生成领料出库</title> |
| | | <link rel="stylesheet" href="../../static/layui/css/layui.css" media="all"> |
| | | <link rel="stylesheet" href="../../static/css/pda.css" media="all"> |
| | | <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script> |
| | | </head> |
| | | <style> |
| | | .form-box span { |
| | | font-size: 16px; |
| | | display: inline-block; |
| | | text-align: right; |
| | | } |
| | | .form-box input { |
| | | width: 165px; |
| | | padding-left: 5px; |
| | | height: 30px; |
| | | border: 1px solid #777777; |
| | | overflow:hidden; |
| | | white-space:nowrap; |
| | | text-overflow:ellipsis; |
| | | } |
| | | .number-tool { |
| | | margin-left: 10px; |
| | | padding: 1px 0 1px 5px; |
| | | display: inline-block; |
| | | width: 120px; |
| | | } |
| | | |
| | | .number-tool:after { |
| | | clear: both; |
| | | content: ""; |
| | | display: table; |
| | | } |
| | | |
| | | .number-tool button { |
| | | background-color: #fff; |
| | | margin-top: 3px; |
| | | font-size: 16px; |
| | | height: 25px; |
| | | float: left; |
| | | width: 25px; |
| | | border: 1px solid #777777; |
| | | } |
| | | |
| | | .number-tool input { |
| | | text-align: center; |
| | | height: 30px; |
| | | float: left; |
| | | margin: 0 5px; |
| | | width: 50px; |
| | | padding: 0; |
| | | } |
| | | </style> |
| | | <body> |
| | | |
| | | <header class="layui-form"> |
| | | <div class="layui-input-inline"> |
| | | <label class="layui-form-label" >生产单号</label> |
| | | <div class="layui-input-inline"> |
| | | <input id="soCode" class="layui-input" autocomplete="off" oninput="findCode(this, 'soCode')" |
| | | placeholder="扫码 / 输入" style="width: 175px"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-input-inline"> |
| | | <label class="layui-form-label">任务单号</label> |
| | | <input class="layui-input" id="fbillno" onkeyup="findCode(this, 'fbillno')" placeholder="扫码 / 输入" |
| | | style="width: 175px" |
| | | autocomplete="off"> |
| | | </div> |
| | | <div class="layui-input-inline"> |
| | | <label class="layui-form-label">物料编码</label> |
| | | <input class="layui-input" id="invCode" onkeyup="findCode(this, 'invCode')" placeholder="扫码 / 输入" |
| | | style="width: 175px" |
| | | autocomplete="off"> |
| | | </div> |
| | | </header> |
| | | |
| | | <main> |
| | | <table class="layui-table" id="materialReceiveOut" lay-filter="materialReceiveOut"></table> |
| | | </main> |
| | | |
| | | <footer> |
| | | <div class="layui-btn-container"> |
| | | <button type="button" id="reset-btn" class="layui-btn layui-btn-primary" onclick="reset()">重置</button> |
| | | <button type="button" id="out-btn" class="layui-btn layui-btn-normal " onclick="materialReceiveOut()" |
| | | style="margin-left: 20px">出库 |
| | | </button> |
| | | <button type="button" id="retrun-btn" class="layui-btn layui-btn-primary " onclick="back()" |
| | | style="margin-left: 20px">返回 |
| | | </button> |
| | | <span id="tips"></span> |
| | | </div> |
| | | </footer> |
| | | |
| | | <!-- 修改数量弹窗 --> |
| | | <div id="modify" style="display: none; padding-top: 10px; text-align: center;"> |
| | | <div class="form-box"> |
| | | <div class="form-item"> |
| | | <table style="display: inline"> |
| | | <tr> |
| | | <td> |
| | | <span style="width: 35px; margin-right: 5px">编码</span> |
| | | </td> |
| | | <td style="text-align: left"> |
| | | <input id="invCode2" type="text" disabled="disabled"> |
| | | </td> |
| | | </tr> |
| | | </table> |
| | | </div> |
| | | <div class="form-item"> |
| | | <table style="display: inline"> |
| | | <tr> |
| | | <td style="vertical-align: top"> |
| | | <span style="width: 35px; margin-right: 5px">名称</span> |
| | | </td> |
| | | <td style="text-align: left"> |
| | | <textarea rows="3" style="resize: none; width: 165px" id="invName2" type="text" disabled="disabled" |
| | | readonly="readonly"></textarea> |
| | | </td> |
| | | </tr> |
| | | </table> |
| | | </div> |
| | | </div> |
| | | <input id="index" type="text" disabled="disabled" style="display: none;"> |
| | | <div class="form-item" style="margin-top: 5px; margin-bottom: 8px;"> |
| | | <span style="vertical-align: middle">数量</span> |
| | | <div class="number-tool" style="vertical-align: middle"> |
| | | <button onclick="reduce()">-</button> |
| | | <input id="outQtyInput" type="number" onchange="fix(this)"> |
| | | <button onclick="add()">+</button> |
| | | </div> |
| | | </div> |
| | | <button id="remove" onclick="remove()">移除</button> |
| | | <button id="confirm" onclick="confirm()">保存</button> |
| | | </div> |
| | | |
| | | </body> |
| | | <script> |
| | | var countLayer; |
| | | // 当前点击物料的最大剩余数量 |
| | | var maxRemainQty; |
| | | // 表格数据 |
| | | var materialReceiveList = []; |
| | | window.onload = function () { |
| | | document.getElementById("soCode").focus(); |
| | | } |
| | | |
| | | /** |
| | | * 提示信息 |
| | | * @param msg 提示内容 |
| | | * @param warn true:红色字体 |
| | | */ |
| | | function tips(msg, warn) { |
| | | layer.msg(msg, {icon: warn ? 2 : 1}) |
| | | } |
| | | |
| | | function back() { |
| | | parent.backIndex(); |
| | | } |
| | | |
| | | var tableIns; |
| | | layui.use(['table', 'form'], function () { |
| | | var table = layui.table; |
| | | var $ = layui.jquery; |
| | | var layer = layui.layer; |
| | | var form = layui.form; |
| | | |
| | | tableIns = table.render({ |
| | | id: 'materialReceiveOut', |
| | | elem: '#materialReceiveOut', |
| | | data: [], |
| | | limit: 500, |
| | | cellMinWidth: 50, |
| | | cols: [[ |
| | | {field: 'invCode', align: 'center', title: '编码', event: 'detail', width: 80}, |
| | | {field: 'fbillno', align: 'center', title: '任务单号', event: 'detail', width: 100}, |
| | | {field: 'invName', align: 'center', title: '名称', event: 'detail'}, |
| | | {field: 'whName', align: 'center', title: '库区', event: 'detail', width: 50}, |
| | | { |
| | | field: 'remainQty', |
| | | align: 'center', |
| | | title: '数量', |
| | | event: 'detail', |
| | | style: 'color: blue', |
| | | event: 'modify', |
| | | style: 'cursor: pointer;color: blue', |
| | | width: 50, |
| | | templet: function(d) { |
| | | // 显示出库数量(如果已修改)或剩余数量 |
| | | // 优先显示修改后的出库数量,如果没有修改则显示剩余数量 |
| | | if (d.outQtyOut !== undefined && d.outQtyOut !== null) { |
| | | return d.outQtyOut; |
| | | } |
| | | return d.remainQty || 0; |
| | | } |
| | | }, |
| | | {type: 'checkbox', fixed: 'right', width: 30}, |
| | | ]], |
| | | done: function (res, curr, count) { |
| | | } |
| | | }); |
| | | |
| | | // 监听行工具事件 |
| | | table.on('tool(materialReceiveOut)', function (obj) { |
| | | var data = obj.data; |
| | | switch (obj.event) { |
| | | case 'modify': |
| | | countLayer = layer.open({ |
| | | type: 1, |
| | | offset: '20px', |
| | | title: '修改数量', |
| | | shadeClose: true, |
| | | area: ['80%', '240px'], |
| | | content: $("#modify"), |
| | | success: function (layero, index) { |
| | | $('#invCode2').val(data.invCode); |
| | | $('#invName2').val(data.invName); |
| | | $('#index').val(data.id); |
| | | // 显示当前出库数量(如果已修改)或剩余数量 |
| | | var currentOutQty = (data.outQtyOut !== undefined && data.outQtyOut !== null) ? data.outQtyOut : (data.remainQty || 0); |
| | | $('#outQtyInput').val(currentOutQty); |
| | | maxRemainQty = parseFloat(data.remainQty) || 0; |
| | | } |
| | | }); |
| | | break; |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | var soCodeBar; |
| | | var fbillnoBar; |
| | | var invCodeBar; |
| | | |
| | | /* 扫码、输入生产单号、任务单号和物料编码 */ |
| | | function findCode(el, type) { |
| | | soCodeBar = $('#soCode').val(); |
| | | fbillnoBar = $("#fbillno").val(); |
| | | invCodeBar = $("#invCode").val(); |
| | | |
| | | // 去除空格 |
| | | if (soCodeBar) { |
| | | soCodeBar = soCodeBar.trim(); |
| | | } |
| | | if (fbillnoBar) { |
| | | fbillnoBar = fbillnoBar.trim(); |
| | | } |
| | | if (invCodeBar) { |
| | | invCodeBar = invCodeBar.trim(); |
| | | } |
| | | |
| | | switch (type) { |
| | | case 'soCode': |
| | | break; |
| | | case 'fbillno': |
| | | break; |
| | | case 'invCode': |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | // 构建查询参数 |
| | | var params = {}; |
| | | if (soCodeBar) { |
| | | params.soCode = soCodeBar; |
| | | } |
| | | if (fbillnoBar) { |
| | | params.fbillno = fbillnoBar; |
| | | } |
| | | if (invCodeBar) { |
| | | params.invCode = invCodeBar; |
| | | } |
| | | |
| | | // 如果所有查询条件都为空,不执行查询 |
| | | if (!soCodeBar && !fbillnoBar && !invCodeBar) { |
| | | // 清空表格数据 |
| | | tableIns.reload({ |
| | | data: [], |
| | | }); |
| | | materialReceiveList = []; |
| | | return; |
| | | } |
| | | |
| | | // PDA出库查询,添加pdaQuery参数,后端会过滤掉已全部出库的物料 |
| | | params.pdaQuery = true; |
| | | |
| | | $.ajax({ |
| | | url: baseUrl + "/materialReceive/list/auth", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | data: params, |
| | | method: 'GET', |
| | | async: false, |
| | | success: function (res) { |
| | | if (res && res.code === 200) { |
| | | var records = []; |
| | | if (res.data && res.data.records && res.data.records.length > 0) { |
| | | records = res.data.records; |
| | | } else if (res.data && Array.isArray(res.data)) { |
| | | records = res.data; |
| | | } |
| | | // 初始化出库数量字段(初始等于剩余数量,用户可以修改) |
| | | records.map(function (item) { |
| | | // 如果还没有设置出库数量,则初始化为剩余数量 |
| | | if (item.outQtyOut === undefined || item.outQtyOut === null) { |
| | | item.outQtyOut = item.remainQty || 0; |
| | | } |
| | | }); |
| | | tableIns.reload({ |
| | | data: records, |
| | | }); |
| | | materialReceiveList = records; |
| | | } else if (res && res.code === 403) { |
| | | top.location.href = baseUrl + "/pda"; |
| | | } else if (res && res.msg) { |
| | | tips(res.msg, true) |
| | | } else { |
| | | tips("查询失败,请稍后重试", true); |
| | | } |
| | | }, |
| | | error: function(xhr, status, error) { |
| | | console.error("查询失败:", status, error); |
| | | tips("查询失败,请稍后重试", true); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /* 修改数量 */ |
| | | var countDom = $('#outQtyInput'); |
| | | |
| | | function add() { |
| | | if (Number(countDom.val()) >= maxRemainQty) { |
| | | return; |
| | | } |
| | | countDom.val(Number(countDom.val()) + 1); |
| | | } |
| | | |
| | | function reduce() { |
| | | if (Number(countDom.val()) <= 0) { |
| | | return; |
| | | } |
| | | countDom.val(Number(countDom.val()) - 1); |
| | | } |
| | | |
| | | function fix(e) { |
| | | var val = Number(e.value) || 0; |
| | | if (val > maxRemainQty) { |
| | | countDom.val(maxRemainQty); |
| | | } |
| | | } |
| | | |
| | | function remove() { |
| | | var invCode = $('#invCode2').val(); |
| | | var id = $('#index').val(); |
| | | for (var j = 0; j < materialReceiveList.length; j++) { |
| | | if (invCode === materialReceiveList[j].invCode && id == materialReceiveList[j].id) { |
| | | materialReceiveList.splice(j, 1); |
| | | } |
| | | } |
| | | tableIns.reload({data: materialReceiveList}); |
| | | layer.close(countLayer); |
| | | tips("移除成功"); |
| | | } |
| | | |
| | | // 修改数量 |
| | | function confirm() { |
| | | var invCode = $('#invCode2').val(); |
| | | var outQty = parseFloat($('#outQtyInput').val()) || 0; |
| | | var id = $('#index').val(); |
| | | |
| | | if (outQty < 0) { |
| | | tips("数量不能小于0", true); |
| | | return; |
| | | } |
| | | |
| | | if (outQty > maxRemainQty) { |
| | | tips("数量不能超过剩余数量", true); |
| | | return; |
| | | } |
| | | |
| | | // 更新表格中的出库数量 |
| | | for (var j = 0; j < materialReceiveList.length; j++) { |
| | | if (invCode === materialReceiveList[j].invCode && id == materialReceiveList[j].id) { |
| | | materialReceiveList[j].outQtyOut = outQty; |
| | | } |
| | | } |
| | | tableIns.reload({data: materialReceiveList}); |
| | | layer.close(countLayer); |
| | | tips("修改成功"); |
| | | } |
| | | |
| | | /* 生成领料出库 */ |
| | | function materialReceiveOut() { |
| | | var table = layui.table; |
| | | var checkStatus = table.checkStatus('materialReceiveOut'); |
| | | var data = checkStatus.data; |
| | | if (data.length == 0) { |
| | | layer.msg("请选择物料!"); |
| | | return; |
| | | } |
| | | |
| | | // 批量出库 |
| | | var outQtyList = []; |
| | | for (var i = 0; i < data.length; i++) { |
| | | var item = data[i]; |
| | | // 使用修改后的出库数量(outQtyOut),如果没有修改则使用剩余数量 |
| | | var outQty = 0; |
| | | if (item.outQtyOut !== undefined && item.outQtyOut !== null) { |
| | | outQty = parseFloat(item.outQtyOut) || 0; |
| | | } else { |
| | | outQty = parseFloat(item.remainQty) || 0; |
| | | } |
| | | |
| | | if (outQty <= 0) { |
| | | tips("物料 " + item.invCode + " 出库数量必须大于0", true); |
| | | return; |
| | | } |
| | | var remainQty = parseFloat(item.remainQty) || 0; |
| | | if (outQty > remainQty) { |
| | | tips("物料 " + item.invCode + " 出库数量不能超过剩余数量", true); |
| | | return; |
| | | } |
| | | outQtyList.push({ |
| | | id: item.id, |
| | | outQty: outQty |
| | | }); |
| | | } |
| | | |
| | | // 确认出库 |
| | | layer.confirm('确定要出库选中的物料吗?', {icon: 3, title: '提示'}, function(index){ |
| | | // 批量更新出库数量 |
| | | var successCount = 0; |
| | | var failCount = 0; |
| | | for (var i = 0; i < outQtyList.length; i++) { |
| | | $.ajax({ |
| | | url: baseUrl + "/materialReceive/updateOutQty/auth", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | data: JSON.stringify(outQtyList[i]), |
| | | contentType: 'application/json;charset=UTF-8', |
| | | method: 'POST', |
| | | async: false, |
| | | success: function (res) { |
| | | if (res && res.code === 200) { |
| | | successCount++; |
| | | } else { |
| | | failCount++; |
| | | } |
| | | }, |
| | | error: function(xhr, status, error) { |
| | | console.error("出库失败:", status, error); |
| | | failCount++; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | if (successCount > 0) { |
| | | tips("成功出库 " + successCount + " 条记录"); |
| | | // 清空输入框并刷新列表 |
| | | $("#soCode").val(""); |
| | | $("#fbillno").val(""); |
| | | $("#invCode").val(""); |
| | | materialReceiveList = []; |
| | | tableIns.reload({data: materialReceiveList}); |
| | | document.getElementById("soCode").focus(); |
| | | } |
| | | if (failCount > 0) { |
| | | tips("出库失败 " + failCount + " 条记录", true); |
| | | } |
| | | |
| | | layer.close(index); |
| | | }); |
| | | } |
| | | |
| | | function reset() { |
| | | $("#soCode").val(null); |
| | | $("#fbillno").val(null); |
| | | $("#invCode").val(null); |
| | | materialReceiveList = []; |
| | | tableIns.reload({data: materialReceiveList}); |
| | | layer.closeAll(); |
| | | document.getElementById("soCode").focus(); |
| | | } |
| | | </script> |
| | | </html> |