| | |
| | | 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.experimental.Accessors; |
| | | 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 = "SyncOrdersParams", description = "同步盘点参数") |
| | | @ApiModel(value = "SyncOrdersItem", description = "单据明细参数") |
| | | public class SyncOrdersItem implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @ApiModelProperty("盘点单明细ID") |
| | | /** |
| | | * 货主类型枚举 |
| | | */ |
| | | 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("物料标识") |
| | | @ApiModelProperty(value = "物料标识", example = "2001") |
| | | private Long matnrId; |
| | | |
| | | @ApiModelProperty("物料编码") |
| | | private String matnrCode; |
| | | @NotBlank(message = "物料编码不能为空") |
| | | @Size(max = 50, message = "物料编码长度不能超过50个字符") |
| | | @ApiModelProperty(value = "物料编码", required = true, example = "MAT001") |
| | | private String matnr; |
| | | |
| | | @ApiModelProperty("物料名称") |
| | | @ApiModelProperty(value = "物料名称", example = "原材料A") |
| | | private String maktx; |
| | | |
| | | @ApiModelProperty("客单号") |
| | | @ApiModelProperty(value = "客单号", example = "CUST20240101001") |
| | | private String platOrderCode; |
| | | |
| | | @ApiModelProperty("平台标识(行号)") |
| | | @NotBlank(message = "平台标识(行号)不能为空") |
| | | @ApiModelProperty(value = "平台标识(行号)", required = true, example = "10") |
| | | private String platItemId; |
| | | |
| | | @ApiModelProperty("工单号") |
| | | @ApiModelProperty(value = "工单号", example = "WO20240101001") |
| | | private String platWorkCode; |
| | | |
| | | @ApiModelProperty("项目号") |
| | | @ApiModelProperty(value = "项目号", example = "PROJ001") |
| | | private String projectCode; |
| | | |
| | | @ApiModelProperty("字段索引") |
| | | @ApiModelProperty(value = "字段索引", example = "ext_001") |
| | | private String fieldsIndex; |
| | | |
| | | @ApiModelProperty("规格") |
| | | @ApiModelProperty(value = "规格", example = "10 * 20 * 30") |
| | | private String spec; |
| | | |
| | | @ApiModelProperty("型号") |
| | | @ApiModelProperty(value = "型号", example = "MODEL-A") |
| | | private String model; |
| | | |
| | | @ApiModelProperty("数量") |
| | | @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("库存单位") |
| | | @ApiModelProperty(value = "库存单位", example = "个") |
| | | private String unit; |
| | | |
| | | @ApiModelProperty("库存批次") |
| | | @NotBlank(message = "库存批次不能为空") |
| | | @Size(max = 50, message = "库存批次长度不能超过50个字符") |
| | | @ApiModelProperty(value = "库存批次", required = true, example = "BATCH20240101") |
| | | private String batch; |
| | | |
| | | @ApiModelProperty("已收数量") |
| | | @DecimalMin(value = "0.0", message = "已收数量不能为负数") |
| | | @ApiModelProperty(value = "已收数量", example = "50.0") |
| | | private Double qty; |
| | | |
| | | @ApiModelProperty("条形码") |
| | | @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); |
| | | } |
| | | } |