fix(dispatch): limit appointment prerequisite to inbound cargo
This commit is contained in:
+3
-3
@@ -174,10 +174,10 @@ public class DispatchAggregateService implements DispatchExecutionPort,DispatchA
|
||||
var destination=new Destination(com.baiyue.lastleg.fleet.trip.FleetDispatchGateway.destinationKey(row.destinationType(),row.destinationWarehouseCode(),
|
||||
row.fbaWarehouseCode(),row.deliveryAddress(),row.postalCode()),row.destinationType(),row.destinationWarehouseId(),row.destinationWarehouseCode(),
|
||||
row.destinationWarehouseName(),row.deliveryAddress(),row.postalCode(),row.contactName(),row.contactPhone());
|
||||
var fact=row.planPalletId()==null?null:batchFacts.get(new com.baiyue.lastleg.appointmentwork.AppointmentWorkDispatchPort.CandidatePhysical(row.customerId(),row.planPalletId()));
|
||||
var fact=type!=SourceType.INBOUND_PLAN || row.planPalletId()==null?null:batchFacts.get(new com.baiyue.lastleg.appointmentwork.AppointmentWorkDispatchPort.CandidatePhysical(row.customerId(),row.planPalletId()));
|
||||
var references=new ArrayList<BatchReference>();var reasons=new LinkedHashSet<String>();
|
||||
reasons.addAll(group.blockReasons());
|
||||
if("FBA".equals(destination.type()) && fact==null)reasons.add("本票FBA货物缺少可核对的正式预约来源,请先确认送仓时间");
|
||||
if(type==SourceType.INBOUND_PLAN && "FBA".equals(destination.type()) && fact==null)reasons.add("本票FBA货物缺少可核对的正式预约来源,请先确认送仓时间");
|
||||
if(fact!=null) {
|
||||
reasons.addAll(fact.blockReasons());
|
||||
// 来源候选已验证当前草稿拥有者并过滤其他作业占用,不能把本草稿占用再次当禁用。
|
||||
@@ -859,7 +859,7 @@ public class DispatchAggregateService implements DispatchExecutionPort,DispatchA
|
||||
for(var source:resolved) {
|
||||
if(keys.contains(source.sourceKey())) throw conflict("所选来源已在本车,不能重复加入");
|
||||
var c=source.candidate();
|
||||
if("FBA".equals(c.destinationType()) && c.planPalletId()==null)throw conflict("本票FBA货物缺少可核对的正式预约来源,请先确认送仓时间");
|
||||
if(source.selection().sourceType()==SourceType.INBOUND_PLAN && "FBA".equals(c.destinationType()) && c.planPalletId()==null)throw conflict("本票FBA货物缺少可核对的正式预约来源,请先确认送仓时间");
|
||||
sources.expire(c.customerId(),source.sourceKey(),at);
|
||||
if(!sources.claims(c.customerId(),source.sourceKey(),at).isEmpty())throw conflict("所选逻辑来源已被有效运输安排占用");
|
||||
try {if(sources.claim(IdWorker.getId(),c.customerId(),source.sourceKey(),c.planPalletId(),c.warehousePalletId(),c.returnReceiptId(),c.warehousePalletId()==null?null:physical.get(c.warehousePalletId()),row.getId(),actor.operatorId(),at)!=1)throw conflict("来源占用未保存");}
|
||||
|
||||
+2
-1
@@ -423,13 +423,14 @@ public class DispatchDraftService implements DispatchPlanningPort {
|
||||
.map(c->new DraftContainer(c.getId(),c.getSequenceNo(),c.getCapacity(),keys(c))).toList(),selected(row).stream().map(c->projectCandidate(c,batches)).toList(),row.getExpiresAt().toInstant(ZoneOffset.UTC));}
|
||||
/** 真实客户及计划托盘来自服务器来源,整页一次调用原Provider。 */
|
||||
private Map<AppointmentWorkDispatchPort.CandidatePhysical,AppointmentWorkDispatchPort.CandidateBatches> readCandidateBatches(List<Candidate> candidates) {
|
||||
var identities=candidates.stream().filter(c->c.selection().planPalletId()!=null).map(c->new AppointmentWorkDispatchPort.CandidatePhysical(c.customerId(),c.selection().planPalletId())).distinct().toList();
|
||||
var identities=candidates.stream().filter(c->c.selection().sourceType()==DispatchTypes.SourceType.INBOUND_PLAN && c.selection().planPalletId()!=null).map(c->new AppointmentWorkDispatchPort.CandidatePhysical(c.customerId(),c.selection().planPalletId())).distinct().toList();
|
||||
var result=new LinkedHashMap<AppointmentWorkDispatchPort.CandidatePhysical,AppointmentWorkDispatchPort.CandidateBatches>();
|
||||
for(int offset=0;offset<identities.size();offset+=1000)result.putAll(appointmentProvider.candidateBatches(identities.subList(offset,Math.min(offset+1000,identities.size()))));
|
||||
return result;
|
||||
}
|
||||
/** 显式映射原批次版本;本草稿有效占用不作为所选来源自身的阻断理由。 */
|
||||
private Candidate projectCandidate(Candidate c,Map<AppointmentWorkDispatchPort.CandidatePhysical,AppointmentWorkDispatchPort.CandidateBatches> batches) {
|
||||
if(c.selection().sourceType()!=DispatchTypes.SourceType.INBOUND_PLAN)return c;
|
||||
if(c.selection().planPalletId()==null) {
|
||||
if(!"FBA".equals(c.destination().type()))return c;
|
||||
return new Candidate(c.selection(),c.sourceKey(),c.sourceVersion(),c.customerId(),c.customerName(),c.warehouseId(),c.warehouseCode(),c.warehouseName(),c.orderNo(),c.inboundCode(),c.planNo(),c.palletNo(),c.containerNo(),c.destination(),c.availablePackages(),c.suggestedWeightKg(),c.suggestedVolumeCbm(),c.measurementSource(),List.of(),List.of("本票FBA货物缺少可核对的正式预约来源,请先确认送仓时间"),c.selectionGroup());
|
||||
|
||||
@@ -5974,7 +5974,7 @@ components:
|
||||
required:
|
||||
- idempotencyKey
|
||||
- warehouseId
|
||||
description: 同一起运仓,允许空单或多客户、多目的仓。FBA货物须先取得覆盖本客户、来源和箱数的正式预约时间才能选入,不能等提交审核才检查。客户和来源归属由服务端解析,临时资源不生成假档案。
|
||||
description: 同一起运仓,允许空单或多客户、多目的仓。普通入仓FBA货物须先取得覆盖本客户、来源和箱数的正式预约时间才能选入,不能等提交审核才检查;换标和退货独立来源保留原流程。客户和来源归属由服务端解析,临时资源不生成假档案。
|
||||
DispatchAddCargo:
|
||||
type: object
|
||||
x-java-record: DispatchCommands.AddCargo
|
||||
|
||||
+12
-7
@@ -129,12 +129,17 @@ class DispatchDraftPersistenceTest {
|
||||
assertThat(db.queryForObject("SELECT COUNT(*) FROM ll_dispatch_pallet_lock",Integer.class)).isZero();
|
||||
assertThat(service.drafts(owner).get(0).sources()).isEmpty();
|
||||
}
|
||||
/** FBA来源尚不能关联正式预约时不得借其他来源类型绕过确认门禁。 */
|
||||
@Test void fbaWithoutAuthoritativeAppointmentSourceCannotEnterDraft() {
|
||||
var state=empty(1);var choice=new DispatchCommands.CargoSelection(SourceType.RELABEL,null,null,"RL-1",1001L,"version-1");choices.put(1L,choice);
|
||||
/** 本轮门禁限普通入仓,独立换标和退货FBA保留原排车流程。 */
|
||||
@ParameterizedTest
|
||||
@org.junit.jupiter.params.provider.EnumSource(value=SourceType.class,names={"RELABEL","RETURN_RECEIPT"})
|
||||
void independentFbaKeepsExistingDraftFlow(SourceType sourceType) {
|
||||
var state=empty(1);var choice=sourceType==SourceType.RELABEL
|
||||
?new DispatchCommands.CargoSelection(sourceType,null,null,"RL-1",1001L,"version-1")
|
||||
:new DispatchCommands.CargoSelection(sourceType,null,1L,null,1001L,"version-1");choices.put(1L,choice);
|
||||
doAnswer(call->{var resolved=whole(choice);resolved.candidate().destinationType("FBA");return List.of(resolved);}).when(sources).lockDraftSources(anyString(),anyList());
|
||||
assertThatThrownBy(()->select(state,0,1)).hasMessageContaining("缺少可核对的正式预约来源");
|
||||
assertThat(db.queryForObject("SELECT COUNT(*) FROM ll_dispatch_pallet_lock",Integer.class)).isZero();
|
||||
assertThat(select(state,0,1).sources()).hasSize(1);
|
||||
assertThat(db.queryForObject("SELECT COUNT(*) FROM ll_dispatch_pallet_lock",Integer.class)).isOne();
|
||||
verify(appointmentProvider,never()).candidateBatches(anyList());
|
||||
}
|
||||
/** 新实例只依赖数据库命令结果,关闭内存对象也能恢复创建及原布局结果。 */
|
||||
@Test void restartRecoversOriginalResultsAndRejectsChangedActorVersionOrPayload(){
|
||||
@@ -540,8 +545,8 @@ class DispatchDraftPersistenceTest {
|
||||
.returnReceiptId(selection.sourceType()==SourceType.RETURN_RECEIPT?sourceId:null)
|
||||
.customerId(sourceId%2==0?"102":"101").customerName("客户"+sourceId).overseasWarehouseId("92")
|
||||
.overseasWarehouseCode("WH").overseasWarehouseName("海外仓").packageCount(10).palletNo("P-"+pallet)
|
||||
.sourceStage(selection.sourceType()==SourceType.RELABEL?"RELABEL":"RECEIVED").destinationType(selection.sourceType()==SourceType.INBOUND_PLAN?"FBA":"PRIVATE")
|
||||
.destinationWarehouseCode(destination).fbaWarehouseCode(selection.sourceType()==SourceType.INBOUND_PLAN?destination:null);
|
||||
.sourceStage(selection.sourceType()==SourceType.RELABEL?"RELABEL":"RECEIVED").destinationType("FBA")
|
||||
.destinationWarehouseCode(destination).fbaWarehouseCode(destination);
|
||||
return new DispatchService.WholeSource(row,selection,DispatchService.wholeSourceKey(selection),selection.sourceVersion());
|
||||
}
|
||||
/** 受控事实按原来源类型及服务器版本产生完整组,不把组装责任交给前端。 */
|
||||
|
||||
Reference in New Issue
Block a user