Calculate orders with all lines available after prior orders

Viewed 137

I have the following data:

order part cat qty available qty ordered priority
aa a 1 4 2 2
aa a 2 2 3 2
bb b 1 3 3 3
bb a 1 4 3 3
cc a 2 2 1 1
cc a 1 4 2 1

With this data I'd like to calculate the which orders have enough inventory available to pick each order line in the order of the priority column.

The data is in cells A3:F8, where A2:F2 are it's headers.

The qty available column shows the total available quantity of a product with a certain category (cat), so if all lines of that order are available the next order will have the available quantity minus the previously selected from orders.

I'm using Office 365 and used the following to calculate if all lines of an order are available:

=LET(data,SORT(A3:F8,{6;2;3}),
     a,INDEX(data,,1),
     d,INDEX(data,,4),
     e,INDEX(data,,5),
BYROW(a,LAMBDA(x,SUM(N(FILTER(d,a=x)>=FILTER(e,a=x)))=COUNTA(FILTER(a,a=x)))))

The data part of the LET-function above sorts the original data's orders to it's priority to the following data:

order part cat qty available qty ordered priority
cc a 1 4 2 1
cc a 2 2 1 1
aa a 1 4 2 2
aa a 2 2 3 2
bb a 1 4 3 3
bb b 1 3 3 3

The formula results in:

Result
TRUE
TRUE
FALSE
FALSE
TRUE
TRUE

If I would substract the ordered qty from the available qty from each order that has enough available according to the calculation result, I would have the following qty remaining.

remaining inventory up to current order
2
1
2
1
-1
0

As you can see this means that for order bb, part a, cat 1 I would not have enough inventory after picking the orders with higher priorities, so all order lines of this order should be skipped as well and the remaining inventory would be usable for a later order if there was any.

I can't think of a way to iterate through the orders and it's remaining qty after substracting prior orders.

Would this be possible with LAMBDA maybe?

Or am I starting it all wrong and is there another way to calculate this maybe?

P.S. The actual data set contains thousands of lines. I tried MMULT with multiple conditions, but this resulted in a "not enough resources to calculate"-error.

This table may explain the logic of fulfilling an order a little further (data sorted in priority order):

order part cat qty available qty ordered priority explanation calc remaining qty
cc a 1 4 2 1 order cc all available, so available wty - ordered qty 4-2 2
cc a 2 2 1 1 order cc all available, so available wty - ordered qty 2-1 1
aa a 1 4 2 2 order aa part a cat 2 ordered qty > remaining inventory, so nothing gets delivered for any of the order lines remaining 2 after order cc -0 2
aa a 2 2 3 2 order aa part a cat 2 ordered qty > remaining inventory, so nothing gets delivered for any of the order lines remaining 1 after order cc -0 1
bb a 1 4 3 3 order bb part a cat 1 ordered qty > remaining inventory, so nothing gets delivered for any of the order lines remaining 2 after order cc -0 2
bb b 1 3 3 3 order bb part a cat 1 ordered qty > remaining inventory, so nothing gets delivered for any of the order lines 3-0 3
2 Answers

I managed to get a working answer by combining formulas and VBA. Formulas to get the spill per order and VBA to workaround overwriting the spill causing spill errors:

Sub ordercalc()

Dim lr As Integer

Dim order As Range, part As Range, cat As Range, available As Range, ordered As Range, priority As Range

Dim i As Variant

Application.ScreenUpdating = False

With Sheet2  

    .Range("A2").Formula2R1C1 = _        
"=LET(a,sheet1!C1:C16,i,COLUMN(C[3]),s,SORT(a,{14,4}),FILTER(INDEX(s,,i),ISNUMBER(INDEX(s,,1))))"

    .Range("B2").Formula2R1C1 = _        
"=LET(a,sheet1!C1:C16,i,COLUMN(C[7]),s,SORT(a,{14,4}),FILTER(INDEX(s,,i),ISNUMBER(INDEX(s,,1))))"

    .Range("C2").Formula2R1C1 = _        
"=LET(a,sheet1!C1:C16,i,COLUMN(C[13]),s,SORT(a,{14,4}),FILTER(INDEX(s,,i),ISNUMBER(INDEX(s,,1))))"

    .Range("D2").Formula2R1C1 = _        
