fix(dispatch): calculate received pallet cargo from current stock
This commit is contained in:
+133
-6
@@ -10,10 +10,11 @@ import java.util.UUID;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
class DispatchPhysicalWeightTest {
|
||||
/** 计划估算不能用材积重补毛重,混合来源缺失时保持未知。 */
|
||||
@Test void billingVolumeWeightCannotFillMissingPhysicalWeight() {
|
||||
var jdbc = new JdbcTemplate(new DriverManagerDataSource("jdbc:h2:mem:" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1", "sa", ""));
|
||||
jdbc.execute("CREATE TABLE ll_order_detail(customer_id VARCHAR(64),order_id BIGINT,gross_weight_kg DECIMAL(12,3))");
|
||||
jdbc.execute("CREATE TABLE ll_inbound_order(id BIGINT,customer_id VARCHAR(64),gross_weight_kg DECIMAL(12,3))");
|
||||
currentStockSchema(jdbc);
|
||||
jdbc.execute("CREATE TABLE ll_inbound_plan_pallet_allocation(line_id BIGINT)");
|
||||
jdbc.update("INSERT INTO ll_inbound_plan_pallet_allocation VALUES(1)");
|
||||
var tested = new java.util.HashSet<String>();
|
||||
@@ -30,15 +31,17 @@ class DispatchPhysicalWeightTest {
|
||||
assertThat(jdbc.queryForObject("SELECT " + projection + " FROM (SELECT 80 gross_weight_kg) wtp", BigDecimal.class)).isEqualByComparingTo("80");
|
||||
continue;
|
||||
}
|
||||
String query = "SELECT " + projection + " FROM (SELECT '10' AS customer_id) ip "
|
||||
+ "CROSS JOIN (SELECT 1 AS order_id,10 AS volume_cbm) l "
|
||||
+ "CROSS JOIN (SELECT 1 AS line_id,5 AS allocated_volume_cbm) a "
|
||||
+ "CROSS JOIN (SELECT 999 AS volume_weight_kg) o";
|
||||
String query = "SELECT " + projection + " FROM (SELECT '10' AS customer_id,1 AS source_order_id) ip "
|
||||
+ "CROSS JOIN (SELECT 1 AS order_id,10 AS volume_cbm,20 AS required_package_count) l "
|
||||
+ "CROSS JOIN (SELECT 1 AS line_id,5 AS allocated_volume_cbm,10 AS counted_package_count) a "
|
||||
+ "CROSS JOIN (SELECT 999 AS volume_weight_kg) o "
|
||||
+ "CROSS JOIN (SELECT CAST(NULL AS BIGINT) AS warehouse_pallet_id,'RESERVED' AS assignment_status,0 AS actual_package_count) p "
|
||||
+ "GROUP BY p.warehouse_pallet_id,p.assignment_status,p.actual_package_count,ip.customer_id,ip.source_order_id";
|
||||
assertThat(jdbc.queryForObject(query, BigDecimal.class)).as(method.getName() + " missing gross weight").isNull();
|
||||
if (projection.trim().equals("NULL")) continue; // Relabel candidates have no measured pallet weight source yet.
|
||||
jdbc.update("INSERT INTO ll_order_detail VALUES('10',1,40)");
|
||||
assertThat(jdbc.queryForObject(query, BigDecimal.class)).as(method.getName() + " physical weight allocation").isEqualByComparingTo("20");
|
||||
String mixed = query.replace("SELECT 1 AS order_id,10 AS volume_cbm", "SELECT 1 AS order_id,10 AS volume_cbm UNION ALL SELECT 2,10");
|
||||
String mixed = query.replace("SELECT 1 AS order_id,10 AS volume_cbm,20 AS required_package_count", "SELECT 1 AS order_id,10 AS volume_cbm,20 AS required_package_count UNION ALL SELECT 2,10,20");
|
||||
assertThat(jdbc.queryForObject(mixed, BigDecimal.class)).as(method.getName() + " incomplete aggregate").isNull();
|
||||
jdbc.update("INSERT INTO ll_order_detail VALUES('10',2,40)");
|
||||
assertThat(jdbc.queryForObject(mixed, BigDecimal.class)).as(method.getName() + " complete aggregate").isEqualByComparingTo("40");
|
||||
@@ -47,4 +50,128 @@ class DispatchPhysicalWeightTest {
|
||||
}
|
||||
assertThat(tested).containsExactlyInAnyOrder("available","wholeAvailable","readSelected","lockSelected");
|
||||
}
|
||||
|
||||
/** 执行正式候选投影,21箱两托计划可均分,实收10+11后必须切换到对应货量。 */
|
||||
@Test void receivedCandidatesUseTenAndElevenCartonsInsteadOfEqualPlanShares() {
|
||||
var jdbc=new JdbcTemplate(new DriverManagerDataSource("jdbc:h2:mem:"+UUID.randomUUID()+";MODE=MySQL;DB_CLOSE_DELAY=-1","sa",""));
|
||||
jdbc.execute("CREATE TABLE ll_order_detail(customer_id VARCHAR,order_id BIGINT,gross_weight_kg DECIMAL(18,3))");
|
||||
currentStockSchema(jdbc);
|
||||
jdbc.execute("CREATE TABLE ll_inbound_plan_pallet_allocation(line_id BIGINT)");
|
||||
jdbc.update("INSERT INTO ll_order_detail VALUES('10',1,570.5),('other',1,9999)");
|
||||
jdbc.update("INSERT INTO ll_inbound_order VALUES(1,'10',570.5,21,3.528)");
|
||||
jdbc.update("INSERT INTO ll_inventory_balance VALUES(1,'10',1)");
|
||||
jdbc.update("INSERT INTO ll_inventory_position VALUES(1,'10',1,501,'9',10,0,0,0,0)");
|
||||
var tested=new java.util.HashSet<String>();
|
||||
for(var method:DispatchCandidateMapper.class.getDeclaredMethods()) {
|
||||
if(!java.util.Set.of("available","wholeAvailable","readSelected","lockSelected").contains(method.getName()))continue;
|
||||
String sql=String.join(" ",method.getAnnotation(Select.class).value()).replace(">",">").replace("<","<");
|
||||
String weight=sql.substring(sql.indexOf("AS volumeWeightKg,")+"AS volumeWeightKg,".length(),sql.indexOf(" AS grossWeightKg,"));
|
||||
String volume=sql.substring(sql.indexOf("AS packageCount,")+"AS packageCount,".length(),sql.indexOf(" AS volumeCbm,"));
|
||||
String from=" FROM (SELECT '10' AS customer_id,1 AS source_order_id) ip "
|
||||
+ "CROSS JOIN (SELECT 1 AS order_id,3.528 AS volume_cbm,21 AS required_package_count) l "
|
||||
+ "CROSS JOIN (SELECT 1 AS line_id,1.764 AS allocated_volume_cbm,10 AS counted_package_count,1.680 AS actual_volume_cbm) a "
|
||||
+ "CROSS JOIN (SELECT 501 AS warehouse_pallet_id,'RECEIVING' AS assignment_status,10 AS actual_package_count,1.764 AS allocated_volume_cbm) p "
|
||||
+ "GROUP BY p.warehouse_pallet_id,p.assignment_status,p.actual_package_count,p.allocated_volume_cbm,ip.customer_id,ip.source_order_id";
|
||||
assertThat(jdbc.queryForObject("SELECT "+weight+from,BigDecimal.class)).as(method.getName()).isEqualByComparingTo("271.667");
|
||||
assertThat(jdbc.queryForObject("SELECT "+volume+from,BigDecimal.class)).isEqualByComparingTo("1.680");
|
||||
String second=from.replace("10 AS counted_package_count,1.680 AS actual_volume_cbm","11 AS counted_package_count,1.848 AS actual_volume_cbm");
|
||||
jdbc.update("UPDATE ll_inventory_position SET available_packages=11");
|
||||
assertThat(jdbc.queryForObject("SELECT "+weight+second,BigDecimal.class)).isEqualByComparingTo("298.833");
|
||||
assertThat(jdbc.queryForObject("SELECT "+volume+second,BigDecimal.class)).isEqualByComparingTo("1.848");
|
||||
String plan=from.replace("'RECEIVING' AS assignment_status,10 AS actual_package_count","'RESERVED' AS assignment_status,0 AS actual_package_count");
|
||||
assertThat(jdbc.queryForObject("SELECT "+weight+plan,BigDecimal.class)).isEqualByComparingTo("285.25");
|
||||
assertThat(jdbc.queryForObject("SELECT "+volume+plan,BigDecimal.class)).isEqualByComparingTo("1.764");
|
||||
jdbc.update("UPDATE ll_inbound_order SET package_count=0");
|
||||
assertThat(jdbc.queryForObject("SELECT "+weight+from,BigDecimal.class)).isNull();
|
||||
jdbc.update("UPDATE ll_inbound_order SET package_count=21,volume_cbm=0");
|
||||
assertThat(jdbc.queryForObject("SELECT "+volume+from,BigDecimal.class)).isNull();
|
||||
jdbc.update("UPDATE ll_inbound_order SET volume_cbm=3.528");
|
||||
jdbc.update("INSERT INTO ll_inventory_position VALUES(2,'10',2,501,'9',1,0,0,0,0)");
|
||||
assertThat(jdbc.queryForObject("SELECT "+weight+from,BigDecimal.class)).isNull();
|
||||
jdbc.update("DELETE FROM ll_inventory_position WHERE id=2");
|
||||
jdbc.update("UPDATE ll_inventory_position SET available_packages=10");
|
||||
tested.add(method.getName());
|
||||
}
|
||||
assertThat(tested).containsExactlyInAnyOrder("available","wholeAvailable","readSelected","lockSelected");
|
||||
}
|
||||
|
||||
/** 拆出、部分出库及合入其他来源后,按当前来源组成重算;同客户同箱数也不能复用旧货量。 */
|
||||
@Test void currentStockMeasurementsFollowSplitPartialShipmentAndMergedSources() {
|
||||
var jdbc=measurementDatabase();
|
||||
jdbc.update("INSERT INTO ll_inbound_order VALUES(1,'10',200,20,4),(2,'10',300,10,3),(3,'other',9000,10,9)");
|
||||
jdbc.update("INSERT INTO ll_inventory_balance VALUES(1,'10',1),(2,'10',2),(3,'other',3)");
|
||||
jdbc.update("INSERT INTO ll_inventory_position VALUES(1,'10',1,501,'9',10,0,0,0,0),(3,'other',3,501,'9',10,0,0,0,0)");
|
||||
assertMetrics(jdbc,501,"100","2");
|
||||
jdbc.update("UPDATE ll_inventory_position SET available_packages=5 WHERE id=1");
|
||||
jdbc.update("INSERT INTO ll_inventory_position VALUES(2,'10',1,502,'9',5,0,0,0,0)");
|
||||
assertMetrics(jdbc,501,"50","1");assertMetrics(jdbc,502,"50","1");
|
||||
jdbc.update("UPDATE ll_inventory_position SET available_packages=2,picked_packages=3 WHERE id=1");
|
||||
assertMetrics(jdbc,501,"20","0.4");
|
||||
jdbc.update("INSERT INTO ll_inventory_position VALUES(4,'10',2,501,'9',8,0,0,0,0)");
|
||||
assertMetrics(jdbc,501,"260","2.8");
|
||||
// 任一有效库存来源无法解析时不能只返回其他来源的部分合计。
|
||||
jdbc.update("UPDATE ll_inventory_balance SET inbound_order_id=999 WHERE id=2");
|
||||
assertMetrics(jdbc,501,null,null);
|
||||
}
|
||||
|
||||
/** 尾程订单9001与入仓快照7001不同,仍按库存真实关联读取各托实测,而不是均摊总重量。 */
|
||||
@Test void transferMeasurementsUseLinkedInboundAndPreserveDifferentPalletWeights() {
|
||||
var jdbc=measurementDatabase();
|
||||
jdbc.update("INSERT INTO ll_inbound_order VALUES(7001,'10',300,20,3),(9001,'other',9000,20,9)");
|
||||
jdbc.update("INSERT INTO ll_inventory_balance VALUES(1,'10',7001)");
|
||||
jdbc.update("INSERT INTO ll_inventory_position VALUES(1,'10',1,501,'9',10,0,0,0,0),(2,'10',1,502,'9',10,0,0,0,0)");
|
||||
jdbc.update("INSERT INTO ll_warehouse_transfer_order VALUES(80,'10',9001,7001,'9')");
|
||||
jdbc.update("INSERT INTO ll_warehouse_transfer_pallet VALUES(80,'10',501,'COMPLETED',TIMESTAMP '2026-10-23 01:00:00',10,100,1),(80,'10',502,'COMPLETED',TIMESTAMP '2026-10-23 01:00:00',10,200,2)");
|
||||
assertMetrics(jdbc,501,"100","1");assertMetrics(jdbc,502,"200","2");
|
||||
jdbc.update("UPDATE ll_inventory_position SET available_packages=5 WHERE id=1");
|
||||
assertMetrics(jdbc,501,"75","0.75");
|
||||
jdbc.update("UPDATE ll_inventory_position SET available_packages=10 WHERE id=1");
|
||||
jdbc.update("INSERT INTO ll_inventory_ledger VALUES(501,TIMESTAMP '2026-10-23 02:00:00','PALLET_DISASSEMBLE')");
|
||||
assertMetrics(jdbc,501,"150","1.5");
|
||||
}
|
||||
|
||||
/** 同托混入其他来源或冻结货物后,旧整托测量不能冒充当前可用份额;未知单项分别保留。 */
|
||||
@Test void changedCompositionAndMissingMetricsCannotReuseWholePalletMeasurements() {
|
||||
var jdbc=measurementDatabase();
|
||||
jdbc.update("INSERT INTO ll_inbound_order VALUES(7001,'10',300,20,3),(7002,'10',400,20,4)");
|
||||
jdbc.update("INSERT INTO ll_inventory_balance VALUES(1,'10',7001),(2,'10',7002)");
|
||||
jdbc.update("INSERT INTO ll_inventory_position VALUES(1,'10',1,501,'9',5,0,0,0,0),(2,'10',2,501,'9',5,0,0,0,0)");
|
||||
jdbc.update("INSERT INTO ll_warehouse_transfer_order VALUES(80,'10',9001,7001,'9')");
|
||||
jdbc.update("INSERT INTO ll_warehouse_transfer_pallet VALUES(80,'10',501,'COMPLETED',TIMESTAMP '2026-10-23 01:00:00',10,100,1)");
|
||||
assertMetrics(jdbc,501,"175","1.75");
|
||||
jdbc.update("DELETE FROM ll_inventory_position WHERE id=2");
|
||||
jdbc.update("UPDATE ll_inventory_position SET available_packages=10,hold_packages=5");
|
||||
assertMetrics(jdbc,501,"150","1.5");
|
||||
jdbc.update("UPDATE ll_inbound_order SET gross_weight_kg=NULL WHERE id=7001");
|
||||
assertMetrics(jdbc,501,null,"1.5");
|
||||
jdbc.update("UPDATE ll_inbound_order SET package_count=0 WHERE id=7001");
|
||||
assertMetrics(jdbc,501,null,null);
|
||||
}
|
||||
|
||||
/** 每个场景独享内存数据,执行生产SQL而不连接共享环境。 */
|
||||
private static JdbcTemplate measurementDatabase() {
|
||||
var jdbc=new JdbcTemplate(new DriverManagerDataSource("jdbc:h2:mem:"+UUID.randomUUID()+";MODE=MySQL;DB_CLOSE_DELAY=-1","sa",""));
|
||||
jdbc.execute("CREATE TABLE ll_order_detail(customer_id VARCHAR,order_id BIGINT,gross_weight_kg DECIMAL(18,3))");
|
||||
currentStockSchema(jdbc);return jdbc;
|
||||
}
|
||||
|
||||
/** 直接执行候选共用的生产货量表达式,分别检查未知值和精度。 */
|
||||
private static void assertMetrics(JdbcTemplate jdbc,long pallet,String weight,String volume) {
|
||||
String sql=("SELECT "+DispatchCandidateMapper.CURRENT_GROSS_WEIGHT+" AS weight,"+DispatchCandidateMapper.CURRENT_VOLUME+" AS volume "
|
||||
+"FROM (SELECT '10' AS customer_id,9001 AS source_order_id) ip CROSS JOIN (SELECT CAST(? AS BIGINT) AS warehouse_pallet_id) p")
|
||||
.replace(">",">").replace("<","<");
|
||||
var values=jdbc.queryForObject(sql,(rs,n)->new BigDecimal[]{rs.getBigDecimal("weight"),rs.getBigDecimal("volume")},pallet);
|
||||
if(weight==null)assertThat(values[0]).isNull();else assertThat(values[0]).isEqualByComparingTo(weight);
|
||||
if(volume==null)assertThat(values[1]).isNull();else assertThat(values[1]).isEqualByComparingTo(volume);
|
||||
}
|
||||
|
||||
/** 与生产库存来源及转运实测相同的最小表结构,不借旧计划数量充当当前库存。 */
|
||||
private static void currentStockSchema(JdbcTemplate jdbc) {
|
||||
jdbc.execute("CREATE TABLE ll_inbound_order(id BIGINT,customer_id VARCHAR,gross_weight_kg DECIMAL(18,3),package_count INT,volume_cbm DECIMAL(18,4))");
|
||||
jdbc.execute("CREATE TABLE ll_inventory_balance(id BIGINT,customer_id VARCHAR,inbound_order_id BIGINT)");
|
||||
jdbc.execute("CREATE TABLE ll_inventory_position(id BIGINT,customer_id VARCHAR,balance_id BIGINT,pallet_id BIGINT,overseas_warehouse_id VARCHAR,available_packages INT,hold_packages INT,allocated_packages INT,picked_packages INT,receiving_packages INT)");
|
||||
jdbc.execute("CREATE TABLE ll_warehouse_transfer_order(id BIGINT,customer_id VARCHAR,lastleg_order_id BIGINT,inbound_order_id BIGINT,operating_warehouse_id VARCHAR)");
|
||||
jdbc.execute("CREATE TABLE ll_warehouse_transfer_pallet(transfer_order_id BIGINT,customer_id VARCHAR,warehouse_pallet_id BIGINT,status VARCHAR,completed_at TIMESTAMP,carton_count INT,gross_weight_kg DECIMAL(18,3),volume_cbm DECIMAL(18,4))");
|
||||
jdbc.execute("CREATE TABLE ll_inventory_ledger(pallet_id BIGINT,created_at TIMESTAMP,business_type VARCHAR)");
|
||||
}
|
||||
}
|
||||
|
||||
+49
-5
@@ -16,6 +16,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import java.time.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
@@ -23,30 +24,66 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
/** 使用真实排车、预约绑定和MyBatis验证收货交接,再连续整托出货;库存外部命令沿原夹具隔离。 */
|
||||
class DispatchInboundReceiptPersistenceTest {
|
||||
/** 直接读取9+11实收,不读10+10计划估算,也不依赖可用库存或出货冻结状态。 */
|
||||
/** 直接读取9+11实收及对应货量,不读10+10计划估算,也不依赖可用库存或出货冻结状态。 */
|
||||
@Test void confirmedSourceMapperReadsReceiptCountsWithoutInventoryAvailability() throws Exception {
|
||||
var ds=new org.springframework.jdbc.datasource.DriverManagerDataSource("jdbc:h2:mem:receipt-source-"+UUID.randomUUID()+";MODE=MySQL;DB_CLOSE_DELAY=-1","sa","");
|
||||
var db=new org.springframework.jdbc.core.JdbcTemplate(ds);
|
||||
db.execute("CREATE TABLE ll_inbound_plan(id BIGINT,customer_id VARCHAR,overseas_warehouse_id BIGINT,status VARCHAR)");
|
||||
db.execute("CREATE TABLE ll_inbound_plan_pallet(id BIGINT,plan_id BIGINT,customer_id VARCHAR,warehouse_pallet_id BIGINT,pallet_no VARCHAR,delivery_address VARCHAR)");
|
||||
db.execute("CREATE TABLE ll_inbound_plan_line(id BIGINT,customer_id VARCHAR,destination_type VARCHAR,fba_warehouse_code VARCHAR)");
|
||||
db.execute("CREATE TABLE ll_inbound_plan_pallet_allocation(pallet_id BIGINT,line_id BIGINT,customer_id VARCHAR,planned_package_count INT,counted_package_count INT)");
|
||||
db.execute("CREATE TABLE ll_inbound_plan_line(id BIGINT,customer_id VARCHAR,destination_type VARCHAR,fba_warehouse_code VARCHAR,order_id BIGINT,required_package_count INT)");
|
||||
db.execute("CREATE TABLE ll_inbound_plan_pallet_allocation(pallet_id BIGINT,line_id BIGINT,customer_id VARCHAR,planned_package_count INT,counted_package_count INT,actual_volume_cbm DECIMAL(18,4))");
|
||||
db.execute("CREATE TABLE ll_order_detail(customer_id VARCHAR,order_id BIGINT,gross_weight_kg DECIMAL(18,3))");
|
||||
db.execute("CREATE TABLE ll_inbound_order(id BIGINT,customer_id VARCHAR,gross_weight_kg DECIMAL(18,3))");
|
||||
db.update("INSERT INTO ll_order_detail VALUES('101',9001,570.5),('other',9001,9999)");
|
||||
db.update("INSERT INTO ll_inbound_plan VALUES(1,'101',9,'COMPLETED')");
|
||||
db.update("INSERT INTO ll_inbound_plan_line VALUES(1,'101','FBA','ONT8')");
|
||||
db.update("INSERT INTO ll_inbound_plan_line VALUES(1,'101','FBA','ONT8',9001,20)");
|
||||
for(int n=1;n<=2;n++) {
|
||||
db.update("INSERT INTO ll_inbound_plan_pallet VALUES(?,1,'101',?,?,NULL)",n,500+n,"P-"+n);
|
||||
db.update("INSERT INTO ll_inbound_plan_pallet_allocation VALUES(?,1,'101',10,?)",n,n==1?9:11);
|
||||
db.update("INSERT INTO ll_inbound_plan_pallet_allocation VALUES(?,1,'101',10,?,?)",n,n==1?9:11,new BigDecimal(n==1?"1.5876":"1.9404"));
|
||||
}
|
||||
var config=new MybatisConfiguration();config.setMapUnderscoreToCamelCase(true);config.addMapper(DispatchSourceMapper.class);
|
||||
var factory=new MybatisSqlSessionFactoryBean();factory.setDataSource(ds);factory.setConfiguration(config);factory.afterPropertiesSet();
|
||||
var mapper=new SqlSessionTemplate(factory.getObject()).getMapper(DispatchSourceMapper.class);
|
||||
assertThat(mapper.confirmedPlan("101",1).get(0).packageCount()).isEqualTo(9);
|
||||
assertThat(mapper.confirmedPlan("101",2).get(0).packageCount()).isEqualTo(11);
|
||||
assertThat(mapper.confirmedPlan("101",1).get(0).grossWeightKg()).isEqualByComparingTo("256.725");
|
||||
assertThat(mapper.confirmedPlan("101",2).get(0).grossWeightKg()).isEqualByComparingTo("313.775");
|
||||
assertThat(mapper.confirmedPlan("101",1).get(0).volumeCbm()).isEqualByComparingTo("1.5876");
|
||||
assertThat(mapper.confirmedPlan("101",2).get(0).volumeCbm()).isEqualByComparingTo("1.9404");
|
||||
db.update("DELETE FROM ll_order_detail WHERE customer_id='101'");
|
||||
assertThat(mapper.confirmedPlan("101",1).get(0).grossWeightKg()).isNull();
|
||||
assertThat(mapper.confirmedPlan("other",1)).isEmpty();
|
||||
db.update("UPDATE ll_inbound_plan SET status='RECEIVING'");
|
||||
assertThat(mapper.confirmedPlan("101",1)).isEmpty();
|
||||
}
|
||||
|
||||
/** 原排车重量体积随实收交接持久化,重复确认不覆盖已经交接的快照。 */
|
||||
@Test void receiptReplacesEqualPlannedMeasurementsWithUnequalReceivedMeasurements() throws Exception {
|
||||
var f=new Fixture();
|
||||
f.p.f.db.update("UPDATE ll_dispatch_item SET gross_weight_kg=285.25,volume_cbm=1.764");
|
||||
f.p.f.candidates.get(1L).grossWeightKg(new BigDecimal("256.725"));
|
||||
f.p.f.candidates.get(1L).volumeCbm(new BigDecimal("1.5876"));
|
||||
f.p.f.candidates.get(2L).grossWeightKg(new BigDecimal("313.775"));
|
||||
f.p.f.candidates.get(2L).volumeCbm(new BigDecimal("1.9404"));
|
||||
f.confirm();f.confirm();
|
||||
var items=f.p.f.repository.cargo(f.p.f.repository.find(f.p.number).getId());
|
||||
items=items.stream().sorted(Comparator.comparing(com.baiyue.lastleg.dispatch.entity.DispatchItem::getPlanPalletId)).toList();
|
||||
assertThat(items.get(0).getGrossWeightKg()).isEqualByComparingTo("256.725");
|
||||
assertThat(items.get(1).getGrossWeightKg()).isEqualByComparingTo("313.775");
|
||||
assertThat(items.get(0).getVolumeCbm()).isEqualByComparingTo("1.5876");
|
||||
assertThat(items.get(1).getVolumeCbm()).isEqualByComparingTo("1.9404");
|
||||
assertThat(f.quantities()).containsExactly(9,11);
|
||||
}
|
||||
|
||||
/** 收货来源缺失货量时显式清空旧估算,不能因MyBatis忽略NULL而显示过期计划值。 */
|
||||
@Test void missingReceivedMeasurementsClearPlannedValues() throws Exception {
|
||||
var f=new Fixture();
|
||||
f.p.f.db.update("UPDATE ll_dispatch_item SET gross_weight_kg=285.25,volume_cbm=1.764");
|
||||
f.p.f.candidates.values().forEach(c->{c.grossWeightKg(null);c.volumeCbm(null);});
|
||||
f.confirm();
|
||||
assertThat(f.p.f.db.queryForObject("SELECT COUNT(*) FROM ll_dispatch_item WHERE gross_weight_kg IS NOT NULL OR volume_cbm IS NOT NULL",Integer.class)).isZero();
|
||||
}
|
||||
|
||||
/** 入库确认立即将10+10交接为9+11,尚未扫码也已核平预约;出货不再补改数量。 */
|
||||
@Test void receiptConfirmsNineAndElevenBeforeAnyPickingAndBothPalletsCanLeave() throws Exception {
|
||||
var f=new Fixture();f.confirm();
|
||||
@@ -103,6 +140,7 @@ class DispatchInboundReceiptPersistenceTest {
|
||||
var f=new Fixture();long dispatch=f.p.f.repository.find(f.p.number).getId();
|
||||
f.p.f.db.update("UPDATE ll_dispatch_item SET destination_type='PRIVATE'");
|
||||
f.p.f.db.update("UPDATE ll_inbound_plan_line SET destination_type='PRIVATE'");
|
||||
f.p.f.db.update("UPDATE ll_order SET destination_type='PRIVATE' WHERE id=9001");
|
||||
f.p.f.db.update("UPDATE ll_dispatch_appointment_allocation SET batch_key=?,destination_key='CUSTOMER:9001'","DISPATCH:"+dispatch);
|
||||
f.p.f.candidates.values().forEach(c->c.destinationType("PRIVATE"));
|
||||
f.p.f.db.update("UPDATE ll_dispatch_order SET review_basis_hash=?",(String)ReflectionTestUtils.invokeMethod(f.p.f.service,"basis",f.p.f.repository.find(f.p.number)));
|
||||
@@ -149,6 +187,12 @@ class DispatchInboundReceiptPersistenceTest {
|
||||
p.f.db.update("UPDATE ll_dispatch_item SET customer_id='101',order_id=9001,destination_type='FBA',fba_warehouse_code='ONT8'");
|
||||
p.f.db.update("UPDATE ll_inventory_position SET customer_id='101',available_packages=0");
|
||||
p.f.db.update("UPDATE ll_inbound_stock_receipt SET customer_id='101'");
|
||||
// 审核按当前库存及已拣归属核平;补齐实托、库存账和订单之间的真实读取关系。
|
||||
p.f.db.execute("CREATE TABLE ll_inbound_plan_pallet(id BIGINT,customer_id VARCHAR(64),warehouse_pallet_id BIGINT)");
|
||||
p.f.db.execute("ALTER TABLE ll_order ADD (inbound_code VARCHAR(128),destination_type VARCHAR(20),fba_warehouse_code VARCHAR(128))");
|
||||
p.f.db.update("UPDATE ll_order SET inbound_code='IN-1',destination_type='FBA',fba_warehouse_code='ONT8' WHERE id=9001");
|
||||
p.f.db.update("INSERT INTO ll_inventory_balance(id,customer_id,inbound_code) VALUES(21,'101','IN-1'),(22,'101','IN-1')");
|
||||
p.f.db.update("INSERT INTO ll_inbound_plan_pallet VALUES(1,'101',501),(2,'101',502)");
|
||||
p.f.db.update("UPDATE ll_dispatch_order SET estimated_arrival_at=DATEADD('HOUR',1,planned_departure_at)");
|
||||
p.f.candidates.get(2L).customerId("101");p.f.candidates.get(2L).orderId(9001L);p.f.candidates.get(2L).fbaWarehouseCode("ONT8");
|
||||
p.f.candidates.get(2L).destinationWarehouseCode("ONT8");
|
||||
|
||||
Reference in New Issue
Block a user