出入库
This commit is contained in:
@@ -75,8 +75,12 @@ public class MatMaterialController extends BaseController {
|
||||
@Log(title = "配料配件基础信息", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody MatMaterialBo bo) {
|
||||
return toAjax(iMatMaterialService.insertByBo(bo));
|
||||
public R<Long> add(@Validated(AddGroup.class) @RequestBody MatMaterialBo bo) {
|
||||
boolean ok = iMatMaterialService.insertByBo(bo);
|
||||
if (!ok) {
|
||||
return R.fail();
|
||||
}
|
||||
return R.ok(bo.getMaterialId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.gear.oa.controller;
|
||||
|
||||
import com.gear.common.annotation.Log;
|
||||
import com.gear.common.annotation.RepeatSubmit;
|
||||
import com.gear.common.core.controller.BaseController;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.domain.R;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import com.gear.common.enums.BusinessType;
|
||||
import com.gear.oa.domain.bo.GearStockIoOrderBo;
|
||||
import com.gear.oa.domain.bo.GearStockIoOrderWithDetailBo;
|
||||
import com.gear.oa.domain.vo.GearStockIoOrderVo;
|
||||
import com.gear.oa.domain.vo.GearStockIoOrderWithDetailVo;
|
||||
import com.gear.oa.service.IGearStockIoOrderService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/gear/stockIoOrder")
|
||||
public class GearStockIoOrderController extends BaseController {
|
||||
|
||||
private final IGearStockIoOrderService stockIoOrderService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<GearStockIoOrderVo> list(GearStockIoOrderBo bo, PageQuery pageQuery) {
|
||||
return stockIoOrderService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
@GetMapping("/{orderId}")
|
||||
public R<GearStockIoOrderVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long orderId) {
|
||||
return R.ok(stockIoOrderService.queryById(orderId));
|
||||
}
|
||||
|
||||
@GetMapping("/withDetail/{orderId}")
|
||||
public R<GearStockIoOrderWithDetailVo> getWithDetail(@NotNull(message = "主键不能为空") @PathVariable Long orderId) {
|
||||
return R.ok(stockIoOrderService.queryWithDetail(orderId));
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit
|
||||
@PostMapping("/withDetail")
|
||||
public R<Long> addWithDetail(@Validated(AddGroup.class) @RequestBody GearStockIoOrderWithDetailBo bo) {
|
||||
return R.ok(stockIoOrderService.createWithDetail(bo));
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit
|
||||
@PutMapping("/withDetail")
|
||||
public R<Void> editWithDetail(@Validated(EditGroup.class) @RequestBody GearStockIoOrderWithDetailBo bo) {
|
||||
stockIoOrderService.updateWithDetail(bo);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{orderIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] orderIds) {
|
||||
return toAjax(stockIoOrderService.deleteWithValidByIds(Arrays.asList(orderIds), true));
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/submit/{orderId}")
|
||||
public R<Void> submit(@NotNull(message = "主键不能为空") @PathVariable Long orderId) {
|
||||
stockIoOrderService.submitOrder(orderId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/audit/{orderId}")
|
||||
public R<Void> audit(@NotNull(message = "主键不能为空") @PathVariable Long orderId) {
|
||||
stockIoOrderService.auditOrder(orderId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/execute/{orderId}")
|
||||
public R<Void> execute(@NotNull(message = "主键不能为空") @PathVariable Long orderId) {
|
||||
stockIoOrderService.executeOrder(orderId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/arrival/{orderId}")
|
||||
public R<Void> arrival(@NotNull(message = "主键不能为空") @PathVariable Long orderId) {
|
||||
stockIoOrderService.confirmArrival(orderId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/finish/{orderId}")
|
||||
public R<Void> finish(@NotNull(message = "主键不能为空") @PathVariable Long orderId) {
|
||||
stockIoOrderService.finishOrder(orderId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit
|
||||
@PostMapping("/cancel/{orderId}")
|
||||
public R<Void> cancel(@NotNull(message = "主键不能为空") @PathVariable Long orderId,
|
||||
@RequestBody(required = false) Map<String, Object> payload) {
|
||||
String reason = payload == null ? "" : String.valueOf(payload.getOrDefault("reason", ""));
|
||||
stockIoOrderService.cancelOrder(orderId, reason);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Log(title = "出入库单据", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit
|
||||
@PostMapping("/reverse/{orderId}")
|
||||
public R<Long> reverse(@NotNull(message = "主键不能为空") @PathVariable Long orderId,
|
||||
@RequestBody(required = false) Map<String, Object> payload) {
|
||||
String reason = payload == null ? "" : String.valueOf(payload.getOrDefault("reason", ""));
|
||||
return R.ok(stockIoOrderService.reverseOrder(orderId, reason));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.gear.oa.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("gear_stock_io_order")
|
||||
public class GearStockIoOrder extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "order_id")
|
||||
private Long orderId;
|
||||
|
||||
private String orderCode;
|
||||
|
||||
private String ioType;
|
||||
|
||||
private String bizType;
|
||||
|
||||
private String sourceType;
|
||||
|
||||
private String sourceNo;
|
||||
|
||||
private Long sourceOrderId;
|
||||
|
||||
private Long responsibleId;
|
||||
|
||||
private String responsibleName;
|
||||
|
||||
private Date planArrivalTime;
|
||||
|
||||
private Date actualArrivalTime;
|
||||
|
||||
private Date planFinishTime;
|
||||
|
||||
private Date actualFinishTime;
|
||||
|
||||
private Integer delayMinutes;
|
||||
|
||||
private String delayReason;
|
||||
|
||||
private String delayStatus;
|
||||
|
||||
private Long warehouseId;
|
||||
|
||||
private Long fromWarehouseId;
|
||||
|
||||
private Long toWarehouseId;
|
||||
|
||||
private String status;
|
||||
|
||||
private String execFlag;
|
||||
|
||||
private String reversalFlag;
|
||||
|
||||
private Long reversalOrderId;
|
||||
|
||||
private String reversalReason;
|
||||
|
||||
private Date reversalTime;
|
||||
|
||||
private String cancelReason;
|
||||
|
||||
private Date cancelTime;
|
||||
|
||||
private String auditBy;
|
||||
|
||||
private Date auditTime;
|
||||
|
||||
private String executeBy;
|
||||
|
||||
private Date executeTime;
|
||||
|
||||
private Long sourceIoId;
|
||||
|
||||
private BigDecimal totalQty;
|
||||
|
||||
private String remark;
|
||||
|
||||
@TableLogic(value = "0", delval = "2")
|
||||
private String delFlag;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.gear.oa.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("gear_stock_io_order_detail")
|
||||
public class GearStockIoOrderDetail extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "detail_id")
|
||||
private Long detailId;
|
||||
|
||||
private Long orderId;
|
||||
|
||||
private Integer lineNo;
|
||||
|
||||
private String itemType;
|
||||
|
||||
private Long itemId;
|
||||
|
||||
private String itemName;
|
||||
|
||||
private String specName;
|
||||
|
||||
private Long warehouseId;
|
||||
|
||||
private Long fromWarehouseId;
|
||||
|
||||
private BigDecimal quantity;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String batchNo;
|
||||
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
private String sourceDetailNo;
|
||||
|
||||
private Long reversalDetailId;
|
||||
|
||||
private String remark;
|
||||
|
||||
@TableLogic(value = "0", delval = "2")
|
||||
private String delFlag;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.gear.oa.domain.bo;
|
||||
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GearStockIoOrderBo extends BaseEntity {
|
||||
|
||||
private Long orderId;
|
||||
|
||||
private String orderCode;
|
||||
|
||||
@NotBlank(message = "出入库类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String ioType;
|
||||
|
||||
@NotBlank(message = "业务类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String bizType;
|
||||
|
||||
private String sourceType;
|
||||
|
||||
private String sourceNo;
|
||||
|
||||
private Long sourceOrderId;
|
||||
|
||||
private Long responsibleId;
|
||||
|
||||
private String responsibleName;
|
||||
|
||||
private Date planArrivalTime;
|
||||
|
||||
private Date actualArrivalTime;
|
||||
|
||||
private Date planFinishTime;
|
||||
|
||||
private Date actualFinishTime;
|
||||
|
||||
private Integer delayMinutes;
|
||||
|
||||
private String delayReason;
|
||||
|
||||
private String delayStatus;
|
||||
|
||||
private Long warehouseId;
|
||||
|
||||
private Long fromWarehouseId;
|
||||
|
||||
private Long toWarehouseId;
|
||||
|
||||
private String status;
|
||||
|
||||
private String execFlag;
|
||||
|
||||
private String reversalFlag;
|
||||
|
||||
private Long reversalOrderId;
|
||||
|
||||
private String reversalReason;
|
||||
|
||||
private Date reversalTime;
|
||||
|
||||
private String cancelReason;
|
||||
|
||||
private Date cancelTime;
|
||||
|
||||
private String auditBy;
|
||||
|
||||
private Date auditTime;
|
||||
|
||||
private String executeBy;
|
||||
|
||||
private Date executeTime;
|
||||
|
||||
private Long sourceIoId;
|
||||
|
||||
private BigDecimal totalQty;
|
||||
|
||||
private String remark;
|
||||
|
||||
private String delFlag;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.gear.oa.domain.bo;
|
||||
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GearStockIoOrderDetailBo extends BaseEntity {
|
||||
|
||||
private Long detailId;
|
||||
|
||||
private Long orderId;
|
||||
|
||||
private Integer lineNo;
|
||||
|
||||
@NotBlank(message = "物料类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String itemType;
|
||||
|
||||
@NotNull(message = "物料ID不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private Long itemId;
|
||||
|
||||
private String itemName;
|
||||
|
||||
private String specName;
|
||||
|
||||
private Long warehouseId;
|
||||
|
||||
private Long fromWarehouseId;
|
||||
|
||||
@NotNull(message = "数量不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private BigDecimal quantity;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String batchNo;
|
||||
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
private String sourceDetailNo;
|
||||
|
||||
private Long reversalDetailId;
|
||||
|
||||
private String remark;
|
||||
|
||||
private String delFlag;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.gear.oa.domain.bo;
|
||||
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class GearStockIoOrderWithDetailBo {
|
||||
|
||||
private Long orderId;
|
||||
|
||||
private String orderCode;
|
||||
|
||||
@NotBlank(message = "出入库类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String ioType;
|
||||
|
||||
@NotBlank(message = "业务类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String bizType;
|
||||
|
||||
private String sourceType;
|
||||
|
||||
private String sourceNo;
|
||||
|
||||
private Long sourceOrderId;
|
||||
|
||||
private Long responsibleId;
|
||||
|
||||
private String responsibleName;
|
||||
|
||||
private Date planArrivalTime;
|
||||
|
||||
private Date planFinishTime;
|
||||
|
||||
private Long warehouseId;
|
||||
|
||||
private Long fromWarehouseId;
|
||||
|
||||
private Long toWarehouseId;
|
||||
|
||||
private String remark;
|
||||
|
||||
private BigDecimal totalQty;
|
||||
|
||||
private List<GearStockIoOrderDetailBo> details;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.gear.oa.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class GearStockIoOrderDetailVo extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long detailId;
|
||||
|
||||
private Long orderId;
|
||||
|
||||
private Integer lineNo;
|
||||
|
||||
private String itemType;
|
||||
|
||||
private Long itemId;
|
||||
|
||||
@ExcelProperty(value = "物料名称")
|
||||
private String itemName;
|
||||
|
||||
private String specName;
|
||||
|
||||
private Long warehouseId;
|
||||
|
||||
private Long fromWarehouseId;
|
||||
|
||||
@ExcelProperty(value = "数量")
|
||||
private BigDecimal quantity;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String batchNo;
|
||||
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
private String sourceDetailNo;
|
||||
|
||||
private Long reversalDetailId;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.gear.oa.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class GearStockIoOrderVo extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelProperty(value = "单据ID")
|
||||
private Long orderId;
|
||||
|
||||
@ExcelProperty(value = "单据编号")
|
||||
private String orderCode;
|
||||
|
||||
@ExcelProperty(value = "出入库类型")
|
||||
private String ioType;
|
||||
|
||||
@ExcelProperty(value = "业务类型")
|
||||
private String bizType;
|
||||
|
||||
private String sourceType;
|
||||
|
||||
private String sourceNo;
|
||||
|
||||
private Long sourceOrderId;
|
||||
|
||||
private Long responsibleId;
|
||||
|
||||
private String responsibleName;
|
||||
|
||||
private Date planArrivalTime;
|
||||
|
||||
private Date actualArrivalTime;
|
||||
|
||||
private Date planFinishTime;
|
||||
|
||||
private Date actualFinishTime;
|
||||
|
||||
private Integer delayMinutes;
|
||||
|
||||
private String delayReason;
|
||||
|
||||
private String delayStatus;
|
||||
|
||||
private Long warehouseId;
|
||||
|
||||
private Long fromWarehouseId;
|
||||
|
||||
private Long toWarehouseId;
|
||||
|
||||
@ExcelProperty(value = "状态")
|
||||
private String status;
|
||||
|
||||
private String execFlag;
|
||||
|
||||
private String reversalFlag;
|
||||
|
||||
private Long reversalOrderId;
|
||||
|
||||
private String reversalReason;
|
||||
|
||||
private Date reversalTime;
|
||||
|
||||
private String cancelReason;
|
||||
|
||||
private Date cancelTime;
|
||||
|
||||
private String auditBy;
|
||||
|
||||
private Date auditTime;
|
||||
|
||||
private String executeBy;
|
||||
|
||||
private Date executeTime;
|
||||
|
||||
private Long sourceIoId;
|
||||
|
||||
private BigDecimal totalQty;
|
||||
|
||||
private String remark;
|
||||
|
||||
private String materialNames;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.gear.oa.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class GearStockIoOrderWithDetailVo {
|
||||
|
||||
private GearStockIoOrderVo order;
|
||||
|
||||
private List<GearStockIoOrderDetailVo> details;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gear.oa.mapper;
|
||||
|
||||
import com.gear.common.core.mapper.BaseMapperPlus;
|
||||
import com.gear.oa.domain.GearStockIoOrderDetail;
|
||||
import com.gear.oa.domain.vo.GearStockIoOrderDetailVo;
|
||||
|
||||
public interface GearStockIoOrderDetailMapper extends BaseMapperPlus<GearStockIoOrderDetailMapper, GearStockIoOrderDetail, GearStockIoOrderDetailVo> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gear.oa.mapper;
|
||||
|
||||
import com.gear.common.core.mapper.BaseMapperPlus;
|
||||
import com.gear.oa.domain.GearStockIoOrder;
|
||||
import com.gear.oa.domain.vo.GearStockIoOrderVo;
|
||||
|
||||
public interface GearStockIoOrderMapper extends BaseMapperPlus<GearStockIoOrderMapper, GearStockIoOrder, GearStockIoOrderVo> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.gear.oa.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
public interface MatMaterialSimpleMapper {
|
||||
|
||||
@Select("select material_id as materialId, material_name as materialName, material_type as materialType, unit as unit, current_stock as currentStock " +
|
||||
"from mat_material where material_id = #{materialId} and del_flag = 0 limit 1")
|
||||
Map<String, Object> selectSnapshot(@Param("materialId") Long materialId);
|
||||
|
||||
@Update("update mat_material set current_stock = ifnull(current_stock, 0) + #{delta} where material_id = #{materialId} and del_flag = 0")
|
||||
int updateStockDelta(@Param("materialId") Long materialId, @Param("delta") BigDecimal delta);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.gear.oa.service;
|
||||
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.oa.domain.bo.GearStockIoOrderBo;
|
||||
import com.gear.oa.domain.bo.GearStockIoOrderWithDetailBo;
|
||||
import com.gear.oa.domain.vo.GearStockIoOrderVo;
|
||||
import com.gear.oa.domain.vo.GearStockIoOrderWithDetailVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IGearStockIoOrderService {
|
||||
|
||||
GearStockIoOrderVo queryById(Long orderId);
|
||||
|
||||
GearStockIoOrderWithDetailVo queryWithDetail(Long orderId);
|
||||
|
||||
TableDataInfo<GearStockIoOrderVo> queryPageList(GearStockIoOrderBo bo, PageQuery pageQuery);
|
||||
|
||||
List<GearStockIoOrderVo> queryList(GearStockIoOrderBo bo);
|
||||
|
||||
Long createWithDetail(GearStockIoOrderWithDetailBo bo);
|
||||
|
||||
void updateWithDetail(GearStockIoOrderWithDetailBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
void submitOrder(Long orderId);
|
||||
|
||||
void auditOrder(Long orderId);
|
||||
|
||||
void executeOrder(Long orderId);
|
||||
|
||||
void confirmArrival(Long orderId);
|
||||
|
||||
void finishOrder(Long orderId);
|
||||
|
||||
void cancelOrder(Long orderId, String reason);
|
||||
|
||||
Long reverseOrder(Long orderId, String reason);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,706 @@
|
||||
package com.gear.oa.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.exception.ServiceException;
|
||||
import com.gear.common.helper.LoginHelper;
|
||||
import com.gear.common.utils.StringUtils;
|
||||
import com.gear.oa.domain.GearStockIoOrder;
|
||||
import com.gear.oa.domain.GearStockIoOrderDetail;
|
||||
import com.gear.oa.domain.bo.GearStockIoOrderBo;
|
||||
import com.gear.oa.domain.bo.GearStockIoOrderDetailBo;
|
||||
import com.gear.oa.domain.bo.GearStockIoOrderWithDetailBo;
|
||||
import com.gear.oa.domain.vo.GearStockIoOrderDetailVo;
|
||||
import com.gear.oa.domain.vo.GearStockIoOrderVo;
|
||||
import com.gear.oa.domain.vo.GearStockIoOrderWithDetailVo;
|
||||
import com.gear.oa.mapper.*;
|
||||
import com.gear.oa.service.IGearStockIoOrderService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class GearStockIoOrderServiceImpl implements IGearStockIoOrderService {
|
||||
|
||||
private final GearStockIoOrderMapper baseMapper;
|
||||
private final GearStockIoOrderDetailMapper detailMapper;
|
||||
private final MatMaterialSimpleMapper matMaterialMapper;
|
||||
|
||||
@Override
|
||||
public GearStockIoOrderVo queryById(Long orderId) {
|
||||
return baseMapper.selectVoById(orderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GearStockIoOrderWithDetailVo queryWithDetail(Long orderId) {
|
||||
GearStockIoOrderVo order = baseMapper.selectVoById(orderId);
|
||||
if (order == null) {
|
||||
return null;
|
||||
}
|
||||
List<GearStockIoOrderDetailVo> details = detailMapper.selectVoList(Wrappers.<GearStockIoOrderDetail>lambdaQuery()
|
||||
.eq(GearStockIoOrderDetail::getOrderId, orderId)
|
||||
.orderByAsc(GearStockIoOrderDetail::getLineNo));
|
||||
|
||||
GearStockIoOrderWithDetailVo vo = new GearStockIoOrderWithDetailVo();
|
||||
vo.setOrder(order);
|
||||
vo.setDetails(details);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<GearStockIoOrderVo> queryPageList(GearStockIoOrderBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<GearStockIoOrder> lqw = buildQueryWrapper(bo);
|
||||
Page<GearStockIoOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
fillMaterialNames(result.getRecords());
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GearStockIoOrderVo> queryList(GearStockIoOrderBo bo) {
|
||||
LambdaQueryWrapper<GearStockIoOrder> lqw = buildQueryWrapper(bo);
|
||||
List<GearStockIoOrderVo> list = baseMapper.selectVoList(lqw);
|
||||
fillMaterialNames(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private void fillMaterialNames(List<GearStockIoOrderVo> orders) {
|
||||
if (orders == null || orders.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<Long> orderIds = new ArrayList<>();
|
||||
for (GearStockIoOrderVo o : orders) {
|
||||
if (o != null && o.getOrderId() != null) {
|
||||
orderIds.add(o.getOrderId());
|
||||
}
|
||||
}
|
||||
if (orderIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<GearStockIoOrderDetail> details = detailMapper.selectList(Wrappers.<GearStockIoOrderDetail>lambdaQuery()
|
||||
.in(GearStockIoOrderDetail::getOrderId, orderIds)
|
||||
.eq(GearStockIoOrderDetail::getDelFlag, "0")
|
||||
.orderByAsc(GearStockIoOrderDetail::getLineNo));
|
||||
if (details == null || details.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, LinkedHashSet<String>> namesMap = new HashMap<>();
|
||||
for (GearStockIoOrderDetail d : details) {
|
||||
if (d == null || d.getOrderId() == null) {
|
||||
continue;
|
||||
}
|
||||
String name = d.getItemName();
|
||||
if (StringUtils.isBlank(name)) {
|
||||
continue;
|
||||
}
|
||||
namesMap.computeIfAbsent(d.getOrderId(), k -> new LinkedHashSet<>()).add(name);
|
||||
}
|
||||
for (GearStockIoOrderVo o : orders) {
|
||||
if (o == null || o.getOrderId() == null) {
|
||||
continue;
|
||||
}
|
||||
LinkedHashSet<String> set = namesMap.get(o.getOrderId());
|
||||
if (set == null || set.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
o.setMaterialNames(String.join("、", set));
|
||||
}
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<GearStockIoOrder> buildQueryWrapper(GearStockIoOrderBo bo) {
|
||||
LambdaQueryWrapper<GearStockIoOrder> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getOrderId() != null, GearStockIoOrder::getOrderId, bo.getOrderId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getOrderCode()), GearStockIoOrder::getOrderCode, bo.getOrderCode());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getIoType()), GearStockIoOrder::getIoType, bo.getIoType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getBizType()), GearStockIoOrder::getBizType, bo.getBizType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), GearStockIoOrder::getStatus, bo.getStatus());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getExecFlag()), GearStockIoOrder::getExecFlag, bo.getExecFlag());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getReversalFlag()), GearStockIoOrder::getReversalFlag, bo.getReversalFlag());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getSourceNo()), GearStockIoOrder::getSourceNo, bo.getSourceNo());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getResponsibleName()), GearStockIoOrder::getResponsibleName, bo.getResponsibleName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getDelayStatus()), GearStockIoOrder::getDelayStatus, bo.getDelayStatus());
|
||||
lqw.orderByDesc(GearStockIoOrder::getCreateTime);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createWithDetail(GearStockIoOrderWithDetailBo bo) {
|
||||
if (bo.getDetails() == null || bo.getDetails().isEmpty()) {
|
||||
throw new ServiceException("单据明细不能为空");
|
||||
}
|
||||
|
||||
GearStockIoOrder order = BeanUtil.toBean(bo, GearStockIoOrder.class);
|
||||
if (StringUtils.isBlank(order.getOrderCode())) {
|
||||
order.setOrderCode("SIOO_" + IdUtil.getSnowflakeNextIdStr());
|
||||
}
|
||||
order.setStatus("0");
|
||||
order.setExecFlag("0");
|
||||
order.setReversalFlag("0");
|
||||
order.setDelayMinutes(0);
|
||||
order.setDelayStatus("0");
|
||||
order.setDelFlag("0");
|
||||
|
||||
BigDecimal totalQty = sumQty(bo.getDetails());
|
||||
order.setTotalQty(totalQty);
|
||||
|
||||
boolean ok = baseMapper.insert(order) > 0;
|
||||
if (!ok) {
|
||||
throw new ServiceException("创建单据失败");
|
||||
}
|
||||
|
||||
insertDetails(order.getOrderId(), bo.getDetails());
|
||||
|
||||
if ("O".equalsIgnoreCase(order.getIoType())) {
|
||||
List<GearStockIoOrderDetail> details = detailMapper.selectList(Wrappers.<GearStockIoOrderDetail>lambdaQuery()
|
||||
.eq(GearStockIoOrderDetail::getOrderId, order.getOrderId())
|
||||
.orderByAsc(GearStockIoOrderDetail::getLineNo));
|
||||
applyMaterialStockChange(order.getIoType(), details);
|
||||
GearStockIoOrder update = new GearStockIoOrder();
|
||||
update.setOrderId(order.getOrderId());
|
||||
update.setExecFlag("1");
|
||||
update.setExecuteBy(LoginHelper.getNickName());
|
||||
update.setExecuteTime(new Date());
|
||||
update.setStatus("1");
|
||||
update.setActualFinishTime(new Date());
|
||||
baseMapper.updateById(update);
|
||||
}
|
||||
return order.getOrderId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateWithDetail(GearStockIoOrderWithDetailBo bo) {
|
||||
if (bo.getOrderId() == null) {
|
||||
throw new ServiceException("单据ID不能为空");
|
||||
}
|
||||
|
||||
GearStockIoOrder dbOrder = baseMapper.selectById(bo.getOrderId());
|
||||
if (dbOrder == null) {
|
||||
throw new ServiceException("单据不存在");
|
||||
}
|
||||
ensureEditable(dbOrder);
|
||||
|
||||
if (bo.getDetails() == null || bo.getDetails().isEmpty()) {
|
||||
throw new ServiceException("单据明细不能为空");
|
||||
}
|
||||
|
||||
GearStockIoOrder update = BeanUtil.toBean(bo, GearStockIoOrder.class);
|
||||
update.setStatus(null);
|
||||
update.setExecFlag(null);
|
||||
update.setReversalFlag(null);
|
||||
update.setReversalOrderId(null);
|
||||
update.setReversalReason(null);
|
||||
update.setReversalTime(null);
|
||||
update.setCancelReason(null);
|
||||
update.setCancelTime(null);
|
||||
update.setAuditBy(null);
|
||||
update.setAuditTime(null);
|
||||
update.setExecuteBy(null);
|
||||
update.setExecuteTime(null);
|
||||
update.setSourceIoId(null);
|
||||
update.setDelayMinutes(null);
|
||||
update.setDelayStatus(null);
|
||||
update.setDelFlag(null);
|
||||
|
||||
BigDecimal totalQty = sumQty(bo.getDetails());
|
||||
update.setTotalQty(totalQty);
|
||||
|
||||
if (baseMapper.updateById(update) <= 0) {
|
||||
throw new ServiceException("更新单据失败");
|
||||
}
|
||||
|
||||
List<GearStockIoOrderDetail> existing = detailMapper.selectList(Wrappers.<GearStockIoOrderDetail>lambdaQuery()
|
||||
.eq(GearStockIoOrderDetail::getOrderId, bo.getOrderId())
|
||||
.orderByAsc(GearStockIoOrderDetail::getLineNo));
|
||||
|
||||
Set<Long> incomingIds = new HashSet<>();
|
||||
int nextLineNo = 1;
|
||||
for (GearStockIoOrderDetailBo d : bo.getDetails()) {
|
||||
if (d == null) {
|
||||
continue;
|
||||
}
|
||||
if (d.getLineNo() == null) {
|
||||
d.setLineNo(nextLineNo++);
|
||||
} else {
|
||||
nextLineNo = Math.max(nextLineNo, d.getLineNo() + 1);
|
||||
}
|
||||
GearStockIoOrderDetail entity = BeanUtil.toBean(d, GearStockIoOrderDetail.class);
|
||||
entity.setOrderId(bo.getOrderId());
|
||||
if (StringUtils.isBlank(entity.getItemType())) {
|
||||
entity.setItemType("material");
|
||||
}
|
||||
if (!"material".equals(entity.getItemType())) {
|
||||
throw new ServiceException("仅支持原料(主材/辅材)出入库");
|
||||
}
|
||||
fillMaterialSnapshotIfNeeded(entity);
|
||||
entity.setUnit(resolveUnit(entity.getUnit(), entity.getItemType(), entity.getItemId()));
|
||||
fillAmount(entity);
|
||||
|
||||
if (entity.getDetailId() == null) {
|
||||
detailMapper.insert(entity);
|
||||
} else {
|
||||
incomingIds.add(entity.getDetailId());
|
||||
detailMapper.updateById(entity);
|
||||
}
|
||||
}
|
||||
|
||||
List<Long> toDelete = new ArrayList<>();
|
||||
for (GearStockIoOrderDetail e : existing) {
|
||||
if (e.getDetailId() != null && !incomingIds.contains(e.getDetailId())) {
|
||||
toDelete.add(e.getDetailId());
|
||||
}
|
||||
}
|
||||
if (!toDelete.isEmpty()) {
|
||||
detailMapper.deleteBatchIds(toDelete);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (Long id : ids) {
|
||||
GearStockIoOrder order = baseMapper.selectById(id);
|
||||
if (order == null) {
|
||||
continue;
|
||||
}
|
||||
if ("1".equals(order.getStatus()) || "1".equals(order.getExecFlag())) {
|
||||
throw new ServiceException("已执行/已完成单据不允许删除");
|
||||
}
|
||||
}
|
||||
for (Long orderId : ids) {
|
||||
detailMapper.delete(Wrappers.<GearStockIoOrderDetail>lambdaQuery().eq(GearStockIoOrderDetail::getOrderId, orderId));
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submitOrder(Long orderId) {
|
||||
GearStockIoOrder order = requireOrder(orderId);
|
||||
ensureNotCanceled(order);
|
||||
Long cnt = detailMapper.selectCount(Wrappers.<GearStockIoOrderDetail>lambdaQuery().eq(GearStockIoOrderDetail::getOrderId, orderId));
|
||||
if (cnt == null || cnt <= 0) {
|
||||
throw new ServiceException("单据明细不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void auditOrder(Long orderId) {
|
||||
GearStockIoOrder order = requireOrder(orderId);
|
||||
ensureNotCanceled(order);
|
||||
if (order.getAuditTime() != null) {
|
||||
return;
|
||||
}
|
||||
GearStockIoOrder update = new GearStockIoOrder();
|
||||
update.setOrderId(orderId);
|
||||
update.setAuditBy(LoginHelper.getNickName());
|
||||
update.setAuditTime(new Date());
|
||||
baseMapper.updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void executeOrder(Long orderId) {
|
||||
GearStockIoOrder order = requireOrder(orderId);
|
||||
ensureNotCanceled(order);
|
||||
if ("1".equals(order.getExecFlag())) {
|
||||
return;
|
||||
}
|
||||
List<GearStockIoOrderDetail> details = detailMapper.selectList(Wrappers.<GearStockIoOrderDetail>lambdaQuery()
|
||||
.eq(GearStockIoOrderDetail::getOrderId, orderId)
|
||||
.orderByAsc(GearStockIoOrderDetail::getLineNo));
|
||||
if (details == null || details.isEmpty()) {
|
||||
throw new ServiceException("单据明细不能为空");
|
||||
}
|
||||
applyMaterialStockChange(order.getIoType(), details);
|
||||
|
||||
GearStockIoOrder update = new GearStockIoOrder();
|
||||
update.setOrderId(orderId);
|
||||
update.setExecFlag("1");
|
||||
update.setExecuteBy(LoginHelper.getNickName());
|
||||
update.setExecuteTime(new Date());
|
||||
update.setStatus("1");
|
||||
update.setActualFinishTime(new Date());
|
||||
baseMapper.updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void confirmArrival(Long orderId) {
|
||||
GearStockIoOrder order = requireOrder(orderId);
|
||||
ensureNotCanceled(order);
|
||||
if (!"I".equalsIgnoreCase(order.getIoType())) {
|
||||
throw new ServiceException("仅入库单支持到货确认");
|
||||
}
|
||||
if ("1".equals(order.getExecFlag())) {
|
||||
return;
|
||||
}
|
||||
List<GearStockIoOrderDetail> details = detailMapper.selectList(Wrappers.<GearStockIoOrderDetail>lambdaQuery()
|
||||
.eq(GearStockIoOrderDetail::getOrderId, orderId)
|
||||
.orderByAsc(GearStockIoOrderDetail::getLineNo));
|
||||
if (details == null || details.isEmpty()) {
|
||||
throw new ServiceException("单据明细不能为空");
|
||||
}
|
||||
applyMaterialStockChange("I", details);
|
||||
GearStockIoOrder update = new GearStockIoOrder();
|
||||
update.setOrderId(orderId);
|
||||
update.setActualArrivalTime(new Date());
|
||||
update.setExecFlag("1");
|
||||
update.setExecuteBy(LoginHelper.getNickName());
|
||||
update.setExecuteTime(new Date());
|
||||
baseMapper.updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void finishOrder(Long orderId) {
|
||||
GearStockIoOrder order = requireOrder(orderId);
|
||||
ensureNotCanceled(order);
|
||||
if ("1".equals(order.getStatus())) {
|
||||
return;
|
||||
}
|
||||
Date finishTime = order.getActualFinishTime() != null ? order.getActualFinishTime() : new Date();
|
||||
|
||||
GearStockIoOrder update = new GearStockIoOrder();
|
||||
update.setOrderId(orderId);
|
||||
update.setStatus("1");
|
||||
update.setActualFinishTime(finishTime);
|
||||
fillDelayOnFinish(order, update, finishTime);
|
||||
baseMapper.updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancelOrder(Long orderId, String reason) {
|
||||
GearStockIoOrder order = requireOrder(orderId);
|
||||
ensureNotCanceled(order);
|
||||
if ("1".equals(order.getExecFlag())) {
|
||||
throw new ServiceException("已执行单据不允许作废");
|
||||
}
|
||||
if ("1".equals(order.getStatus())) {
|
||||
throw new ServiceException("已完成单据不允许作废");
|
||||
}
|
||||
|
||||
GearStockIoOrder update = new GearStockIoOrder();
|
||||
update.setOrderId(orderId);
|
||||
update.setCancelReason(StringUtils.isBlank(reason) ? "" : reason);
|
||||
update.setCancelTime(new Date());
|
||||
update.setDelFlag("2");
|
||||
baseMapper.updateById(update);
|
||||
|
||||
detailMapper.delete(Wrappers.<GearStockIoOrderDetail>lambdaQuery().eq(GearStockIoOrderDetail::getOrderId, orderId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long reverseOrder(Long orderId, String reason) {
|
||||
GearStockIoOrder original = requireOrder(orderId);
|
||||
ensureNotCanceled(original);
|
||||
if ("1".equals(original.getReversalFlag())) {
|
||||
throw new ServiceException("冲销单不允许再冲销");
|
||||
}
|
||||
if (!"1".equals(original.getExecFlag())) {
|
||||
throw new ServiceException("原单未执行,无法冲销");
|
||||
}
|
||||
|
||||
Long exists = baseMapper.selectCount(Wrappers.<GearStockIoOrder>lambdaQuery()
|
||||
.eq(GearStockIoOrder::getReversalFlag, "1")
|
||||
.eq(GearStockIoOrder::getReversalOrderId, orderId));
|
||||
if (exists != null && exists > 0) {
|
||||
throw new ServiceException("该单据已创建冲销单");
|
||||
}
|
||||
|
||||
List<GearStockIoOrderDetail> originalDetails = detailMapper.selectList(Wrappers.<GearStockIoOrderDetail>lambdaQuery()
|
||||
.eq(GearStockIoOrderDetail::getOrderId, orderId)
|
||||
.orderByAsc(GearStockIoOrderDetail::getLineNo));
|
||||
if (originalDetails == null || originalDetails.isEmpty()) {
|
||||
throw new ServiceException("原单明细为空,无法冲销");
|
||||
}
|
||||
|
||||
GearStockIoOrder reversal = new GearStockIoOrder();
|
||||
reversal.setOrderCode("SIOO_R_" + IdUtil.getSnowflakeNextIdStr());
|
||||
reversal.setIoType(reverseIoType(original.getIoType()));
|
||||
reversal.setBizType(original.getBizType());
|
||||
reversal.setSourceType("reversal");
|
||||
reversal.setSourceNo(original.getOrderCode());
|
||||
reversal.setSourceOrderId(original.getOrderId());
|
||||
reversal.setResponsibleId(original.getResponsibleId());
|
||||
reversal.setResponsibleName(original.getResponsibleName());
|
||||
reversal.setWarehouseId(original.getWarehouseId());
|
||||
reversal.setFromWarehouseId(original.getFromWarehouseId());
|
||||
reversal.setToWarehouseId(original.getToWarehouseId());
|
||||
reversal.setStatus("0");
|
||||
reversal.setExecFlag("0");
|
||||
reversal.setReversalFlag("1");
|
||||
reversal.setReversalOrderId(original.getOrderId());
|
||||
reversal.setReversalReason(StringUtils.isBlank(reason) ? "" : reason);
|
||||
reversal.setReversalTime(new Date());
|
||||
reversal.setDelayMinutes(0);
|
||||
reversal.setDelayStatus("0");
|
||||
reversal.setDelFlag("0");
|
||||
reversal.setTotalQty(original.getTotalQty());
|
||||
reversal.setRemark(original.getRemark());
|
||||
baseMapper.insert(reversal);
|
||||
|
||||
int lineNo = 1;
|
||||
for (GearStockIoOrderDetail od : originalDetails) {
|
||||
GearStockIoOrderDetail rd = new GearStockIoOrderDetail();
|
||||
rd.setOrderId(reversal.getOrderId());
|
||||
rd.setLineNo(od.getLineNo() == null ? lineNo++ : od.getLineNo());
|
||||
rd.setItemType(od.getItemType());
|
||||
rd.setItemId(od.getItemId());
|
||||
rd.setItemName(od.getItemName());
|
||||
rd.setSpecName(od.getSpecName());
|
||||
rd.setWarehouseId(od.getWarehouseId());
|
||||
rd.setFromWarehouseId(od.getFromWarehouseId());
|
||||
rd.setQuantity(od.getQuantity());
|
||||
rd.setUnit(od.getUnit());
|
||||
rd.setBatchNo(od.getBatchNo());
|
||||
rd.setUnitPrice(od.getUnitPrice());
|
||||
rd.setAmount(od.getAmount());
|
||||
rd.setSourceDetailNo(od.getSourceDetailNo());
|
||||
rd.setReversalDetailId(od.getDetailId());
|
||||
rd.setRemark(od.getRemark());
|
||||
rd.setDelFlag("0");
|
||||
detailMapper.insert(rd);
|
||||
}
|
||||
|
||||
List<GearStockIoOrderDetail> reversalDetails = detailMapper.selectList(Wrappers.<GearStockIoOrderDetail>lambdaQuery()
|
||||
.eq(GearStockIoOrderDetail::getOrderId, reversal.getOrderId())
|
||||
.orderByAsc(GearStockIoOrderDetail::getLineNo));
|
||||
applyMaterialStockChange(reversal.getIoType(), reversalDetails);
|
||||
|
||||
GearStockIoOrder update = new GearStockIoOrder();
|
||||
update.setOrderId(reversal.getOrderId());
|
||||
update.setAuditBy(LoginHelper.getNickName());
|
||||
update.setAuditTime(new Date());
|
||||
update.setExecFlag("1");
|
||||
update.setExecuteBy(LoginHelper.getNickName());
|
||||
update.setExecuteTime(new Date());
|
||||
update.setStatus("1");
|
||||
update.setActualFinishTime(new Date());
|
||||
baseMapper.updateById(update);
|
||||
return reversal.getOrderId();
|
||||
}
|
||||
|
||||
private GearStockIoOrder requireOrder(Long orderId) {
|
||||
if (orderId == null) {
|
||||
throw new ServiceException("单据ID不能为空");
|
||||
}
|
||||
GearStockIoOrder order = baseMapper.selectById(orderId);
|
||||
if (order == null) {
|
||||
throw new ServiceException("单据不存在");
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
private void ensureNotCanceled(GearStockIoOrder order) {
|
||||
if (order.getCancelTime() != null || "2".equals(order.getDelFlag())) {
|
||||
throw new ServiceException("单据已作废");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureEditable(GearStockIoOrder order) {
|
||||
ensureNotCanceled(order);
|
||||
if ("1".equals(order.getStatus())) {
|
||||
throw new ServiceException("已完成单据不允许修改");
|
||||
}
|
||||
if ("1".equals(order.getExecFlag())) {
|
||||
throw new ServiceException("已执行单据不允许修改");
|
||||
}
|
||||
if (order.getAuditTime() != null) {
|
||||
throw new ServiceException("已审核单据不允许修改");
|
||||
}
|
||||
}
|
||||
|
||||
private void insertDetails(Long orderId, List<GearStockIoOrderDetailBo> details) {
|
||||
int lineNo = 1;
|
||||
for (GearStockIoOrderDetailBo d : details) {
|
||||
if (d == null) {
|
||||
continue;
|
||||
}
|
||||
GearStockIoOrderDetail entity = BeanUtil.toBean(d, GearStockIoOrderDetail.class);
|
||||
entity.setOrderId(orderId);
|
||||
if (entity.getLineNo() == null) {
|
||||
entity.setLineNo(lineNo++);
|
||||
} else {
|
||||
lineNo = Math.max(lineNo, entity.getLineNo() + 1);
|
||||
}
|
||||
if (StringUtils.isBlank(entity.getItemType())) {
|
||||
entity.setItemType("material");
|
||||
}
|
||||
if (!"material".equals(entity.getItemType())) {
|
||||
throw new ServiceException("仅支持原料(主材/辅材)出入库");
|
||||
}
|
||||
entity.setDelFlag("0");
|
||||
fillMaterialSnapshotIfNeeded(entity);
|
||||
entity.setUnit(resolveUnit(entity.getUnit(), entity.getItemType(), entity.getItemId()));
|
||||
fillAmount(entity);
|
||||
detailMapper.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal sumQty(List<GearStockIoOrderDetailBo> details) {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
if (details == null) {
|
||||
return total;
|
||||
}
|
||||
for (GearStockIoOrderDetailBo d : details) {
|
||||
if (d == null || d.getQuantity() == null) {
|
||||
continue;
|
||||
}
|
||||
total = total.add(d.getQuantity());
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private void fillAmount(GearStockIoOrderDetail detail) {
|
||||
if (detail.getQuantity() == null) {
|
||||
detail.setQuantity(BigDecimal.ZERO);
|
||||
}
|
||||
if (detail.getUnitPrice() == null) {
|
||||
detail.setUnitPrice(BigDecimal.ZERO);
|
||||
}
|
||||
if (detail.getAmount() == null) {
|
||||
detail.setAmount(detail.getUnitPrice().multiply(detail.getQuantity()));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillDelayOnFinish(GearStockIoOrder dbOrder, GearStockIoOrder update, Date finishTime) {
|
||||
if ("2".equals(dbOrder.getDelayStatus())) {
|
||||
return;
|
||||
}
|
||||
Date planFinish = dbOrder.getPlanFinishTime();
|
||||
if (planFinish == null || finishTime == null) {
|
||||
update.setDelayMinutes(0);
|
||||
update.setDelayStatus("0");
|
||||
return;
|
||||
}
|
||||
long diffMs = finishTime.getTime() - planFinish.getTime();
|
||||
int minutes = (int) Math.max(0, diffMs / (60_000L));
|
||||
update.setDelayMinutes(minutes);
|
||||
update.setDelayStatus(minutes > 0 ? "1" : "0");
|
||||
}
|
||||
|
||||
private void applyMaterialStockChange(String ioType, List<GearStockIoOrderDetail> details) {
|
||||
if (!"I".equalsIgnoreCase(ioType) && !"O".equalsIgnoreCase(ioType)) {
|
||||
throw new ServiceException("仅支持入库/出库类型");
|
||||
}
|
||||
if (details == null || details.isEmpty()) {
|
||||
throw new ServiceException("单据明细不能为空");
|
||||
}
|
||||
for (GearStockIoOrderDetail d : details) {
|
||||
if (d == null) {
|
||||
continue;
|
||||
}
|
||||
if (!"material".equals(d.getItemType())) {
|
||||
throw new ServiceException("仅支持原料(主材/辅材)出入库");
|
||||
}
|
||||
fillMaterialSnapshotIfNeeded(d);
|
||||
BigDecimal qty = d.getQuantity() == null ? BigDecimal.ZERO : d.getQuantity();
|
||||
if (qty.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
continue;
|
||||
}
|
||||
BigDecimal delta = "I".equalsIgnoreCase(ioType) ? qty : qty.negate();
|
||||
adjustMaterialStock(d.getItemId(), delta);
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveUnit(String preferred, String itemType, Long itemId) {
|
||||
if (StringUtils.isNotBlank(preferred)) {
|
||||
return preferred;
|
||||
}
|
||||
if ("material".equals(itemType)) {
|
||||
Map<String, Object> snapshot = matMaterialMapper.selectSnapshot(itemId);
|
||||
if (snapshot != null) {
|
||||
Object unit = snapshot.get("unit");
|
||||
if (unit != null && StringUtils.isNotBlank(String.valueOf(unit))) {
|
||||
return String.valueOf(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "个";
|
||||
}
|
||||
|
||||
private void fillMaterialSnapshotIfNeeded(GearStockIoOrderDetail d) {
|
||||
if (d.getItemId() == null) {
|
||||
throw new ServiceException("原料ID不能为空");
|
||||
}
|
||||
Map<String, Object> snapshot = matMaterialMapper.selectSnapshot(d.getItemId());
|
||||
if (snapshot == null) {
|
||||
throw new ServiceException("原料不存在:" + d.getItemId());
|
||||
}
|
||||
if (StringUtils.isBlank(d.getItemName())) {
|
||||
Object name = snapshot.get("materialName");
|
||||
if (name != null) {
|
||||
d.setItemName(String.valueOf(name));
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(d.getUnit())) {
|
||||
Object unit = snapshot.get("unit");
|
||||
if (unit != null && StringUtils.isNotBlank(String.valueOf(unit))) {
|
||||
d.setUnit(String.valueOf(unit));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustMaterialStock(Long materialId, BigDecimal delta) {
|
||||
if (delta == null || delta.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> snapshot = matMaterialMapper.selectSnapshot(materialId);
|
||||
if (snapshot == null) {
|
||||
throw new ServiceException("原料不存在:" + materialId);
|
||||
}
|
||||
BigDecimal currentStock = BigDecimal.ZERO;
|
||||
Object cur = snapshot.get("currentStock");
|
||||
if (cur instanceof BigDecimal) {
|
||||
currentStock = (BigDecimal) cur;
|
||||
} else if (cur != null) {
|
||||
currentStock = new BigDecimal(String.valueOf(cur));
|
||||
}
|
||||
BigDecimal after = currentStock.add(delta);
|
||||
if (after.compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new ServiceException("原料库存不足,无法出库");
|
||||
}
|
||||
matMaterialMapper.updateStockDelta(materialId, delta);
|
||||
}
|
||||
|
||||
private String reverseIoType(String ioType) {
|
||||
if ("I".equalsIgnoreCase(ioType)) {
|
||||
return "O";
|
||||
}
|
||||
if ("O".equalsIgnoreCase(ioType)) {
|
||||
return "I";
|
||||
}
|
||||
if ("T".equalsIgnoreCase(ioType)) {
|
||||
return "T";
|
||||
}
|
||||
return ioType;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static <T> T firstNonNull(T... values) {
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
for (T v : values) {
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
98
gear-ui3/src/api/wms/stockIoOrder.js
Normal file
98
gear-ui3/src/api/wms/stockIoOrder.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function listStockIoOrder(query) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getStockIoOrder(orderId) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/' + orderId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getStockIoOrderWithDetail(orderId) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/withDetail/' + orderId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addStockIoOrderWithDetail(data) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/withDetail',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateStockIoOrderWithDetail(data) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/withDetail',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function delStockIoOrder(orderId) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/' + orderId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function submitStockIoOrder(orderId) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/submit/' + orderId,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function auditStockIoOrder(orderId) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/audit/' + orderId,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function executeStockIoOrder(orderId) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/execute/' + orderId,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function arrivalStockIoOrder(orderId) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/arrival/' + orderId,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function finishStockIoOrder(orderId) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/finish/' + orderId,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function cancelStockIoOrder(orderId, reason) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/cancel/' + orderId,
|
||||
method: 'post',
|
||||
data: { reason: reason || '' }
|
||||
})
|
||||
}
|
||||
|
||||
export function reverseStockIoOrder(orderId, reason) {
|
||||
return request({
|
||||
url: '/gear/stockIoOrder/reverse/' + orderId,
|
||||
method: 'post',
|
||||
data: { reason: reason || '' }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 选择弹窗:表格+分页+底部按钮 -->
|
||||
<el-dialog title="选择配料" v-model="open" width="900px" destroy-on-close>
|
||||
<el-dialog title="选择配料" v-model="open" width="900px" destroy-on-close append-to-body>
|
||||
<!-- 检索功能 -->
|
||||
<el-form :model="searchForm" :inline="true" class="mb-4">
|
||||
<el-form-item label="配料名称">
|
||||
@@ -23,14 +23,34 @@
|
||||
<el-option label="辅料" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="配料规格">
|
||||
<el-input v-model="searchForm.spec" placeholder="请输入配料规格" clearable @keyup.enter="fetchMaterialList" />
|
||||
</el-form-item>
|
||||
<el-form-item label="配料型号">
|
||||
<el-input v-model="searchForm.model" placeholder="请输入配料型号" clearable @keyup.enter="fetchMaterialList" />
|
||||
</el-form-item>
|
||||
<el-form-item label="生产厂家">
|
||||
<el-input v-model="searchForm.factory" placeholder="请输入生产厂家" clearable @keyup.enter="fetchMaterialList" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="fetchMaterialList">搜索</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
<el-button v-if="props.allowAdd" type="primary" plain @click="openAdd">新增原料</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="list" style="width: 100%" border stripe @row-click="handleRowSelect" highlight-current-row
|
||||
row-key="materialId" :current-row-key="materialId">
|
||||
<el-table
|
||||
:data="list"
|
||||
style="width: 100%"
|
||||
border
|
||||
stripe
|
||||
@row-click="handleRowSelect"
|
||||
@selection-change="handleSelectionChange"
|
||||
highlight-current-row
|
||||
row-key="materialId"
|
||||
:current-row-key="materialId"
|
||||
>
|
||||
<el-table-column v-if="props.multiple" type="selection" width="55" align="center" />
|
||||
<el-table-column prop="materialName" label="配料名称" min-width="120" align="center" />
|
||||
<el-table-column label="物料类型" width="100" align="center">
|
||||
<template #default="scope">
|
||||
@@ -56,40 +76,111 @@
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button v-if="props.multiple" type="primary" @click="confirmMultiple">确定</el-button>
|
||||
<el-button @click="open = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="新增原料" v-model="addOpen" width="600px" destroy-on-close append-to-body>
|
||||
<el-form ref="addFormRef" :model="addForm" :rules="addRules" label-width="90px">
|
||||
<el-form-item label="原料名称" prop="materialName">
|
||||
<el-input v-model="addForm.materialName" placeholder="请输入原料名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="归属类型" prop="materialType">
|
||||
<el-select v-model="addForm.materialType" placeholder="请选择主材/辅材">
|
||||
<el-option label="主材" :value="2" />
|
||||
<el-option label="辅材" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="规格" prop="spec">
|
||||
<el-input v-model="addForm.spec" placeholder="请输入规格" />
|
||||
</el-form-item>
|
||||
<el-form-item label="型号" prop="model">
|
||||
<el-input v-model="addForm.model" placeholder="请输入型号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="厂家" prop="factory">
|
||||
<el-input v-model="addForm.factory" placeholder="请输入厂家" />
|
||||
</el-form-item>
|
||||
<el-form-item label="单位" prop="unit">
|
||||
<el-input v-model="addForm.unit" placeholder="请输入单位" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="addForm.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :loading="addLoading" type="primary" @click="submitAdd">保存</el-button>
|
||||
<el-button @click="addOpen = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="RawSelector">
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { listMaterial, getMaterial } from '@/api/mat/material';
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { listMaterial, getMaterial, addMaterial } from '@/api/mat/material';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { formatDecimal } from '@/utils/gear'
|
||||
|
||||
const props = defineProps({
|
||||
allowAdd: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// 双向绑定物料ID
|
||||
const materialId = defineModel({
|
||||
type: String,
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
})
|
||||
|
||||
const materialIds = defineModel('materialIds', {
|
||||
type: Array,
|
||||
default: () => []
|
||||
})
|
||||
|
||||
// 计算当前选中的物料信息
|
||||
const currentMaterial = computed(() => {
|
||||
if (!materialId.value) return {};
|
||||
return list.value.find(item => item.materialId === materialId.value) || {};
|
||||
const mid = String(materialId.value)
|
||||
return list.value.find(item => String(item.materialId) === mid) || {};
|
||||
});
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits(['change']);
|
||||
const emit = defineEmits(['change', 'multiChange']);
|
||||
|
||||
// 基础响应式数据
|
||||
const list = ref([]); // 物料列表
|
||||
const open = ref(false); // 弹窗显隐
|
||||
const selectedRows = ref([])
|
||||
const addOpen = ref(false)
|
||||
const addLoading = ref(false)
|
||||
const addFormRef = ref()
|
||||
const addForm = ref({
|
||||
materialName: '',
|
||||
materialType: undefined,
|
||||
spec: '',
|
||||
model: '',
|
||||
factory: '',
|
||||
unit: '',
|
||||
remark: ''
|
||||
})
|
||||
const addRules = {
|
||||
materialName: [{ required: true, message: '原料名称不能为空', trigger: 'blur' }],
|
||||
materialType: [{ required: true, message: '请选择归属类型', trigger: 'change' }]
|
||||
}
|
||||
|
||||
// 搜索表单数据
|
||||
const searchForm = ref({
|
||||
materialName: '',
|
||||
materialType: ''
|
||||
materialType: '',
|
||||
spec: '',
|
||||
model: '',
|
||||
factory: ''
|
||||
});
|
||||
|
||||
// 分页响应式数据(核心新增)
|
||||
@@ -97,6 +188,10 @@ const pageNum = ref(1); // 当前页码
|
||||
const pageSize = ref(10); // 每页条数
|
||||
const total = ref(0); // 总数据条数
|
||||
|
||||
function formatUnit(row) {
|
||||
return row && row.unit ? row.unit : ''
|
||||
}
|
||||
|
||||
// 组件挂载加载列表
|
||||
onMounted(async () => {
|
||||
await fetchMaterialList();
|
||||
@@ -110,7 +205,10 @@ async function fetchMaterialList() {
|
||||
pageNum: pageNum.value,
|
||||
pageSize: pageSize.value,
|
||||
materialName: searchForm.value.materialName,
|
||||
materialType: searchForm.value.materialType
|
||||
materialType: searchForm.value.materialType,
|
||||
spec: searchForm.value.spec,
|
||||
model: searchForm.value.model,
|
||||
factory: searchForm.value.factory
|
||||
};
|
||||
|
||||
const res = await listMaterial(params);
|
||||
@@ -118,9 +216,9 @@ async function fetchMaterialList() {
|
||||
list.value = res.rows;
|
||||
total.value = res.total; // 赋值总条数供分页使用
|
||||
// 分页查询后检查是否存在当前选中的物料ID
|
||||
const isExist = res.rows.some(item => item.materialId === materialId.value);
|
||||
const mid = String(materialId.value || '')
|
||||
const isExist = res.rows.some(item => String(item.materialId) === mid);
|
||||
if (!isExist) {
|
||||
console.log('获取物料详情:', materialId.value);
|
||||
if (!materialId.value) return;
|
||||
const res = await getMaterial(materialId.value);
|
||||
list.value.push(res.data);
|
||||
@@ -135,7 +233,10 @@ async function fetchMaterialList() {
|
||||
function resetSearch() {
|
||||
searchForm.value = {
|
||||
materialName: '',
|
||||
materialType: ''
|
||||
materialType: '',
|
||||
spec: '',
|
||||
model: '',
|
||||
factory: ''
|
||||
};
|
||||
pageNum.value = 1;
|
||||
fetchMaterialList();
|
||||
@@ -143,16 +244,84 @@ function resetSearch() {
|
||||
|
||||
// 表格行选择物料
|
||||
function handleRowSelect(row) {
|
||||
if (row.materialId === materialId.value) return;
|
||||
if (props.multiple) {
|
||||
return
|
||||
}
|
||||
if (String(row.materialId) === String(materialId.value)) return;
|
||||
materialId.value = row.materialId;
|
||||
emit('change', row);
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
function handleSelectionChange(rows) {
|
||||
selectedRows.value = rows || []
|
||||
}
|
||||
|
||||
function confirmMultiple() {
|
||||
const rows = selectedRows.value || []
|
||||
materialIds.value = rows.map(r => r.materialId)
|
||||
emit('multiChange', rows)
|
||||
open.value = false
|
||||
}
|
||||
|
||||
// 清空已选物料
|
||||
function handleClear() {
|
||||
materialId.value = '';
|
||||
emit('change', {});
|
||||
ElMessage.info('已清空配料选择');
|
||||
}
|
||||
</script>
|
||||
|
||||
function openAdd() {
|
||||
addForm.value = {
|
||||
materialName: searchForm.value.materialName || '',
|
||||
materialType: searchForm.value.materialType ? Number(searchForm.value.materialType) : undefined,
|
||||
spec: '',
|
||||
model: '',
|
||||
factory: '',
|
||||
unit: '',
|
||||
remark: ''
|
||||
}
|
||||
addOpen.value = true
|
||||
}
|
||||
|
||||
function submitAdd() {
|
||||
if (!addFormRef.value) {
|
||||
return
|
||||
}
|
||||
addFormRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
addLoading.value = true
|
||||
try {
|
||||
const payload = Object.assign({}, addForm.value)
|
||||
const res = await addMaterial(payload)
|
||||
const newId = res && res.data
|
||||
if (!newId) {
|
||||
ElMessage.success('新增成功')
|
||||
addOpen.value = false
|
||||
await fetchMaterialList()
|
||||
return
|
||||
}
|
||||
materialId.value = newId
|
||||
const detailRes = await getMaterial(newId)
|
||||
const row = detailRes && detailRes.data ? detailRes.data : null
|
||||
if (row) {
|
||||
list.value = [row].concat(list.value.filter(i => String(i.materialId) !== String(newId)))
|
||||
emit('change', row)
|
||||
}
|
||||
ElMessage.success('新增成功')
|
||||
addOpen.value = false
|
||||
open.value = false
|
||||
} catch (e) {
|
||||
ElMessage.error('新增失败,请重试')
|
||||
} finally {
|
||||
addLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
open.value = true
|
||||
}
|
||||
|
||||
defineExpose({ openDialog })
|
||||
</script>
|
||||
|
||||
@@ -68,8 +68,6 @@
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Plus" @click="handleIn(scope.row)">入库</el-button>
|
||||
<el-button link type="primary" icon="Minus" @click="handleOut(scope.row)">出库</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
@@ -461,4 +459,4 @@ function submitFormOut() {
|
||||
}
|
||||
|
||||
getList();
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -72,8 +72,6 @@
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Plus" @click="handleIn(scope.row)">入库</el-button>
|
||||
<el-button link type="primary" icon="Minus" @click="handleOut(scope.row)">出库</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
|
||||
514
gear-ui3/src/views/wms/stockIoOrder/in.vue
Normal file
514
gear-ui3/src/views/wms/stockIoOrder/in.vue
Normal file
@@ -0,0 +1,514 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
|
||||
<el-form-item label="单号" prop="orderCode">
|
||||
<el-input v-model="queryParams.orderCode" placeholder="请输入单据编号" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务类型" prop="bizType">
|
||||
<el-input v-model="queryParams.bizType" placeholder="请输入业务类型" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="责任人" prop="responsibleName">
|
||||
<el-input v-model="queryParams.responsibleName" placeholder="请输入责任人" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="来源单号" prop="sourceNo">
|
||||
<el-input v-model="queryParams.sourceNo" placeholder="请输入来源单号" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" size="mini" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Clock" size="mini" :disabled="single" @click="handleArrival()">到货确认</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="RefreshLeft" size="mini" :disabled="single" @click="handleReverse()">撤回</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="单据编号" align="center" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="物料" align="center" min-width="220">
|
||||
<template #default="scope">
|
||||
<el-tooltip v-if="scope.row.materialNames && String(scope.row.materialNames).length > 16" effect="dark" placement="top">
|
||||
<template #content>
|
||||
<div style="max-width: 420px; white-space: normal; word-break: break-all;">
|
||||
{{ scope.row.materialNames }}
|
||||
</div>
|
||||
</template>
|
||||
<span>{{ String(scope.row.materialNames).slice(0, 16) + '…' }}</span>
|
||||
</el-tooltip>
|
||||
<span v-else>{{ scope.row.materialNames || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="业务类型" align="center" prop="bizType" min-width="120" />
|
||||
<el-table-column label="责任人" align="center" prop="responsibleName" min-width="120" />
|
||||
<el-table-column label="实际到货" align="center" prop="actualArrivalTime" min-width="160" />
|
||||
<el-table-column label="来源单号" align="center" prop="sourceNo" min-width="140" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
|
||||
<template #default="scope">
|
||||
<el-button size="mini" type="text" icon="Document" @click="showDetail(scope.row)">明细</el-button>
|
||||
<el-button size="mini" type="text" icon="Clock" @click="handleArrival(scope.row)">到货确认</el-button>
|
||||
<el-button size="mini" type="text" icon="RefreshLeft" @click="handleReverse(scope.row)">撤回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<el-dialog :title="editTitle" v-model="editOpen" width="1100px" top="5vh" append-to-body>
|
||||
<el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="90px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="类型">
|
||||
<el-tag type="success">入库</el-tag>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="业务类型" prop="bizType">
|
||||
<el-input v-model="editForm.bizType" placeholder="请输入业务类型" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="责任人" prop="responsibleName">
|
||||
<el-input v-model="editForm.responsibleName" placeholder="请输入责任人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="来源单号" prop="sourceNo">
|
||||
<el-input v-model="editForm.sourceNo" placeholder="请输入来源单号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="editForm.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="display:flex;gap:8px;align-items:center;margin-bottom:10px;">
|
||||
<el-button type="primary" plain icon="Plus" size="mini" @click="addDetailRow">新增明细行</el-button>
|
||||
<RawSelector v-model:materialIds="batchMaterialIds" :multiple="true" :allowAdd="true" @multi-change="handleBatchPicked">
|
||||
<template #trigger>
|
||||
<el-button type="primary" plain size="mini">批量选择原料</el-button>
|
||||
</template>
|
||||
</RawSelector>
|
||||
<el-button type="danger" plain icon="Delete" size="mini" :disabled="detailSelection.length === 0" @click="removeDetailRows">删除选中</el-button>
|
||||
<div style="margin-left:auto;">
|
||||
<span>合计数量:{{ totalQtyText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="editDetails" border style="width: 100%" @selection-change="onDetailSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="行号" width="70" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.lineNo }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物料类型" width="100" align="center">
|
||||
<template #default>
|
||||
原料
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="原料" min-width="260" align="center">
|
||||
<template #default="scope">
|
||||
<RawSelector v-model="scope.row.itemId" :allowAdd="true" @change="onMaterialPicked(scope.row, $event)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称快照" min-width="180" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.itemName" placeholder="可选" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" min-width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.quantity" placeholder="请输入数量" @input="recalcAmount(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.unit" placeholder="单位" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="批次号" min-width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.batchNo" placeholder="批次号" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价" min-width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.unitPrice" placeholder="单价" @input="recalcAmount(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" min-width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.amount" placeholder="金额" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="140" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.remark" placeholder="备注" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitEdit">保存</el-button>
|
||||
<el-button @click="editOpen = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="单据明细" v-model="detailOpen" width="1100px" append-to-body>
|
||||
<template v-if="detailData && detailData.order">
|
||||
<el-descriptions :title="'单号:' + (detailData.order.orderCode || '-')" :column="2" border>
|
||||
<el-descriptions-item label="类型">入库</el-descriptions-item>
|
||||
<el-descriptions-item label="业务类型">{{ detailData.order.bizType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="责任人">{{ detailData.order.responsibleName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实际到货">{{ detailData.order.actualArrivalTime || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div style="margin: 12px 0; text-align: right;">
|
||||
<el-button type="warning" size="small" @click="handleArrival(detailData.order)">到货确认</el-button>
|
||||
<el-button type="danger" size="small" @click="handleReverse(detailData.order)">撤回</el-button>
|
||||
</div>
|
||||
<el-table :data="detailData.details || []" border style="width: 100%">
|
||||
<el-table-column label="行号" prop="lineNo" width="70" align="center" />
|
||||
<el-table-column label="原料ID" prop="itemId" width="120" align="center" />
|
||||
<el-table-column label="名称快照" prop="itemName" min-width="160" align="center" />
|
||||
<el-table-column label="数量" prop="quantity" width="120" align="center" />
|
||||
<el-table-column label="单位" prop="unit" width="90" align="center" />
|
||||
<el-table-column label="批次号" prop="batchNo" min-width="120" align="center" />
|
||||
<el-table-column label="备注" prop="remark" min-width="140" align="center" />
|
||||
</el-table>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div style="height:200px;line-height:200px;text-align:center;">未获取到单据数据</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-button @click="detailOpen = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RawSelector from '@/components/RawSelector/index.vue'
|
||||
import {
|
||||
listStockIoOrder,
|
||||
getStockIoOrderWithDetail,
|
||||
addStockIoOrderWithDetail,
|
||||
arrivalStockIoOrder,
|
||||
reverseStockIoOrder
|
||||
} from '@/api/wms/stockIoOrder'
|
||||
|
||||
export default {
|
||||
name: 'StockIoOrderIn',
|
||||
components: { RawSelector },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
buttonLoading: false,
|
||||
ids: [],
|
||||
rows: [],
|
||||
single: true,
|
||||
multiple: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
list: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
orderCode: undefined,
|
||||
ioType: 'I',
|
||||
bizType: undefined,
|
||||
responsibleName: undefined,
|
||||
sourceNo: undefined
|
||||
},
|
||||
editOpen: false,
|
||||
editTitle: '',
|
||||
editForm: {},
|
||||
editDetails: [],
|
||||
batchMaterialIds: [],
|
||||
detailSelection: [],
|
||||
editRules: {
|
||||
bizType: [{ required: true, message: '业务类型不能为空', trigger: 'blur' }]
|
||||
},
|
||||
detailOpen: false,
|
||||
detailData: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalQtyText() {
|
||||
const total = (this.editDetails || []).reduce((sum, r) => sum + this.toNumber(r.quantity), 0)
|
||||
return String(total)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
toNumber(val) {
|
||||
const n = Number(val)
|
||||
return Number.isFinite(n) ? n : 0
|
||||
},
|
||||
getList() {
|
||||
this.loading = true
|
||||
listStockIoOrder(this.queryParams)
|
||||
.then((res) => {
|
||||
this.list = res.rows || []
|
||||
this.total = res.total || 0
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
this.queryParams.ioType = 'I'
|
||||
this.handleQuery()
|
||||
},
|
||||
handleSelectionChange(selection) {
|
||||
this.rows = selection
|
||||
this.ids = selection.map((r) => r.orderId)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
resetEdit() {
|
||||
this.editForm = {
|
||||
ioType: 'I',
|
||||
bizType: undefined,
|
||||
sourceNo: undefined,
|
||||
responsibleName: undefined,
|
||||
remark: undefined
|
||||
}
|
||||
this.editDetails = []
|
||||
this.detailSelection = []
|
||||
this.resetForm('editFormRef')
|
||||
},
|
||||
handleAdd() {
|
||||
this.resetEdit()
|
||||
this.editTitle = '新增入库单据'
|
||||
this.editOpen = true
|
||||
this.addDetailRow()
|
||||
},
|
||||
showDetail(row) {
|
||||
this.detailOpen = true
|
||||
this.detailData = null
|
||||
getStockIoOrderWithDetail(row.orderId).then((res) => {
|
||||
this.detailData = res.data
|
||||
})
|
||||
},
|
||||
handleArrival(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) return
|
||||
this.$modal
|
||||
.confirm('确认到货?')
|
||||
.then(() => {
|
||||
this.buttonLoading = true
|
||||
return arrivalStockIoOrder(target.orderId)
|
||||
})
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('已确认到货')
|
||||
this.getList()
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
handleReverse(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) return
|
||||
this.$modal
|
||||
.confirm('确认冲销该单据?系统将创建一张反向单据并自动执行。')
|
||||
.then(() => {
|
||||
this.buttonLoading = true
|
||||
return reverseStockIoOrder(target.orderId, '')
|
||||
})
|
||||
.then((res) => {
|
||||
const newId = res.data
|
||||
this.$modal.msgSuccess('冲销成功,冲销单ID:' + newId)
|
||||
this.getList()
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
submitEdit() {
|
||||
this.$refs.editFormRef.validate((valid) => {
|
||||
if (!valid) return
|
||||
if (!this.editDetails || this.editDetails.length === 0) {
|
||||
this.$modal.msgError('请先添加明细')
|
||||
return
|
||||
}
|
||||
const check = this.validateDetails()
|
||||
if (!check.ok) {
|
||||
this.$modal.msgError(check.message)
|
||||
return
|
||||
}
|
||||
const payload = this.buildPayload()
|
||||
if (!payload.details || payload.details.length === 0) {
|
||||
this.$modal.msgError('请至少填写一条明细:选择原料并输入数量')
|
||||
return
|
||||
}
|
||||
this.buttonLoading = true
|
||||
addStockIoOrderWithDetail(payload)
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('保存成功')
|
||||
this.editOpen = false
|
||||
this.getList()
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
validateDetails() {
|
||||
for (const d of this.editDetails || []) {
|
||||
if (!d) continue
|
||||
const hasAny =
|
||||
(d.itemId != null && String(d.itemId) !== '') ||
|
||||
(d.quantity != null && String(d.quantity) !== '') ||
|
||||
(d.unitPrice != null && String(d.unitPrice) !== '') ||
|
||||
(d.batchNo != null && String(d.batchNo) !== '') ||
|
||||
(d.remark != null && String(d.remark) !== '') ||
|
||||
(d.itemName != null && String(d.itemName) !== '')
|
||||
if (!hasAny) {
|
||||
continue
|
||||
}
|
||||
const lineNo = d.lineNo != null ? d.lineNo : '-'
|
||||
if (d.itemId == null || String(d.itemId) === '') {
|
||||
return { ok: false, message: `第${lineNo}行请选择原料` }
|
||||
}
|
||||
const qty = this.toNumber(d.quantity)
|
||||
if (qty <= 0) {
|
||||
return { ok: false, message: `第${lineNo}行请输入数量` }
|
||||
}
|
||||
}
|
||||
return { ok: true, message: '' }
|
||||
},
|
||||
buildPayload() {
|
||||
const details = (this.editDetails || [])
|
||||
.filter((d) => d && d.itemId != null && String(d.itemId) !== '' && this.toNumber(d.quantity) > 0)
|
||||
.map((d, idx) => {
|
||||
const lineNo = d.lineNo != null ? d.lineNo : idx + 1
|
||||
const unitPrice = this.toNumber(d.unitPrice)
|
||||
const quantity = this.toNumber(d.quantity)
|
||||
const amount = d.amount != null && String(d.amount) !== '' ? this.toNumber(d.amount) : unitPrice * quantity
|
||||
return {
|
||||
detailId: d.detailId,
|
||||
lineNo,
|
||||
itemType: 'material',
|
||||
itemId: d.itemId,
|
||||
itemName: d.itemName,
|
||||
quantity: quantity,
|
||||
unit: d.unit,
|
||||
batchNo: d.batchNo,
|
||||
unitPrice: unitPrice,
|
||||
amount: amount,
|
||||
remark: d.remark
|
||||
}
|
||||
})
|
||||
|
||||
const totalQty = details.reduce((sum, d) => sum + this.toNumber(d.quantity), 0)
|
||||
return Object.assign({}, this.editForm, { ioType: 'I', totalQty, details })
|
||||
},
|
||||
addDetailRow() {
|
||||
const nextNo = (this.editDetails || []).length + 1
|
||||
this.editDetails.push({
|
||||
detailId: undefined,
|
||||
lineNo: nextNo,
|
||||
itemType: 'material',
|
||||
itemId: undefined,
|
||||
itemName: undefined,
|
||||
quantity: undefined,
|
||||
unit: undefined,
|
||||
batchNo: undefined,
|
||||
unitPrice: undefined,
|
||||
amount: undefined,
|
||||
remark: undefined
|
||||
})
|
||||
this.rebuildLineNo()
|
||||
},
|
||||
onDetailSelectionChange(rows) {
|
||||
this.detailSelection = rows || []
|
||||
},
|
||||
removeDetailRows() {
|
||||
const ids = new Set((this.detailSelection || []).map((r) => r.lineNo))
|
||||
this.editDetails = (this.editDetails || []).filter((r) => !ids.has(r.lineNo))
|
||||
this.detailSelection = []
|
||||
if (this.editDetails.length === 0) {
|
||||
this.addDetailRow()
|
||||
} else {
|
||||
this.rebuildLineNo()
|
||||
}
|
||||
},
|
||||
rebuildLineNo() {
|
||||
let i = 1
|
||||
this.editDetails = (this.editDetails || []).map((r) => Object.assign({}, r, { lineNo: i++ }))
|
||||
},
|
||||
handleBatchPicked(materials) {
|
||||
const rows = materials || []
|
||||
if (rows.length === 0) {
|
||||
return
|
||||
}
|
||||
let startIndex = 0
|
||||
if (this.editDetails.length === 1) {
|
||||
const r = this.editDetails[0]
|
||||
const empty =
|
||||
(r.itemId == null || String(r.itemId) === '') &&
|
||||
(r.quantity == null || String(r.quantity) === '') &&
|
||||
(r.unitPrice == null || String(r.unitPrice) === '')
|
||||
if (empty) {
|
||||
r.itemId = rows[0].materialId
|
||||
this.onMaterialPicked(r, rows[0])
|
||||
startIndex = 1
|
||||
}
|
||||
}
|
||||
for (let i = startIndex; i < rows.length; i++) {
|
||||
this.addDetailRow()
|
||||
const r = this.editDetails[this.editDetails.length - 1]
|
||||
r.itemId = rows[i].materialId
|
||||
this.onMaterialPicked(r, rows[i])
|
||||
}
|
||||
this.batchMaterialIds = []
|
||||
},
|
||||
onMaterialPicked(row, material) {
|
||||
if (material && material.materialName) {
|
||||
row.itemName = material.materialName
|
||||
if (!row.unit && material.unit) {
|
||||
row.unit = material.unit
|
||||
}
|
||||
if (material.unitPrice != null && row.unitPrice == null) {
|
||||
row.unitPrice = material.unitPrice
|
||||
}
|
||||
this.recalcAmount(row)
|
||||
}
|
||||
},
|
||||
recalcAmount(row) {
|
||||
const qty = this.toNumber(row.quantity)
|
||||
const unitPrice = this.toNumber(row.unitPrice)
|
||||
if (qty > 0 || unitPrice > 0) {
|
||||
row.amount = String(unitPrice * qty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
12
gear-ui3/src/views/wms/stockIoOrder/index.vue
Normal file
12
gear-ui3/src/views/wms/stockIoOrder/index.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<StockIoOrderPage />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StockIoOrderPage from './panels/stockIoOrderPage.vue'
|
||||
|
||||
export default {
|
||||
name: 'StockIoOrder',
|
||||
components: { StockIoOrderPage }
|
||||
}
|
||||
</script>
|
||||
486
gear-ui3/src/views/wms/stockIoOrder/out.vue
Normal file
486
gear-ui3/src/views/wms/stockIoOrder/out.vue
Normal file
@@ -0,0 +1,486 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
|
||||
<el-form-item label="单号" prop="orderCode">
|
||||
<el-input v-model="queryParams.orderCode" placeholder="请输入单据编号" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务类型" prop="bizType">
|
||||
<el-input v-model="queryParams.bizType" placeholder="请输入业务类型" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="责任人" prop="responsibleName">
|
||||
<el-input v-model="queryParams.responsibleName" placeholder="请输入责任人" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" size="mini" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="RefreshLeft" size="mini" :disabled="single" @click="handleRevoke()">撤回</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="单据编号" align="center" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="物料" align="center" min-width="220">
|
||||
<template #default="scope">
|
||||
<el-tooltip v-if="scope.row.materialNames && String(scope.row.materialNames).length > 16" effect="dark" placement="top">
|
||||
<template #content>
|
||||
<div style="max-width: 420px; white-space: normal; word-break: break-all;">
|
||||
{{ scope.row.materialNames }}
|
||||
</div>
|
||||
</template>
|
||||
<span>{{ String(scope.row.materialNames).slice(0, 16) + '…' }}</span>
|
||||
</el-tooltip>
|
||||
<span v-else>{{ scope.row.materialNames || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="业务类型" align="center" prop="bizType" min-width="120" />
|
||||
<el-table-column label="责任人" align="center" prop="responsibleName" min-width="120" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
|
||||
<template #default="scope">
|
||||
<el-button size="mini" type="text" icon="Document" @click="showDetail(scope.row)">明细</el-button>
|
||||
<el-button size="mini" type="text" icon="RefreshLeft" @click="handleRevoke(scope.row)">撤回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<el-dialog :title="editTitle" v-model="editOpen" width="1100px" top="5vh" append-to-body>
|
||||
<el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="90px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="类型">
|
||||
<el-tag type="warning">出库</el-tag>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="业务类型" prop="bizType">
|
||||
<el-input v-model="editForm.bizType" placeholder="请输入业务类型" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="责任人" prop="responsibleName">
|
||||
<el-input v-model="editForm.responsibleName" placeholder="请输入责任人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="来源单号" prop="sourceNo">
|
||||
<el-input v-model="editForm.sourceNo" placeholder="请输入来源单号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="editForm.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="display:flex;gap:8px;align-items:center;margin-bottom:10px;">
|
||||
<el-button type="primary" plain icon="Plus" size="mini" @click="addDetailRow">新增明细行</el-button>
|
||||
<RawSelector v-model:materialIds="batchMaterialIds" :multiple="true" :allowAdd="false" @multi-change="handleBatchPicked">
|
||||
<template #trigger>
|
||||
<el-button type="primary" plain size="mini">批量选择原料</el-button>
|
||||
</template>
|
||||
</RawSelector>
|
||||
<el-button type="danger" plain icon="Delete" size="mini" :disabled="detailSelection.length === 0" @click="removeDetailRows">删除选中</el-button>
|
||||
<div style="margin-left:auto;">
|
||||
<span>合计数量:{{ totalQtyText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="editDetails" border style="width: 100%" @selection-change="onDetailSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="行号" width="70" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.lineNo }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物料类型" width="100" align="center">
|
||||
<template #default>
|
||||
原料
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="原料" min-width="260" align="center">
|
||||
<template #default="scope">
|
||||
<RawSelector v-model="scope.row.itemId" :allowAdd="false" @change="onMaterialPicked(scope.row, $event)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称快照" min-width="180" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.itemName" placeholder="可选" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" min-width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.quantity" placeholder="请输入数量" @input="recalcAmount(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.unit" placeholder="单位" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="批次号" min-width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.batchNo" placeholder="批次号" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价" min-width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.unitPrice" placeholder="单价" @input="recalcAmount(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" min-width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.amount" placeholder="金额" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="140" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.remark" placeholder="备注" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitEdit">出库</el-button>
|
||||
<el-button @click="editOpen = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="单据明细" v-model="detailOpen" width="1100px" append-to-body>
|
||||
<template v-if="detailData && detailData.order">
|
||||
<el-descriptions :title="'单号:' + (detailData.order.orderCode || '-')" :column="2" border>
|
||||
<el-descriptions-item label="类型">出库</el-descriptions-item>
|
||||
<el-descriptions-item label="业务类型">{{ detailData.order.bizType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="责任人">{{ detailData.order.responsibleName }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div style="margin: 12px 0; text-align: right;">
|
||||
<el-button type="danger" size="small" @click="handleRevoke(detailData.order)">撤回</el-button>
|
||||
</div>
|
||||
<el-table :data="detailData.details || []" border style="width: 100%">
|
||||
<el-table-column label="行号" prop="lineNo" width="70" align="center" />
|
||||
<el-table-column label="原料ID" prop="itemId" width="120" align="center" />
|
||||
<el-table-column label="名称快照" prop="itemName" min-width="160" align="center" />
|
||||
<el-table-column label="数量" prop="quantity" width="120" align="center" />
|
||||
<el-table-column label="单位" prop="unit" width="90" align="center" />
|
||||
<el-table-column label="批次号" prop="batchNo" min-width="120" align="center" />
|
||||
<el-table-column label="备注" prop="remark" min-width="140" align="center" />
|
||||
</el-table>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div style="height:200px;line-height:200px;text-align:center;">未获取到单据数据</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-button @click="detailOpen = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RawSelector from '@/components/RawSelector/index.vue'
|
||||
import {
|
||||
listStockIoOrder,
|
||||
getStockIoOrderWithDetail,
|
||||
addStockIoOrderWithDetail,
|
||||
reverseStockIoOrder
|
||||
} from '@/api/wms/stockIoOrder'
|
||||
|
||||
export default {
|
||||
name: 'StockIoOrderOut',
|
||||
components: { RawSelector },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
buttonLoading: false,
|
||||
ids: [],
|
||||
rows: [],
|
||||
single: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
list: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
orderCode: undefined,
|
||||
ioType: 'O',
|
||||
bizType: undefined,
|
||||
responsibleName: undefined
|
||||
},
|
||||
editOpen: false,
|
||||
editTitle: '',
|
||||
editForm: {},
|
||||
editDetails: [],
|
||||
batchMaterialIds: [],
|
||||
detailSelection: [],
|
||||
editRules: {
|
||||
bizType: [{ required: true, message: '业务类型不能为空', trigger: 'blur' }]
|
||||
},
|
||||
detailOpen: false,
|
||||
detailData: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalQtyText() {
|
||||
const total = (this.editDetails || []).reduce((sum, r) => sum + this.toNumber(r.quantity), 0)
|
||||
return String(total)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
toNumber(val) {
|
||||
const n = Number(val)
|
||||
return Number.isFinite(n) ? n : 0
|
||||
},
|
||||
getList() {
|
||||
this.loading = true
|
||||
listStockIoOrder(this.queryParams)
|
||||
.then((res) => {
|
||||
this.list = res.rows || []
|
||||
this.total = res.total || 0
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
this.queryParams.ioType = 'O'
|
||||
this.handleQuery()
|
||||
},
|
||||
handleSelectionChange(selection) {
|
||||
this.rows = selection
|
||||
this.ids = selection.map((r) => r.orderId)
|
||||
this.single = selection.length !== 1
|
||||
},
|
||||
resetEdit() {
|
||||
this.editForm = {
|
||||
ioType: 'O',
|
||||
bizType: undefined,
|
||||
sourceNo: undefined,
|
||||
responsibleName: undefined,
|
||||
remark: undefined
|
||||
}
|
||||
this.editDetails = []
|
||||
this.detailSelection = []
|
||||
this.resetForm('editFormRef')
|
||||
},
|
||||
handleAdd() {
|
||||
this.resetEdit()
|
||||
this.editTitle = '新增出库单据'
|
||||
this.editOpen = true
|
||||
this.addDetailRow()
|
||||
},
|
||||
showDetail(row) {
|
||||
this.detailOpen = true
|
||||
this.detailData = null
|
||||
getStockIoOrderWithDetail(row.orderId).then((res) => {
|
||||
this.detailData = res.data
|
||||
})
|
||||
},
|
||||
handleRevoke(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) return
|
||||
this.$modal
|
||||
.confirm('确认撤回该出库?系统将创建一张反向单据并自动执行。')
|
||||
.then(() => {
|
||||
this.buttonLoading = true
|
||||
return reverseStockIoOrder(target.orderId, '')
|
||||
})
|
||||
.then((res) => {
|
||||
const newId = res.data
|
||||
this.$modal.msgSuccess('撤回成功,撤回单ID:' + newId)
|
||||
this.getList()
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
submitEdit() {
|
||||
this.$refs.editFormRef.validate((valid) => {
|
||||
if (!valid) return
|
||||
if (!this.editDetails || this.editDetails.length === 0) {
|
||||
this.$modal.msgError('请先添加明细')
|
||||
return
|
||||
}
|
||||
const check = this.validateDetails()
|
||||
if (!check.ok) {
|
||||
this.$modal.msgError(check.message)
|
||||
return
|
||||
}
|
||||
const payload = this.buildPayload()
|
||||
if (payload.orderId) {
|
||||
this.$modal.msgError('已出库单据不支持修改,请撤回后重新出库')
|
||||
return
|
||||
}
|
||||
if (!payload.details || payload.details.length === 0) {
|
||||
this.$modal.msgError('请至少填写一条明细:选择原料并输入数量')
|
||||
return
|
||||
}
|
||||
this.buttonLoading = true
|
||||
addStockIoOrderWithDetail(payload)
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('出库成功')
|
||||
this.editOpen = false
|
||||
this.getList()
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
validateDetails() {
|
||||
for (const d of this.editDetails || []) {
|
||||
if (!d) continue
|
||||
const hasAny =
|
||||
(d.itemId != null && String(d.itemId) !== '') ||
|
||||
(d.quantity != null && String(d.quantity) !== '') ||
|
||||
(d.unitPrice != null && String(d.unitPrice) !== '') ||
|
||||
(d.batchNo != null && String(d.batchNo) !== '') ||
|
||||
(d.remark != null && String(d.remark) !== '') ||
|
||||
(d.itemName != null && String(d.itemName) !== '')
|
||||
if (!hasAny) {
|
||||
continue
|
||||
}
|
||||
const lineNo = d.lineNo != null ? d.lineNo : '-'
|
||||
if (d.itemId == null || String(d.itemId) === '') {
|
||||
return { ok: false, message: `第${lineNo}行请选择原料` }
|
||||
}
|
||||
const qty = this.toNumber(d.quantity)
|
||||
if (qty <= 0) {
|
||||
return { ok: false, message: `第${lineNo}行请输入数量` }
|
||||
}
|
||||
}
|
||||
return { ok: true, message: '' }
|
||||
},
|
||||
buildPayload() {
|
||||
const details = (this.editDetails || [])
|
||||
.filter((d) => d && d.itemId != null && String(d.itemId) !== '' && this.toNumber(d.quantity) > 0)
|
||||
.map((d, idx) => {
|
||||
const lineNo = d.lineNo != null ? d.lineNo : idx + 1
|
||||
const unitPrice = this.toNumber(d.unitPrice)
|
||||
const quantity = this.toNumber(d.quantity)
|
||||
const amount = d.amount != null && String(d.amount) !== '' ? this.toNumber(d.amount) : unitPrice * quantity
|
||||
return {
|
||||
detailId: d.detailId,
|
||||
lineNo,
|
||||
itemType: 'material',
|
||||
itemId: d.itemId,
|
||||
itemName: d.itemName,
|
||||
quantity: quantity,
|
||||
unit: d.unit,
|
||||
batchNo: d.batchNo,
|
||||
unitPrice: unitPrice,
|
||||
amount: amount,
|
||||
remark: d.remark
|
||||
}
|
||||
})
|
||||
|
||||
const totalQty = details.reduce((sum, d) => sum + this.toNumber(d.quantity), 0)
|
||||
return Object.assign({}, this.editForm, { ioType: 'O', totalQty, details })
|
||||
},
|
||||
addDetailRow() {
|
||||
const nextNo = (this.editDetails || []).length + 1
|
||||
this.editDetails.push({
|
||||
detailId: undefined,
|
||||
lineNo: nextNo,
|
||||
itemType: 'material',
|
||||
itemId: undefined,
|
||||
itemName: undefined,
|
||||
quantity: undefined,
|
||||
unit: undefined,
|
||||
batchNo: undefined,
|
||||
unitPrice: undefined,
|
||||
amount: undefined,
|
||||
remark: undefined
|
||||
})
|
||||
this.rebuildLineNo()
|
||||
},
|
||||
onDetailSelectionChange(rows) {
|
||||
this.detailSelection = rows || []
|
||||
},
|
||||
removeDetailRows() {
|
||||
const ids = new Set((this.detailSelection || []).map((r) => r.lineNo))
|
||||
this.editDetails = (this.editDetails || []).filter((r) => !ids.has(r.lineNo))
|
||||
this.detailSelection = []
|
||||
if (this.editDetails.length === 0) {
|
||||
this.addDetailRow()
|
||||
} else {
|
||||
this.rebuildLineNo()
|
||||
}
|
||||
},
|
||||
rebuildLineNo() {
|
||||
let i = 1
|
||||
this.editDetails = (this.editDetails || []).map((r) => Object.assign({}, r, { lineNo: i++ }))
|
||||
},
|
||||
handleBatchPicked(materials) {
|
||||
const rows = materials || []
|
||||
if (rows.length === 0) {
|
||||
return
|
||||
}
|
||||
let startIndex = 0
|
||||
if (this.editDetails.length === 1) {
|
||||
const r = this.editDetails[0]
|
||||
const empty =
|
||||
(r.itemId == null || String(r.itemId) === '') &&
|
||||
(r.quantity == null || String(r.quantity) === '') &&
|
||||
(r.unitPrice == null || String(r.unitPrice) === '')
|
||||
if (empty) {
|
||||
r.itemId = rows[0].materialId
|
||||
this.onMaterialPicked(r, rows[0])
|
||||
startIndex = 1
|
||||
}
|
||||
}
|
||||
for (let i = startIndex; i < rows.length; i++) {
|
||||
this.addDetailRow()
|
||||
const r = this.editDetails[this.editDetails.length - 1]
|
||||
r.itemId = rows[i].materialId
|
||||
this.onMaterialPicked(r, rows[i])
|
||||
}
|
||||
this.batchMaterialIds = []
|
||||
},
|
||||
onMaterialPicked(row, material) {
|
||||
if (material && material.materialName) {
|
||||
row.itemName = material.materialName
|
||||
if (!row.unit && material.unit) {
|
||||
row.unit = material.unit
|
||||
}
|
||||
if (material.unitPrice != null && row.unitPrice == null) {
|
||||
row.unitPrice = material.unitPrice
|
||||
}
|
||||
this.recalcAmount(row)
|
||||
}
|
||||
},
|
||||
recalcAmount(row) {
|
||||
const qty = this.toNumber(row.quantity)
|
||||
const unitPrice = this.toNumber(row.unitPrice)
|
||||
if (qty > 0 || unitPrice > 0) {
|
||||
row.amount = String(unitPrice * qty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
808
gear-ui3/src/views/wms/stockIoOrder/panels/stockIoOrderPage.vue
Normal file
808
gear-ui3/src/views/wms/stockIoOrder/panels/stockIoOrderPage.vue
Normal file
@@ -0,0 +1,808 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
|
||||
<el-form-item label="单号" prop="orderCode">
|
||||
<el-input v-model="queryParams.orderCode" placeholder="请输入单据编号" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="showIoTypeSelector" label="类型" prop="ioType">
|
||||
<el-select v-model="queryParams.ioType" placeholder="请选择类型" clearable style="width: 120px">
|
||||
<el-option v-for="it in ioTypeOptions" :key="it.value" :label="it.label" :value="it.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务类型" prop="bizType">
|
||||
<el-input v-model="queryParams.bizType" placeholder="请输入业务类型" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 120px">
|
||||
<el-option v-for="it in statusOptions" :key="it.value" :label="it.label" :value="it.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="已执行" prop="execFlag">
|
||||
<el-select v-model="queryParams.execFlag" placeholder="请选择" clearable style="width: 120px">
|
||||
<el-option v-for="it in yesNoOptions" :key="it.value" :label="it.label" :value="it.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="延迟" prop="delayStatus">
|
||||
<el-select v-model="queryParams.delayStatus" placeholder="请选择" clearable style="width: 120px">
|
||||
<el-option v-for="it in delayStatusOptions" :key="it.value" :label="it.label" :value="it.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="责任人" prop="responsibleName">
|
||||
<el-input v-model="queryParams.responsibleName" placeholder="请输入责任人" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="来源单号" prop="sourceNo">
|
||||
<el-input v-model="queryParams.sourceNo" placeholder="请输入来源单号" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" size="mini" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" size="mini" :disabled="single" @click="handleUpdate">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" size="mini" :disabled="multiple" @click="handleDelete">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="CircleCheck" size="mini" :disabled="single" @click="handleAudit()">审核</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Check" size="mini" :disabled="single" @click="handleExecute()">执行</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Clock" size="mini" :disabled="single" @click="handleArrival()">到货确认</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Finished" size="mini" :disabled="single" @click="handleFinish()">完成</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="RefreshLeft" size="mini" :disabled="single" @click="handleReverse()">冲销</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Close" size="mini" :disabled="single" @click="handleCancel()">作废</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="单据ID" align="center" prop="orderId" v-if="false" />
|
||||
<el-table-column label="单据编号" align="center" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="类型" align="center" prop="ioType" width="90">
|
||||
<template #default="scope">
|
||||
<el-tag :type="ioTypeTag(scope.row.ioType)">{{ ioTypeLabel(scope.row.ioType) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="业务类型" align="center" prop="bizType" min-width="120" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === '1' ? 'success' : 'info'">{{ scope.row.status === '1' ? '已完成' : '进行中' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已执行" align="center" prop="execFlag" width="90">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.execFlag === '1' ? 'success' : 'warning'">{{ scope.row.execFlag === '1' ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="责任人" align="center" prop="responsibleName" min-width="120" />
|
||||
<el-table-column label="预计到货" align="center" prop="planArrivalTime" min-width="160" />
|
||||
<el-table-column label="实际到货" align="center" prop="actualArrivalTime" min-width="160" />
|
||||
<el-table-column label="预计完成" align="center" prop="planFinishTime" min-width="160" />
|
||||
<el-table-column label="实际完成" align="center" prop="actualFinishTime" min-width="160" />
|
||||
<el-table-column label="延迟(分钟)" align="center" prop="delayMinutes" width="110" />
|
||||
<el-table-column label="延迟状态" align="center" prop="delayStatus" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="delayStatusTag(scope.row.delayStatus)">{{ delayStatusLabel(scope.row.delayStatus) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="来源单号" align="center" prop="sourceNo" min-width="140" />
|
||||
<el-table-column label="执行流水ID" align="center" prop="sourceIoId" min-width="120" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
|
||||
<template #default="scope">
|
||||
<el-button size="mini" type="text" icon="Document" @click="showDetail(scope.row)">明细</el-button>
|
||||
<el-button size="mini" type="text" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<el-dialog :title="editTitle" v-model="editOpen" width="1200px" append-to-body>
|
||||
<el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="出入库类型" prop="ioType">
|
||||
<el-select v-model="editForm.ioType" placeholder="请选择类型" style="width: 100%" :disabled="!showIoTypeSelector" @change="onIoTypeChanged">
|
||||
<el-option v-for="it in ioTypeOptions" :key="it.value" :label="it.label" :value="it.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="业务类型" prop="bizType">
|
||||
<el-input v-model="editForm.bizType" placeholder="请输入业务类型" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="责任人" prop="responsibleName">
|
||||
<el-input v-model="editForm.responsibleName" placeholder="请输入责任人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="来源单号" prop="sourceNo">
|
||||
<el-input v-model="editForm.sourceNo" placeholder="请输入来源单号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="主仓库" prop="warehouseId">
|
||||
<WarehouseSelect v-model="editForm.warehouseId" placeholder="请选择仓库" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" v-if="editForm.ioType === 'T'">
|
||||
<el-form-item label="调出仓库" prop="fromWarehouseId">
|
||||
<WarehouseSelect v-model="editForm.fromWarehouseId" placeholder="请选择调出仓库" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" v-if="editForm.ioType === 'T'">
|
||||
<el-form-item label="调入仓库" prop="toWarehouseId">
|
||||
<WarehouseSelect v-model="editForm.toWarehouseId" placeholder="请选择调入仓库" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="预计到货" prop="planArrivalTime">
|
||||
<el-date-picker v-model="editForm.planArrivalTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="预计完成" prop="planFinishTime">
|
||||
<el-date-picker v-model="editForm.planFinishTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="editForm.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="display:flex;gap:8px;align-items:center;margin-bottom:10px;">
|
||||
<el-button type="primary" plain icon="Plus" size="mini" @click="addDetailRow">新增明细行</el-button>
|
||||
<el-button type="danger" plain icon="Delete" size="mini" :disabled="detailSelection.length === 0" @click="removeDetailRows">删除选中</el-button>
|
||||
<div style="margin-left:auto;">
|
||||
<span>合计数量:{{ totalQtyText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="editDetails" border style="width: 100%" @selection-change="onDetailSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="行号" width="70" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.lineNo }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物料类型" min-width="120" align="center">
|
||||
<template #default="scope">
|
||||
<span v-if="fixedMaterialMode">原料</span>
|
||||
<el-select v-else v-model="scope.row.itemType" placeholder="请选择" @change="onItemTypeChanged(scope.row)">
|
||||
<el-option label="产品" value="product" />
|
||||
<el-option label="原料" value="material" />
|
||||
<el-option label="其他" value="other" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物料" min-width="220" align="center">
|
||||
<template #default="scope">
|
||||
<ProductSelect
|
||||
v-if="scope.row.itemType === 'product'"
|
||||
v-model="scope.row.itemId"
|
||||
@change="onProductPicked(scope.row, $event)"
|
||||
/>
|
||||
<RawSelector
|
||||
v-else-if="scope.row.itemType === 'material'"
|
||||
v-model="scope.row.itemId"
|
||||
:allowAdd="true"
|
||||
@change="onMaterialPicked(scope.row, $event)"
|
||||
/>
|
||||
<el-input v-else v-model="scope.row.itemId" placeholder="请输入物料ID" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称快照" min-width="180" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.itemName" placeholder="可选" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="仓库" min-width="180" align="center">
|
||||
<template #default="scope">
|
||||
<WarehouseSelect v-model="scope.row.warehouseId" placeholder="请选择仓库" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="editForm.ioType === 'T'" label="源仓库" min-width="180" align="center">
|
||||
<template #default="scope">
|
||||
<WarehouseSelect v-model="scope.row.fromWarehouseId" placeholder="请选择源仓库" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" min-width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.quantity" placeholder="请输入数量" @input="recalcAmount(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.unit" placeholder="单位" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="批次号" min-width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.batchNo" placeholder="批次号" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价" min-width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.unitPrice" placeholder="单价" @input="recalcAmount(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" min-width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.amount" placeholder="金额" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="140" align="center">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.remark" placeholder="备注" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitEdit">保存</el-button>
|
||||
<el-button @click="editOpen = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="单据明细" v-model="detailOpen" width="1100px" append-to-body>
|
||||
<template v-if="detailData && detailData.order">
|
||||
<el-descriptions :title="'单号:' + (detailData.order.orderCode || '-')" :column="2" border>
|
||||
<el-descriptions-item label="类型">{{ ioTypeLabel(detailData.order.ioType) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="业务类型">{{ detailData.order.bizType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{ detailData.order.status === '1' ? '已完成' : '进行中' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="已执行">{{ detailData.order.execFlag === '1' ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="责任人">{{ detailData.order.responsibleName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="延迟">{{ delayStatusLabel(detailData.order.delayStatus) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div style="margin: 12px 0; text-align: right;">
|
||||
<el-button type="primary" size="small" @click="handleAudit(detailData.order)">审核</el-button>
|
||||
<el-button type="primary" size="small" @click="handleExecute(detailData.order)">执行</el-button>
|
||||
<el-button type="warning" size="small" @click="handleArrival(detailData.order)">到货确认</el-button>
|
||||
<el-button type="success" size="small" @click="handleFinish(detailData.order)">完成</el-button>
|
||||
<el-button type="danger" size="small" @click="handleReverse(detailData.order)">冲销</el-button>
|
||||
</div>
|
||||
<el-table :data="detailData.details || []" border style="width: 100%">
|
||||
<el-table-column label="行号" prop="lineNo" width="70" align="center" />
|
||||
<el-table-column label="物料类型" prop="itemType" width="110" align="center" />
|
||||
<el-table-column label="物料ID" prop="itemId" width="120" align="center" />
|
||||
<el-table-column label="名称快照" prop="itemName" min-width="160" align="center" />
|
||||
<el-table-column label="仓库" prop="warehouseId" width="110" align="center" />
|
||||
<el-table-column label="源仓库" prop="fromWarehouseId" width="110" align="center" />
|
||||
<el-table-column label="数量" prop="quantity" width="120" align="center" />
|
||||
<el-table-column label="单位" prop="unit" width="90" align="center" />
|
||||
<el-table-column label="批次号" prop="batchNo" min-width="120" align="center" />
|
||||
<el-table-column label="备注" prop="remark" min-width="140" align="center" />
|
||||
</el-table>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div style="height:200px;line-height:200px;text-align:center;">未获取到单据数据</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-button @click="detailOpen = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WarehouseSelect from '@/components/WarehouseSelect'
|
||||
import ProductSelect from '@/components/ProductSelect'
|
||||
import RawSelector from '@/components/RawSelector/index.vue'
|
||||
import {
|
||||
listStockIoOrder,
|
||||
getStockIoOrderWithDetail,
|
||||
addStockIoOrderWithDetail,
|
||||
updateStockIoOrderWithDetail,
|
||||
delStockIoOrder,
|
||||
auditStockIoOrder,
|
||||
executeStockIoOrder,
|
||||
arrivalStockIoOrder,
|
||||
finishStockIoOrder,
|
||||
cancelStockIoOrder,
|
||||
reverseStockIoOrder
|
||||
} from '@/api/wms/stockIoOrder'
|
||||
|
||||
export default {
|
||||
name: 'StockIoOrderPage',
|
||||
components: { WarehouseSelect, ProductSelect, RawSelector },
|
||||
props: {
|
||||
ioType: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ioTypeOptions: [
|
||||
{ label: '入库', value: 'I' },
|
||||
{ label: '出库', value: 'O' },
|
||||
{ label: '调拨', value: 'T' }
|
||||
],
|
||||
statusOptions: [
|
||||
{ label: '进行中', value: '0' },
|
||||
{ label: '已完成', value: '1' }
|
||||
],
|
||||
yesNoOptions: [
|
||||
{ label: '否', value: '0' },
|
||||
{ label: '是', value: '1' }
|
||||
],
|
||||
delayStatusOptions: [
|
||||
{ label: '正常', value: '0' },
|
||||
{ label: '超时', value: '1' },
|
||||
{ label: '已处理', value: '2' }
|
||||
],
|
||||
loading: true,
|
||||
buttonLoading: false,
|
||||
ids: [],
|
||||
rows: [],
|
||||
single: true,
|
||||
multiple: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
list: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
orderCode: undefined,
|
||||
ioType: this.ioType || undefined,
|
||||
bizType: undefined,
|
||||
status: undefined,
|
||||
execFlag: undefined,
|
||||
reversalFlag: undefined,
|
||||
delayStatus: undefined,
|
||||
responsibleName: undefined,
|
||||
sourceNo: undefined
|
||||
},
|
||||
editOpen: false,
|
||||
editTitle: '',
|
||||
editForm: {},
|
||||
editDetails: [],
|
||||
detailSelection: [],
|
||||
editRules: {
|
||||
ioType: [{ required: true, message: '出入库类型不能为空', trigger: 'change' }],
|
||||
bizType: [{ required: true, message: '业务类型不能为空', trigger: 'blur' }]
|
||||
},
|
||||
detailOpen: false,
|
||||
detailData: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showIoTypeSelector() {
|
||||
return !this.ioType
|
||||
},
|
||||
fixedMaterialMode() {
|
||||
return this.ioType === 'I' || this.ioType === 'O'
|
||||
},
|
||||
totalQtyText() {
|
||||
const total = (this.editDetails || []).reduce((sum, r) => sum + this.toNumber(r.quantity), 0)
|
||||
return String(total)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
toNumber(val) {
|
||||
const n = Number(val)
|
||||
return Number.isFinite(n) ? n : 0
|
||||
},
|
||||
ioTypeLabel(v) {
|
||||
const map = { I: '入库', O: '出库', T: '调拨' }
|
||||
return map[v] || v
|
||||
},
|
||||
ioTypeTag(v) {
|
||||
if (v === 'I') return 'success'
|
||||
if (v === 'O') return 'primary'
|
||||
if (v === 'T') return 'warning'
|
||||
return 'info'
|
||||
},
|
||||
delayStatusLabel(v) {
|
||||
const map = { '0': '正常', '1': '超时', '2': '已处理' }
|
||||
return map[v] || v
|
||||
},
|
||||
delayStatusTag(v) {
|
||||
if (v === '1') return 'danger'
|
||||
if (v === '2') return 'success'
|
||||
return 'info'
|
||||
},
|
||||
getList() {
|
||||
this.loading = true
|
||||
listStockIoOrder(this.queryParams)
|
||||
.then((res) => {
|
||||
this.list = res.rows || []
|
||||
this.total = res.total || 0
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
this.queryParams.ioType = this.ioType || undefined
|
||||
this.handleQuery()
|
||||
},
|
||||
handleSelectionChange(selection) {
|
||||
this.rows = selection
|
||||
this.ids = selection.map((r) => r.orderId)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
resetEdit() {
|
||||
this.editForm = {
|
||||
orderId: undefined,
|
||||
orderCode: undefined,
|
||||
ioType: this.ioType || 'I',
|
||||
bizType: undefined,
|
||||
sourceType: undefined,
|
||||
sourceNo: undefined,
|
||||
sourceOrderId: undefined,
|
||||
responsibleId: undefined,
|
||||
responsibleName: undefined,
|
||||
planArrivalTime: undefined,
|
||||
planFinishTime: undefined,
|
||||
warehouseId: undefined,
|
||||
fromWarehouseId: undefined,
|
||||
toWarehouseId: undefined,
|
||||
remark: undefined
|
||||
}
|
||||
this.editDetails = []
|
||||
this.detailSelection = []
|
||||
this.resetForm('editFormRef')
|
||||
},
|
||||
handleAdd() {
|
||||
this.resetEdit()
|
||||
this.editTitle = '新增出入库单据'
|
||||
this.editOpen = true
|
||||
this.addDetailRow()
|
||||
},
|
||||
handleUpdate(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) {
|
||||
return
|
||||
}
|
||||
this.buttonLoading = true
|
||||
getStockIoOrderWithDetail(target.orderId)
|
||||
.then((res) => {
|
||||
const data = res.data || {}
|
||||
this.resetEdit()
|
||||
this.editForm = Object.assign(this.editForm, data.order || {})
|
||||
if (this.ioType) {
|
||||
this.editForm.ioType = this.ioType
|
||||
}
|
||||
this.editDetails = (data.details || []).map((d) => {
|
||||
const next = Object.assign({}, d)
|
||||
if (this.fixedMaterialMode) {
|
||||
next.itemType = 'material'
|
||||
}
|
||||
return next
|
||||
})
|
||||
if (this.editDetails.length === 0) {
|
||||
this.addDetailRow()
|
||||
}
|
||||
this.editTitle = '修改出入库单据'
|
||||
this.editOpen = true
|
||||
this.rebuildLineNo()
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
handleDelete(row) {
|
||||
const orderIds = row && row.orderId ? row.orderId : this.ids
|
||||
if (!orderIds || (Array.isArray(orderIds) && orderIds.length === 0)) {
|
||||
return
|
||||
}
|
||||
this.$modal
|
||||
.confirm('是否确认删除选中的单据?')
|
||||
.then(() => {
|
||||
this.loading = true
|
||||
return delStockIoOrder(orderIds)
|
||||
})
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.getList()
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
showDetail(row) {
|
||||
this.detailOpen = true
|
||||
this.detailData = null
|
||||
getStockIoOrderWithDetail(row.orderId).then((res) => {
|
||||
this.detailData = res.data
|
||||
})
|
||||
},
|
||||
handleAudit(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) return
|
||||
this.$modal
|
||||
.confirm('确认审核该单据?')
|
||||
.then(() => {
|
||||
this.buttonLoading = true
|
||||
return auditStockIoOrder(target.orderId)
|
||||
})
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('审核成功')
|
||||
this.getList()
|
||||
if (this.detailOpen && this.detailData && this.detailData.order) {
|
||||
return getStockIoOrderWithDetail(this.detailData.order.orderId).then((res) => (this.detailData = res.data))
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
handleExecute(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) return
|
||||
this.$modal
|
||||
.confirm('确认执行该单据?执行后将影响库存。')
|
||||
.then(() => {
|
||||
this.buttonLoading = true
|
||||
return executeStockIoOrder(target.orderId)
|
||||
})
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('执行成功')
|
||||
this.getList()
|
||||
if (this.detailOpen && this.detailData && this.detailData.order) {
|
||||
return getStockIoOrderWithDetail(this.detailData.order.orderId).then((res) => (this.detailData = res.data))
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
handleArrival(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) return
|
||||
this.$modal
|
||||
.confirm('确认到货?')
|
||||
.then(() => {
|
||||
this.buttonLoading = true
|
||||
return arrivalStockIoOrder(target.orderId)
|
||||
})
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('已确认到货')
|
||||
this.getList()
|
||||
if (this.detailOpen && this.detailData && this.detailData.order) {
|
||||
return getStockIoOrderWithDetail(this.detailData.order.orderId).then((res) => (this.detailData = res.data))
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
handleFinish(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) return
|
||||
this.$modal
|
||||
.confirm('确认完成该单据?')
|
||||
.then(() => {
|
||||
this.buttonLoading = true
|
||||
return finishStockIoOrder(target.orderId)
|
||||
})
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('已完成')
|
||||
this.getList()
|
||||
if (this.detailOpen && this.detailData && this.detailData.order) {
|
||||
return getStockIoOrderWithDetail(this.detailData.order.orderId).then((res) => (this.detailData = res.data))
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
handleCancel(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) return
|
||||
this.$modal
|
||||
.confirm('确认作废该单据?')
|
||||
.then(() => {
|
||||
this.buttonLoading = true
|
||||
return cancelStockIoOrder(target.orderId, '')
|
||||
})
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('已作废')
|
||||
this.getList()
|
||||
this.detailOpen = false
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
handleReverse(row) {
|
||||
const target = row || (this.rows && this.rows[0])
|
||||
if (!target || !target.orderId) return
|
||||
this.$modal
|
||||
.confirm('确认冲销该单据?系统将创建一张反向单据并自动执行。')
|
||||
.then(() => {
|
||||
this.buttonLoading = true
|
||||
return reverseStockIoOrder(target.orderId, '')
|
||||
})
|
||||
.then((res) => {
|
||||
const newId = res.data
|
||||
this.$modal.msgSuccess('冲销成功,冲销单ID:' + newId)
|
||||
this.getList()
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
},
|
||||
submitEdit() {
|
||||
this.$refs.editFormRef.validate((valid) => {
|
||||
if (!valid) return
|
||||
if (!this.editDetails || this.editDetails.length === 0) {
|
||||
this.$modal.msgError('请先添加明细')
|
||||
return
|
||||
}
|
||||
const payload = this.buildPayload()
|
||||
this.buttonLoading = true
|
||||
const req = payload.orderId ? updateStockIoOrderWithDetail(payload) : addStockIoOrderWithDetail(payload)
|
||||
req
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('保存成功')
|
||||
this.editOpen = false
|
||||
this.getList()
|
||||
})
|
||||
.finally(() => {
|
||||
this.buttonLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
buildPayload() {
|
||||
const details = (this.editDetails || [])
|
||||
.filter((d) => d && (this.fixedMaterialMode || d.itemType) && d.itemId != null && String(d.itemId) !== '' && this.toNumber(d.quantity) > 0)
|
||||
.map((d, idx) => {
|
||||
const lineNo = d.lineNo != null ? d.lineNo : idx + 1
|
||||
const unitPrice = this.toNumber(d.unitPrice)
|
||||
const quantity = this.toNumber(d.quantity)
|
||||
const amount = d.amount != null && String(d.amount) !== '' ? this.toNumber(d.amount) : unitPrice * quantity
|
||||
return {
|
||||
detailId: d.detailId,
|
||||
lineNo,
|
||||
itemType: this.fixedMaterialMode ? 'material' : d.itemType,
|
||||
itemId: d.itemId,
|
||||
itemName: d.itemName,
|
||||
specName: d.specName,
|
||||
warehouseId: d.warehouseId,
|
||||
fromWarehouseId: d.fromWarehouseId,
|
||||
quantity: quantity,
|
||||
unit: d.unit,
|
||||
batchNo: d.batchNo,
|
||||
unitPrice: unitPrice,
|
||||
amount: amount,
|
||||
sourceDetailNo: d.sourceDetailNo,
|
||||
reversalDetailId: d.reversalDetailId,
|
||||
remark: d.remark
|
||||
}
|
||||
})
|
||||
|
||||
const totalQty = details.reduce((sum, d) => sum + this.toNumber(d.quantity), 0)
|
||||
return Object.assign({}, this.editForm, { totalQty, details })
|
||||
},
|
||||
addDetailRow() {
|
||||
const nextNo = (this.editDetails || []).length + 1
|
||||
this.editDetails.push({
|
||||
detailId: undefined,
|
||||
lineNo: nextNo,
|
||||
itemType: this.fixedMaterialMode ? 'material' : 'product',
|
||||
itemId: undefined,
|
||||
itemName: undefined,
|
||||
specName: undefined,
|
||||
warehouseId: undefined,
|
||||
fromWarehouseId: undefined,
|
||||
quantity: undefined,
|
||||
unit: undefined,
|
||||
batchNo: undefined,
|
||||
unitPrice: undefined,
|
||||
amount: undefined,
|
||||
remark: undefined
|
||||
})
|
||||
if (!this.showIoTypeSelector) {
|
||||
this.editForm.ioType = this.ioType
|
||||
}
|
||||
this.rebuildLineNo()
|
||||
},
|
||||
onDetailSelectionChange(rows) {
|
||||
this.detailSelection = rows || []
|
||||
},
|
||||
removeDetailRows() {
|
||||
const ids = new Set((this.detailSelection || []).map((r) => r.lineNo))
|
||||
this.editDetails = (this.editDetails || []).filter((r) => !ids.has(r.lineNo))
|
||||
this.detailSelection = []
|
||||
if (this.editDetails.length === 0) {
|
||||
this.addDetailRow()
|
||||
} else {
|
||||
this.rebuildLineNo()
|
||||
}
|
||||
},
|
||||
rebuildLineNo() {
|
||||
let i = 1
|
||||
this.editDetails = (this.editDetails || []).map((r) => Object.assign({}, r, { lineNo: i++ }))
|
||||
},
|
||||
onIoTypeChanged() {
|
||||
if (!this.showIoTypeSelector) {
|
||||
return
|
||||
}
|
||||
if (this.editForm.ioType !== 'T') {
|
||||
this.editForm.fromWarehouseId = undefined
|
||||
this.editForm.toWarehouseId = undefined
|
||||
this.editDetails = (this.editDetails || []).map((r) => Object.assign({}, r, { fromWarehouseId: undefined }))
|
||||
}
|
||||
},
|
||||
onItemTypeChanged(row) {
|
||||
if (this.fixedMaterialMode) {
|
||||
row.itemType = 'material'
|
||||
return
|
||||
}
|
||||
row.itemId = undefined
|
||||
row.itemName = undefined
|
||||
row.specName = undefined
|
||||
row.unit = undefined
|
||||
},
|
||||
onProductPicked(row, product) {
|
||||
if (product && product.productName) {
|
||||
row.itemName = product.productName
|
||||
if (!row.unit && product.unit) {
|
||||
row.unit = product.unit
|
||||
}
|
||||
}
|
||||
},
|
||||
onMaterialPicked(row, material) {
|
||||
if (material && material.materialName) {
|
||||
row.itemName = material.materialName
|
||||
if (!row.unit && material.unit) {
|
||||
row.unit = material.unit
|
||||
}
|
||||
if (material.unitPrice != null && row.unitPrice == null) {
|
||||
row.unitPrice = material.unitPrice
|
||||
}
|
||||
if (material.materialType != null) {
|
||||
row._materialType = material.materialType
|
||||
}
|
||||
this.recalcAmount(row)
|
||||
}
|
||||
},
|
||||
recalcAmount(row) {
|
||||
const qty = this.toNumber(row.quantity)
|
||||
const unitPrice = this.toNumber(row.unitPrice)
|
||||
if (qty > 0 || unitPrice > 0) {
|
||||
row.amount = String(unitPrice * qty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user