The formula below matches Batch Codes in worksheet Production JP37 to those in worksheet Internal Transfer of Goods and calculates updated the stock levels.
=LET(
old_stock, FILTER('Production JP37'!E5:F600,'Production JP37'!D5:D600="JP37"),
old_batch, INDEX(old_stock,,1),
old_qty, INDEX(old_stock,,2),
stock_in, FILTER('Internal Transfer of Goods'!F5:G600,'Internal Transfer of Goods'!E5:E600="JP37"),
stock_in_qty, BYROW(
IF(
old_batch=TRANSPOSE(INDEX(stock_in,,1)),
TRANSPOSE(INDEX(stock_in,,2)),
0),
LAMBDA(row,SUM(row))
),
stock_out, FILTER('Internal Transfer of Goods'!F5:G600,'Internal Transfer of Goods'!D5:D600="JP37"),
stock_out_qty, BYROW(
IF(
old_batch=TRANSPOSE(INDEX(stock_out,,1)),
TRANSPOSE(INDEX(stock_out,,2)),
0),
LAMBDA(row,SUM(row))
),
HSTACK(old_batch,old_qty+stock_in_qty-stock_out_qty)
)
In the formula:
FILTER is used instead of IF for creating arrays. This is because the IF formulation used in the question inserts a blank row into the resulting array whenever a row in a data worksheet does not contain "JP37" in the relevant column.
In addition, only the Batch Code and QTY values are extracted from the two data worksheets. This is because no other columns in these worksheets are relevant to determining the stock levels at location JP37.
old_stock is an array showing the current stock position at JP37 before accounting for any transfers, and comprises old_batch and old_qty.
stock_in is an array of stock transfers to location JP37
stock_out is an array of stock transfers from location JP37
The expressions for stock_in_qty and stock_out_qty are workarounds to the previously (and erroneously) used SUMIFS functions. The expressions allow for multiple transfers inwards and outwards of a particular batch in the calculation of the updated stock for that batch.
The result of the formula is an array showing the updated stock position.
Each of the 4 arrays (old_stock, stock_in, stock_out and the formula result) has two columns with the first containing Batch Codes and the second containing QTY.
The formula is just an array version of the standard stocks and flows equation
New Stock = Old Stock + Stock In - Stock Out
Although the formula is reasonably sound, there are some issues.
- Excel does not permit self-referencing formulas. For example, putting the formula
=A1+2 in cell A1 results in a circular reference warning. In consequence, workbook cells containing stock levels cannot be directly updated using formulas. All that can be achieved with formulas (whether array or otherwise) is the calculation of New Stock levels which are derived, in part, from the Old Stock levels. Both must co-exist and are linked through the formula.
- Multiple sets of transfers require the Old Stock values to be replaced by the New Stock ones after each Stock In, Stock Out pair has been processed. Avoiding the circular reference problem when performing the replacement is tricky and contains traps for the unwary.
- Depending on the external data behind worksheet Production JP37 it might not contain information for Batch Codes for which there is no stock showing at location JP37. This can happen if the source system suppresses the creation of records where the stock level associated with a particular Batch Code is zero. However, if such a suppressed Batch Code is present in the Stock In data (because it is on worksheet Internal Transfer of Goods), the formula ignores it. Only codes present in Old Stock get matched and so, in this case, the stock transfer is erroneously ignored by the formula.
Issue 1 underlines why Excel is not always a suitable tool for managing data. Issues 2 and 3 can be resolved and outline approaches are sketched below.
Issue 2
The stocks and flows equation can be re-written as a pair of equations:
New Stock(n) = Old Stock(n) + Stock In(n) - Stock Out(n)
Old Stock(n+1) = New Stock(n)
This re-writing reflects an ongoing series of stock transfers, perhaps identified across daily, weekly or monthly periods and the need to replace Old Stock with New Stock after processing each Stock_In, Stock_Out pair.
The problem of circular references can be resolved making a copy of the New Stock(n) array by copying the formula's results array to a convenient location using Copy/Paste Values. This copy, which will be a worksheet range rather than an array, is then used as a lookup table to adjust the stock values in the QTY column of worksheet Production JP37. These adjusted values will be determined using a function such as VLOOKUP and, once calculated, a second Copy/Paste Values operation should be used to break the formula links between the stock levels on Production JP37 and those in New Stock(n). After this is done, Production JP37 will be consistent with Old Stock(n+1).
The formula simply uses whatever happens to be on worksheet Internal Transfer of Goods as the array equivalents of Stock In and Stock Out. Consequently, immediately after Production JP37 is updated to reflect its consistency with Old Stock(n+1), the formula will be implementing the array equivalent of equation
New Stock(n+1) = Old Stock(n+1) + Stock In(n) - Stock Out(n)
This is an incorrect equation. The Stock In and Stock Out arrays have not been updated to be Stock In(n+1) and Stock Out(n+1). In consequence the results of the formula will be showing incorrect values. This is the trap for the unwary.
Avoiding this trap will require good data management skills, so that it is clear at all times whether there is consistency between the data worksheets of Production JP37 and Internal Transfer of Goods.
Issue 3
Here, the issue can be resolved by appending to the Old Stock array (represented as old_stock in the formula) rows for which a Branch Code is in the Stock In array but not in Old Stock.
This is achieved by changing the definition of old_stock in the formula so that the array obtained from worksheet Production JP37 is renamed as old_stock_ws, the missing codes are called missing and the formula for old_stock is changed to
IF(ISERROR(missing),old_stock_ws,VSTACK(old_stock_ws,HSTACK(missing,0)))
where missing has formula
FILTER(INDEX(stock_in,,1),ISNA(MATCH(INDEX(stock_in,,1),INDEX(old_stock_ws,,1),0)))
IFfunctions are unnecessary. A formula such as='Production JP37'!A5:J600can reference a range directly - no need to wrap it inIF. What does "pretty much the same" mean? Specifically, do each of the three arrays have the same number of rows and do corresponding rows of each array match in terms of the batch number (so all three of the first rows correspond to same batch number, all three of the second rows correspond to a second batch number, etc) - because that is the implication of the illustrative examples in the question.