"=LET(a,sheet1!C1:C16,i,COLUMN(C[8]),s,SORT(a,{14,4}),FILTER(INDEX(s,,i),ISNUMBER(INDEX(s,,1))))"

    .Range("E2").Formula2R1C1 = _        
"=LET(a,sheet1!C1:C16,i,COLUMN(C[6]),s,SORT(a,{14,4}),FILTER(INDEX(s,,i),ISNUMBER(INDEX(s,,1))))"

    .Range("F2").Formula2R1C1 = _  
"=LET(a,sheet1!C1:C16,i,COLUMN(C[8]),s,SORT(a,{14,4}),FILTER(INDEX(s,,i),ISNUMBER(INDEX(s,,1))))"

    .Range("A1").Value = "order"
    .Range("B1").Value = "part"
    .Range("C1").Value = "cat"
    .Range("D1").Value = "available qty"
    .Range("E1").Value = "qty ordered"
    .Range("F1").Value = "priority"
    .Range("G1").Value = "Pickable"
    .Range("H1").Value = "Left after picking"

    lr = .Cells(.Rows.Count, "A").End(xlUp).Row

    For i = 2 To lr

        If .Range("A" & i).Value <> .Range("A" & i - 1).Value Then .Range("H" & i).Formula2R1C1 = _
        "=LET(result,IFERROR(XLOOKUP(FILTER(R2C2:R" & lr & "C2,R2C1:R" & lr & "C1=RC1)&""|""&FILTER(R2C3:R" & lr & "C3,R2C1:R" & lr & "C1=RC1),FILTER(R2C2:R" & lr & "C2,(ROW(R2C1:R" & lr & "C1)<ROW())*(R2C1:R" & lr & "C1<>RC1))&""|""&FILTER(R2C3:R" & lr & "C3,(ROW(R2C1:R" & lr & "C1)<ROW())*(R2C1:R" & lr & "C1<>RC1)),R2C8:INDEX(C8,ROW()-1),RC[1]:R[1]C[1],0,-1),FILTER(R2C4:R" & lr & "C4,R2C1:R" & lr & "C1=RC1))," & Chr(10) & "ordered,FILTER(R2C5:R" & lr & "C5,R2C1:R" & lr & "C1=RC1),IF(SUM(N(result>=ordered))=COU" & _
        "NTA(ordered),result-ordered,result))"

        If .Range("A" & i).Value <> .Range("A" & i - 1).Value Then .Range("G" & i).Formula2R1C1 = _          
  "=LET(result,IFERROR(XLOOKUP(FILTER(R2C2:R" & lr & "C2,R2C1:R" & lr & "C1=RC1)&""|""&FILTER(R2C3:R" & lr & "C3,R2C1:R" & lr & "C1=RC1),FILTER(R2C2:R" & lr & "C2,(ROW(R2C1:R" & lr & "C1)<ROW())*(R2C1:R" & lr & "C1<>RC1))&""|""&FILTER(R2C3:R" & lr & "C3,(ROW(R2C1:R" & lr & "C1)<ROW())*(R2C1:R" & lr & "C1<>RC1)),R2C8:INDEX(C8,ROW()-1),RC[2]:R[1]C[2],0,-1),FILTER(R2C4:R" & lr & "C4,R2C1:R" & lr & "C1=RC1))," & Chr(10) & "ordered,FILTER(R2C5:R" & lr & "C5,R2C1:R" & lr & "C1=RC1),IF(SUM(N(result>=ordered))=COU" & _        
        "NTA(ordered),INDEX(""Yes"",SEQUENCE(COUNTA(ordered),,1,0)),INDEX(""No"",SEQUENCE(COUNTA(ordered),,1,0))))"   

    Next

.Range("G2:H" & lr).Value = .Range("G2:H" & lr).Value


End With

Application.ScreenUpdating = True

End Sub

It first sorts the data from a report to the priority, the it calculates the values left after order picking (if all order lines are available only), otherwise the qty left stays the same.

I also added a column to show if the order is pickable yes/no.

There's likely to be a better way to solve this either in VBA (likely) or Excel (lambda map maybe?), but this got me the result and I couldn't think anymore afterwards.

This isn't the sophisticated answer you were looking for, but if I were doing it I would go back to basics - sort on part, cat, priority then just use a pull-down formula:

=IF(AND(B2=B1,C2=C1),IF(E2>=G1,G1,G1-E2),IF(E2>=D2,D2,D2-E2))

enter image description here

Related