fix(dispatch): confirm scanned loading without repeated measurements
This commit is contained in:
+7
-5
@@ -283,10 +283,8 @@ public class DispatchAggregatePersistenceTest {
|
||||
var preview=f.tx.execute(s->f.service.loading(f.actor,created.dispatchNo()));
|
||||
assertThat(preview.pallets().get(0).weightKg()).isEqualByComparingTo("100");
|
||||
assertThat(preview.pallets().get(0).evidence()).allMatch(DispatchPlanningModels.MeasurementEvidence::actual);
|
||||
assertThat(preview.pallets().get(1).weightKg()).isNull();assertThat(preview.pallets().get(1).missingFields()).contains("weightKg","volumeCbm","palletSpaces");
|
||||
var measurements=preview.pallets().stream().map(p->new DispatchContracts.LoadingMeasurement(p.warehousePalletId(),1,
|
||||
p.warehousePalletId()==501L?new BigDecimal("100"):new BigDecimal("200"),p.warehousePalletId()==501L?BigDecimal.ONE:new BigDecimal("2"),p.deliveryIndex())).toList();
|
||||
var request=new DispatchCommands.LoadingComplete(approved.recordVersion(),"explicit-loaded",preview.manifestHash(),measurements,"实际核对");
|
||||
assertThat(preview.pallets().get(1).weightKg()).isNull();assertThat(preview.pallets().get(1).missingFields()).contains("weightKg","volumeCbm");
|
||||
var request=new DispatchCommands.LoadingComplete(approved.recordVersion(),"explicit-loaded",preview.manifestHash(),"实际核对");
|
||||
// 捕获失败必须回滚同一原生装车事务,不能留下装车后状态或成功命令。
|
||||
org.mockito.Mockito.doThrow(new IllegalStateException("ownership-capture-failed")).doNothing()
|
||||
.when(f.ownedBusinessOwnership).capture(anyLong(),org.mockito.ArgumentMatchers.nullable(Long.class),
|
||||
@@ -298,6 +296,10 @@ public class DispatchAggregatePersistenceTest {
|
||||
assertThat(f.db.queryForObject("SELECT COUNT(*) FROM ll_dispatch_command WHERE idempotency_key='explicit-loaded'",Long.class)).isZero();
|
||||
var loaded=f.tx.execute(s->f.service.loadingComplete(f.actor,created.dispatchNo(),request));
|
||||
assertThat(loaded.stage()).isEqualTo(Stage.AFTER_LOADING);assertThat(loaded.result()).isInstanceOf(DispatchResults.Loaded.class);
|
||||
var facts=((DispatchResults.Loaded)loaded.result()).confirmedLoad();
|
||||
assertThat(facts.pallets()).hasSize(2);
|
||||
assertThat(facts.pallets().get(0).weightKg()).isEqualByComparingTo("100");
|
||||
assertThat(facts.pallets().get(1).weightKg()).isNull();assertThat(facts.pallets().get(1).volumeCbm()).isNull();
|
||||
assertThat(f.db.queryForObject("SELECT status FROM ll_fleet_trip",String.class)).isEqualTo("PLANNED");
|
||||
var replay=f.tx.execute(s->f.service.loadingComplete(f.actor,created.dispatchNo(),request));
|
||||
assertThat(replay.replayed()).isTrue();assertThat(replay.result()).isEqualTo(loaded.result());
|
||||
@@ -316,7 +318,7 @@ public class DispatchAggregatePersistenceTest {
|
||||
f.db.update("UPDATE ll_dispatch_pda_pick_order SET packages=2");
|
||||
var preview=f.tx.execute(s->f.service.loading(f.actor,created.dispatchNo()));
|
||||
assertThat(preview.pickingReconciled()).isFalse();assertThat(preview.loadingComplete().allowed()).isFalse();
|
||||
assertThatThrownBy(()->f.tx.execute(s->f.service.loadingComplete(f.actor,created.dispatchNo(),new DispatchCommands.LoadingComplete(approved.recordVersion(),"missing-rejected",preview.manifestHash(),List.of(),null)))).isInstanceOf(BizException.class).hasMessageContaining("核平");
|
||||
assertThatThrownBy(()->f.tx.execute(s->f.service.loadingComplete(f.actor,created.dispatchNo(),new DispatchCommands.LoadingComplete(approved.recordVersion(),"missing-rejected",preview.manifestHash(),null)))).isInstanceOf(BizException.class).hasMessageContaining("核平");
|
||||
assertThat(f.db.queryForObject("SELECT stage FROM ll_dispatch_order",String.class)).isEqualTo("LOADING");
|
||||
}
|
||||
/** 已提交根的公开资格关闭;新键重复提交不改变事实,原键仍先于当前状态恢复结果。 */
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ class DispatchDeparturePersistenceTest {
|
||||
var created=f.tx.execute(s->f.service.create(f.actor,f.configured("depart-create",List.of(a,b))));number=created.dispatchNo();var approved=f.approve(created);
|
||||
f.picked(1,"101",501,3);f.picked(2,"202",502,5);f.db.update("UPDATE ll_dispatch_order SET picked_package_count=8");
|
||||
var loading=f.tx.execute(s->f.service.loading(f.actor,number));var measurements=loading.pallets().stream().map(p->new LoadingMeasurement(p.warehousePalletId(),1,BigDecimal.ONE,BigDecimal.ONE,p.deliveryIndex())).toList();
|
||||
f.tx.executeWithoutResult(s->f.service.loadingComplete(f.actor,number,new DispatchCommands.LoadingComplete(approved.recordVersion(),"depart-loaded",loading.manifestHash(),measurements,"实际实载")));
|
||||
f.tx.executeWithoutResult(s->f.service.loadingComplete(f.actor,number,new DispatchCommands.LoadingComplete(approved.recordVersion(),"depart-loaded",loading.manifestHash(),"实际实载")));
|
||||
var context=f.tx.execute(s->f.service.preview(f.actor,number));
|
||||
outbounds=List.of(new OutboundReference("101","测试客户101","OUT-A","WAIT_OUTBOUND",3,1),new OutboundReference("202","测试客户202","OUT-B","WAIT_OUTBOUND",5,1));
|
||||
deliveryRefs=List.of(new DeliveryReference("GROUP-A","101","测试客户101","OUT-A","DL-A",context.cargo().get(0).destination(),"WAIT_DELIVERY",3,1),new DeliveryReference("GROUP-B","202","测试客户202","OUT-B","DL-B",context.cargo().get(1).destination(),"WAIT_DELIVERY",5,1));
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ class DispatchLoadingDeparturePersistenceTest {
|
||||
@Test void pdaUsesOnePermissionButRejectsOtherWarehouseAndInvalidEvidence() throws Exception {
|
||||
authorize();var f=new Fixture();var pda=new Actor("picker",ActorMode.PDA,"9");
|
||||
assertThatThrownBy(()->f.f.tx.execute(s->f.service.complete(new Actor("picker",ActorMode.PDA,"99"),f.number,f.request("foreign")))).hasMessageContaining("当前绑定仓");
|
||||
var bad=new DispatchCommands.LoadingComplete(f.version,"bad-manifest","changed",f.measurements,null);
|
||||
var bad=new DispatchCommands.LoadingComplete(f.version,"bad-manifest","changed",null);
|
||||
assertThatThrownBy(()->f.f.tx.execute(s->f.service.complete(pda,f.number,bad))).hasMessageContaining("真实装载清单已变化");
|
||||
verify(f.delivery,never()).prepareForDeparture(any(),any(),anyString());
|
||||
var result=f.f.tx.execute(s->f.service.complete(pda,f.number,f.request("pda")));assertThat(result.stage()).isEqualTo(Stage.AFTER_LOADING);
|
||||
@@ -243,7 +243,7 @@ class DispatchLoadingDeparturePersistenceTest {
|
||||
return f.tx.execute(s->documents.bind(actor,number,DispatchLoadingDocumentService.SLOTS.get(index),new BindPhoto(Integer.toString(index),"photo-bind-"+index,Long.toString(1000L+index))));
|
||||
}
|
||||
/** 每次请求沿用最初真实测量和版本,便于验证竞争及原键恢复。 */
|
||||
DispatchCommands.LoadingComplete request(String key){return new DispatchCommands.LoadingComplete(version,key,manifest,measurements,"实装发车");}
|
||||
DispatchCommands.LoadingComplete request(String key){return new DispatchCommands.LoadingComplete(version,key,manifest,"实装发车");}
|
||||
/** 两客户子出库保持独立业务号和箱数。 */
|
||||
List<OutboundReference> outbounds(String status){return List.of(new OutboundReference("101","客户101","OUT-A",status,3,1),new OutboundReference("202","客户202","OUT-B",status,5,1));}
|
||||
/** 两目的地仍对应各自真实客户,不把整车合并为单客户。 */
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ class DispatchLoadingEligibilityPersistenceTest {
|
||||
"ll_dispatch_pda_pick","ll_dispatch_pda_pick_order","ll_dispatch_pallet_lock","ll_dispatch_physical_pallet_lock",
|
||||
"ll_fleet_trip","ll_fleet_trip_history");
|
||||
var before=snapshot(f,tables);
|
||||
var request=new DispatchCommands.LoadingComplete(approved.recordVersion(),"unreceived-negative",preview.manifestHash(),List.of(),"未实收负例");
|
||||
var request=new DispatchCommands.LoadingComplete(approved.recordVersion(),"unreceived-negative",preview.manifestHash(),"未实收负例");
|
||||
assertThatThrownBy(()->f.tx.execute(t->f.service.loadingComplete(actor,created.dispatchNo(),request)))
|
||||
.isInstanceOfSatisfying(BizException.class,error->assertThat(error.getResultCode()).isEqualTo(ResultCode.CONFLICT))
|
||||
.hasMessage("逐项真实拣货尚未核平");
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ class DispatchMergedRelabelPersistenceTest {
|
||||
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(),
|
||||
List.of(new DispatchContracts.LoadingMeasurement(501L,1,new BigDecimal("10"),BigDecimal.ONE,pallet.deliveryIndex()==null?1:pallet.deliveryIndex())),null)));
|
||||
null)));
|
||||
assertThat(loaded.stage()).isEqualTo(Stage.AFTER_LOADING);
|
||||
assertThat(f.count("ll_inventory_ledger WHERE business_type='SHIP'")).isEqualTo(2);
|
||||
}
|
||||
|
||||
+14
@@ -53,4 +53,18 @@ class TripCapacityPolicyTest {
|
||||
@Test void emptyTripRetainsCapacityOnEachSegment() {
|
||||
assertThat(calculate(3, capacity, List.of())).allSatisfy(segment -> assertThat(segment.remaining()).isEqualTo(capacity));
|
||||
}
|
||||
|
||||
@Test void thirtyActualPalletsWithoutMeasurementsDoNotBecomeZeroWeightOrSellableCapacity() {
|
||||
var pallets=java.util.stream.IntStream.range(0,30).mapToObj(i->new Booking("P"+i,0,1,new Load(1,null,null))).toList();
|
||||
var segment=calculateActual(2,capacity,pallets).get(0);
|
||||
assertThat(segment.occupied().palletSpaces()).isEqualTo(30);
|
||||
assertThat(segment.occupied().weightKg()).isNull();assertThat(segment.remaining().volumeCbm()).isNull();
|
||||
assertThatThrownBy(()->calculate(2,capacity,pallets)).hasMessageContaining("重量和体积");
|
||||
var tooMany=new java.util.ArrayList<>(pallets);tooMany.add(new Booking("EXTRA",0,1,new Load(1,null,null)));
|
||||
assertThatThrownBy(()->calculateActual(2,capacity,tooMany)).hasMessageContaining("超过");
|
||||
}
|
||||
@Test void unknownPalletDoesNotHideKnownOverweight() {
|
||||
assertThatThrownBy(()->calculateActual(2,capacity,List.of(new Booking("UNKNOWN",0,1,new Load(1,null,null)),
|
||||
new Booking("KNOWN",0,1,load(1,"18001","1"))))).hasMessageContaining("超过");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user