package com.vincent.rsf.server.api.controller.erp.params;
|
|
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 javax.validation.constraints.*;
|
import java.io.Serializable;
|
import java.math.BigDecimal;
|
|
@Data
|
@Accessors(chain = true)
|
@ApiModel(value = "SyncOrdersItem", description = "单据明细参数")
|
public class SyncOrdersItem implements Serializable {
|
|
private static final long serialVersionUID = 1L;
|
|
/**
|
* 货主类型枚举
|
*/
|
public interface OwnerType {
|
String SUPPLIER = "supplier"; // 供应商
|
String CUSTOMER = "customer"; // 客户
|
String COMPANY = "company"; // 公司
|
String THIRD_PARTY = "third_party"; // 第三方
|
}
|
|
@ApiModelProperty(value = "盘点单明细ID", example = "1001")
|
private Long id;
|
|
@ApiModelProperty(value = "物料标识", example = "2001")
|
private Long matnrId;
|
|
@NotBlank(message = "物料编码不能为空")
|
@Size(max = 50, message = "物料编码长度不能超过50个字符")
|
@ApiModelProperty(value = "物料编码", required = true, example = "MAT001")
|
private String matnr;
|
|
@ApiModelProperty(value = "物料名称", example = "原材料A")
|
private String maktx;
|
|
@ApiModelProperty(value = "客单号", example = "CUST20240101001")
|
private String platOrderCode;
|
|
@NotBlank(message = "平台标识(行号)不能为空")
|
@ApiModelProperty(value = "平台标识(行号)", required = true, example = "10")
|
private String platItemId;
|
|
@ApiModelProperty(value = "工单号", example = "WO20240101001")
|
private String platWorkCode;
|
|
@ApiModelProperty(value = "项目号", example = "PROJ001")
|
private String projectCode;
|
|
@ApiModelProperty(value = "字段索引", example = "ext_001")
|
private String fieldsIndex;
|
|
@ApiModelProperty(value = "规格", example = "10 * 20 * 30")
|
private String spec;
|
|
@ApiModelProperty(value = "型号", example = "MODEL-A")
|
private String model;
|
|
@NotNull(message = "数量不能为空")
|
@DecimalMin(value = "0.0", inclusive = false, message = "数量必须大于0")
|
@Digits(integer = 10, fraction = 3, message = "数量整数位不超过10位,小数位不超过3位")
|
@ApiModelProperty(value = "数量", required = true, example = "100.5")
|
private Double anfme;
|
|
@ApiModelProperty(value = "库存单位", example = "个")
|
private String unit;
|
|
@NotBlank(message = "库存批次不能为空")
|
@Size(max = 50, message = "库存批次长度不能超过50个字符")
|
@ApiModelProperty(value = "库存批次", required = true, example = "BATCH20240101")
|
private String batch;
|
|
@DecimalMin(value = "0.0", message = "已收数量不能为负数")
|
@ApiModelProperty(value = "已收数量", example = "50.0")
|
private Double qty;
|
|
@ApiModelProperty(value = "条形码", example = "6936983800013")
|
private String barcode;
|
|
@ApiModelProperty(value = "现金票号", example = "CASH001")
|
private String crushNo;
|
|
@NotBlank(message = "计划跟踪号不能为空")
|
@Size(max = 50, message = "计划跟踪号长度不能超过50个字符")
|
@ApiModelProperty(value = "计划跟踪号", required = true, example = "PLAN20240101")
|
private String planNo;
|
|
@NotBlank(message = "行内码不能为空")
|
@Size(max = 50, message = "行内码长度不能超过50个字符")
|
@ApiModelProperty(value = "行内码,唯一标识", required = true, example = "LINE001")
|
private String lineId;
|
|
@ApiModelProperty(value = "物料编码", example = "MAT001")
|
private String matNr;
|
|
@ApiModelProperty(value = "物料名称", example = "原材料A")
|
private String makTx;
|
|
@ApiModelProperty(value = "规格", example = "10 * 20 * 30")
|
private String specs;
|
|
@ApiModelProperty(value = "基本单位", example = "PCS")
|
private String baseUnitId;
|
|
@ApiModelProperty(value = "托盘码,半成品/成品入库ERP需要传,非则ERP不需要传", example = "PALLET001")
|
private String palletId;
|
|
@ApiModelProperty(value = "计价单位", example = "BOX")
|
private String priceUnitId;
|
|
@ApiModelProperty(value = "建议目标仓库", example = "WH001")
|
private String targetWareHouseId;
|
|
@ApiModelProperty(value = "调出仓", example = "WH002")
|
private String sourceWareHouseId;
|
|
@ApiModelProperty(value = "入库类型", example = "purchase")
|
private String inStockType;
|
|
@NotBlank(message = "货主类型不能为空")
|
@Pattern(regexp = "^(supplier|customer|company|third_party)$",
|
message = "货主类型必须是supplier、customer、company或third_party")
|
@ApiModelProperty(value = "货主类型", required = true, example = "company")
|
private String ownerTypeId;
|
|
@NotBlank(message = "货主不能为空")
|
@Size(max = 50, message = "货主长度不能超过50个字符")
|
@ApiModelProperty(value = "货主", required = true, example = "OWNER001")
|
private String ownerId;
|
|
@NotBlank(message = "货主名称不能为空")
|
@Size(max = 100, message = "货主名称长度不能超过100个字符")
|
@ApiModelProperty(value = "货主名称", required = true, example = "XX有限公司")
|
private String ownerName;
|
|
@ApiModelProperty(value = "保管者类型", example = "warehouse")
|
private String keeperTypeId;
|
|
@ApiModelProperty(value = "保管者", example = "KEEPER001")
|
private String keeperId;
|
|
@ApiModelProperty(value = "保管者名称", example = "仓库管理部")
|
private String keeperName;
|
|
/**
|
* 获取实际的物料编码
|
* 优先使用 matnr,如果为空则使用 matNr
|
*/
|
@JsonIgnore
|
public String getActualMatnr() {
|
return !Cools.isEmpty(matnr) ? matnr : matNr;
|
}
|
|
/**
|
* 获取实际的物料名称
|
* 优先使用 maktx,如果为空则使用 makTx
|
*/
|
@JsonIgnore
|
public String getActualMaktx() {
|
return !Cools.isEmpty(maktx) ? maktx : makTx;
|
}
|
|
/**
|
* 获取实际的规格
|
* 优先使用 spec,如果为空则使用 specs
|
*/
|
@JsonIgnore
|
public String getActualSpec() {
|
return !Cools.isEmpty(spec) ? spec : specs;
|
}
|
|
/**
|
* 设置物料编码(保持两个字段同步)
|
*/
|
public void setMatnr(String matnr) {
|
this.matnr = matnr;
|
this.matNr = matnr;
|
}
|
|
public void setMatNr(String matNr) {
|
this.matNr = matNr;
|
this.matnr = matNr;
|
}
|
|
/**
|
* 设置物料名称(保持两个字段同步)
|
*/
|
public void setMaktx(String maktx) {
|
this.maktx = maktx;
|
this.makTx = maktx;
|
}
|
|
public void setMakTx(String makTx) {
|
this.makTx = makTx;
|
this.maktx = makTx;
|
}
|
|
/**
|
* 设置规格(保持两个字段同步)
|
*/
|
public void setSpec(String spec) {
|
this.spec = spec;
|
this.specs = spec;
|
}
|
|
public void setSpecs(String specs) {
|
this.specs = specs;
|
this.spec = specs;
|
}
|
|
/**
|
* 转换为BigDecimal(精度计算使用)
|
*/
|
@JsonIgnore
|
public BigDecimal getAnfmeAsBigDecimal() {
|
return anfme != null ? BigDecimal.valueOf(anfme) : BigDecimal.ZERO;
|
}
|
|
@JsonIgnore
|
public BigDecimal getQtyAsBigDecimal() {
|
return qty != null ? BigDecimal.valueOf(qty) : BigDecimal.ZERO;
|
}
|
|
/**
|
* 获取剩余数量
|
*/
|
@JsonIgnore
|
public Double getRemainingQty() {
|
if (anfme == null) {
|
return 0.0;
|
}
|
double currentQty = qty != null ? qty : 0.0;
|
double remaining = anfme - currentQty;
|
return Math.max(0, remaining);
|
}
|
|
/**
|
* 是否已完成
|
*/
|
@JsonIgnore
|
public boolean isCompleted() {
|
if (anfme == null) {
|
return false;
|
}
|
double currentQty = qty != null ? qty : 0.0;
|
return currentQty >= anfme;
|
}
|
|
/**
|
* 获取完成百分比
|
*/
|
@JsonIgnore
|
public int getCompletionPercentage() {
|
if (anfme == null || anfme == 0) {
|
return 0;
|
}
|
double currentQty = qty != null ? qty : 0.0;
|
int percentage = (int) ((currentQty / anfme) * 100);
|
return Math.min(Math.max(percentage, 0), 100);
|
}
|
|
/**
|
* 业务验证
|
*/
|
public void validateBusiness(String orderType) {
|
if ("purchase".equals(inStockType) && Cools.isEmpty(palletId)) {
|
throw new IllegalArgumentException("采购入库明细必须提供托盘码");
|
}
|
|
if ("production".equals(inStockType) && Cools.isEmpty(palletId)) {
|
throw new IllegalArgumentException("生产入库明细必须提供托盘码");
|
}
|
|
if (!Cools.isEmpty(targetWareHouseId) && !Cools.isEmpty(sourceWareHouseId)) {
|
if (targetWareHouseId.equals(sourceWareHouseId)) {
|
throw new IllegalArgumentException("目标仓库和调出仓库不能相同");
|
}
|
}
|
|
if (qty != null && qty > anfme) {
|
throw new IllegalArgumentException("已收数量不能大于计划数量");
|
}
|
}
|
|
/**
|
* 是否需要托盘码
|
*/
|
@JsonIgnore
|
public boolean requiresPallet() {
|
return "purchase".equals(inStockType) ||
|
"production".equals(inStockType) ||
|
"return".equals(inStockType);
|
}
|
|
/**
|
* 是否调拨明细
|
*/
|
@JsonIgnore
|
public boolean isTransferItem() {
|
return !Cools.isEmpty(sourceWareHouseId) && !Cools.isEmpty(targetWareHouseId);
|
}
|
|
/**
|
* 获取物料唯一标识
|
*/
|
@JsonIgnore
|
public String getMaterialKey() {
|
return String.format("%s_%s_%s", getActualMatnr(), batch, getActualSpec());
|
}
|
|
/**
|
* 获取明细摘要
|
*/
|
@JsonIgnore
|
public String getSummary() {
|
return String.format("物料[%s] 批次[%s] 数量[%s/%s]",
|
getActualMatnr(), batch, qty, anfme);
|
}
|
}
|