I am implementing a basic ledger system for which I want to calculate costs based on the FIFO system. Is there a more clever method than an O(n) search?
Example
Suppose I have two lists of transactions:
Incoming Inventory:
Date | Batch | Quantity | Price |
---|---|---|---|
Jan 1 | A | 3 | 5.00 |
Jan 15 | B | 5 | 7.00 |
Inventory Sold:
Order | Date | Quantity |
---|---|---|
X | Jan 1 | 2 |
Y | Jan 2 | 3 |
Z | Jan 3 | 1 |
I want to calculate that the items in order X cost $5 each, there was one item in order Y which was $5 and two that were $7, and order Z's items were $7.
My current solution
Naively, I would have to iterate through every single sale and inventory batch since the beginning of time:
inventory = [
{"quantity": 3, "price": 5},
{"quantity": 5, "price": 7},
]
sold = [
{"order": "X", "quantity": 2},
{"order": "Y", "quantity": 3},
{"order": "Z", "quantity": 1},
]
i = 0
for s in sold:
for sold_item in range(0, s['quantity']):
if inventory[i]['quantity'] == 0:
i += 1
print(f'Order {s["order"]} price {inventory[i]["price"]}')
inventory[i]['quantity'] -= 1
This outputs the following:
Order X price 5
Order X price 5
Order Y price 5
Order Y price 7
Order Y price 7
Order Z price 7
If I want to ask "for order Y, what were my prices?" then I have to compute this from the very beginning of my order history. Furthermore, if the orders are input out of order, then this changes the cost basis for every other subsequent order.
More details about the question
I am wondering if there is a more clever way to do this. It seems that if I could keep a cumulative sum, then it would be easier for me to find the exact bucket in which my inventory belongs. I haven't been able to come up with anything.
If it helps, I am storing this data in a SQL database (though open to using pretty much any kind of disk-based datastore.) However, regardless of the database being used, I'm interested to know if there is a technique (for pre-computing, or aggregating, or a different structure all together) which would be useful for solving this problem.