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
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 Capacity
| PRINTED 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?