1
1 天以前 9f43ee66e8fa2e0d02945f4bdd40d9c3a53a4bd7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
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<SyncOrdersItem> 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();
    }
}