diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/service/DispatchAggregateService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/service/DispatchAggregateService.java index 1a96dc146..5b6b233c2 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/service/DispatchAggregateService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/service/DispatchAggregateService.java @@ -664,7 +664,7 @@ public class DispatchAggregateService implements DispatchExecutionPort,DispatchA var origin=pick.dispatchItemSourceId()==null?null:origins.get(pick.dispatchItemSourceId()); var sourceOrigin=origin==null?null:new SourceOrigin(origin.getId(),origin.getRelabelApplicationId(),origin.getRelabelItemId(),origin.getSourceInventoryBalanceId(),origin.getSourceInboundOrderId(),origin.getSourceInboundOrderNo(),origin.getSourceInboundCode(),origin.getProductQuantity()!=null || pick.productQuantity()>0?pick.productQuantity():null); cargo.add(new CargoLine(item.getId(),pick.customerId(),item.getCustomerName(),pick.orderId(),pick.orderNo(),source,item.getSourceStage(),pick.sourceRegion(),pick.sourceInboundId(),pick.inboundCode(), - item.getPlanPalletId(),item.getWarehousePalletId(),item.getPalletNo(),pick.pickId(),pick.attributionId(),pick.positionId(),pick.receiptId(),pick.returnReceiptId(),pick.returnCartonId(),pick.cartonMark(), + item.getPlanPalletId(),item.getWarehousePalletId(),item.getPalletNo(),pick.pickId(),pick.attributionId(),pick.positionId(),pick.receiptId(),pick.returnReceiptId(),pick.returnCartonId(),origin==null?pick.cartonMark():relabelCartonMarks(origin), "RELABEL_ORDER".equals(item.getSourceBusinessType())?item.getSourceBusinessNo():null,destination(item),pick.packages(),pick.productQuantity(),null,null,allocations,sourceOrigin)); } var picked=executionReads.itemPicked(row.getId()).stream().collect(Collectors.toMap(p->p.itemId(),p->p.packages())); @@ -675,6 +675,22 @@ public class DispatchAggregateService implements DispatchExecutionPort,DispatchA read(row.getResourcesJson(),Resources.class),counts(row),reviews(row),complete,List.copyOf(cargo)); } + /** 普通换标出库沿本次冻结贡献的新箱标,不从退货箱表或原箱唛生成快照。 */ + private String relabelCartonMarks(com.baiyue.lastleg.dispatch.entity.DispatchItemSource origin) { + try { + var facts=json.readTree(origin.getUnitFactsJson());var marks=new java.util.TreeSet(); + if(!facts.isArray())throw conflict("换标冻结箱标事实缺失"); + for(var fact:facts)if("RELABELLED".equals(fact.path("result").asText())) { + var mark=fact.path("cartonMark").asText("").trim(); + if(mark.isEmpty() || !marks.add(mark))throw conflict("换标冻结新箱标缺失或重复"); + } + if(marks.isEmpty())throw conflict("换标冻结新箱标缺失"); + return String.join(",",marks); + } catch(com.fasterxml.jackson.core.JsonProcessingException error) { + throw new IllegalStateException("换标冻结箱标事实无法读取",error); + } + } + /** 共同根锁顺序固定,当前阶段资格留给原命令恢复之后。 */ private DispatchAggregate lockPlatform(Actor actor,String number) { requirePlatform(actor);lockReviewRoot(actor,number);return required(actor,number,true); } /** 运输取货锁原车次,仓库派送锁其整车资源;统一先父后主单。 */ diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/relabel/port/RelabelDispatchSourcePort.java b/lastleg-service/src/main/java/com/baiyue/lastleg/relabel/port/RelabelDispatchSourcePort.java index e3a8ccbc8..70a0c412e 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/relabel/port/RelabelDispatchSourcePort.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/relabel/port/RelabelDispatchSourcePort.java @@ -6,7 +6,7 @@ import java.util.List; /** 换标域向排车提供原始组成事实,不读取排车账本或代替排车锁定库存。 */ public interface RelabelDispatchSourcePort { /** 当前作业轮次的逐件成功或异常事实;未知件数保持空值。 */ - record UnitFact(Long id, String oldLabel, String newLabel, String result, Integer quantity) { } + record UnitFact(Long id, String oldLabel, String newLabel, String result, Integer quantity, String cartonMark) { } /** 原申请身份、内部作业状态、履约订单及客户确认轮次。 */ record Application(Long applicationId, String relabelNo, String customerId, diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/relabel/service/RelabelDispatchSourceService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/relabel/service/RelabelDispatchSourceService.java index 5052440a0..a32717810 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/relabel/service/RelabelDispatchSourceService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/relabel/service/RelabelDispatchSourceService.java @@ -168,7 +168,7 @@ public class RelabelDispatchSourceService implements RelabelDispatchSourcePort { item.getSourceInboundOrderId(), item.getSourceInboundOrderNo(), item.getSourceInboundCode(), notified, bound ? notified : 0, productQuantity(item, itemFacts, histories, round), basis, itemFacts.stream().map(f -> new UnitFact(f.getId(), f.getOldLabel(), f.getNewLabel(), - f.getResult(), f.getQuantity())).toList())); + f.getResult(), f.getQuantity(), "SKU_LABEL".equals(item.getOperationType()) ? f.getOldLabel() : f.getNewLabel())).toList())); } if (rows.items().isEmpty()) reasons.add(app.getRelabelNo() + ":申请明细为空"); if (facts.stream().anyMatch(f -> rows.items().stream().noneMatch(i -> Objects.equals(i.getId(), f.getItemId())))) diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/service/DispatchItemSourcePersistenceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/service/DispatchItemSourcePersistenceTest.java index 4ac5c08d0..93cc4801f 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/service/DispatchItemSourcePersistenceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/service/DispatchItemSourcePersistenceTest.java @@ -416,8 +416,8 @@ class DispatchItemSourcePersistenceTest { long balance, long sourceBalance, long inbound, long packages, Long pieces, String mark) { return new BoundSource(app, no, item, customer, "1001", pallet, position, balance, sourceBalance, inbound, "IN-" + inbound, "CODE-" + inbound, packages, packages, pieces, "b".repeat(64), - List.of(new UnitFact(item, mark, "NEW-" + mark, "RELABELLED", 1), - new UnitFact(item + 1000, "EXCEPTION-" + item, null, "EXCEPTION", 1))); + List.of(new UnitFact(item, mark, "NEW-" + mark, "RELABELLED", 1, "NEW-" + mark), + new UnitFact(item + 1000, "EXCEPTION-" + item, null, "EXCEPTION", 1, null))); } /** 同一实托可以含不同客户位置,每个位置的 HOLD 独立守恒。 */ diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/service/DispatchMergedRelabelPersistenceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/service/DispatchMergedRelabelPersistenceTest.java index ed245b2eb..326a23dbc 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/service/DispatchMergedRelabelPersistenceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/service/DispatchMergedRelabelPersistenceTest.java @@ -93,6 +93,7 @@ class DispatchMergedRelabelPersistenceTest { var context=f.f.service.preview(f.f.actor,created.dispatchNo());assertThat(context.cargo()).hasSize(2); assertThat(context.cargo()).extracting(c->c.sourceOrigin().sourceInboundOrderNo()).containsExactlyInAnyOrder("ORIGIN-A","ORIGIN-B"); assertThat(context.cargo()).extracting(c->c.orderId()).containsExactlyInAnyOrder(9001L,9002L); + assertThat(context.cargo()).extracting(c->c.cartonMark()).containsExactlyInAnyOrder("NEW-A","NEW-B"); var preview=f.f.service.loading(f.f.actor,created.dispatchNo());assertThat(preview.pallets()).hasSize(1);assertThat(preview.pallets().get(0).scheduledPackages()).isEqualTo(5); var pallet=preview.pallets().get(0); var loaded=f.f.tx.execute(s->f.f.service.loadingComplete(f.picker,created.dispatchNo(),new DispatchCommands.LoadingComplete(last.recordVersion(),"load",preview.manifestHash(), diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/relabel/service/RelabelDispatchSourcePersistenceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/relabel/service/RelabelDispatchSourcePersistenceTest.java index 1d7f46504..a15cba8e8 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/relabel/service/RelabelDispatchSourcePersistenceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/relabel/service/RelabelDispatchSourcePersistenceTest.java @@ -157,6 +157,7 @@ class RelabelDispatchSourcePersistenceTest { assertEquals(3, facts.size()); assertEquals(2, facts.stream().filter(f -> f.result().equals("RELABELLED")).count()); assertEquals(1, facts.stream().filter(f -> f.result().equals("EXCEPTION")).count()); + assertTrue(facts.stream().filter(f -> f.result().equals("RELABELLED")).allMatch(f -> f.newLabel().equals(f.cartonMark()))); assertTrue(snapshot.sources().stream().allMatch(source -> source.productQuantity() == null)); } @@ -169,6 +170,8 @@ class RelabelDispatchSourcePersistenceTest { assertNull(snapshot().sources().stream().filter(s -> s.itemId() == 11).findFirst().orElseThrow().productQuantity()); skuScan(31, 1, 7, "INITIAL"); assertEquals(12L, snapshot().sources().stream().filter(s -> s.itemId() == 11).findFirst().orElseThrow().productQuantity()); + assertTrue(snapshot().sources().stream().filter(s -> s.itemId() == 11).flatMap(s -> s.unitFacts().stream()) + .allMatch(f -> f.oldLabel().equals(f.cartonMark())), "仅换SKU不能把SKU标签当作出库箱标"); jdbc.update("UPDATE ll_relabel_pda_history SET detail_json=? WHERE id=31", "{\"round\":\"INITIAL\",\"itemId\":\"11\",\"skuIndex\":1,\"quantity\":99}"); assertNull(snapshot().sources().stream().filter(s -> s.itemId() == 11).findFirst().orElseThrow().productQuantity());