feat(selfop): hide known zero inventory before turnover pagination
This commit is contained in:
+3
-2
@@ -10,12 +10,13 @@ public record InventoryTurnoverRequest(String sourceAccount, String warehouseCod
|
||||
@DateTimeFormat(iso=DateTimeFormat.ISO.DATE) LocalDate endDate, Integer baseWindowDays,
|
||||
String groupBy, String customerCode, String sku, String focus, Integer coverageDaysThreshold,
|
||||
String turnoverStatus, String sortBy, Integer current, Integer size, Boolean includeOverview,
|
||||
String snapshotContext) {
|
||||
String snapshotContext, Boolean hideZeroInventory) {
|
||||
private static final Set<String> PARAMETERS=Set.of("sourceAccount","warehouseCode","endDate","baseWindowDays",
|
||||
"groupBy","customerCode","sku","focus","coverageDaysThreshold","turnoverStatus","sortBy","current","size","includeOverview","snapshotContext");
|
||||
"groupBy","customerCode","sku","focus","coverageDaysThreshold","turnoverStatus","sortBy","current","size","includeOverview","snapshotContext","hideZeroInventory");
|
||||
public static void validateParameters(Set<String> keys) {
|
||||
if(!PARAMETERS.containsAll(keys)) throw new BizException("库存周转不接受其他视图的查询参数");
|
||||
}
|
||||
public boolean hideZero(){return !Boolean.FALSE.equals(hideZeroInventory);}
|
||||
public int days(){return baseWindowDays==null?30:baseWindowDays;}
|
||||
public java.util.List<Integer> windows(){return java.util.stream.Stream.of(7,15,30,days()).distinct().sorted().toList();}
|
||||
public String group(){return groupBy==null?"SKU":groupBy;}
|
||||
|
||||
+1
@@ -209,6 +209,7 @@ public final class InventoryTurnoverSql {
|
||||
case "UNASSESSABLE" -> "unassessable>0";
|
||||
default -> "1=1";
|
||||
};
|
||||
if(p.query.hideZero()) predicate+=" AND (physical IS NULL OR physical<>0)";
|
||||
if(p.query.turnoverStatus()!=null) predicate+=" AND sample_count<#{p.query.days}";
|
||||
if(!owner&&p.query.sku()!=null) predicate+=" AND sku_code=#{p.query.sku}";
|
||||
return " WHERE "+predicate;
|
||||
|
||||
@@ -19,7 +19,7 @@ paths:
|
||||
operationId: inventoryTurnover
|
||||
summary: 单仓SKU或货主三窗口库存周转
|
||||
description: >-
|
||||
精确customerCode限定列表与概览;sku、focus及turnoverStatus只筛列表。
|
||||
精确customerCode限定列表与概览;sku、focus、turnoverStatus及hideZeroInventory只筛列表。
|
||||
按完整候选计算依赖后排序分页,未知末尾,同值按SKU及货主或货主编码稳定排序。
|
||||
OWNER不计算整体可售天数;销售事实完整即可展示货主实际出库,不受单SKU观察期影响。
|
||||
SKU出库及无出库判断同样不受首次观察时间限制:可信完整覆盖的缺销量行可计零;实际异常单位或未覆盖日期保持未知。
|
||||
@@ -34,6 +34,7 @@ paths:
|
||||
- {name: groupBy, in: query, schema: {type: string, enum: [SKU, OWNER], default: SKU}}
|
||||
- {name: customerCode, in: query, schema: {type: string, minLength: 1, maxLength: 64}}
|
||||
- {name: sku, in: query, description: SKU分组的精确查询,OWNER禁止传入, schema: {type: string, minLength: 1, maxLength: 128}}
|
||||
- {name: hideZeroInventory, in: query, description: 默认隐藏当前全品质实物件数明确为0的SKU或货主汇总行,保留未知;全量筛选后分页,不改变概览及图表, schema: {type: boolean, default: true}}
|
||||
- name: focus
|
||||
in: query
|
||||
schema: {type: string, enum: [ALL, NO_OUTBOUND_IN_WINDOW, COVERAGE_GT, COVERAGE_LE, FROZEN, DEFECTIVE, UNASSESSABLE], default: ALL}
|
||||
|
||||
+2
-2
@@ -44,9 +44,9 @@ class PlatformInventoryTurnoverControllerTest {
|
||||
.param("sourceAccount","demo").param("warehouseCode","W").param("endDate","2026-10-12")
|
||||
.param("baseWindowDays","15").param("groupBy","OWNER").param("focus","COVERAGE_GT")
|
||||
.param("coverageDaysThreshold","60").param("current","2").param("size","20")
|
||||
.param("snapshotContext","fixed-token").param("includeOverview","false")).andExpect(status().isOk());
|
||||
.param("hideZeroInventory","false").param("snapshotContext","fixed-token").param("includeOverview","false")).andExpect(status().isOk());
|
||||
var capture=ArgumentCaptor.forClass(InventoryTurnoverRequest.class);verify(service).query(capture.capture());
|
||||
var q=capture.getValue();assertEquals(LocalDate.of(2026,10,12),q.endDate());assertEquals(15,q.days());
|
||||
assertEquals("OWNER",q.group());assertEquals(60,q.coverageDaysThreshold());assertEquals(20,q.offset());assertFalse(q.overview());assertEquals("fixed-token",q.snapshotContext());
|
||||
assertEquals("OWNER",q.group());assertEquals(60,q.coverageDaysThreshold());assertEquals(20,q.offset());assertFalse(q.overview());assertFalse(q.hideZero());assertEquals("fixed-token",q.snapshotContext());
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -35,7 +35,7 @@ class InventoryTurnoverContextTest {
|
||||
assertEquals(END.minusDays(29),first.analyticsContext().rangeStartDate());
|
||||
assertEquals(END.plusDays(1),first.analyticsContext().rangeEndDateExclusive());
|
||||
assertEquals(first,at(NOW.plusSeconds(60)).resolve(request(first.snapshotContext())).response());
|
||||
var page2=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",2,20,false,first.snapshotContext());
|
||||
var page2=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",2,20,false,first.snapshotContext(),false);
|
||||
var second=at(NOW.plusSeconds(90)).resolve(page2);
|
||||
assertEquals(first,second.response());
|
||||
assertEquals(InventoryTurnoverSql.input(request(first.snapshotContext()),contexts.resolve(request(first.snapshotContext()))).days(),
|
||||
@@ -47,11 +47,11 @@ class InventoryTurnoverContextTest {
|
||||
assertThrows(BizException.class,()->contexts.resolve(request(first.snapshotContext())));
|
||||
}
|
||||
@Test void fullYearContextRemainsSmallAndReconstructsEveryMissingDate() {
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,366,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",1,20,true,null);
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,366,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",1,20,true,null,false);
|
||||
var first=contexts.resolve(q).response();
|
||||
assertEquals(366,first.dailySamples().size());assertEquals(365,first.missingSampleDates().size());
|
||||
assertTrue(first.snapshotContext().length()<2048);
|
||||
var next=new InventoryTurnoverRequest("demo","W",END,366,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",2,20,false,first.snapshotContext());
|
||||
var next=new InventoryTurnoverRequest("demo","W",END,366,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",2,20,false,first.snapshotContext(),false);
|
||||
assertEquals(first,at(NOW.plusSeconds(60)).resolve(next).response());
|
||||
when(mapper.samples(eq("demo"),eq("W"),eq(IH),any(),any(),any())).thenReturn(List.of());
|
||||
assertThrows(BizException.class,()->contexts.resolve(next));
|
||||
@@ -62,7 +62,7 @@ class InventoryTurnoverContextTest {
|
||||
((com.fasterxml.jackson.databind.node.ObjectNode)evidence).put("salesRun",999L);
|
||||
String tampered=Base64.getUrlEncoder().encodeToString(json.writeValueAsBytes(evidence));
|
||||
assertThrows(BizException.class,()->contexts.resolve(request(tampered)));
|
||||
var another=new InventoryTurnoverRequest("demo","OTHER",END,30,"SKU",null,null,"ALL",null,null,null,1,20,true,token);
|
||||
var another=new InventoryTurnoverRequest("demo","OTHER",END,30,"SKU",null,null,"ALL",null,null,null,1,20,true,token,false);
|
||||
assertThrows(BizException.class,()->contexts.resolve(another));
|
||||
assertThrows(BizException.class,()->at(NOW.plusSeconds(1800)).resolve(request(token)));
|
||||
assertThrows(BizException.class,()->at(NOW.minusSeconds(1)).resolve(request(token)));
|
||||
@@ -138,11 +138,11 @@ class InventoryTurnoverContextTest {
|
||||
verify(mapper,never()).page(any());verify(mapper,never()).count(any());verify(mapper,never()).overview(any());verify(mapper,never()).owners(any());
|
||||
clearInvocations(mapper);
|
||||
when(mapper.report(any())).thenReturn(List.of(Map.of("section","TOTAL","payload","{\"total\":0}")));
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,null,2,20,false,first.context().snapshotContext());
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,null,2,20,false,first.context().snapshotContext(),false);
|
||||
var next=service.query(q);assertNull(next.summary());assertNull(next.charts());verify(mapper).report(any());verify(mapper,never()).chartWindows(any());
|
||||
}
|
||||
private InventoryTurnoverContext at(Instant instant){return new InventoryTurnoverContext(analytics,mapper,json,Clock.fixed(instant,ZoneOffset.UTC),mock(SelfopOwnerDirectory.class));}
|
||||
static InventoryTurnoverRequest request(String token){return new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,null,1,20,true,token);}
|
||||
static InventoryTurnoverRequest request(String token){return new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,null,1,20,true,token,false);}
|
||||
private static LocalDateTime utc(Instant instant){return LocalDateTime.ofInstant(instant,ZoneOffset.UTC);}
|
||||
private static Scope scope(long id,String family,String hash,long run){return new Scope(id,"demo","W",family,hash,"Asia/Shanghai","Asia/Shanghai",93600,run,1L,1L,false,null,null,7);}
|
||||
private static Run run(long id,long scope,String hash){
|
||||
|
||||
+3
-1
@@ -16,7 +16,9 @@ class InventoryTurnoverRequestTest {
|
||||
}
|
||||
@Test void defaultsToYesterdayThirtyDaysAndFirstPageOverview() {
|
||||
var q=request(Map.of("includeOverview",false));q.validate(TODAY);
|
||||
assertEquals(TODAY.minusDays(1),q.end(TODAY));assertEquals(30,q.days());assertEquals("SKU",q.group());assertEquals(20,q.pageSize());assertTrue(q.overview());
|
||||
assertEquals(TODAY.minusDays(1),q.end(TODAY));assertEquals(30,q.days());assertEquals("SKU",q.group());assertEquals(20,q.pageSize());assertTrue(q.overview());assertTrue(q.hideZero());
|
||||
assertFalse(request(Map.of("hideZeroInventory",false)).hideZero());
|
||||
assertTrue(request(Map.of("hideZeroInventory",true)).hideZero());
|
||||
}
|
||||
@Test void rejectsIncompleteDatesFiltersSortsAndUnpinnedPagination() {
|
||||
for(var invalid:List.of(Map.<String,Object>of("endDate","2026-10-13"),Map.<String,Object>of("baseWindowDays",367),
|
||||
|
||||
+35
-12
@@ -33,6 +33,29 @@ class InventoryTurnoverSqlTest {
|
||||
private void product(String owner,String sku,String declared,String measured) throws Exception {
|
||||
execute("INSERT INTO ll_selfop_product VALUES(901,'demo','"+owner+"','"+sku+"',"+declared+","+measured+")");
|
||||
}
|
||||
@Test void hidesOnlyKnownZeroPhysicalRowsBeforeCountAndPagingWithoutChangingOverview() throws Exception {
|
||||
seed("A","POSITIVE",0);seed("A","ZERO",0);seed("B","ZERO",0);seed("C","UNKNOWN",0);
|
||||
execute("UPDATE ll_selfop_inventory_snapshot SET physical_quantity=0 WHERE snapshot_run_id=22 AND sku_code='ZERO'");
|
||||
execute("UPDATE ll_selfop_inventory_snapshot SET available_quantity=0 WHERE snapshot_run_id=22 AND customer_code='A'");
|
||||
execute("UPDATE ll_selfop_inventory_snapshot SET physical_quantity=NULL,quality_status='UNKNOWN' WHERE snapshot_run_id=22 AND customer_code='C'");
|
||||
for(String group:List.of("SKU","OWNER")) {
|
||||
var all=new InventoryTurnoverRequest("demo","W",END,30,group,null,null,"ALL",null,null,"PHYSICAL_ASC",1,20,true,"fixed",false);
|
||||
var hidden=new InventoryTurnoverRequest("demo","W",END,30,group,null,null,"ALL",null,null,"PHYSICAL_ASC",1,20,true,"fixed",null);
|
||||
var visible=reportRows(input(hidden,30,0,true));
|
||||
assertEquals(List.of("A","C"),visible.stream().map(r->r.get("customer_code")).toList());
|
||||
assertEquals(group.equals("SKU")?4:3,reportRows(input(all,30,0,true)).size());
|
||||
var report=query("report",input(hidden,30,0,true));
|
||||
assertEquals(2,payload(report.stream().filter(r->"TOTAL".equals(r.get("section"))).findFirst().orElseThrow()).path("total").asInt());
|
||||
assertEquals(query("overview",input(all,30,0,true)),query("overview",input(hidden,30,0,true)));
|
||||
for(int page=1;page<=2;page++) {
|
||||
var paged=new InventoryTurnoverRequest("demo","W",END,30,group,null,null,"ALL",null,null,"PHYSICAL_ASC",page,1,false,"fixed",true);
|
||||
assertEquals(visible.get(page-1).get("customer_code"),reportRows(input(paged,30,0,true)).get(0).get("customer_code"));
|
||||
}
|
||||
if(group.equals("SKU"))assertEquals(4,query("page",input(hidden,30,0,false)).size(),"Stale stock stays unknown, not zero");
|
||||
}
|
||||
var exact=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,"ZERO","ALL",null,null,"PHYSICAL_DESC",1,20,true,"fixed",true);
|
||||
assertTrue(reportRows(input(exact,30,0,true)).isEmpty());
|
||||
}
|
||||
@Test void ownerVolumeChartRanksFullTotalsExcludesUnknownsAndIgnoresListFilters() throws Exception {
|
||||
master();
|
||||
for(int i=0;i<12;i++) {
|
||||
@@ -43,7 +66,7 @@ class InventoryTurnoverSqlTest {
|
||||
product("UNKNOWN","SAME","100,1,1,'m'","NULL,NULL,NULL,NULL");
|
||||
execute("UPDATE ll_selfop_inventory_snapshot SET quality_status='INVALID' WHERE customer_code='UNKNOWN'");
|
||||
for(int days:List.of(7,30)) {
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,days,"SKU",null,"not-present","FROZEN",null,null,"PHYSICAL_DESC",2,1,true,"fixed");
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,days,"SKU",null,"not-present","FROZEN",null,null,"PHYSICAL_DESC",2,1,true,"fixed",false);
|
||||
var report=query("report",input(q,30,0,true));
|
||||
var ranking=new ArrayList<com.fasterxml.jackson.databind.JsonNode>();
|
||||
for(var part:report)if("OWNER_VOLUME".equals(part.get("section")))ranking.add(payload(part));
|
||||
@@ -78,7 +101,7 @@ class InventoryTurnoverSqlTest {
|
||||
: direction.equals("ASC")?List.of("A","D","B","C"):List.of("B","A","D","C");
|
||||
var actual=new ArrayList<Object>();
|
||||
for(int page=1;page<=2;page++) {
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,days,group,null,null,"ALL",null,null,metric+"_"+direction,page,2,true,"fixed");
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,days,group,null,null,"ALL",null,null,metric+"_"+direction,page,2,true,"fixed",false);
|
||||
q.validate(END.plusDays(1));
|
||||
actual.addAll(reportRows(input(q,30,0,true)).stream().map(r->r.get("customer_code")).toList());
|
||||
}
|
||||
@@ -142,7 +165,7 @@ class InventoryTurnoverSqlTest {
|
||||
decimal("1",reportRows(later).get(0).get("unit_volume"));
|
||||
}
|
||||
static InventoryTurnoverRequest request(String group,String focus,String sort,String sku,Integer threshold,int page,int size) {
|
||||
return new InventoryTurnoverRequest("demo","W",END,30,group,null,sku,focus,threshold,null,sort,page,size,true,null);
|
||||
return new InventoryTurnoverRequest("demo","W",END,30,group,null,sku,focus,threshold,null,sort,page,size,true,null,false);
|
||||
}
|
||||
static InventoryTurnoverSql.Input input(InventoryTurnoverRequest q,int covered,int samples,boolean fresh) {
|
||||
List<InventoryTurnoverSql.Day> days=new ArrayList<>();
|
||||
@@ -153,7 +176,7 @@ class InventoryTurnoverSqlTest {
|
||||
seed("A","SKU",1);master();product("A","SKU","10,10,10,'cm'","NULL,NULL,NULL,NULL");
|
||||
execute("UPDATE ll_selfop_sku_profile SET earliest_evidence_at_utc=TIMESTAMP '2025-01-01 00:00:00'");
|
||||
for(int n:List.of(3,45,366)) {
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,n,"SKU",null,null,"ALL",null,null,null,1,20,true,null);
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,n,"SKU",null,null,"ALL",null,null,null,1,20,true,null,false);
|
||||
var dates=new ArrayList<InventoryTurnoverSql.Day>();
|
||||
int horizon=Math.max(30,n);
|
||||
for(int i=0;i<horizon;i++) {
|
||||
@@ -311,7 +334,7 @@ class InventoryTurnoverSqlTest {
|
||||
@Test void independentBaseWindowsUseMatchingHistoricalAverages() throws Exception {
|
||||
seed("A","SKU",30);
|
||||
for(int days:List.of(7,15)) {
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,days,"SKU",null,null,"ALL",null,null,null,1,20,true,null);
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,days,"SKU",null,null,"ALL",null,null,null,1,20,true,null,false);
|
||||
var row=query("page",input(q,30,30,true)).get(0);
|
||||
decimal("70",row.get("w7_outbound"));decimal("150",row.get("w15_outbound"));decimal("300",row.get("w30_outbound"));
|
||||
decimal("60",row.get("coverage_days"));decimal(days==7?"0.14":"0.3",row.get("turnover"));decimal(String.valueOf(days),row.get("sample_count"));
|
||||
@@ -353,7 +376,7 @@ class InventoryTurnoverSqlTest {
|
||||
dates.add(new InventoryTurnoverSql.Day(date,date.atStartOfDay().minusHours(8),1,present?100L:null));
|
||||
if(i>=30-n&&!present)missing.add(date.toString());
|
||||
}
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,n,group,null,null,"ALL",null,null,null,1,20,true,null);
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,n,group,null,null,"ALL",null,null,null,1,20,true,null,false);
|
||||
var p=new InventoryTurnoverSql.Input(q,11,22,true,true,List.copyOf(dates),LocalDateTime.of(2026,10,13,0,0));
|
||||
var row=query("page",p).get(0);
|
||||
assertNotNull(row.get("sample_mask"));
|
||||
@@ -368,7 +391,7 @@ class InventoryTurnoverSqlTest {
|
||||
// Both edge bits and a middle bit, with different missing members per day.
|
||||
execute("DELETE FROM ll_selfop_inventory_snapshot WHERE warehouse_code='W' AND ((sku_code='ONE' AND snapshot_run_id IN (100,114)) OR (sku_code='TWO' AND snapshot_run_id=129))");
|
||||
for(int n:List.of(7,15,30)) {
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,n,"OWNER",null,null,"ALL",null,null,null,1,20,true,null);
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,n,"OWNER",null,null,"ALL",null,null,null,1,20,true,null,false);
|
||||
var p=input(q,30,30,true);
|
||||
var row=reportRows(p).get(0);
|
||||
var expected=new ArrayList<LocalDate>();
|
||||
@@ -378,7 +401,7 @@ class InventoryTurnoverSqlTest {
|
||||
assertNull(row.get("average_physical"));assertNull(row.get("turnover"));
|
||||
assertEquals(row.get("missing_dates"),query("page",p).get(0).get("missing_dates"));
|
||||
}
|
||||
var pageTwo=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,null,2,1,false,null);
|
||||
var pageTwo=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,null,2,1,false,null,false);
|
||||
var second=reportRows(input(pageTwo,30,30,true)).get(0);
|
||||
assertEquals("TWO",second.get("sku_code"));decimal("29",second.get("sample_count"));assertEquals(END.toString(),second.get("missing_dates"));
|
||||
// A stock/profile-only candidate without samples invalidates all owner days.
|
||||
@@ -396,7 +419,7 @@ class InventoryTurnoverSqlTest {
|
||||
List<InventoryTurnoverSql.Day> days=new ArrayList<>();
|
||||
for(int i=0;i<30;i++){var date=END.minusDays(29-i);days.add(new InventoryTurnoverSql.Day(date,date.atStartOfDay().minusHours(8),1,first+i));}
|
||||
for(int n:List.of(7,15,30)) {
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,n,"SKU",null,null,"ALL",null,null,null,1,20,true,null);
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,n,"SKU",null,null,"ALL",null,null,null,1,20,true,null,false);
|
||||
var p=new InventoryTurnoverSql.Input(q,11,22,true,true,List.copyOf(days),LocalDateTime.of(2026,10,13,0,0));
|
||||
var row=reportRows(p).get(0);
|
||||
decimal(String.valueOf(n),row.get("sample_count"));
|
||||
@@ -412,12 +435,12 @@ class InventoryTurnoverSqlTest {
|
||||
@Test void reportPaginationKeepsTotalAndCanOmitOverview() throws Exception {
|
||||
seed("A","SKU",30);seed("B","SKU",30);
|
||||
var first=reportRows(input(request("SKU","ALL","PHYSICAL_DESC",null,null,1,1),30,30,true));
|
||||
var secondQuery=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",2,1,false,"frozen");
|
||||
var secondQuery=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",2,1,false,"frozen",false);
|
||||
var second=query("report",input(secondQuery,30,30,true));
|
||||
var row=second.stream().filter(r->"ROW".equals(r.get("section"))).findFirst().orElseThrow();
|
||||
assertNotEquals(first.get(0).get("customer_code"),payload(row).path("customer_code").asText());
|
||||
assertEquals(2,second.size());
|
||||
var beyond=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",3,1,false,"frozen");
|
||||
var beyond=new InventoryTurnoverRequest("demo","W",END,30,"SKU",null,null,"ALL",null,null,"PHYSICAL_DESC",3,1,false,"frozen",false);
|
||||
var report=query("report",input(beyond,30,30,true));assertEquals(1,report.size());
|
||||
assertEquals("TOTAL",report.get(0).get("section"));assertEquals(2,payload(report.get(0)).path("total").asInt());
|
||||
}
|
||||
@@ -665,7 +688,7 @@ class InventoryTurnoverSqlTest {
|
||||
execute("UPDATE ll_selfop_sku_daily_sales SET quantity_unit='UNKNOWN',outbound_quantity=9999 WHERE sku_code='UNKNOWN'");
|
||||
for(int days:List.of(7,15,30)) {
|
||||
for(int page=1;page<=3;page++) {
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,days,"SKU",null,null,"ALL",null,null,"AVERAGE_DAILY_DESC",page,1,true,"fixed-context");
|
||||
var q=new InventoryTurnoverRequest("demo","W",END,days,"SKU",null,null,"ALL",null,null,"AVERAGE_DAILY_DESC",page,1,true,"fixed-context",false);
|
||||
var expected=days==7?List.of("RECENT","OLDER","UNKNOWN"):List.of("OLDER","RECENT","UNKNOWN");
|
||||
assertEquals(expected.get(page-1),reportRows(input(q,30,0,true)).get(0).get("sku_code"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user