feat(procurement): implement sample fulfillment and settled fee workflow

This commit is contained in:
2026-10-10 02:19:38 +08:00
parent 040f3bbd93
commit a973be2103
104 changed files with 9823 additions and 714 deletions
@@ -22,11 +22,11 @@ public final class OverseasProcurementRequests {
@Size(max = 500) String productSpecification,
@Size(max = 2000) String productUrl,
@NotNull @Positive Long purchaseQuantity,
@DecimalMin(value = "0.00") BigDecimal budgetUnitPrice,
@Size(min = 3, max = 3) String currency,
@Size(max = 1000) String receivingAddress,
@NotNull @DecimalMin(value = "0.00") @jakarta.validation.constraints.Digits(integer=16,fraction=2) BigDecimal budgetUnitPrice,
@NotBlank @jakarta.validation.constraints.Pattern(regexp="CNY|USD") String currency,
@NotBlank @Size(max = 1000) String receivingAddress,
@NotBlank @Size(max = 128) String contactName,
@NotBlank @Size(max = 64) String contactPhone,
@NotBlank @Size(max = 64) @jakarta.validation.constraints.Pattern(regexp="[+0-9][0-9 ()-]{5,63}") String contactPhone,
@Size(max = 1000) String remark,
@NotBlank @Size(max = 255) String idempotencyKey) {}
@@ -51,38 +51,15 @@ public final class OverseasProcurementRequests {
@NotNull @Min(0) Long version,
@NotBlank String decision,
@NotBlank @Size(max = 255) String idempotencyKey,
@Size(max = 1000) String remark) {}
public record CreateFee(
@NotBlank @Size(max = 32) String feeItemCode,
@NotBlank @Size(max = 128) String feeItemName,
@NotNull @DecimalMin(value = "0.01") BigDecimal amount,
@NotNull @Positive Long voucherFileId,
@Size(max = 500) String remark) {}
@Size(max = 1000) String remark,
@Positive Long purchaseWarehouseId) {}
public record Update(
@NotNull @Min(0) Long version,
@Size(max = 128) String purchaseCategory,
@Size(max = 255) String productName,
JsonNode productSpecification,
JsonNode productUrl,
@Min(0) Long inboundQuantity,
JsonNode purchaseWarehouseId,
JsonNode purchaseWarehouseName,
JsonNode domesticCarrier,
JsonNode domesticTrackingNo,
JsonNode overseasCarrier,
JsonNode overseasTrackingNo,
JsonNode estimatedArrivalAt,
JsonNode remark,
JsonNode outboundDetails) {
public Update(Long version, String purchaseCategory, String productName, JsonNode productSpecification,
JsonNode productUrl, Long inboundQuantity, JsonNode purchaseWarehouseId, JsonNode purchaseWarehouseName,
JsonNode domesticCarrier, JsonNode domesticTrackingNo, JsonNode overseasCarrier, JsonNode overseasTrackingNo,
JsonNode estimatedArrivalAt, JsonNode remark) {
this(version, purchaseCategory, productName, productSpecification, productUrl, inboundQuantity,
purchaseWarehouseId, purchaseWarehouseName, domesticCarrier, domesticTrackingNo, overseasCarrier,
overseasTrackingNo, estimatedArrivalAt, remark, null);
JsonNode remark) {
@com.fasterxml.jackson.annotation.JsonAnySetter
public void rejectUnknownField(String name, JsonNode value) {
throw new IllegalArgumentException("采购通用编辑仅支持备注,业务字段请使用对应履约操作");
}
}
}
@@ -7,14 +7,11 @@ import java.util.List;
public final class OverseasProcurementResponses {
private OverseasProcurementResponses() {}
public record OutboundDetail(String id, long quantity, String carrier, String trackingNo, LocalDateTime shippedAt, String remark) {}
public record Summary(
Long id, String orderNo, String customerId, String customerName,
String purchaseCategory, String productName, String productSpecification,
long purchaseQuantity, BigDecimal budgetUnitPrice, String currency,
String purchaseWarehouseId, String purchaseWarehouseName,
long inboundQuantity, long outboundQuantity, long inventoryQuantity,
String status, long version, LocalDateTime createdAt, LocalDateTime updatedAt) {}
public record CustomerOption(String customerId, String customerName) {}
@@ -38,10 +35,9 @@ public final class OverseasProcurementResponses {
public record Detail(
Summary summary, String productUrl, String receivingAddress,
String contactName, String contactPhone,
String domesticCarrier, String domesticTrackingNo,
String overseasCarrier, String overseasTrackingNo,
LocalDateTime estimatedArrivalAt, String remark,
String createdBy, String reviewedBy, String reviewRemark, LocalDateTime reviewedAt,
List<History> histories, List<OutboundDetail> outboundDetails,
List<History> histories,
List<FileInfo> files, List<Fee> fees) {}
}
@@ -0,0 +1,35 @@
package com.baiyue.lastleg.procurement.dto;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
public final class ProcurementChargeRequests {
private ProcurementChargeRequests() { }
public record Amount(@NotBlank @Pattern(regexp="POSITIVE|ZERO|NOT_APPLICABLE") String determination,
@NotNull @DecimalMin("0") @Digits(integer=16,fraction=2) BigDecimal amount,
@NotBlank @Pattern(regexp="CNY|USD") String currency,@NotNull LocalDate businessDate,
@Positive Long voucherFileId,@NotBlank @Size(max=500) String reason) { }
/** Existing immutable fee OR a newly determined amount; never both. */
public record Selection(@Positive Long feeId,@Valid Amount value) { }
public record Other(@NotBlank @Pattern(regexp="[A-Z][A-Z0-9_]{0,31}") String code,
@NotBlank @Size(max=128) String name,@NotNull @Valid Selection selection) { }
public record Confirm(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@NotNull @Valid Selection internationalFreight,@NotNull @Valid Selection duty,
@NotNull @Valid Selection domesticFreight,
@NotBlank @Pattern(regexp="NOT_APPLICABLE|ITEMIZED") String otherDetermination,
@NotNull List<@NotNull @Valid Other> otherFees,@NotBlank @Size(max=1000) String reason) { }
public record Additional(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@NotBlank @Size(max=128) String name,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=16,fraction=2) BigDecimal amount,
@NotBlank @Pattern(regexp="CNY|USD") String currency,@NotNull @PastOrPresent LocalDate businessDate,
@Positive long voucherFileId,@NotBlank @Size(max=500) String reason) { }
public record Correct(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@NotNull @DecimalMin("0") @Digits(integer=16,fraction=2) BigDecimal targetOriginalAmount,
@Positive long voucherFileId,@NotBlank @Size(max=500) String reason) { }
public record Refund(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=16,fraction=2) BigDecimal amount,
@Positive long voucherFileId,@NotBlank @Size(max=500) String reason) { }
}
@@ -0,0 +1,25 @@
package com.baiyue.lastleg.procurement.dto;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.util.List;
public final class ProcurementControlRequests {
private ProcurementControlRequests() { }
public record Note(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@NotBlank @Pattern(regexp="PURCHASE|WAREHOUSE|RECEIPT|SHIPMENT") String targetType,@Positive Long targetId,
boolean blocking,@Positive long voucherFileId,@NotBlank @Size(max=1000) String reason,
@Size(max=500) String clientSummary) { }
public record Resolve(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@Positive long voucherFileId,@NotBlank @Size(max=1000) String reason) { }
public record WarehouseCorrection(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@Positive long warehouseId,@Positive long voucherFileId,@NotBlank @Size(max=1000) String reason) { }
public record Resolution(@NotBlank @Pattern(regexp="PURCHASE_REMAINDER|TRANSIT_REMAINDER|GOOD|DAMAGED|EXTRA") String kind,
@Positive Long sourceId,@Positive long quantity,
@NotBlank @Pattern(regexp="SUPPLIER_RETAINED|RETURNED_TO_SUPPLIER|RETURNED_TO_SENDER|LOSS_CONFIRMED|CUSTOMER_AUTHORIZED_RETENTION|DISPOSED") String decision,
@NotBlank @Size(max=500) String destination,@Positive long voucherFileId) { }
public record Close(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@NotNull List<@NotNull @Valid Resolution> resolutions,@Positive long customerVoucherFileId,
@NotBlank @Size(max=1000) String financialConclusion,@NotBlank @Size(max=1000) String reason,
@NotBlank @Size(max=500) String clientSummary) { }
}
@@ -0,0 +1,23 @@
package com.baiyue.lastleg.procurement.dto;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public final class ProcurementLifecycleRequests {
private ProcurementLifecycleRequests() { }
public record Command(@NotNull @Min(0) Long version, @NotBlank @Size(max=128) String idempotencyKey,
@NotBlank @Size(max=1000) String reason) { }
public record Purchase(@NotNull @Min(0) Long version, @NotBlank @Size(max=128) String idempotencyKey,
@NotBlank @Size(max=255) String productName, @Size(max=500) String specification,
@Positive long quantity, @NotNull @DecimalMin("0.00") @Digits(integer=16,fraction=2) BigDecimal amount,
@NotBlank @Pattern(regexp="CNY|USD") String currency,
@NotNull @PastOrPresent LocalDateTime purchasedAt, @NotNull LocalDateTime estimatedArrivalAt,
@Positive long voucherFileId, @NotBlank @Size(max=1000) String reason) { }
public record Budget(@NotNull @Min(0) Long version, @NotBlank @Size(max=128) String idempotencyKey,
@NotNull @DecimalMin("0.00") @Digits(integer=16,fraction=2) BigDecimal amount,
@NotBlank @Pattern(regexp="CNY|USD") String currency, @Positive long voucherFileId,
@NotBlank @Size(max=1000) String reason) { }
public record Warehouse(@NotNull @Min(0) Long version, @NotBlank @Size(max=128) String idempotencyKey,
@Positive long warehouseId, @NotBlank @Size(max=1000) String reason) { }
}
@@ -0,0 +1,37 @@
package com.baiyue.lastleg.procurement.dto;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
public final class ProcurementPhysicalRequests {
private ProcurementPhysicalRequests() { }
public enum Leg { OVERSEAS, DOMESTIC }
public record Receipt(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,@NotNull Leg leg,
@Positive Long warehouseId,@Positive Long sourceCartonId,
@Min(0) long goodQuantity,@Min(0) long damagedQuantity,@Min(0) long extraQuantity,
@NotNull @PastOrPresent LocalDateTime receivedAt,@NotBlank @Size(max=1000) String reason) { }
public record FinishReceipt(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,@NotNull Leg leg,
@NotBlank @Size(max=1000) String reason) { }
public record Disposition(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,@NotNull Leg leg,
@Positive Long sourceCartonId,@Positive Long receiptId,
@NotBlank @Pattern(regexp="SHORTAGE|DAMAGED|EXTRA") String kind,
@Positive long quantity,@NotBlank @Pattern(regexp="ACCEPT_SHORTAGE|DISPOSE_DAMAGED|RETURN_EXTRA") String decision,
@Positive long voucherFileId,@NotBlank @Size(max=1000) String reason) { }
public record Source(@Positive long receiptId,@Positive long quantity) { }
public record Correction(@NotNull @Valid Receipt receipt,@Positive long voucherFileId) { }
public record RevokeDisposition(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@Positive long voucherFileId,@NotBlank @Size(max=1000) String reason) { }
public record Carton(@NotBlank @Size(max=64) String cartonNo,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=10,fraction=2) BigDecimal lengthCm,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=10,fraction=2) BigDecimal widthCm,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=10,fraction=2) BigDecimal heightCm,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=9,fraction=3) BigDecimal grossWeightKg,
@NotEmpty List<@NotNull @Valid Source> sources) { }
public record Packing(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@NotEmpty List<@NotNull @Valid Carton> cartons,@NotBlank @Size(max=1000) String reason) { }
public record ConfirmPacking(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@Min(1) int packingVersion,@NotBlank @Size(max=1000) String reason) { }
}
@@ -0,0 +1,38 @@
package com.baiyue.lastleg.procurement.dto;
import com.baiyue.lastleg.procurement.dto.ProcurementPhysicalRequests.Leg;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
public final class ProcurementTransportRequests {
private ProcurementTransportRequests() { }
public record Address(@NotBlank @Size(max=128) String name,
@NotBlank @Pattern(regexp="[+0-9 ()-]{5,64}") String phone,
@NotBlank @Size(max=1000) String address) { }
public record Label(@Positive long cartonId,@Positive long fileId,@Min(1) int page,
@NotBlank @Size(max=128) String carrier,@NotBlank @Size(max=128) String trackingNo) { }
public record Labels(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@NotNull Leg leg,@Min(1) int packingVersion,@NotNull @Valid Address receiver,
@Positive Long addressVoucherFileId,@NotEmpty List<@NotNull @Valid Label> labels,
@NotBlank @Size(max=1000) String reason) { }
public record Estimate(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@Min(1) int labelVersion,@NotNull @DecimalMin("0") @Digits(integer=16,fraction=2) BigDecimal amount,
@NotNull LocalDate businessDate,@NotBlank @Size(max=500) String reason) { }
public record ApplyLabels(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@Min(1) int labelVersion,@NotEmpty List<@NotNull @Positive Long> cartonIds,
@NotNull @PastOrPresent LocalDateTime appliedAt,@NotBlank @Size(max=1000) String reason) { }
public record Ship(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@NotNull Leg leg,@Min(1) int labelVersion,@NotEmpty List<@NotNull @Positive Long> cartonIds,
@NotNull @PastOrPresent LocalDateTime shippedAt,@NotBlank @Size(max=1000) String reason) { }
public record Repack(@NotNull @Min(0) Long version,@NotBlank @Size(max=128) String idempotencyKey,
@Positive long cartonId,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=10,fraction=2) BigDecimal lengthCm,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=10,fraction=2) BigDecimal widthCm,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=10,fraction=2) BigDecimal heightCm,
@NotNull @DecimalMin(value="0",inclusive=false) @Digits(integer=9,fraction=3) BigDecimal grossWeightKg,
@Positive long voucherFileId,@NotBlank @Size(max=1000) String reason) { }
}