package com.baiyue.lastleg.mapper; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.jdbc.core.JdbcTemplate; import java.util.UUID; 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)"); 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("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); assertThat(mapper.sources("22",null)).singleElement().satisfies(s->assertThat(s.deliveringPackages()).isEqualTo(7)); assertThat(mapper.sources("UNRELATED",null)).isEmpty(); } /** 使用无客户头的真实新模型,按明细验证查询归属。 */ @BeforeEach void setup() throws Exception { var ds=new JdbcDataSource();ds.setURL("jdbc:h2:mem:tail-progress-"+UUID.randomUUID()+";MODE=MySQL;DB_CLOSE_DELAY=-1"); jdbc=new JdbcTemplate(ds); jdbc.execute("CREATE TABLE ll_order(id BIGINT,customer_id VARCHAR,source_order_id VARCHAR,source_inbound_id VARCHAR,source_region VARCHAR,order_type VARCHAR,package_count INT)"); jdbc.execute("CREATE TABLE ll_dispatch_order(id BIGINT,status VARCHAR,stage VARCHAR,departed_at TIMESTAMP)"); jdbc.execute("CREATE TABLE ll_dispatch_item(id BIGINT,dispatch_order_id BIGINT,customer_id VARCHAR,plan_pallet_id BIGINT,warehouse_pallet_id BIGINT,overseas_warehouse_id VARCHAR,destination_type VARCHAR,delivery_address VARCHAR,postal_code VARCHAR,destination_warehouse_code VARCHAR,fba_warehouse_code VARCHAR,destination_warehouse_id VARCHAR)"); jdbc.execute("CREATE TABLE ll_inbound_plan_pallet_allocation(pallet_id BIGINT,line_id BIGINT,planned_package_count INT)"); jdbc.execute("CREATE TABLE ll_inbound_plan_line(id BIGINT,customer_id VARCHAR,order_id BIGINT)"); jdbc.execute("CREATE TABLE ll_inventory_position(id BIGINT,customer_id VARCHAR,pallet_id BIGINT,overseas_warehouse_id VARCHAR,balance_id BIGINT,available_packages INT)"); jdbc.execute("CREATE TABLE ll_inventory_balance(id BIGINT,customer_id VARCHAR,inbound_order_id BIGINT)"); jdbc.execute("CREATE TABLE ll_dispatch_pda_pick(id BIGINT,customer_id VARCHAR,dispatch_item_id BIGINT,inventory_position_id BIGINT,packages INT,task_id BIGINT)"); jdbc.execute("CREATE TABLE ll_dispatch_pda_pick_order(id BIGINT,pick_id BIGINT,customer_id VARCHAR,order_id BIGINT,packages INT)"); 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.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')"); jdbc.update("INSERT INTO ll_inbound_plan_line VALUES(1000,'11',1),(1001,'11',2),(1002,'22',3)"); jdbc.update("INSERT INTO ll_inbound_plan_pallet_allocation VALUES(500,1000,10),(500,1001,20),(500,1002,30)"); var config=new MybatisConfiguration();config.setMapUnderscoreToCamelCase(true);config.addMapper(CustomerOrderProgressMapper.class); var factory=new MybatisSqlSessionFactoryBean();factory.setDataSource(ds);factory.setConfiguration(config); mapper=new SqlSessionTemplate(factory.getObject()).getMapper(CustomerOrderProgressMapper.class); } /** 计划托盘上的所有本客户来源均计入,取消后不再计入。 */ @Test void plannedSharedPalletIncludesEveryOwnSourceAndExcludesCancellation() { 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); assertThat(mapper.sources("22", null)).hasSize(1).allMatch(s->s.scheduledPackages()==0); } /** 已收货使用实际库存和扫码,不与计划量重复累计。 */ @Test void receivedPalletUsesInventoryAndActualPicksWithoutCountingBothPlanAndStock() { 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(sources.get(1).scheduledPackages()).isEqualTo(20); assertThat(mapper.sources("11",java.util.List.of("101"))).containsExactly(sources.get(0)); } /** 实际发车、出库及目的地须同时成立才计入在途。 */ @Test void deliveryRequiresActualDepartureOutboundAndMatchingDestinationAttribution() { 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)"); jdbc.update("INSERT INTO ll_dispatch_pda_pick_order VALUES(900,600,'11',1,4)"); jdbc.update("INSERT INTO ll_outbound_order VALUES(10000,'11','WAIT_OUTBOUND')"); jdbc.update("INSERT INTO ll_delivery_order VALUES(10001,'11',10,10000,'DELIVERING','CODE:FBA1')"); assertThat(mapper.sources("11", null)).allMatch(s->s.deliveringPackages()==0); jdbc.update("UPDATE ll_dispatch_order SET stage='DELIVERING',departed_at=CURRENT_TIMESTAMP"); assertThat(mapper.sources("11", null)).allMatch(s->s.deliveringPackages()==0); jdbc.update("UPDATE ll_outbound_order SET status='OUTBOUNDED'"); assertThat(mapper.sources("11", null).get(0).deliveringPackages()).isEqualTo(4); assertThat(mapper.sources("11",java.util.List.of("101"))).containsExactly(mapper.sources("11",null).get(0)); assertThat(mapper.sources("11", null).get(1).deliveringPackages()).isZero(); jdbc.update("UPDATE ll_delivery_order SET delivery_group_key='CODE:FBA2'"); assertThat(mapper.sources("11", null)).allMatch(s->s.deliveringPackages()==0); jdbc.update("UPDATE ll_delivery_order SET delivery_group_key='CODE:FBA1',status='RETURN_PENDING'"); 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)"); 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); assertThat(mapper.sources("22",java.util.List.of("101"))).isEmpty(); assertThat(mapper.sources("11",java.util.List.of("999"))).isEmpty(); } }