2026-10-19 17:56:36 +08:00
|
|
|
package com.baiyue.lastleg.delivery;
|
|
|
|
|
|
|
|
|
|
import com.baiyue.lastleg.delivery.model.DeliveryCargoGroups;
|
2026-10-23 04:47:33 +08:00
|
|
|
import com.baiyue.lastleg.dispatch.port.DispatchContracts.CargoLine;
|
|
|
|
|
import com.baiyue.lastleg.dispatch.port.DispatchContracts.Destination;
|
2026-10-19 17:56:36 +08:00
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import static com.baiyue.lastleg.delivery.DeliveryDispatchFixtures.*;
|
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
|
|
|
|
|
|
|
|
class DeliveryCargoGroupsTest {
|
|
|
|
|
private final DeliveryCargoGroups policy=new DeliveryCargoGroups(new ObjectMapper().findAndRegisterModules());
|
|
|
|
|
/** 同目的仓不同客户分开,同托多来源保留且物理托盘仅计一次。 */
|
|
|
|
|
@Test void groupsByCustomerAndDestinationWithoutLosingMixedPalletSources() {
|
|
|
|
|
var a=cargo(1,"101","ONT8",51,2);var b=cargo(2,"101","ONT8",51,3);
|
|
|
|
|
var c=cargo(3,"101","LAX9",52,4);var d=cargo(4,"102","ONT8",53,5);
|
|
|
|
|
var groups=policy.groups(context(List.of(d,c,b,a),false));
|
|
|
|
|
assertEquals(3,groups.size());
|
|
|
|
|
var mixed=groups.stream().filter(g->g.customerId().equals("101")&&g.destination().warehouseCode().equals("ONT8")).findFirst().orElseThrow();
|
|
|
|
|
assertEquals(5,mixed.packages());assertEquals(1,mixed.palletCount());assertEquals(2,mixed.cargo().size());
|
|
|
|
|
assertEquals(mixed.cargoHash(),policy.groups(context(List.of(a,b,c,d),false)).stream().filter(g->g.key().equals(mixed.key())).findFirst().orElseThrow().cargoHash());
|
|
|
|
|
assertNotEquals(policy.groupKey("101",a.destination()),policy.groupKey("102",a.destination()));
|
|
|
|
|
}
|
|
|
|
|
/** 空车和重复归属不能靠总箱数相等冒充完整真实来源。 */
|
|
|
|
|
@Test void emptyOrDuplicatedAttributionCannotBecomeArrangement() {
|
|
|
|
|
assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(),false)));
|
|
|
|
|
var row=cargo(1,"101","ONT8",51,2);
|
|
|
|
|
assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(row,row),false)));
|
|
|
|
|
assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(row,cargo(2,"101","LAX9",51,3)),false)));
|
|
|
|
|
}
|
2026-10-23 04:47:33 +08:00
|
|
|
@Test void sameFbaCodeKeepsKnownWarehouseReferenceWhenIncomingCargoHasNoInternalId() {
|
|
|
|
|
var incoming=cargo(1,"101","ONT8",51,3);
|
|
|
|
|
var known=destination(cargo(2,"101","ONT8",52,4),new Destination("FBA:ONT8","FBA","900","ONT8","ONT8",null,null,null,null));
|
|
|
|
|
var group=policy.groups(context(List.of(known,incoming),false)).get(0);
|
|
|
|
|
assertEquals("900",group.destination().warehouseId());assertEquals(7,group.packages());
|
|
|
|
|
assertNull(group.cargo().get(0).destination().warehouseId(),"真实来源快照不被覆盖");
|
|
|
|
|
assertEquals(group,policy.groups(context(List.of(incoming,known),false)).get(0));
|
|
|
|
|
var conflicting=destination(incoming,new Destination("FBA:ONT8","FBA","901","ONT8","ONT8",null,null,null,null));
|
|
|
|
|
assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(known,conflicting),false)));
|
|
|
|
|
var wrongCode=destination(incoming,new Destination("FBA:ONT8","FBA",null,"LAX9","ONT8",null,null,null,null));
|
|
|
|
|
assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(known,wrongCode),false)));
|
|
|
|
|
var changedAddress=destination(incoming,new Destination("FBA:ONT8","FBA",null,"ONT8","ONT8","other",null,null,null));
|
|
|
|
|
assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(known,changedAddress),false)));
|
|
|
|
|
}
|
|
|
|
|
private CargoLine destination(CargoLine row,Destination destination) {
|
|
|
|
|
return new CargoLine(row.dispatchItemId(),row.customerId(),row.customerName(),row.orderId(),row.orderNo(),row.sourceKey(),row.sourceStage(),row.sourceRegion(),
|
|
|
|
|
row.sourceInboundId(),row.inboundCode(),row.planPalletId(),row.warehousePalletId(),row.palletNo(),row.pickId(),row.attributionId(),row.inventoryPositionId(),
|
|
|
|
|
row.receiptId(),row.returnReceiptId(),row.returnCartonId(),row.cartonMark(),row.relabelNo(),destination,row.packages(),row.productQuantity(),
|
|
|
|
|
row.grossWeightKg(),row.volumeCbm(),row.batchAllocations(),row.sourceOrigin());
|
|
|
|
|
}
|
2026-10-19 17:56:36 +08:00
|
|
|
}
|