fix: retain scanned carton traceability in measurement drafts
This commit is contained in:
@@ -14,8 +14,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -71,6 +73,7 @@ public class MeasurementBatchService {
|
||||
throw concurrentModification();
|
||||
}
|
||||
}
|
||||
preserveScannedTraceability(command, measurement, calculatedItems);
|
||||
calculatedItems.forEach(item -> {
|
||||
item.setTenantId(command.tenantId());
|
||||
item.setMeasurementId(measurement.getId());
|
||||
@@ -79,6 +82,39 @@ public class MeasurementBatchService {
|
||||
return measurement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 人工补录会整批替换可编辑测量项,但不能抹掉同一项已冻结的扫码身份与设备取证。
|
||||
* 只按入库货物明细和明细号精确匹配,避免把一个箱码猜测性地复制给其他测量项。
|
||||
*/
|
||||
private void preserveScannedTraceability(MeasurementBatchCommand command,
|
||||
InboundMeasurementEntity measurement,
|
||||
List<InboundMeasurementItemEntity> replacement) {
|
||||
if (command.measurementId() == null || replacement.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, InboundMeasurementItemEntity> scannedByItem = new HashMap<>();
|
||||
for (InboundMeasurementItemEntity existing : repository.listItems(command.tenantId(), measurement.getId())) {
|
||||
if (existing.getBoxCode() != null && !existing.getBoxCode().isBlank()) {
|
||||
scannedByItem.put(itemKey(existing.getInboundDetailId(), existing.getItemNo()), existing);
|
||||
}
|
||||
}
|
||||
for (InboundMeasurementItemEntity item : replacement) {
|
||||
InboundMeasurementItemEntity scanned = scannedByItem.get(itemKey(item.getInboundDetailId(), item.getItemNo()));
|
||||
if (scanned == null) {
|
||||
continue;
|
||||
}
|
||||
item.setExternalDetailNo(scanned.getExternalDetailNo());
|
||||
item.setBoxCode(scanned.getBoxCode());
|
||||
item.setScannedShipmentId(scanned.getScannedShipmentId());
|
||||
item.setMachineNo(scanned.getMachineNo());
|
||||
item.setDeviceMeasuredAt(scanned.getDeviceMeasuredAt());
|
||||
}
|
||||
}
|
||||
|
||||
private String itemKey(Long inboundDetailId, String itemNo) {
|
||||
return String.valueOf(inboundDetailId) + ':' + itemNo;
|
||||
}
|
||||
|
||||
private void validateSource(MeasurementBatchCommand command) {
|
||||
if (!SOURCE_MANUAL.equals(command.sourceType()) && !SOURCE_THIRD_PARTY.equals(command.sourceType())) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "测量数据来源不支持");
|
||||
|
||||
@@ -138,6 +138,41 @@ class MeasurementBatchServiceTest {
|
||||
org.mockito.ArgumentMatchers.eq(100L), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void manualDraftKeepsExactScannedCartonTraceability() {
|
||||
InboundMeasurementEntity existing = new InboundMeasurementEntity();
|
||||
existing.setId(100L);
|
||||
existing.setTenantId(1L);
|
||||
existing.setInboundId(10L);
|
||||
existing.setStatus("RECEIVED");
|
||||
existing.setVersion(3);
|
||||
InboundMeasurementItemEntity scanned = new InboundMeasurementItemEntity();
|
||||
scanned.setInboundDetailId(80001L);
|
||||
scanned.setItemNo("001");
|
||||
scanned.setExternalDetailNo("INB-001#1-1");
|
||||
scanned.setBoxCode("INB-001#1-1");
|
||||
scanned.setScannedShipmentId("FBA-001");
|
||||
scanned.setMachineNo("MJ-01");
|
||||
scanned.setDeviceMeasuredAt(LocalDateTime.of(2026, 9, 2, 10, 0));
|
||||
when(repository.findMeasurement(1L, 10L, 100L)).thenReturn(existing);
|
||||
when(repository.updateMeasurement(existing)).thenReturn(true);
|
||||
when(repository.listItems(1L, 100L)).thenReturn(List.of(scanned));
|
||||
|
||||
service.saveBatch(new MeasurementBatchCommand(
|
||||
1L, 10L, 100L, 3, "MANUAL", "PLATFORM", null, null, null,
|
||||
LocalDateTime.now(), null, null, null, null, null, 9L, List.of(item(BigDecimal.ONE))));
|
||||
|
||||
ArgumentCaptor<List<InboundMeasurementItemEntity>> items = ArgumentCaptor.forClass(List.class);
|
||||
verify(repository).replaceItems(org.mockito.ArgumentMatchers.eq(1L),
|
||||
org.mockito.ArgumentMatchers.eq(100L), items.capture());
|
||||
InboundMeasurementItemEntity saved = items.getValue().get(0);
|
||||
assertThat(saved.getBoxCode()).isEqualTo("INB-001#1-1");
|
||||
assertThat(saved.getExternalDetailNo()).isEqualTo("INB-001#1-1");
|
||||
assertThat(saved.getScannedShipmentId()).isEqualTo("FBA-001");
|
||||
assertThat(saved.getMachineNo()).isEqualTo("MJ-01");
|
||||
assertThat(saved.getDeviceMeasuredAt()).isEqualTo(LocalDateTime.of(2026, 9, 2, 10, 0));
|
||||
}
|
||||
|
||||
private MeasurementBatchCommand.Item item(BigDecimal unitWeight) {
|
||||
return new MeasurementBatchCommand.Item(
|
||||
80001L, "001", "A", 2, new BigDecimal("40"), new BigDecimal("30"),
|
||||
|
||||
Reference in New Issue
Block a user