This commit is contained in:
2026-10-21 10:52:17 +08:00
parent f00d0e16cf
commit e5f9cdcf01
8 changed files with 144 additions and 73 deletions
@@ -13,16 +13,19 @@ import static org.assertj.core.api.Assertions.*;
class CustomerOrderProgressMapperTest {
private JdbcTemplate jdbc;
private CustomerOrderProgressMapper mapper;
/** 两客户共用一单一任务,计划和在途均按各自真实明细归属统计。 */
/** 两客户共用一单一任务,出库和在途均按各自真实明细归属统计。 */
@Test void sharedDispatchAndTaskKeepBothCustomerProgressSeparate() {
jdbc.update("INSERT INTO ll_dispatch_item(id,dispatch_order_id,customer_id,plan_pallet_id,overseas_warehouse_id,destination_warehouse_code) VALUES(101,10,'22',501,'W1','FBA2')");
jdbc.update("INSERT INTO ll_inbound_plan_pallet_allocation VALUES(501,1002,30)");
pendingOutbound(10000,"11",1,10);
pendingOutbound(10000,"11",2,20);
pendingOutbound(10002,"22",3,30);
assertThat(mapper.sources("11",null)).extracting(s->s.scheduledPackages()).containsExactly(10L,20L);
assertThat(mapper.sources("22",null)).singleElement().satisfies(s->assertThat(s.scheduledPackages()).isEqualTo(30));
jdbc.update("INSERT INTO ll_dispatch_pda_task VALUES(700,10)");
jdbc.update("INSERT INTO ll_dispatch_pda_pick VALUES(600,'11',100,400,4,700),(601,'22',101,401,7,700)");
jdbc.update("INSERT INTO ll_dispatch_pda_pick_order VALUES(900,600,'11',1,4),(901,601,'22',3,7)");
jdbc.update("INSERT INTO ll_outbound_order VALUES(10000,'11','OUTBOUNDED'),(10002,'22','OUTBOUNDED')");
jdbc.update("UPDATE ll_outbound_order SET status='OUTBOUNDED'");
jdbc.update("INSERT INTO ll_delivery_order VALUES(10001,'11',10,10000,'DELIVERING','CODE:FBA1'),(10003,'22',10,10002,'DELIVERING','CODE:FBA2')");
jdbc.update("UPDATE ll_dispatch_order SET stage='DELIVERING',departed_at=CURRENT_TIMESTAMP");
assertThat(mapper.sources("11",null)).extracting(s->s.deliveringPackages()).containsExactly(4L,0L);
@@ -45,6 +48,7 @@ class CustomerOrderProgressMapperTest {
jdbc.execute("CREATE TABLE ll_dispatch_pda_task(id BIGINT,dispatch_order_id BIGINT UNIQUE)");
jdbc.execute("CREATE TABLE ll_delivery_order(id BIGINT,customer_id VARCHAR,dispatch_order_id BIGINT,outbound_order_id BIGINT,status VARCHAR,delivery_group_key VARCHAR)");
jdbc.execute("CREATE TABLE ll_outbound_order(id BIGINT,customer_id VARCHAR,status VARCHAR)");
jdbc.execute("CREATE TABLE ll_outbound_pallet_item(outbound_order_id BIGINT,customer_id VARCHAR,source_order_id BIGINT,package_count INT)");
jdbc.update("INSERT INTO ll_order VALUES(1,'11','101','201','FIRSTLEG','FBA',10),(2,'11','102','202','FIRSTLEG','FBA',20),(3,'22','103','203','FIRSTLEG','FBA',30)");
jdbc.update("INSERT INTO ll_dispatch_order VALUES(10,'ACTIVE','BEFORE_LOADING',NULL)");
jdbc.update("INSERT INTO ll_dispatch_item(id,dispatch_order_id,customer_id,plan_pallet_id,overseas_warehouse_id,destination_warehouse_code) VALUES(100,10,'11',500,'W1','FBA1')");
@@ -54,24 +58,40 @@ class CustomerOrderProgressMapperTest {
var factory=new MybatisSqlSessionFactoryBean();factory.setDataSource(ds);factory.setConfiguration(config);
mapper=new SqlSessionTemplate(factory.getObject()).getMapper(CustomerOrderProgressMapper.class);
}
/** 计划托盘上的所有本客户来源均计入,取消后不再计入。 */
@Test void plannedSharedPalletIncludesEveryOwnSourceAndExcludesCancellation() {
/** 排车号及审核阶段不代表有效出库,生成出库后才计入,取消后自动移出。 */
@Test void onlyEffectiveOutboundSourcesCountAsScheduled() {
jdbc.execute("ALTER TABLE ll_dispatch_order ADD review_state VARCHAR");
for (String review : java.util.List.of("DRAFT","DISPATCH_PENDING","DISPATCH_REJECTED","LOADING_PENDING","LOADING_REJECTED","APPROVED")) {
jdbc.update("UPDATE ll_dispatch_order SET review_state=?",review);
assertThat(mapper.sources("11", null)).allMatch(s->s.scheduledPackages()==0);
assertThat(new com.baiyue.lastleg.service.CustomerOrderProgressService(mapper).read(11,null).progress()).isEmpty();
}
jdbc.update("UPDATE ll_dispatch_order SET stage='AFTER_LOADING'");
assertThat(mapper.sources("11", null)).allMatch(s->s.scheduledPackages()==0);
pendingOutbound(10000,"11",1,10);
pendingOutbound(10000,"11",2,20);
var sources=mapper.sources("11", null);assertThat(sources).hasSize(2);
assertThat(sources.get(0).scheduledPackages()).isEqualTo(10);
assertThat(sources.get(1).scheduledPackages()).isEqualTo(20);
assertThat(mapper.sources("11",java.util.List.of("101"))).containsExactly(sources.get(0));
assertThat(sources).allMatch(s->s.deliveringPackages()==0);
jdbc.update("UPDATE ll_dispatch_order SET status='CANCELLED'");
assertThat(mapper.sources("11", null)).allMatch(s->s.scheduledPackages()==0);
for (String status : java.util.List.of("CANCELLED","CANCELED","OUTBOUNDED")) {
jdbc.update("UPDATE ll_outbound_order SET status=?",status);
assertThat(mapper.sources("11", null)).allMatch(s->s.scheduledPackages()==0);
}
assertThat(mapper.sources("22", null)).hasSize(1).allMatch(s->s.scheduledPackages()==0);
}
/** 已收货使用实际库存和扫码,不与计划量重复累计。 */
@Test void receivedPalletUsesInventoryAndActualPicksWithoutCountingBothPlanAndStock() {
/** 已收货和扫码不能提前推进,出库来源快照不随剩余库存变化。 */
@Test void scheduledUsesOutboundSnapshotInsteadOfPlannedOrRemainingInventory() {
jdbc.update("UPDATE ll_dispatch_item SET warehouse_pallet_id=800");
jdbc.update("INSERT INTO ll_inventory_balance VALUES(300,'11',1),(301,'11',2)");
jdbc.update("INSERT INTO ll_inventory_position VALUES(400,'11',800,'W1',300,6),(401,'11',800,'W1',301,20)");
jdbc.update("INSERT INTO ll_dispatch_pda_pick VALUES(600,'11',100,400,4,700)");
var sources=mapper.sources("11", null);assertThat(sources.get(0).scheduledPackages()).isEqualTo(10);
assertThat(mapper.sources("11", null)).allMatch(s->s.scheduledPackages()==0);
pendingOutbound(10000,"11",1,4);
pendingOutbound(10000,"11",2,20);
jdbc.update("UPDATE ll_inventory_position SET available_packages=0");
var sources=mapper.sources("11", null);assertThat(sources.get(0).scheduledPackages()).isEqualTo(4);
assertThat(sources.get(1).scheduledPackages()).isEqualTo(20);
assertThat(mapper.sources("11",java.util.List.of("101"))).containsExactly(sources.get(0));
}
@@ -95,8 +115,10 @@ class CustomerOrderProgressMapperTest {
assertThat(mapper.sources("11", null)).allMatch(s->s.deliveringPackages()==0);
}
/** 同源多入仓片段完整返回,过滤不得扩大到其他订单或客户。 */
@Test void exactSourceOrderScopePreservesAllOwnInboundFragmentsAndExcludesForeignOrders() {
jdbc.update("INSERT INTO ll_order VALUES(4,'11','101','204','FIRSTLEG','FBA',5)");
pendingOutbound(10000,"11",1,10);
var own=mapper.sources("11",java.util.List.of("101","103"));
assertThat(own).extracting(s->s.orderId()).containsExactly("101","101");
assertThat(own).extracting(s->s.scheduledPackages()).containsExactly(10L,0L);
@@ -104,4 +126,30 @@ class CustomerOrderProgressMapperTest {
assertThat(mapper.sources("11",java.util.List.of("999"))).isEmpty();
}
/** 多张出库只汇总本客户的有效来源数量,不借用其他来源或客户单据。 */
@Test void splitOutboundQuantitiesExcludeCancelledAndForeignDocuments() {
pendingOutbound(10000,"11",1,4);
pendingOutbound(10001,"11",1,6);
pendingOutbound(10002,"11",2,20);
jdbc.update("UPDATE ll_outbound_order SET status='CANCELLED' WHERE id=10001");
jdbc.update("INSERT INTO ll_outbound_order VALUES(10003,'22','WAIT_OUTBOUND')");
jdbc.update("INSERT INTO ll_outbound_pallet_item VALUES(10003,'11',1,100),(10000,'22',3,100),(10003,'22',1,100)");
assertThat(mapper.sources("11", null)).extracting(s->s.scheduledPackages()).containsExactly(4L,20L);
assertThat(new com.baiyue.lastleg.service.CustomerOrderProgressService(mapper).read(11,java.util.List.of(101L)).progress())
.singleElement().satisfies(p->{assertThat(p.stage()).isEqualTo("scheduled");assertThat(p.partial()).isTrue();});
assertThat(mapper.sources("22", null)).allMatch(s->s.scheduledPackages()==0);
jdbc.update("UPDATE ll_outbound_order SET status='WAIT_OUTBOUND' WHERE id=10001");
assertThat(mapper.sources("11", null)).extracting(s->s.scheduledPackages()).containsExactly(10L,20L);
assertThat(new com.baiyue.lastleg.service.CustomerOrderProgressService(mapper).read(11,java.util.List.of(101L)).progress())
.singleElement().satisfies(p->{assertThat(p.stage()).isEqualTo("scheduled");assertThat(p.partial()).isFalse();});
}
/** 创建同客户的待出库单及精确来源明细,允许一单包含多个来源。 */
private void pendingOutbound(long outboundId,String customerId,long sourceOrderId,int packages) {
if (jdbc.queryForObject("SELECT COUNT(*) FROM ll_outbound_order WHERE id=?",Integer.class,outboundId)==0) {
jdbc.update("INSERT INTO ll_outbound_order VALUES(?,?,'WAIT_OUTBOUND')",outboundId,customerId);
}
jdbc.update("INSERT INTO ll_outbound_pallet_item VALUES(?,?,?,?)",outboundId,customerId,sourceOrderId,packages);
}
}