| | |
| | | package com.vincent.rsf.server.api.controller.erp.params; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.fasterxml.jackson.annotation.JsonIgnore; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import javax.validation.Valid; |
| | | import javax.validation.constraints.*; |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /* |
| | | * 业务类型,待ERP补充,以下为示例: |
| | | * 入库:收料通知单(PUR_ReceiveBill)、 |
| | | * 采购入库单(STK_InStock)、 |
| | | * 退料申请单(PUR_MRAPP)、 |
| | | * 采购退料单(PUR_MRB)、 |
| | | * 退货通知单(SAL_RETURNNOTICE)、 |
| | | * 销售退货单(SAL_RETURNSTOCK)、 |
| | | * 生产退料单(PRD_ReturnMtrl)、 |
| | | * 生产入库单(PRD_INSTOCK)/ |
| | | * 生产汇报单(PRD_MORPT)、 |
| | | * 其他入库单(STK_MISCELLANEOUS) |
| | | * 出库:发货通知单(SAL_DELIVERYNOTICE)、 |
| | | * 销售出库单(SAL_OUTSTOCK)、 |
| | | * 出库申请单(STK_OutStockApply)、 |
| | | * 生产领料单(PRD_PickMtrl)、 |
| | | * 生产补料单(PRD_FeedMtrl)、 |
| | | * 其他出库单(STK_MisDelivery)调拨: |
| | | * 调拨申请单(STK_TRANSFERAPPLY)、 |
| | | * 直接调拨单(STK_TransferDirect) |
| | | * */ |
| | | @ApiModelProperty("业务类型") |
| | | /** |
| | | * 业务类型枚举 |
| | | */ |
| | | public interface BusinessType { |
| | | // 入库单据 |
| | | String PUR_RECEIVE_BILL = "PUR_ReceiveBill"; // 收料通知单 |
| | | String STK_IN_STOCK = "STK_InStock"; // 采购入库单 |
| | | String PUR_MRAPP = "PUR_MRAPP"; // 退料申请单 |
| | | String PUR_MRB = "PUR_MRB"; // 采购退料单 |
| | | String SAL_RETURNNOTICE = "SAL_RETURNNOTICE"; // 退货通知单 |
| | | String SAL_RETURNSTOCK = "SAL_RETURNSTOCK"; // 销售退货单 |
| | | String PRD_RETURN_MTRL = "PRD_ReturnMtrl"; // 生产退料单 //接驳 |
| | | String PRD_INSTOCK = "PRD_INSTOCK"; // 生产入库单 //接驳 |
| | | String PRD_MORPT = "PRD_MORPT"; // 生产汇报单 |
| | | String STK_MISCELLANEOUS = "STK_MISCELLANEOUS"; // 其他入库单 |
| | | // 出库单据 |
| | | String SAL_DELIVERYNOTICE = "SAL_DELIVERYNOTICE"; // 发货通知单 |
| | | String SAL_OUTSTOCK = "SAL_OUTSTOCK"; // 销售出库单 |
| | | String STK_OUTSTOCK_APPLY = "STK_OutStockApply"; // 出库申请单 |
| | | String PRD_PICK_MTRL = "PRD_PickMtrl"; // 生产领料单 //接驳 |
| | | String PRD_FEED_MTRL = "PRD_FeedMtrl"; // 生产补料单 //接驳 |
| | | String STK_MIS_DELIVERY = "STK_MisDelivery"; // 其他出库单 |
| | | // 调拨单据 |
| | | String STK_TRANSFER_APPLY = "STK_TRANSFERAPPLY"; // 调拨申请单 |
| | | String STK_TRANSFER_DIRECT = "STK_TransferDirect"; // 直接调拨单 |
| | | } |
| | | |
| | | /** |
| | | * 单据类型枚举 |
| | | */ |
| | | public interface OrderType { |
| | | String OUT_STOCK = "1"; // 出库单 |
| | | String IN_STOCK = "2"; // 入库单 |
| | | String TRANSFER = "3"; // 调拨单 |
| | | } |
| | | |
| | | @NotBlank(message = "业务类型不能为空") |
| | | @Pattern(regexp = "^(PUR_ReceiveBill|STK_InStock|PUR_MRAPP|PUR_MRB|SAL_RETURNNOTICE|SAL_RETURNSTOCK|" |
| | | + "PRD_ReturnMtrl|PRD_INSTOCK|PRD_MORPT|STK_MISCELLANEOUS|SAL_DELIVERYNOTICE|SAL_OUTSTOCK|" |
| | | + "STK_OutStockApply|PRD_PickMtrl|PRD_FeedMtrl|STK_MisDelivery|STK_TRANSFERAPPLY|STK_TransferDirect)$", |
| | | message = "业务类型格式不正确") |
| | | @ApiModelProperty(value = "业务类型", required = true, example = "STK_InStock") |
| | | private String wkType; |
| | | |
| | | |
| | | /* |
| | | * 订单类型,1 出库单;2 入库单;3 调拨单; |
| | | * */ |
| | | @ApiModelProperty("单据类型") |
| | | @NotBlank(message = "单据类型不能为空") |
| | | @Pattern(regexp = "^[123]$", message = "单据类型只能是1(出库单)、2(入库单)或3(调拨单)") |
| | | @ApiModelProperty(value = "单据类型: 1-出库单, 2-入库单, 3-调拨单", required = true, example = "2") |
| | | private String type; |
| | | |
| | | @ApiModelProperty("单号") |
| | | @NotBlank(message = "单号不能为空") |
| | | @Size(max = 50, message = "单号长度不能超过50个字符") |
| | | @ApiModelProperty(value = "单号", required = true, example = "PO202401010001") |
| | | private String orderNo; |
| | | |
| | | @ApiModelProperty("单据内码,唯一标识") |
| | | @NotNull(message = "单据内码不能为空") |
| | | @Positive(message = "单据内码必须是正整数") |
| | | @ApiModelProperty(value = "单据内码,唯一标识", required = true, example = "100001") |
| | | private Long orderInternalCode; |
| | | |
| | | @ApiModelProperty("订单ID") |
| | | // @NotNull(message = "订单ID不能为空") |
| | | // @Positive(message = "订单ID必须是正整数") |
| | | // @ApiModelProperty(value = "订单ID", required = true, example = "200001") |
| | | private Long orderId; |
| | | |
| | | @ApiModelProperty("数量") |
| | | // @NotNull(message = "数量不能为空") |
| | | // @DecimalMin(value = "0.0", inclusive = false, message = "数量必须大于0") |
| | | // @ApiModelProperty(value = "数量", required = true, example = "100.5") |
| | | private Double anfme; |
| | | |
| | | @ApiModelProperty("客户编码") |
| | | @ApiModelProperty(value = "客户编码", example = "CUST001") |
| | | private String customerId; |
| | | |
| | | @ApiModelProperty("客户名称") |
| | | @ApiModelProperty(value = "客户名称", example = "XX科技有限公司") |
| | | private String customerName; |
| | | |
| | | @ApiModelProperty("供应商编码") |
| | | @ApiModelProperty(value = "供应商编码", example = "SUP001") |
| | | private String supplierId; |
| | | |
| | | @ApiModelProperty("供应商名称") |
| | | @ApiModelProperty(value = "供应商名称", example = "XX供应商") |
| | | private String supplierName; |
| | | |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:ss:mm") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:ss:mm") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | @ApiModelProperty(value = "到达时间", example = "2024-01-01 10:00:00") |
| | | private Date arrTime; |
| | | |
| | | @ApiModelProperty("创建日期,时间戳,精确到秒") |
| | | @NotNull(message = "创建日期不能为空") |
| | | @Positive(message = "创建日期必须是正整数") |
| | | @ApiModelProperty(value = "创建日期,时间戳,精确到秒", required = true, example = "1704067200") |
| | | private Long createTime; |
| | | |
| | | @ApiModelProperty("业务日期,对账使用,时间戳,精确到秒") |
| | | @NotNull(message = "业务日期不能为空") |
| | | @Positive(message = "业务日期必须是正整数") |
| | | @ApiModelProperty(value = "业务日期,对账使用,时间戳,精确到秒", required = true, example = "1704067200") |
| | | private Long businessTime; |
| | | |
| | | @ApiModelProperty("单据明细信息") |
| | | @Valid |
| | | @NotNull(message = "单据明细信息不能为空") |
| | | @Size(min = 1, message = "至少需要一个明细项") |
| | | @ApiModelProperty(value = "单据明细信息", required = true) |
| | | private List<SyncOrdersItem> orderItems; |
| | | |
| | | @ApiModelProperty("收料/发货组织") |
| | | @ApiModelProperty(value = "收料/发货组织", example = "ORG001") |
| | | private String stockOrgId; |
| | | |
| | | @ApiModelProperty("收料/发货组织名称") |
| | | @ApiModelProperty(value = "收料/发货组织名称", example = "发货部") |
| | | private String stockOrgName; |
| | | |
| | | @ApiModelProperty("采购组织") |
| | | @ApiModelProperty(value = "采购组织", example = "PUR001") |
| | | private String purchaseOrgId; |
| | | |
| | | @ApiModelProperty("采购组织名称") |
| | | @ApiModelProperty(value = "采购组织名称", example = "采购部") |
| | | private String purchaseOrgName; |
| | | |
| | | @ApiModelProperty("采购员") |
| | | @ApiModelProperty(value = "采购员", example = "USER001") |
| | | private String purchaseUserId; |
| | | |
| | | @ApiModelProperty("采购员名称") |
| | | @ApiModelProperty(value = "采购员名称", example = "张三") |
| | | private String purchaseUserName; |
| | | |
| | | @ApiModelProperty("生产组织") |
| | | @ApiModelProperty(value = "生产组织", example = "PRD001") |
| | | private String prdOrgId; |
| | | |
| | | @ApiModelProperty("生产组织名称") |
| | | @ApiModelProperty(value = "生产组织名称", example = "生产部") |
| | | private String prdOrgName; |
| | | |
| | | @ApiModelProperty("销售组织") |
| | | @ApiModelProperty(value = "销售组织", example = "SALE001") |
| | | private String saleOrgId; |
| | | |
| | | @ApiModelProperty("销售组织名称") |
| | | @ApiModelProperty(value = "销售组织名称", example = "销售部") |
| | | private String saleOrgName; |
| | | |
| | | @ApiModelProperty("销售员") |
| | | @ApiModelProperty(value = "销售员", example = "USER002") |
| | | private String saleUserId; |
| | | |
| | | @ApiModelProperty("销售员名称") |
| | | @ApiModelProperty(value = "销售员名称", example = "李四") |
| | | private String saleUserName; |
| | | |
| | | @ApiModelProperty("库存方向") |
| | | @ApiModelProperty(value = "库存方向", example = "IN") |
| | | private String stockDirect; |
| | | |
| | | @ApiModelProperty("出入库接驳站点,出库时将物料出库后运输至该站点,入库时从该站点将物料运回库中") |
| | | @ApiModelProperty(value = "出入库接驳站点,出库时将物料出库后运输至该站点,入库时从该站点将物料运回库中", example = "STATION001") |
| | | private String stationId; |
| | | } |
| | | |
| | | /** |
| | | * 获取实际到达时间 |
| | | * 优先使用 arrTime,如果为空则从 createTime 转换 |
| | | */ |
| | | @JsonIgnore |
| | | public Date getActualArrTime() { |
| | | if (this.arrTime != null) { |
| | | return this.arrTime; |
| | | } |
| | | if (this.createTime != null) { |
| | | return new Date(this.createTime * 1000L); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 获取实际业务时间 |
| | | */ |
| | | @JsonIgnore |
| | | public Date getActualBusinessTime() { |
| | | if (this.businessTime != null) { |
| | | return new Date(this.businessTime * 1000L); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 获取实际创建时间 |
| | | */ |
| | | @JsonIgnore |
| | | public Date getActualCreateTime() { |
| | | if (this.createTime != null) { |
| | | return new Date(this.createTime * 1000L); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 是否入库单 |
| | | */ |
| | | @JsonIgnore |
| | | public boolean isInStockOrder() { |
| | | return OrderType.IN_STOCK.equals(this.type); |
| | | } |
| | | |
| | | /** |
| | | * 是否出库单 |
| | | */ |
| | | @JsonIgnore |
| | | public boolean isOutStockOrder() { |
| | | return OrderType.OUT_STOCK.equals(this.type); |
| | | } |
| | | |
| | | /** |
| | | * 是否调拨单 |
| | | */ |
| | | @JsonIgnore |
| | | public boolean isTransferOrder() { |
| | | return OrderType.TRANSFER.equals(this.type); |
| | | } |
| | | |
| | | /** |
| | | * 业务验证 |
| | | */ |
| | | public void validateBusiness() { |
| | | // if (isOutStockOrder() && Cools.isEmpty(customerId)) { |
| | | // throw new IllegalArgumentException("出库单必须指定客户"); |
| | | // } |
| | | // |
| | | // if (isInStockOrder() && Cools.isEmpty(supplierId)) { |
| | | // throw new IllegalArgumentException("入库单必须指定供应商"); |
| | | // } |
| | | |
| | | // if (Cools.isEmpty(stationId) && !isTransferOrder()) { |
| | | // throw new IllegalArgumentException("必须指定接驳站点"); |
| | | // } |
| | | |
| | | // 验证明细总数与主单数量是否一致 |
| | | // if (orderItems != null && anfme != null) { |
| | | // double itemsTotal = orderItems.stream() |
| | | // .mapToDouble(item -> item.getAnfme() != null ? item.getAnfme() : 0.0) |
| | | // .sum(); |
| | | // |
| | | // if (Math.abs(itemsTotal - anfme) > 0.001) { |
| | | // throw new IllegalArgumentException(String.format( |
| | | // "主单数量(%.3f)与明细总数(%.3f)不一致", anfme, itemsTotal)); |
| | | // } |
| | | // } |
| | | } |
| | | |
| | | /** |
| | | * 计算明细总数量 |
| | | */ |
| | | @JsonIgnore |
| | | public Double calculateItemsTotal() { |
| | | if (orderItems == null || orderItems.isEmpty()) { |
| | | return 0.0; |
| | | } |
| | | |
| | | return orderItems.stream() |
| | | .mapToDouble(item -> item.getAnfme() != null ? item.getAnfme() : 0.0) |
| | | .sum(); |
| | | } |
| | | |
| | | /** |
| | | * 获取单据摘要 |
| | | */ |
| | | @JsonIgnore |
| | | public String getSummary() { |
| | | return String.format("单据[%s] 类型[%s] 业务类型[%s] 单据内码[%s]", |
| | | orderNo, type, wkType, orderInternalCode); |
| | | } |
| | | |
| | | /** |
| | | * 是否有效单据 |
| | | */ |
| | | @JsonIgnore |
| | | public boolean isValid() { |
| | | return !Cools.isEmpty(orderNo) && |
| | | orderInternalCode != null && !Cools.isEmpty(orderInternalCode) && |
| | | type != null && !Cools.isEmpty(type) && |
| | | wkType != null && !Cools.isEmpty(wkType) && |
| | | createTime != null && !Cools.isEmpty(createTime) && |
| | | businessTime != null && !Cools.isEmpty(businessTime) && |
| | | orderItems != null && !orderItems.isEmpty(); |
| | | } |
| | | } |