Ask A Question


You’re not receiving notifications from this thread.

Subtract until zero then go to next record

Wouter van den Beld asked in General


I am looking for this:

for a warehouse application for ordering and keeping stock for machine maintenance .

i have multiple record of items that coming in.
On Monday i received a item 5 times for 1€ and on Wednesday i receive the same item for 1.50€ 3 times.

So now i have multiple lines of a product received.

now the mechanic takes out 6 of the item and i want to add budget cost of 6.50 to a machine. this is based on the first in first out principle.

so how can i rotate to the lines that still have items in it and subtract that to zero and go to next line.

I did write down something like this but it looks awful, is this something that could work or do i need a different approach.

def find_item(item, took_from_stock)
item = #find the linetems which has stock sorted on date
set_stock(item, took_from_stock)

def set_stock(item, took_from_stock)
  item = item
  x = took_from_stock

  x.times do
    item.stock - 1 
    x - 1
      item.stock == 0
      find_next_item(item, x)

def find_next_item(item , x)
  if  x == 0
item = #find the linetems which has stock sorted on date
set_stock(item, took_from_stock)

def item_complete
  # do some  stuff


This is an interesting one. I would do one of two things to assume the order for the records:

  1. I would use the database ID order or created_at to determine the order and
  2. I would mark those as taken or "complete" as you mentioned.

With #1, you could assume that you've got the items in order as they came in. You could store each item individually as a record, or the batches that are put into stock and then count down. If you do the latter, you would want to keep a quantity and used count, so once the used count equals the quantity, that batch would be assumed used. I'll just show some example stuff that assumes there are individual records for every item.

class Item
  scope :available, ->{ where(used: true) } # Let us easily grab the available items
has_many :items, ->{ order(created_at: :asc) } # this can have a default ordering set like this so they're always accessed in order

def take_items(quantity)
  taken_items = items.available.limit(quantity) # Grab X number of items
  taken_items.update_all(used: true) # Mark all of these as used
  taken_items # Return the taken items, so then the caller could sum the price ( or something similar)

Does this direction make sense to you?


Thanks for thinking along Chris!

I will store each item individually, i thought this was wrong because when i get 200 screws it makes 200 records, I can however group by PurchaseOrder number and sum the total of the product to keep the views clear.

Also when i store each item individually it is easy to do a return booking i can find the the items and do set used to false and update the cost center to warehouse stock and it is returned.



i am running into a problem with the solution i implemented. this only works when dealing with whole numbers.

I have a WarehouseStock model and when i take items from stock it will set it to used and set the ledger where it was used for.

Now when we have item where the unit is in meters and we receive 6.5 meters how can i take a half item when i store each item individually.


Yeah that solution only really works well for whole numbers. One option would be to interact with meters, but actually store centimeters as whole numbers in the database for example.

Otherwise, you probably will want a separate model for recording each time things are taken. Then you could specify the "amount" on it, such as "6.5" so that instead of having separate records for each individual one, you'll have one record for the total amount taken at the time. You'd sum up the amount column to find out how many were taken rather than count the records like before.

Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 82,329+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.