优化
This commit is contained in:
@@ -216,12 +216,19 @@ public interface DropshipPalletizationMapper {
|
||||
""") List<CartonRow> cartons(@Param("palletId") long palletId);
|
||||
@Select("SELECT c.*,p.pallet_no FROM fl_dropship_pallet_carton c LEFT JOIN fl_dropship_pallet p ON p.id=c.pallet_id WHERE c.task_id=#{taskId} AND c.pallet_id IS NULL ORDER BY c.counted_at,c.id") List<CartonRow> unboundCartons(@Param("taskId") long taskId);
|
||||
@Select("""
|
||||
SELECT COALESCE(SUM(o.actual_gross_weight_kg/NULLIF(o.total_package_count,0)),0) weight_kg,
|
||||
COALESCE(SUM(o.actual_volume_cbm/NULLIF(o.total_package_count,0)),0) volume_cbm
|
||||
FROM fl_dropship_pallet_carton c
|
||||
JOIN fl_inbound_order o ON o.id=c.inbound_id AND o.tenant_id=c.tenant_id
|
||||
WHERE c.pallet_id=#{palletId}""")
|
||||
PalletMetrics metrics(@Param("palletId") long palletId);
|
||||
<script>
|
||||
SELECT c.pallet_id,COUNT(DISTINCT c.id) carton_count,
|
||||
COALESCE(SUM(o.actual_gross_weight_kg/NULLIF(o.total_package_count,0)),0) gross_weight_kg,
|
||||
COALESCE(SUM(o.actual_net_weight_kg/NULLIF(o.total_package_count,0)),0) net_weight_kg,
|
||||
COALESCE(SUM(o.actual_volume_cbm/NULLIF(o.total_package_count,0)),0) volume_cbm
|
||||
FROM fl_dropship_pallet_carton c
|
||||
JOIN fl_inbound_order o ON o.id=c.inbound_id AND o.tenant_id=c.tenant_id
|
||||
WHERE c.pallet_id IN
|
||||
<foreach collection="palletIds" item="palletId" open="(" separator="," close=")">#{palletId}</foreach>
|
||||
GROUP BY c.pallet_id
|
||||
</script>
|
||||
""")
|
||||
List<PalletMetrics> physicalMetricsByPalletIds(@Param("palletIds") Collection<Long> palletIds);
|
||||
|
||||
@Select("""
|
||||
SELECT c.tenant_id,c.inbound_id,o.inbound_code,
|
||||
@@ -340,7 +347,8 @@ public interface DropshipPalletizationMapper {
|
||||
@Getter @Setter class LabelSkuRow { private Long inboundDetailId; private String skuCode,cargoName; private Integer expectedQuantity,lineNo; }
|
||||
@Getter @Setter class CartonRow { private Long id,taskId,palletId,inboundId,inboundDetailId; private String palletNo,inboundCode,skuCode,cargoName,boxCode,discrepancyStatus,discrepancyRemark; private Integer expectedSkuQuantity,actualSkuQuantity; }
|
||||
@Getter @Setter class CartonSkuRow { private Long inboundDetailId; private String skuCode,cargoName,discrepancyStatus,discrepancyRemark; private Integer expectedQuantity,actualQuantity,lineNo; }
|
||||
record PalletMetrics(BigDecimal weightKg,BigDecimal volumeCbm) {}
|
||||
record PalletMetrics(Long palletId,Integer cartonCount,BigDecimal grossWeightKg,
|
||||
BigDecimal netWeightKg,BigDecimal volumeCbm) {}
|
||||
@Getter @Setter class PrintInboundRow { private Long tenantId,inboundId; private String inboundCode,destinationWarehouse; private Integer cartonCount,itemQuantity; }
|
||||
@Getter @Setter class PrintSkuRow { private String skuCode; private Integer cartonCount,itemQuantity,minUnitsPerCarton,maxUnitsPerCarton; }
|
||||
@Getter @Setter class PalletLine { private Long palletId,tenantId,accountId,inboundId,orderId,warehouseId,destinationWarehouseId,inboundDetailId; private String palletNo,palletStatus,orderNo,inboundCode,channelCode,channelName,destinationWarehouseCode,destinationWarehouseName,customerName,skuCode,cargoName; private Integer cartonCount,actualSkuQuantity; private BigDecimal unitGrossWeightKg,unitNetWeightKg,unitVolumeCbm; }
|
||||
|
||||
@@ -45,10 +45,33 @@ public interface LoadingListQueryMapper {
|
||||
(SELECT pick_task.task_status FROM fl_loading_pick_task pick_task
|
||||
WHERE pick_task.loading_plan_id = p.id LIMIT 1) AS pda_task_status,
|
||||
COUNT(DISTINCT NULLIF(i.sku_code, '')) AS sku_count,
|
||||
COALESCE(SUM(i.package_count), 0) AS total_package_count,
|
||||
COALESCE(SUM(i.gross_weight_kg), 0) AS total_gross_weight_kg,
|
||||
COALESCE(SUM(i.net_weight_kg), 0) AS total_net_weight_kg,
|
||||
COALESCE(SUM(i.volume_cbm), 0) AS total_volume_cbm,
|
||||
COALESCE(SUM(CASE WHEN i.pallet_id IS NULL THEN i.package_count ELSE 0 END), 0)
|
||||
+ COALESCE((SELECT COUNT(DISTINCT carton.id)
|
||||
FROM fl_dropship_pallet_carton carton
|
||||
WHERE EXISTS (SELECT 1 FROM fl_loading_plan_item pallet_item
|
||||
WHERE pallet_item.loading_plan_id=p.id AND pallet_item.deleted=0
|
||||
AND pallet_item.pallet_id=carton.pallet_id)), 0) AS total_package_count,
|
||||
COALESCE(SUM(CASE WHEN i.pallet_id IS NULL THEN i.gross_weight_kg ELSE 0 END), 0)
|
||||
+ COALESCE((SELECT SUM(inbound.actual_gross_weight_kg/NULLIF(inbound.total_package_count,0))
|
||||
FROM fl_dropship_pallet_carton carton
|
||||
JOIN fl_inbound_order inbound ON inbound.id=carton.inbound_id AND inbound.tenant_id=carton.tenant_id
|
||||
WHERE EXISTS (SELECT 1 FROM fl_loading_plan_item pallet_item
|
||||
WHERE pallet_item.loading_plan_id=p.id AND pallet_item.deleted=0
|
||||
AND pallet_item.pallet_id=carton.pallet_id)), 0) AS total_gross_weight_kg,
|
||||
COALESCE(SUM(CASE WHEN i.pallet_id IS NULL THEN i.net_weight_kg ELSE 0 END), 0)
|
||||
+ COALESCE((SELECT SUM(inbound.actual_net_weight_kg/NULLIF(inbound.total_package_count,0))
|
||||
FROM fl_dropship_pallet_carton carton
|
||||
JOIN fl_inbound_order inbound ON inbound.id=carton.inbound_id AND inbound.tenant_id=carton.tenant_id
|
||||
WHERE EXISTS (SELECT 1 FROM fl_loading_plan_item pallet_item
|
||||
WHERE pallet_item.loading_plan_id=p.id AND pallet_item.deleted=0
|
||||
AND pallet_item.pallet_id=carton.pallet_id)), 0) AS total_net_weight_kg,
|
||||
COALESCE(SUM(CASE WHEN i.pallet_id IS NULL THEN i.volume_cbm ELSE 0 END), 0)
|
||||
+ COALESCE((SELECT SUM(inbound.actual_volume_cbm/NULLIF(inbound.total_package_count,0))
|
||||
FROM fl_dropship_pallet_carton carton
|
||||
JOIN fl_inbound_order inbound ON inbound.id=carton.inbound_id AND inbound.tenant_id=carton.tenant_id
|
||||
WHERE EXISTS (SELECT 1 FROM fl_loading_plan_item pallet_item
|
||||
WHERE pallet_item.loading_plan_id=p.id AND pallet_item.deleted=0
|
||||
AND pallet_item.pallet_id=carton.pallet_id)), 0) AS total_volume_cbm,
|
||||
COALESCE(SUM(i.product_quantity), 0) AS total_quantity
|
||||
""";
|
||||
|
||||
|
||||
@@ -158,11 +158,12 @@ public class LoadingListRepository {
|
||||
java.util.ArrayList<String> blockers = new java.util.ArrayList<>();
|
||||
if (!editing) blockers.add("LOADING_PLAN_NOT_EDITING");
|
||||
if (linkCount == 0) blockers.add("PRELOAD_LINK_MISSING");
|
||||
if (!containerReady) blockers.add("CONTAINER_NO_MISSING");
|
||||
if (!oceanLinked) blockers.add("OCEAN_BOOKING_NOT_LINKED");
|
||||
else if (!released) blockers.add("OCEAN_BOOKING_NOT_RELEASED");
|
||||
if (taskCreated) blockers.add("PDA_TASK_ALREADY_CREATED");
|
||||
boolean allConfirmed = linkCount > 0 && confirmedCount == linkCount;
|
||||
boolean prerequisitesReady = editing && linkCount > 0 && released;
|
||||
boolean prerequisitesReady = editing && linkCount > 0 && containerReady && released;
|
||||
boolean canConfirm = prerequisitesReady && !taskCreated && !allConfirmed;
|
||||
if (allConfirmed) blockers.add("PRELOAD_ALREADY_CONFIRMED");
|
||||
String confirmationStatus = allConfirmed ? "CONFIRMED"
|
||||
|
||||
+2
-2
@@ -231,10 +231,10 @@ public class DropshipPalletizationService {
|
||||
values.stream().mapToInt(v->v.line().getActualQuantity()==null?0:v.line().getActualQuantity()).sum(),
|
||||
values.stream().map(v->v.carton().getBoxCode()).toList());}).toList();
|
||||
int discrepancies=(int)cartons.stream().filter(c->"ACCEPTED_VARIANCE".equals(c.getDiscrepancyStatus())).count();
|
||||
PalletMetrics metrics=mapper.metrics(row.getId());
|
||||
PalletMetrics metrics=mapper.physicalMetricsByPalletIds(List.of(row.getId())).stream().findFirst().orElse(null);
|
||||
int skuCount=(int)items.stream().map(SkuItem::skuCode).distinct().count();
|
||||
return new Pallet(row.getId(),row.getPalletNo(),row.getPalletStatus(),cartons.size(),skuCount,discrepancies,
|
||||
metrics==null?BigDecimal.ZERO:metrics.weightKg(),metrics==null?BigDecimal.ZERO:metrics.volumeCbm(),row.getVersion(),items,
|
||||
metrics==null?BigDecimal.ZERO:metrics.grossWeightKg(),metrics==null?BigDecimal.ZERO:metrics.volumeCbm(),row.getVersion(),items,
|
||||
cartons.stream().map(c->cartonView(c,c.getInboundCode(),false,null)).toList(),row.getCompletedAt());
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ public class LoadingPickService {
|
||||
public void beginIfReady(long containerBookingId, long operatorId, String operatorName) {
|
||||
var booking = smartLoadingRepository.booking(containerBookingId);
|
||||
if (booking == null) return;
|
||||
if (!StringUtils.hasText(booking.getContainerNo())) return;
|
||||
var plan = smartLoadingRepository.planByBooking(containerBookingId);
|
||||
if (plan == null || !"EDITING".equals(plan.getPlanStatus())) return;
|
||||
inboundHoldGuard.requirePlanNotHeld(plan.getId());
|
||||
@@ -137,6 +138,10 @@ public class LoadingPickService {
|
||||
if (!Set.of("PENDING", "CONFIRMING").contains(link.preloadStatus())) {
|
||||
throw conflict("预装清单已确认或当前状态不允许确认");
|
||||
}
|
||||
var booking = smartLoadingRepository.booking(link.containerBookingId());
|
||||
if (booking == null || !StringUtils.hasText(booking.getContainerNo())) {
|
||||
throw conflict("确认预装前必须录入真实集装箱号");
|
||||
}
|
||||
var allocation = oceanBookingRepository.allocationByContainer(link.containerBookingId());
|
||||
if (allocation == null) throw conflict("确认预装前必须关联真实订舱柜");
|
||||
var oceanBooking = oceanBookingRepository.find(allocation.getOceanBookingId());
|
||||
|
||||
@@ -476,19 +476,28 @@ public class SmartLoadingService {
|
||||
var first = lines.get(0);
|
||||
inboundHoldGuard.requireInboundNotHeld(first.getAccountId(), first.getInboundId());
|
||||
if (!repository.activeItemsByPallet(request.palletId()).isEmpty()) throw conflict("托盘已被有效柜次占用");
|
||||
com.baiyue.firstleg.mapper.DropshipPalletizationMapper.PalletMetrics physical =
|
||||
dropshipPallets.physicalMetricsByPalletIds(List.of(request.palletId())).stream().findFirst()
|
||||
.orElseThrow(() -> conflict("托盘缺少物理箱明细,不能加入预装清单"));
|
||||
int sequence = 0;
|
||||
for (var line : lines) {
|
||||
boolean primaryPalletLine = sequence == 0;
|
||||
BigDecimal lineBoxes = BigDecimal.valueOf(line.getCartonCount());
|
||||
LoadingPlanEntity current = repository.plan(planId);
|
||||
Candidate candidate = new Candidate(line.getAccountId(), line.getInboundId(), line.getInboundDetailId(),
|
||||
line.getOrderId(), line.getOrderNo(), line.getInboundCode(), line.getWarehouseId(), line.getChannelCode(),
|
||||
line.getChannelName(), line.getDestinationWarehouseId(), line.getDestinationWarehouseCode(),
|
||||
line.getDestinationWarehouseName(), line.getCustomerName(), line.getCargoName(), line.getCartonCount(),
|
||||
line.getUnitGrossWeightKg(), line.getUnitNetWeightKg(), line.getUnitVolumeCbm(), null, null, null, null,
|
||||
primaryPalletLine ? unit(physical.grossWeightKg()).divide(lineBoxes, 6, RoundingMode.HALF_UP) : BigDecimal.ZERO,
|
||||
primaryPalletLine ? unit(physical.netWeightKg()).divide(lineBoxes, 6, RoundingMode.HALF_UP) : BigDecimal.ZERO,
|
||||
primaryPalletLine ? unit(physical.volumeCbm()).divide(lineBoxes, 6, RoundingMode.HALF_UP) : BigDecimal.ZERO,
|
||||
null, null, null, null,
|
||||
false, null, "WAIT_SPLIT", null);
|
||||
String childOperation = "PAL-" + sha256(operation + '|' + line.getInboundDetailId()).substring(0, 48);
|
||||
assign(planId, new AssignRequest(line.getInboundId(), line.getInboundDetailId(), line.getCartonCount(),
|
||||
current.getVersion(), childOperation), operatorId, operatorName, true, request.palletId(),
|
||||
line.getPalletNo(), line.getActualSkuQuantity(), sequence++ == 0, candidate, false);
|
||||
line.getPalletNo(), line.getActualSkuQuantity(), primaryPalletLine, candidate, false);
|
||||
sequence++;
|
||||
}
|
||||
LoadingPlanEntity assigned = repository.plan(planId);
|
||||
history(planId, null, "ADD_PALLET", original.getPlanStatus(), assigned.getPlanStatus(), operation,
|
||||
@@ -1689,19 +1698,26 @@ public class SmartLoadingService {
|
||||
Map<Long,List<LoadingPlanItemEntity>> pallets = items.stream().filter(i -> i.getPalletId() != null)
|
||||
.collect(java.util.stream.Collectors.groupingBy(LoadingPlanItemEntity::getPalletId,
|
||||
LinkedHashMap::new, java.util.stream.Collectors.toList()));
|
||||
Map<Long,com.baiyue.firstleg.mapper.DropshipPalletizationMapper.PalletMetrics> physicalByPallet =
|
||||
pallets.isEmpty() ? Map.of() : dropshipPallets.physicalMetricsByPalletIds(pallets.keySet()).stream()
|
||||
.collect(java.util.stream.Collectors.toMap(
|
||||
com.baiyue.firstleg.mapper.DropshipPalletizationMapper.PalletMetrics::palletId,
|
||||
java.util.function.Function.identity()));
|
||||
items.stream().filter(i -> i.getPalletId() == null).forEach(result::add);
|
||||
for (List<LoadingPlanItemEntity> lines : pallets.values()) {
|
||||
LoadingPlanItemEntity unit = lines.get(0);
|
||||
unit.setPackageCount(lines.stream().map(LoadingPlanItemEntity::getPackageCount)
|
||||
.filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
|
||||
var physical = physicalByPallet.get(unit.getPalletId());
|
||||
if (physical == null) throw conflict("托盘缺少物理箱明细,不能汇总预装清单");
|
||||
unit.setPackageCount(physical.cartonCount());
|
||||
unit.setProductQuantity(lines.stream().map(LoadingPlanItemEntity::getProductQuantity)
|
||||
.filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
|
||||
unit.setPalletCount(1);
|
||||
unit.setGrossWeightKg(sumDecimal(lines, LoadingPlanItemEntity::getGrossWeightKg));
|
||||
unit.setNetWeightKg(sumDecimal(lines, LoadingPlanItemEntity::getNetWeightKg));
|
||||
unit.setVolumeCbm(sumDecimal(lines, LoadingPlanItemEntity::getVolumeCbm));
|
||||
unit.setPickedPackageCount(lines.stream().map(LoadingPlanItemEntity::getPickedPackageCount)
|
||||
.filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
|
||||
unit.setGrossWeightKg(physical.grossWeightKg());
|
||||
unit.setNetWeightKg(physical.netWeightKg());
|
||||
unit.setVolumeCbm(physical.volumeCbm());
|
||||
unit.setPickedPackageCount(Math.min(physical.cartonCount(), lines.stream()
|
||||
.map(LoadingPlanItemEntity::getPickedPackageCount).filter(Objects::nonNull)
|
||||
.mapToInt(Integer::intValue).sum()));
|
||||
unit.setTotalPackageCount(unit.getPackageCount());
|
||||
unit.setRemainingPackageCount(0);
|
||||
unit.setCanAdjust(false);
|
||||
@@ -1713,10 +1729,6 @@ public class SmartLoadingService {
|
||||
.thenComparing(LoadingPlanItemEntity::getId, Comparator.nullsLast(Long::compareTo)))
|
||||
.toList();
|
||||
}
|
||||
private static BigDecimal sumDecimal(List<LoadingPlanItemEntity> lines,
|
||||
java.util.function.Function<LoadingPlanItemEntity,BigDecimal> getter) {
|
||||
return lines.stream().map(getter).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
private int adjustmentTotal(LoadingPlanItemEntity item) {
|
||||
int current = item.getPackageCount() == null ? 0 : item.getPackageCount();
|
||||
try {
|
||||
|
||||
+4
-4
@@ -74,7 +74,7 @@ class LoadingListRepositoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingContainerNumberDoesNotBlockPreloadConfirmationOrPdaReadiness() {
|
||||
void missingContainerNumberBlocksPreloadConfirmationAndPdaReadiness() {
|
||||
AggregateRow row = new AggregateRow();
|
||||
row.setPlanId(41L);
|
||||
row.setContainerBookingId(61L);
|
||||
@@ -90,12 +90,12 @@ class LoadingListRepositoryTest {
|
||||
|
||||
var pending = repository.preloadDetail(row).summary().readiness();
|
||||
assertThat(pending.containerNoReady()).isFalse();
|
||||
assertThat(pending.canConfirm()).isTrue();
|
||||
assertThat(pending.blockingReasons()).doesNotContain("CONTAINER_NO_MISSING");
|
||||
assertThat(pending.canConfirm()).isFalse();
|
||||
assertThat(pending.blockingReasons()).contains("CONTAINER_NO_MISSING");
|
||||
|
||||
row.setConfirmedPreloadCount(2L);
|
||||
var confirmed = repository.preloadDetail(row).summary().readiness();
|
||||
assertThat(confirmed.readyForPda()).isTrue();
|
||||
assertThat(confirmed.readyForPda()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -158,30 +158,15 @@ class LoadingPickServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void opensTaskAfterReleasedBookingWithoutWaitingForRealContainerNumber() {
|
||||
void doesNotOpenTaskWithoutRealContainerNumber() {
|
||||
var booking = new com.baiyue.firstleg.entity.ContainerBookingEntity();
|
||||
booking.setId(40L);
|
||||
var plan = new com.baiyue.firstleg.entity.LoadingPlanEntity();
|
||||
plan.setId(10L); plan.setContainerBookingId(40L); plan.setPlanStatus("EDITING");
|
||||
var allocation = new OceanBookingContainerAllocationEntity(); allocation.setOceanBookingId(90L);
|
||||
var ocean = new OceanBookingEntity(); ocean.setId(90L); ocean.setStatus("RELEASED");
|
||||
when(smartLoadingRepository.booking(40L)).thenReturn(booking);
|
||||
when(smartLoadingRepository.planByBooking(40L)).thenReturn(plan);
|
||||
when(smartLoadingRepository.plan(10L)).thenReturn(plan);
|
||||
when(loadingModelBridge.byLoadingPlan(10L)).thenReturn(List.of(
|
||||
new Link(1L, 1L, 50L, 2L, "CONFIRMED", 1, 10L, 40L, null, "EDITING", 1, 1, null)));
|
||||
when(oceanBookingRepository.allocationByContainer(40L)).thenReturn(allocation);
|
||||
when(oceanBookingRepository.find(90L)).thenReturn(ocean);
|
||||
when(repository.taskByPlan(10L)).thenReturn(null);
|
||||
when(smartLoadingRepository.items(10L)).thenReturn(List.of());
|
||||
doAnswer(invocation -> {
|
||||
invocation.getArgument(0, LoadingPickTaskEntity.class).setId(20L);
|
||||
return null;
|
||||
}).when(repository).insert(any(LoadingPickTaskEntity.class));
|
||||
|
||||
service.beginIfReady(40L, 7L, "admin");
|
||||
|
||||
verify(repository).insert(any(LoadingPickTaskEntity.class));
|
||||
verify(repository, never()).insert(any(LoadingPickTaskEntity.class));
|
||||
verify(smartLoadingRepository, never()).planByBooking(40L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -222,11 +207,11 @@ class LoadingPickServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void planConfirmationGateRequiresReleasedBookingButNotRealContainerNumber() {
|
||||
void planConfirmationGateRequiresReleasedBookingAndRealContainerNumber() {
|
||||
Link link = new Link(1L, 1L, 50L, 2L, "PENDING", 1,
|
||||
10L, 40L, "MSCU-001", "EDITING", 3, 2, null);
|
||||
var booking = new com.baiyue.firstleg.entity.ContainerBookingEntity();
|
||||
booking.setId(40L);
|
||||
booking.setId(40L); booking.setContainerNo("MSCU-001");
|
||||
var allocation = new OceanBookingContainerAllocationEntity();
|
||||
allocation.setOceanBookingId(90L);
|
||||
var ocean = new OceanBookingEntity();
|
||||
@@ -240,21 +225,17 @@ class LoadingPickServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void planConfirmationGateAllowsMissingRealContainerNumber() {
|
||||
void planConfirmationGateRejectsMissingRealContainerNumber() {
|
||||
Link link = new Link(1L, 1L, 50L, 2L, "PENDING", 1,
|
||||
10L, 40L, null, "EDITING", 3, 2, null);
|
||||
var booking = new com.baiyue.firstleg.entity.ContainerBookingEntity();
|
||||
booking.setId(40L);
|
||||
var allocation = new OceanBookingContainerAllocationEntity();
|
||||
allocation.setOceanBookingId(90L);
|
||||
var ocean = new OceanBookingEntity();
|
||||
ocean.setId(90L); ocean.setStatus("RELEASED");
|
||||
when(loadingModelBridge.requireAllocatedLinksForConfirmation(10L)).thenReturn(List.of(link));
|
||||
when(smartLoadingRepository.booking(40L)).thenReturn(booking);
|
||||
when(oceanBookingRepository.allocationByContainer(40L)).thenReturn(allocation);
|
||||
when(oceanBookingRepository.find(90L)).thenReturn(ocean);
|
||||
|
||||
assertThat(service.requirePreloadConfirmationReady(10L)).containsExactly(link);
|
||||
assertThatThrownBy(() -> service.requirePreloadConfirmationReady(10L))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("真实集装箱号");
|
||||
verify(oceanBookingRepository, never()).allocationByContainer(40L);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.baiyue.firstleg.client.PendingReviewOrderClient;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.AssignRequest;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.AssignInboundRequest;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.AssignPendingReviewRequest;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.AssignPalletRequest;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.AdjustRequest;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.CandidateOrderStatus;
|
||||
@@ -62,6 +63,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -76,6 +78,7 @@ import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -119,6 +122,11 @@ class SmartLoadingServiceTest {
|
||||
when(fbaPricingService.recalculateUnlockedContainerCustomerFees(anyLong(), anyLong(), anyString()))
|
||||
.thenReturn(new FbaPricingService.ContainerFeeRecalculationResult(
|
||||
FbaPricingService.ContainerFeeRecalculationStatus.NOT_READY, 0));
|
||||
when(dropshipPallets.physicalMetricsByPalletIds(any())).thenAnswer(invocation -> {
|
||||
Collection<Long> palletIds = invocation.getArgument(0);
|
||||
return palletIds.stream().map(palletId -> new DropshipPalletizationMapper.PalletMetrics(
|
||||
palletId, 10, new BigDecimal("20"), new BigDecimal("15"), new BigDecimal("5"))).toList();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,6 +189,9 @@ class SmartLoadingServiceTest {
|
||||
secondMember.setCustomerName("客户"); secondMember.setAvailablePackageCount(5);
|
||||
when(dropshipPallets.palletInboundMembersByPalletIds(List.of(801L)))
|
||||
.thenReturn(List.of(firstMember,secondMember));
|
||||
doReturn(List.of(new DropshipPalletizationMapper.PalletMetrics(
|
||||
801L, 11, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO)))
|
||||
.when(dropshipPallets).physicalMetricsByPalletIds(any());
|
||||
|
||||
Plan result=service.detail(1L);
|
||||
|
||||
@@ -887,6 +898,53 @@ class SmartLoadingServiceTest {
|
||||
eq(99L), eq(9L), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void multiSkuPalletKeepsSkuLinesButCountsPhysicalCartonsAndVolumeOnce() {
|
||||
AtomicInteger version = new AtomicInteger();
|
||||
List<LoadingPlanItemEntity> saved = new ArrayList<>();
|
||||
DropshipPalletizationMapper.PalletLine first = palletLine(801L, "DP-260916-0009", 20);
|
||||
DropshipPalletizationMapper.PalletLine second = palletLine(801L, "DP-260916-0009", 20);
|
||||
second.setInboundDetailId(21L);
|
||||
when(repository.plan(1L)).thenAnswer(invocation -> plan("EDITING", version.get()));
|
||||
when(repository.planByBooking(99L)).thenAnswer(invocation -> plan("EDITING", version.get()));
|
||||
when(repository.booking(99L)).thenReturn(booking());
|
||||
when(repository.items(1L)).thenAnswer(invocation -> List.copyOf(saved));
|
||||
when(repository.activeItemsByPallet(801L)).thenReturn(List.of());
|
||||
when(repository.activePalletItem(eq(1L), eq(801L), anyLong())).thenReturn(null);
|
||||
when(repository.inbound(9L, 10L)).thenReturn(inbound(9L, 10L));
|
||||
when(dropshipPallets.sealedPalletLines(801L)).thenReturn(List.of(first, second));
|
||||
doReturn(List.of(
|
||||
new DropshipPalletizationMapper.PalletMetrics(801L, 20, new BigDecimal("197"),
|
||||
new BigDecimal("180"), new BigDecimal("3.365"))))
|
||||
.when(dropshipPallets).physicalMetricsByPalletIds(any());
|
||||
when(repository.touch(eq(1L), anyInt(), eq(7L), eq("u"))).thenAnswer(invocation -> {
|
||||
int expected = invocation.getArgument(1);
|
||||
if (expected != version.get()) return 0;
|
||||
version.incrementAndGet();
|
||||
return 1;
|
||||
});
|
||||
doAnswer(invocation -> {
|
||||
LoadingPlanItemEntity item = invocation.getArgument(0);
|
||||
item.setId(800L + saved.size());
|
||||
saved.add(item);
|
||||
return null;
|
||||
}).when(repository).insert(any(LoadingPlanItemEntity.class));
|
||||
|
||||
Plan result = service.assignPallet(1L,
|
||||
new AssignPalletRequest(801L, 0, "assign-multi-sku-pallet"), 7L, "u");
|
||||
|
||||
assertThat(saved).hasSize(2);
|
||||
assertThat(saved).extracting(LoadingPlanItemEntity::getInboundDetailId)
|
||||
.containsExactly(20L, 21L);
|
||||
assertThat(saved).extracting(LoadingPlanItemEntity::getVolumeCbm)
|
||||
.containsExactly(new BigDecimal("3.365000"), new BigDecimal("0.000000"));
|
||||
assertThat(result.items()).singleElement().satisfies(item -> {
|
||||
assertThat(item.getPackageCount()).isEqualTo(20);
|
||||
assertThat(item.getGrossWeightKg()).isEqualByComparingTo("197");
|
||||
assertThat(item.getVolumeCbm()).isEqualByComparingTo("3.365");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void recommendSkipsStalePalletCandidateInsteadOfFailingTheWholeRecommendation() {
|
||||
Candidate dropship = candidate(9L, 10L, 20L, 30L, 10);
|
||||
|
||||
Reference in New Issue
Block a user