提交
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ public class PdaCargoDiscardController {
|
||||
}
|
||||
@PostMapping("/resolve") public ApiResponse<PdaCargoDiscardDtos.PalletTask> resolve(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody PdaCargoDiscardDtos.Scan request) {
|
||||
return ApiResponse.success(service.resolve(PdaWarehouseIdentity.require(user), request.palletNo()));
|
||||
return ApiResponse.success(service.resolve(PdaWarehouseIdentity.require(user), request.palletNo(), request.discardNo()));
|
||||
}
|
||||
@PostMapping(value = "/{discardNo}/pallets/{palletNo}/photos/files", consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public ApiResponse<com.baiyue.lastleg.discard.dto.CargoDiscardResponses.Photo> uploadPhoto(
|
||||
|
||||
+9
-3
@@ -10,12 +10,18 @@ import java.util.List;
|
||||
|
||||
public final class PdaCargoDiscardDtos {
|
||||
private PdaCargoDiscardDtos() { }
|
||||
public record Scan(@NotBlank @Size(max = 128) String palletNo) { }
|
||||
public record Scan(@NotBlank @Size(max = 128) String palletNo, @Size(max = 128) String discardNo) {
|
||||
public Scan(String palletNo) { this(palletNo, null); }
|
||||
}
|
||||
public record ActualLine(@NotNull Long itemId, @PositiveOrZero int actualPackages) { }
|
||||
public record Confirm(@NotEmpty List<@Valid ActualLine> lines,
|
||||
@NotEmpty @Size(max = 20) List<@NotNull @jakarta.validation.constraints.Positive Long> photoFileIds) { }
|
||||
@NotEmpty @Size(max = 20) List<@NotNull @jakarta.validation.constraints.Positive Long> photoFileIds,
|
||||
List<@NotBlank String> scannedCartonMarks, Boolean confirmAllCartons) {
|
||||
public Confirm(List<ActualLine> lines, List<Long> photoFileIds) { this(lines, photoFileIds, null, false); }
|
||||
}
|
||||
public record Sku(Long itemId, String skuCode, String productName, int plannedPackages,
|
||||
Integer actualPackages, String status) { }
|
||||
public record PalletTask(String discardNo, String customerId, String overseasWarehouseId,
|
||||
String palletNo, Long profileId, String status, List<Sku> skus) { }
|
||||
String palletNo, Long profileId, String status, List<Sku> skus, String outboundNo,
|
||||
String scanMode, List<String> cartonMarks) { }
|
||||
}
|
||||
|
||||
+59
-21
@@ -25,12 +25,14 @@ import java.util.stream.Collectors;
|
||||
public class PdaCargoDiscardService {
|
||||
@Resource private com.baiyue.lastleg.discard.service.CargoDiscardPhotoService photoService;
|
||||
@Resource private CargoDiscardApplicationMapper applications;
|
||||
@Resource private com.baiyue.lastleg.discard.service.CargoDiscardOutboundService outboundService;
|
||||
@Resource private CargoDiscardItemMapper items;
|
||||
@Resource private CargoDiscardCartonMapper cartons;
|
||||
@Resource private DropshipOrderProfileMapper profiles;
|
||||
@Resource private CargoDiscardInventoryAdjustmentService adjustments;
|
||||
@Resource private CargoDiscardService discardService;
|
||||
@Resource private ReturnInventoryCartonMapper returnCartons;
|
||||
@Resource private com.baiyue.lastleg.discard.service.CargoDiscardStockService stockService;
|
||||
|
||||
public List<PdaCargoDiscardDtos.PalletTask> tasks(PdaWarehouseIdentity identity) {
|
||||
return applications.selectList(new LambdaQueryWrapper<CargoDiscardApplication>()
|
||||
@@ -41,7 +43,14 @@ public class PdaCargoDiscardService {
|
||||
}
|
||||
|
||||
public PdaCargoDiscardDtos.PalletTask resolve(PdaWarehouseIdentity identity, String palletNo) {
|
||||
return tasks(identity).stream().filter(t -> t.palletNo().equals(palletNo.trim())).findFirst()
|
||||
return resolve(identity, palletNo, null);
|
||||
}
|
||||
|
||||
public PdaCargoDiscardDtos.PalletTask resolve(PdaWarehouseIdentity identity, String palletNo, String discardNo) {
|
||||
var matches = tasks(identity).stream().filter(t -> t.palletNo().equals(palletNo.trim()) || t.cartonMarks().contains(palletNo.trim()))
|
||||
.filter(t -> discardNo == null || discardNo.isBlank() || t.discardNo().equals(discardNo.trim())).toList();
|
||||
if (matches.size() > 1) throw conflict("该托盘有多个废弃任务,请先选择废弃单");
|
||||
return matches.stream().findFirst()
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "该仓库没有此托盘的待废弃任务"));
|
||||
}
|
||||
|
||||
@@ -62,12 +71,19 @@ public class PdaCargoDiscardService {
|
||||
if (!actuals.keySet().equals(task.skus().stream().map(PdaCargoDiscardDtos.Sku::itemId).collect(Collectors.toSet()))) {
|
||||
throw conflict("必须提交该托盘全部 SKU 的实际废弃数量");
|
||||
}
|
||||
if (task.skus().stream().anyMatch(line -> !Objects.equals(actuals.get(line.itemId()), line.plannedPackages())))
|
||||
throw conflict("必须完成本次废弃单所选全部箱子,不能漏出");
|
||||
if ("CARTON".equals(task.scanMode()) && !Boolean.TRUE.equals(request.confirmAllCartons())) {
|
||||
var scanned = request.scannedCartonMarks() == null ? List.<String>of() : request.scannedCartonMarks();
|
||||
if (scanned.size() != task.cartonMarks().size() || !new HashSet<>(scanned).equals(new HashSet<>(task.cartonMarks())))
|
||||
throw conflict("必须扫描全部箱唛,或选择一键全部废弃");
|
||||
}
|
||||
List<CargoDiscardItem> palletItems = new ArrayList<>();
|
||||
for (PdaCargoDiscardDtos.Sku line : task.skus()) {
|
||||
CargoDiscardItem item = items.selectById(line.itemId());
|
||||
if (item == null || !application.getCustomerId().equals(item.getCustomerId())
|
||||
|| !Objects.equals(application.getId(), item.getApplicationId())
|
||||
|| !Objects.equals(task.profileId(), isReturnItem(item) ? item.getReturnCartonId() : item.getProfileId()))
|
||||
|| !physicalCode(application, item).equals(task.palletNo()))
|
||||
throw conflict("废弃明细不属于当前申请或托盘");
|
||||
if ("COMPLETED".equals(item.getPdaStatus())
|
||||
&& !Objects.equals(item.getActualDiscardPackages(), actuals.get(item.getId())))
|
||||
@@ -108,6 +124,7 @@ public class PdaCargoDiscardService {
|
||||
discardService.updateLinkedStatus(application.getCustomerId(), application, CargoDiscardService.APPROVED,
|
||||
CargoDiscardService.PDA_COMPLETED, identity.operatorKey(), "全部托盘 PDA 废弃作业完成,待仓库确认");
|
||||
}
|
||||
outboundService.sync(application);
|
||||
return palletTasks(application).stream().filter(value -> value.palletNo().equals(palletNo.trim()))
|
||||
.findFirst().orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "托盘不属于该废弃单"));
|
||||
}
|
||||
@@ -142,27 +159,48 @@ public class PdaCargoDiscardService {
|
||||
return application;
|
||||
}
|
||||
|
||||
private static final String LOOSE = "散箱";
|
||||
private List<PdaCargoDiscardDtos.PalletTask> palletTasks(CargoDiscardApplication application) {
|
||||
return items.listApplication(application.getCustomerId(), application.getId()).stream()
|
||||
.collect(Collectors.groupingBy(this::physicalGroupKey, LinkedHashMap::new, Collectors.toList()))
|
||||
.entrySet().stream().map(entry -> {
|
||||
CargoDiscardItem first = entry.getValue().get(0);
|
||||
boolean returnSource = isReturnItem(first);
|
||||
DropshipOrderProfile profile = returnSource ? null : profiles.selectById(first.getProfileId());
|
||||
ReturnInventoryCarton returnCarton = returnSource ? returnCartons.selectById(first.getReturnCartonId()) : null;
|
||||
if (!returnSource && (profile == null || !application.getCustomerId().equals(profile.getCustomerId()))) throw conflict("废弃明细关联托盘不存在");
|
||||
if (returnSource && (returnCarton == null || !application.getCustomerId().equals(returnCarton.getCustomerId()))) throw conflict("废弃明细关联退货箱不存在");
|
||||
List<PdaCargoDiscardDtos.Sku> skus = entry.getValue().stream().map(i -> new PdaCargoDiscardDtos.Sku(
|
||||
i.getId(), i.getSkuCode(), i.getProductName(), i.getDiscardPackages(), i.getActualDiscardPackages(),
|
||||
i.getPdaStatus() == null ? "PENDING" : i.getPdaStatus())).toList();
|
||||
String status = skus.stream().allMatch(s -> "COMPLETED".equals(s.status())) ? "COMPLETED" : "PENDING";
|
||||
return new PdaCargoDiscardDtos.PalletTask(application.getDiscardNo(), application.getCustomerId(),
|
||||
application.getOverseasWarehouseId(), returnSource ? firstNonBlank(returnCarton.getPalletCode(), returnCarton.getCartonMark()) : profile.getPalletNo(),
|
||||
returnSource ? returnCarton.getId() : profile.getId(), status, skus);
|
||||
}).toList();
|
||||
var allItems = items.listApplication(application.getCustomerId(), application.getId());
|
||||
var grouped = allItems.stream().collect(Collectors.groupingBy(i -> physicalCode(application, i), LinkedHashMap::new, Collectors.toList()));
|
||||
return grouped.entrySet().stream().map(entry -> {
|
||||
CargoDiscardItem first = entry.getValue().get(0);
|
||||
var skus = entry.getValue().stream().map(i -> new PdaCargoDiscardDtos.Sku(i.getId(), i.getSkuCode(), i.getProductName(),
|
||||
i.getDiscardPackages(), i.getActualDiscardPackages(), i.getPdaStatus() == null ? "PENDING" : i.getPdaStatus())).toList();
|
||||
String status = skus.stream().allMatch(i -> "COMPLETED".equals(i.status())) ? "COMPLETED" : "PENDING";
|
||||
var marks = entry.getValue().stream().flatMap(i -> cartonMarks(application, i).stream()).distinct().sorted().toList();
|
||||
boolean cartonScan = LOOSE.equals(entry.getKey()) || entry.getValue().stream().anyMatch(i -> Boolean.TRUE.equals(i.getCartonScanRequired()));
|
||||
if (cartonScan && marks.isEmpty()) throw conflict("散箱废弃任务缺少箱唛");
|
||||
return new PdaCargoDiscardDtos.PalletTask(application.getDiscardNo(), application.getCustomerId(), application.getOverseasWarehouseId(),
|
||||
entry.getKey(), first.getInventoryPositionId() != null ? first.getInventoryPositionId() : isReturnItem(first) ? first.getReturnCartonId() : first.getProfileId(),
|
||||
status, skus, outboundService.outboundNo(application), cartonScan ? "CARTON" : "PALLET", marks);
|
||||
}).toList();
|
||||
}
|
||||
private String physicalGroupKey(CargoDiscardItem item) {
|
||||
return isReturnItem(item) ? "RETURN:" + item.getReturnCartonId() : "DROPSHIP:" + item.getProfileId();
|
||||
private String physicalCode(CargoDiscardApplication application, CargoDiscardItem item) {
|
||||
String code;
|
||||
if ("INVENTORY".equals(item.getSourceType())) {
|
||||
var position = stockService.position(application.getCustomerId(), item.getInventoryPositionId());
|
||||
if (!application.getOverseasWarehouseId().equals(position.getOverseasWarehouseId())) throw conflict("废弃库存不属于当前仓库");
|
||||
code = position.getPalletNo();
|
||||
} else if (isReturnItem(item)) {
|
||||
var carton = returnCartons.selectById(item.getReturnCartonId());
|
||||
if (carton == null || !application.getCustomerId().equals(carton.getCustomerId())) throw conflict("废弃明细关联退货箱不存在");
|
||||
code = carton.getPalletCode();
|
||||
} else {
|
||||
var profile = profiles.selectById(item.getProfileId());
|
||||
if (profile == null || !application.getCustomerId().equals(profile.getCustomerId())) throw conflict("废弃明细关联托盘不存在");
|
||||
code = profile.getPalletNo();
|
||||
}
|
||||
return code == null || code.isBlank() ? LOOSE : code.trim();
|
||||
}
|
||||
private List<String> cartonMarks(CargoDiscardApplication application, CargoDiscardItem item) {
|
||||
if ("INVENTORY".equals(item.getSourceType())) return stockService.cartonMarks(item);
|
||||
if (isReturnItem(item)) {
|
||||
var carton = returnCartons.selectById(item.getReturnCartonId());
|
||||
return carton.getCartonMark() == null ? List.of() : List.of(carton.getCartonMark());
|
||||
}
|
||||
return cartons.listApplication(application.getCustomerId(), application.getId()).stream()
|
||||
.filter(c -> Objects.equals(c.getItemId(), item.getId())).map(CargoDiscardCarton::getCartonMark).filter(Objects::nonNull).toList();
|
||||
}
|
||||
private boolean isReturnItem(CargoDiscardItem item) {
|
||||
return "RETURN_CARTON".equals(item.getSourceType()) || item.getReturnCartonId() != null;
|
||||
|
||||
Reference in New Issue
Block a user