fix: classify equivalent outbound no-stock results
This commit is contained in:
@@ -236,7 +236,7 @@ public class ShadowPlanningEngine {
|
||||
int inserted = inputMapper.insertComparison(
|
||||
UUID.randomUUID().toString(), observation.warehouseId(), "OUTBOUND_COMPARE",
|
||||
observation.snapshotGeneration(), observation.snapshotSynchronizedAt(),
|
||||
observation.sourceSignature(), "LEGACY_" + nullSafe(legacyResultCode),
|
||||
observation.sourceSignature(), outboundComparisonCode(observation, legacyResultCode),
|
||||
outboundComparisonReason(observation, legacyResultCode));
|
||||
requireComparisonInserted(inserted);
|
||||
});
|
||||
@@ -600,6 +600,15 @@ public class ShadowPlanningEngine {
|
||||
? result + "_CONSERVATIVE" : result;
|
||||
}
|
||||
|
||||
private String outboundComparisonCode(OutboundShadowObservation observation, String legacyCode) {
|
||||
if (observation.observed()
|
||||
&& "ERROR_STOCK_NOT_FOUND".equals(observation.resultCode())
|
||||
&& "3".equals(legacyCode)) {
|
||||
return "MATCH_NO_INVENTORY";
|
||||
}
|
||||
return "LEGACY_" + nullSafe(legacyCode);
|
||||
}
|
||||
|
||||
private boolean isBlank(String value) {
|
||||
return value == null || value.isBlank();
|
||||
}
|
||||
|
||||
@@ -211,6 +211,43 @@ class ShadowPlanningEngineTest {
|
||||
eq(SNAPSHOT.synchronizedAt()), eq("ABC123"), eq("LEGACY_10"), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void outboundComparisonMatchesWhenBothPlannersReportNoInventory() {
|
||||
ShadowInputMapper mapper = mock(ShadowInputMapper.class);
|
||||
when(mapper.insertComparison(any(), any(), any(), any(Long.class),
|
||||
any(Instant.class), any(), any(), any())).thenReturn(1);
|
||||
ShadowPlanningEngine engine = engine(mock(SchedulingFacade.class), mapper);
|
||||
OutboundShadowObservation observation = OutboundShadowObservation.unavailable(
|
||||
SNAPSHOT, "DETAIL-1", "ERROR_STOCK_NOT_FOUND", "no matching inventory");
|
||||
|
||||
engine.compareOutbound(observation, "3");
|
||||
|
||||
verify(mapper).insertComparison(any(), eq("01"), eq("OUTBOUND_COMPARE"), eq(42L),
|
||||
eq(SNAPSHOT.synchronizedAt()), eq("ABC123"), eq("MATCH_NO_INVENTORY"),
|
||||
argThat(reason -> reason.contains("shadow=ERROR_STOCK_NOT_FOUND")
|
||||
&& reason.contains("legacy=3")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void outboundComparisonDoesNotMatchNoInventoryAgainstTechnicalOrDifferentOutcomes() {
|
||||
ShadowInputMapper mapper = mock(ShadowInputMapper.class);
|
||||
when(mapper.insertComparison(any(), any(), any(), any(Long.class),
|
||||
any(Instant.class), any(), any(), any())).thenReturn(1);
|
||||
ShadowPlanningEngine engine = engine(mock(SchedulingFacade.class), mapper);
|
||||
OutboundShadowObservation technical = OutboundShadowObservation.unavailable(
|
||||
SNAPSHOT, "DETAIL-1", "ERROR_TECHNICAL", "database failure");
|
||||
OutboundShadowObservation noInventory = OutboundShadowObservation.unavailable(
|
||||
SNAPSHOT, "DETAIL-2", "ERROR_STOCK_NOT_FOUND", "no matching inventory");
|
||||
|
||||
engine.compareOutbound(technical, "3");
|
||||
engine.compareOutbound(noInventory, "10");
|
||||
|
||||
verify(mapper).insertComparison(any(), eq("01"), eq("OUTBOUND_COMPARE"), eq(42L),
|
||||
eq(SNAPSHOT.synchronizedAt()), eq("ABC123"), eq("LEGACY_3"), any());
|
||||
verify(mapper).insertComparison(any(), eq("01"), eq("OUTBOUND_COMPARE"), eq(42L),
|
||||
eq(SNAPSHOT.synchronizedAt()), eq("ABC123"), eq("LEGACY_10"), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void inboundComparisonRetainsRequiredEvidenceWhenUnicodeReasonIsOversized() {
|
||||
ShadowInputMapper mapper = mock(ShadowInputMapper.class);
|
||||
|
||||
Reference in New Issue
Block a user