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; @Data @Accessors(chain = true) @ApiModel(value = "SyncOrderParams", description = "单据同步参数") public class SyncOrderParams implements Serializable { private static final long serialVersionUID = 1L; /** * 业务类型枚举 */ 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; @NotBlank(message = "单据类型不能为空") @Pattern(regexp = "^[123]$", message = "单据类型只能是1(出库单)、2(入库单)或3(调拨单)") @ApiModelProperty(value = "单据类型: 1-出库单, 2-入库单, 3-调拨单", required = true, example = "2") private String type; @NotBlank(message = "单号不能为空") @Size(max = 50, message = "单号长度不能超过50个字符") @ApiModelProperty(value = "单号", required = true, example = "PO202401010001") private String orderNo; @NotNull(message = "单据内码不能为空") @Positive(message = "单据内码必须是正整数") @ApiModelProperty(value = "单据内码,唯一标识", required = true, example = "100001") private Long orderInternalCode; // @NotNull(message = "订单ID不能为空") // @Positive(message = "订单ID必须是正整数") // @ApiModelProperty(value = "订单ID", required = true, example = "200001") private Long orderId; // @NotNull(message = "数量不能为空") // @DecimalMin(value = "0.0", inclusive = false, message = "数量必须大于0") // @ApiModelProperty(value = "数量", required = true, example = "100.5") private Double anfme; @ApiModelProperty(value = "客户编码", example = "CUST001") private String customerId; @ApiModelProperty(value = "客户名称", example = "XX科技有限公司") private String customerName; @ApiModelProperty(value = "供应商编码", example = "SUP001") private String supplierId; @ApiModelProperty(value = "供应商名称", example = "XX供应商") private String supplierName; @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; @NotNull(message = "创建日期不能为空") @Positive(message = "创建日期必须是正整数") @ApiModelProperty(value = "创建日期,时间戳,精确到秒", required = true, example = "1704067200") private Long createTime; @NotNull(message = "业务日期不能为空") @Positive(message = "业务日期必须是正整数") @ApiModelProperty(value = "业务日期,对账使用,时间戳,精确到秒", required = true, example = "1704067200") private Long businessTime; @Valid @NotNull(message = "单据明细信息不能为空") @Size(min = 1, message = "至少需要一个明细项") @ApiModelProperty(value = "单据明细信息", required = true) private List orderItems; @ApiModelProperty(value = "收料/发货组织", example = "ORG001") private String stockOrgId; @ApiModelProperty(value = "收料/发货组织名称", example = "发货部") private String stockOrgName; @ApiModelProperty(value = "采购组织", example = "PUR001") private String purchaseOrgId; @ApiModelProperty(value = "采购组织名称", example = "采购部") private String purchaseOrgName; @ApiModelProperty(value = "采购员", example = "USER001") private String purchaseUserId; @ApiModelProperty(value = "采购员名称", example = "张三") private String purchaseUserName; @ApiModelProperty(value = "生产组织", example = "PRD001") private String prdOrgId; @ApiModelProperty(value = "生产组织名称", example = "生产部") private String prdOrgName; @ApiModelProperty(value = "销售组织", example = "SALE001") private String saleOrgId; @ApiModelProperty(value = "销售组织名称", example = "销售部") private String saleOrgName; @ApiModelProperty(value = "销售员", example = "USER002") private String saleUserId; @ApiModelProperty(value = "销售员名称", example = "李四") private String saleUserName; @ApiModelProperty(value = "库存方向", example = "IN") private String stockDirect; @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(); } }