Forum Discussion
How to reschedule EDD based on production capacity - Power Query
shafiz_p your current daily production capacity (e.g. OFFSET = 100,000) is not enough to place all orders across your current working calendar. I also did not get your message about "urgent" stuff - what's that?
Can't also comment on your "table will grow day by day". This code just works with the list of orders in OrderDetails table.
let
// fx to find position of working day in the list
wd_position = (d as date) => List.PositionOf(working_days, d, Occurrence.First),
// list of working days
working_days = List.Buffer(
List.Sort(
List.Difference(
Table.SelectRows(CalendarTable, (x) => x[Is Working Day] = "True")[Date],
Yearly_Holidays[Date]
)
)
),
// capacity by product type
caps = Function.Invoke(Record.FromList, List.Reverse(Table.ToColumns(Capacity))),
// orders sorted by prod type and estimated DD
sorted = Table.Sort(OrderDetails, {"Product Type", "EDD", "CS Approved Date"}),
// and >> to list
orders = List.Buffer(Table.ToList(sorted, (x) => x)),
// go over the list
processing = List.Generate(
() => [
i = 0,
prod = orders{0}{3},
iw = wd_position(orders{0}{7}),
cap = Record.Field(caps, prod),
flag = orders{0}{5} > cap,
current_qty = if flag then 0 else orders{0}{5},
cap_usage = current_qty
//delivery_date = orders{0}{7}
],
(x) => x[i] < List.Count(orders),
(x) => [
i = x[i] + 1,
prod = orders{i}{3}, // current order's product type
same_prod = x[prod] = prod, // check if prod type is the same as last order
cap = if same_prod then x[cap] else Record.Field(caps, prod), // current capacity
flag = orders{i}{5} > cap, // check if qty is greater than cap
current_qty = if flag then 0 else orders{i}{5}, // this order usage quantity
cap_is_full = (not same_prod) or
(x[cap_usage] + current_qty) > cap or
working_days{x[iw]}? is null or
orders{i}{7} > working_days{x[iw]}, // reset capacity??
cap_usage = if cap_is_full then current_qty else x[cap_usage] + current_qty, // capacity used
iw = if (not same_prod) then wd_position(orders{i}{7}) // position in working days calendar
else if cap_is_full
then x[iw] + 1
else x[iw]
],
(x) => orders{x[i]} & (if working_days{x[iw]}? is null
then {null, null, null, "calendar is full"}
else {x[cap], x[cap_usage], working_days{x[iw]}, if x[flag] then "order out of capacity" else null}
)
),
result = Table.FromList(processing, (x) => x, Table.ColumnNames(sorted) & {"capacity", "usage", "assigned_EDD", "flag"})
in
result
- shafiz_p11 months ago
Super User
Capacity for Offset will be 600,000. That was dummy. We can also, increase the calendar size. No problem at all. There is total 31 columns in the orginal table. and currently have 10 product types and 75k rows. daily orders will intake and size will grow day by day.
There is a column named CS Comment with a tag Urgent. I said if there is urgent, leave it as it is. Meaning don't include in the capacity logic like quantity > capacity.
I have tried your code, with the smaller version OrderDetails table I provided, working fine. But not working with the bigger version. after attaching orginal table with 31 columns, steps producing list only showing 19 columns, so unable to find index, like in orginal table index of EDD is 30 but can't find that value. Why this is happening I don't know.
I think I need to try spliting the table. One with columns you have used and other with all the remaining column with full hight which is currently 75k and will grow day by day. Once found assign EDD then merge back to the reamaining part.
Let me try and get back to you. - shafiz_p11 months ago
Super User
For offset, it is not working or weird result. All assigned EDD for that product should not exceed more than October 2025, it should arround october 15 to 20.
Please extend the duration of calendar current date + 90 days, in the calendar table M code.
Here I am attaching all the rows of the orginal table and exact product type capacity. Also, for performance I only consider the non null EDD. changed your code a little. Yearly holiday table, already merged with the calendartable. so you do not need to use yearly holiday table.let // fx to find position of working day in the list wd_position = (d as date) => List.PositionOf(working_days, d, Occurrence.First), // list of working days working_days = List.Buffer( List.Sort( Table.SelectRows(CalendarTable, (x) => x[Is Working Day] = "True")[Date] ) ), // capacity by product type caps = Function.Invoke(Record.FromList, List.Reverse(Table.ToColumns(Capacity))), // Spliting Tables selectedColumns = {"Date", "Order Code", "Customer", "Product Type", "CS Approved Date", "Order Qty", "Estimated Delivery Date", "EDD"}, orderDetails_Selected = Table.SelectColumns(OrderDetails, selectedColumns), // Creating 2 Tables With. One with null and other with non null OrderDetailsWithNullEDD = Table.SelectRows(orderDetails_Selected, each [EDD] = null), // With non null EDD OrderDetailsWithNonNullEDD = Table.SelectRows(orderDetails_Selected, each [EDD] <> null), // Tables with the remaining columns remainingColumns = List.Difference(Table.ColumnNames(OrderDetails), selectedColumns), orderDetails_Remaining = Table.SelectColumns(OrderDetails, remainingColumns), // orders sorted by prod type and estimated DD sorted = Table.Sort(OrderDetailsWithNonNullEDD, {"Product Type", "EDD", "CS Approved Date"}), // and >> to list orders = List.Buffer(Table.ToList(sorted, (x) => x)), // go over the list processing = List.Generate( () => [ i = 0, prod = orders{0}{3}, iw = wd_position(orders{0}{7}), cap = Record.Field(caps, prod), flag = orders{0}{5} > cap, current_qty = if flag then 0 else orders{0}{5}, cap_usage = current_qty //delivery_date = orders{0}{7} ], (x) => x[i] < List.Count(orders), (x) => [ i = x[i] + 1, prod = orders{i}{3}, // current order's product type same_prod = x[prod] = prod, // check if prod type is the same as last order cap = if same_prod then x[cap] else Record.Field(caps, prod), // current capacity flag = orders{i}{5} > cap, // check if qty is greater than cap current_qty = if flag then 0 else orders{i}{5}, // this order usage quantity cap_is_full = (not same_prod) or (x[cap_usage] + current_qty) > cap or working_days{x[iw]}? is null or orders{i}{7} > working_days{x[iw]}, // reset capacity?? cap_usage = if cap_is_full then current_qty else x[cap_usage] + current_qty, // capacity used iw = if (not same_prod) then wd_position(orders{i}{7}) // position in working days calendar else if cap_is_full then x[iw] + 1 else x[iw] ], (x) => orders{x[i]} & (if working_days{x[iw]}? is null then {null, null, null, "calendar is full"} else {x[cap], x[cap_usage], working_days{x[iw]}, if x[flag] then "order out of capacity" else null} ) ), result = Table.FromList(processing, (x) => x, Table.ColumnNames(sorted) & {"capacity", "usage", "assigned_EDD", "flag"}), #"Changed Type" = Table.TransformColumnTypes(result,{{"Order Qty", Int64.Type}, {"assigned_EDD", type date}}) in #"Changed Type"Here is the updated capacity table:
Product Type CapacityPRINTED FABRIC LABEL 620000 NARROW FABRIC 44000 OFFSET 600000 LEATHER 25000 RUBBER PATCH 6000 SCREEN PRINT 180000 WOVEN LABELS 350000 HIGH DENSITY 4000 HEAT TRANSFER LABEL 120000 THERMAL 150000 ACCESSORIES 700000 for each day, offset should maintain showing orders total sum less than 600,000. other product will follow the same. Any whole order unable to fit that date, spill over to the next date, which must be working date. when spilling consider the spill orders + where you spilling, consider those order also, to check capacity logic.
Any whole order value > capacity, leave it as it is. Also, same for Urgent, Urgent (Revised), Urgent (Additional) in the cs comment column, just leave it as it is.Hope this info helps. Kindly check attached file here.
- AlienSx11 months ago
Super User
shafiz_p , we finally see "CS comment" column and blank EDD in your data.
# offset is not working or weird result: even if we set capacity for OFFSET to 600 000 then we still hit the limit of working days in your calendar. Your data (the one from PBI file) has total order quantity at around 215.7m. Lets make a rough estimation and divide it by max capacity of 600K => we need at least full 360 working days since Sep 01, 2024 (earliest EDD). Your calendar has only 365 working days in your calendar to work on OFFSET orders. No wonder why we hit the limit.
# filter out orders: you keep telling that we don't need to touch orders with blank EDD (you filter them out) as well as orders exceeding daily capacity and orders with "urgent"-like status in CS comment field (I don't see any other status but "urgent..." in that column by the way). Then why did you filter out blank EDD only? Do we need to show smth in "assigned_edd" (calculated) field for orders with excess capacity and/or CS comment <> null ?
# columns in your real data: you don't need to select columns for calculations - it's useless and bring lots of problems. As you see, I am working with the list of lists (not list of records). So that qty and EDD fields positioning is quite important. The number of columns in not important - just positions of qty and edd fields. It's really wierd that you see only 19 columns - this is really unexpected result. Anyway, there is no problem to work with a list of records to retain field names (maybe at the cost of performance) or calculate positions of EDD and Order Qty fields before iterating the list of orders.
Please comment on the orders with comment in CS comment field (aka "urgent" orders) and "> daily capacity" orders - can we simply filter them out beforehand together with orders with blank EDD?