贴标申请,仓储转运

This commit is contained in:
2026-10-13 17:42:54 +08:00
parent ec7e9b36ec
commit c07131b26f
7 changed files with 66 additions and 6 deletions
@@ -84,7 +84,7 @@ public interface LastlegOrderMapper extends BaseMapper<LastlegOrder> {
"ORDER BY r.signed_at DESC,r.id DESC) AS measurementNo " + "ORDER BY r.signed_at DESC,r.id DESC) AS measurementNo " +
"FROM ll_return_receipt r JOIN ll_return_order o ON o.id=r.return_order_id AND o.customer_id=r.customer_id " + "FROM ll_return_receipt r JOIN ll_return_order o ON o.id=r.return_order_id AND o.customer_id=r.customer_id " +
"WHERE o.lastleg_order_id IN <foreach collection='orderIds' item='id' open='(' separator=',' close=')'>#{id}</foreach> " + "WHERE o.lastleg_order_id IN <foreach collection='orderIds' item='id' open='(' separator=',' close=')'>#{id}</foreach> " +
"AND r.unit_type IN ('PALLET','BOX') AND r.length IS NOT NULL AND r.width IS NOT NULL AND r.height IS NOT NULL" + "AND r.unit_type IN ('PALLET','BOX','PARCEL') AND r.length IS NOT NULL AND r.width IS NOT NULL AND r.height IS NOT NULL" +
") m WHERE m.measurementNo=1 GROUP BY m.orderId</script>") ") m WHERE m.measurementNo=1 GROUP BY m.orderId</script>")
List<ReturnVolumeRow> findReturnVolumes(@Param("orderIds") List<Long> orderIds); List<ReturnVolumeRow> findReturnVolumes(@Param("orderIds") List<Long> orderIds);
@@ -20,7 +20,7 @@ public final class RelabelResponses {
String fulfillmentStatus, String outboundOrderNo, LocalDateTime customerConfirmedAt, String fulfillmentStatus, String outboundOrderNo, LocalDateTime customerConfirmedAt,
String reviewChannel, String reviewRemark, LocalDateTime createdAt, String reviewChannel, String reviewRemark, LocalDateTime createdAt,
LocalDateTime reviewedAt, LocalDateTime relabelCompletedAt, LocalDateTime outboundAt, LocalDateTime reviewedAt, LocalDateTime relabelCompletedAt, LocalDateTime outboundAt,
String productNameZh, String labelRequirement) {} String productNameZh, String labelRequirement, List<String> operationTypes) {}
public record Milestones(LocalDateTime reviewedAt, LocalDateTime relabelCompletedAt, public record Milestones(LocalDateTime reviewedAt, LocalDateTime relabelCompletedAt,
LocalDateTime outboundAt) {} LocalDateTime outboundAt) {}
public record Item(Long id, String skuCode, String productImageFileId, String productImageFileName, public record Item(Long id, String skuCode, String productImageFileId, String productImageFileName,
@@ -1226,7 +1226,9 @@ public class RelabelApplicationService implements WalletResultHandler {
detailItems.stream().map(RelabelItem::getProductNameZh).filter(StringUtils::hasText) detailItems.stream().map(RelabelItem::getProductNameZh).filter(StringUtils::hasText)
.map(String::trim).distinct().collect(Collectors.joining("、")), .map(String::trim).distinct().collect(Collectors.joining("、")),
detailItems.stream().map(RelabelItem::getLabelRequirement).filter(StringUtils::hasText) detailItems.stream().map(RelabelItem::getLabelRequirement).filter(StringUtils::hasText)
.map(String::trim).distinct().collect(Collectors.joining(";"))); .map(String::trim).distinct().collect(Collectors.joining(";")),
detailItems.stream().map(RelabelItem::getOperationType).filter(StringUtils::hasText)
.map(String::trim).distinct().toList());
} }
@Override public boolean supports(String businessType) { return "RELABEL_ORDER".equals(businessType); } @Override public boolean supports(String businessType) { return "RELABEL_ORDER".equals(businessType); }
@@ -768,7 +768,7 @@ public class ReturnReceiptService {
tail.setPackageCount(actualBoxes); tail.setReceivedPackageCount(actualBoxes); tail.setPackageCount(actualBoxes); tail.setReceivedPackageCount(actualBoxes);
tail.setProductQuantity(actualUnits); tail.setPalletCount(palletCodes.size()); tail.setProductQuantity(actualUnits); tail.setPalletCount(palletCodes.size());
for (ReturnReceipt signed : signedReceipts) { for (ReturnReceipt signed : signedReceipts) {
if (signed.getLength() == null || signed.getWidth() == null || signed.getHeight() == null || "PARCEL".equals(signed.getUnitType())) continue; if (signed.getLength() == null || signed.getWidth() == null || signed.getHeight() == null) continue;
String code = text(signed.getUnitCode()) == null ? signed.getReceiptNo() : signed.getUnitCode(); String code = text(signed.getUnitCode()) == null ? signed.getReceiptNo() : signed.getUnitCode();
measuredUnits.putIfAbsent(signed.getUnitType() + ":" + code, signed); measuredUnits.putIfAbsent(signed.getUnitType() + ":" + code, signed);
} }
@@ -75,10 +75,11 @@ class RelabelApplicationServiceTest {
when(applications.findMilestones("10001", 42L)) when(applications.findMilestones("10001", 42L))
.thenReturn(new RelabelResponses.Milestones(reviewed, completed, outbound)); .thenReturn(new RelabelResponses.Milestones(reviewed, completed, outbound));
var first = new RelabelItem(); first.setProductNameZh(" 商品甲 "); first.setLabelRequirement(" 贴标要求111 "); var first = new RelabelItem(); first.setProductNameZh(" 商品甲 "); first.setLabelRequirement(" 贴标要求111 ");
first.setSkuCode("SKU-FIRST"); first.setSkuCode("SKU-FIRST"); first.setOperationType("SKU_LABEL");
first.setSkuRelabelItemsJson("[{\"skuCode\":\"SKU-FIRST\",\"newSkuCode\":\"NEW-FIRST\",\"quantity\":10},{\"skuCode\":\"SKU-SECOND\",\"newSkuCode\":\"NEW-SECOND\",\"quantity\":5}]"); first.setSkuRelabelItemsJson("[{\"skuCode\":\"SKU-FIRST\",\"newSkuCode\":\"NEW-FIRST\",\"quantity\":10},{\"skuCode\":\"SKU-SECOND\",\"newSkuCode\":\"NEW-SECOND\",\"quantity\":5}]");
var second = new RelabelItem(); second.setProductNameZh("商品乙"); second.setLabelRequirement("贴标要求111"); var second = new RelabelItem(); second.setProductNameZh("商品乙"); second.setLabelRequirement("贴标要求111");
var empty = new RelabelItem(); empty.setProductNameZh(" "); second.setOperationType("CARTON_LABEL");
var empty = new RelabelItem(); empty.setProductNameZh(" "); empty.setOperationType("SKU_LABEL");
RelabelResponses.Summary result = ReflectionTestUtils.invokeMethod(service, "summary", application, List.of(first, second, empty)); RelabelResponses.Summary result = ReflectionTestUtils.invokeMethod(service, "summary", application, List.of(first, second, empty));
assertNotNull(result); assertNotNull(result);
assertEquals(reviewed, result.reviewedAt()); assertEquals(reviewed, result.reviewedAt());
@@ -87,6 +88,7 @@ class RelabelApplicationServiceTest {
assertEquals("商品甲、商品乙", result.productNameZh()); assertEquals("商品甲、商品乙", result.productNameZh());
assertEquals("贴标要求111", result.labelRequirement()); assertEquals("贴标要求111", result.labelRequirement());
assertEquals("SKU-FIRST,SKU-SECOND", result.skuCodes()); assertEquals("SKU-FIRST,SKU-SECOND", result.skuCodes());
assertEquals(List.of("SKU_LABEL", "CARTON_LABEL"), result.operationTypes());
assertEquals(3, result.itemCount()); assertEquals(3, result.itemCount());
} }
@@ -82,6 +82,25 @@ class ReturnReceiptServiceTest {
verify(details, times(1)).insert(any(com.baiyue.lastleg.entity.LastlegOrderDetail.class)); verify(details, times(1)).insert(any(com.baiyue.lastleg.entity.LastlegOrderDetail.class));
verify(details, times(1)).updateById(any(com.baiyue.lastleg.entity.LastlegOrderDetail.class)); verify(details, times(1)).updateById(any(com.baiyue.lastleg.entity.LastlegOrderDetail.class));
} }
@Test void countedParcelsContributeVolumeOncePerPhysicalPackage() {
ReturnOrder order = new ReturnOrder(); order.setId(1L); order.setCustomerId("C1"); order.setLastlegOrderId(9L);
ReturnOrderItem sku = new ReturnOrderItem(); sku.setSkuCode("SKU1");
ReturnReceipt first = measuredReceipt(2L, "PK-A", "50", "40", "40"); first.setReturnOrderId(1L); first.setUnitType("PARCEL");
ReturnReceipt second = measuredReceipt(3L, "PK-B", "60", "50", "50"); second.setUnitType("PARCEL");
ReturnReceipt earlier = measuredReceipt(4L, "PK-A", "40", "40", "40"); earlier.setUnitType("PARCEL");
var counted = new com.baiyue.lastleg.returnreceipt.entity.ReturnReceiptItem(); counted.setSkuCode("SKU1"); counted.setReceivedQuantity(24L);
LastlegOrder tail = new LastlegOrder(); tail.setId(9L); tail.setCustomerId("C1");
when(orders.selectById(1L)).thenReturn(order); when(orderItems.listOrder(1L)).thenReturn(List.of(sku));
when(lastlegOrders.selectById(9L)).thenReturn(tail); when(receipts.listOrder(1L)).thenReturn(List.of(second, first, earlier));
when(receiptItems.listReceipt(2L)).thenReturn(List.of(counted));
service.refreshCountedTailCargo(first);
assertThat(tail.getVolumeCbm()).isEqualByComparingTo("0.23");
assertEquals(0, tail.getPackageCount());
assertEquals(0, tail.getPalletCount());
verify(lastlegOrders).updateById(tail);
}
@Mock ReturnOrderMapper orders; @Mock ReturnOrderMapper orders;
@Mock ReturnOrderItemMapper orderItems; @Mock ReturnOrderItemMapper orderItems;
@Mock ReturnReceiptMapper receipts; @Mock ReturnReceiptMapper receipts;
@@ -0,0 +1,37 @@
package com.baiyue.lastleg.service;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class LastlegOrderReturnVolumeTest {
@Test void historicalParcelsAreIncludedWithLatestCarrierMeasurementsAndCustomerIsolation() throws Exception {
var config = new MybatisConfiguration();
config.setEnvironment(new Environment("test", new JdbcTransactionFactory(),
new UnpooledDataSource("org.h2.Driver", "jdbc:h2:mem:return-volume;MODE=MySQL", "sa", "")));
config.addMapper(LastlegOrderMapper.class);
try (var session = new SqlSessionFactoryBuilder().build(config).openSession()) {
var sql = session.getConnection().createStatement();
sql.execute("CREATE TABLE ll_return_order (id BIGINT, customer_id VARCHAR, lastleg_order_id BIGINT)");
sql.execute("CREATE TABLE ll_return_receipt (id BIGINT, return_order_id BIGINT, customer_id VARCHAR, unit_type VARCHAR, unit_code VARCHAR, receipt_no VARCHAR, signed_at TIMESTAMP, length DECIMAL(12,3), width DECIMAL(12,3), height DECIMAL(12,3))");
sql.execute("INSERT INTO ll_return_order VALUES (1,'A',9),(2,'B',10)");
sql.execute("INSERT INTO ll_return_receipt VALUES " +
"(1,1,'A','PARCEL','PK-A','RR-1','2026-10-13 10:00:00',40,40,40)," +
"(2,1,'A','PARCEL','PK-A','RR-2','2026-10-13 11:00:00',50,40,40)," +
"(3,1,'A','PARCEL','PK-B','RR-3','2026-10-13 11:00:00',60,50,50)," +
"(4,1,'B','PARCEL','PK-C','RR-4','2026-10-13 11:00:00',100,100,100)," +
"(5,2,'B','PARCEL','PK-D','RR-5','2026-10-13 11:00:00',100,100,100)," +
"(6,1,'A','PARCEL','PK-E','RR-6','2026-10-13 11:00:00',NULL,50,50)");
var rows = session.getMapper(LastlegOrderMapper.class).findReturnVolumes(List.of(9L));
assertThat(rows).hasSize(1);
assertThat(rows.get(0).orderId()).isEqualTo(9L);
assertThat(rows.get(0).volumeCbm()).isEqualByComparingTo("0.23");
}
}
}