The above solution is good, just that row_number will give a false impression if we have multiple rows for same week, because the modulus(row_number/2) should be same for the same week rows.
Instead, prefer using dense_rank() over row_number() and rank() functions for obvious reasons.
val sales_data = Seq((2020,1,1,"1","1","TX",100),
(2020,1,1,"1","1","TX",150),
(2020,1,2,"1","1","TX",150),
(2020,1,3,"1","1","TX",200),
(2020,1,4,"1","1","TX",230),
(2020,1,5,"1","1","TX",400),
(2020,1,6,"1","1","TX",300),
(2020,1,7,"1","1","TX",250),
(2020,1,8,"1","1","TX",100),
(2020,1,9,"1","1","TX",200),
(2020,1,10,"1","1","TX",200),
(2020,1,11,"1","1","TX",300),
(2020,1,11,"1","1","TX",150))
//Calculate moving sales for 2 weeks, 4 weeks, 6 weeks
val sales_df = sales_data.toDF("year", "month", "week", "item", "dept", "state", "sale")
// sales_df.show
sales_df.withColumn("row_no", dense_rank().over(Window.partitionBy("item", "state","dept").orderBy("year", "month", "week"))-1)
.withColumn("sum(sales)_2wks", sum($"sale").over(Window.partitionBy($"item", $"state",$"dept", ($"row_no"/2).cast("int"))))
.withColumn("sum(sales)_3wks", sum($"sale").over(Window.partitionBy($"item", $"state",$"dept", ($"row_no"/3).cast("int"))))
.withColumn("sum(sales)_4wks", sum($"sale").over(Window.partitionBy($"item", $"state",$"dept", ($"row_no"/4).cast("int"))))
.withColumn("sum(sales)_6wks", sum($"sale").over(Window.partitionBy($"item", $"state",$"dept", ($"row_no"/6).cast("int"))))
.show