Search Postgresql Archives

Re: Locking question

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



 
Sent: Wednesday, October 26, 2016 10:42 AM
Subject: Locking question
 
> I am designing an inventory application, and I want to ensure that the stock level of any item cannot go negative.
 
Thanks to all for some really great replies. Much food for thought there.
 
As mentioned previously, I am trying to avoid using PostgreSQL-specific techniques, as I need to support sqlite3 and SQL Server as well.
 
There is an additional complication that I forgot to mention in my original post.
 
For costing purposes, I want to run a FIFO system. This means I have to maintain separate entries for each receipt of stock, and allocate any sales of stock against the receipts ‘oldest first’.
 
Assume the following purchases -
 
2016-06-01  qty 5  Unit price $5.00
2016-06-02  qty 10  Unit price $5.50
2016-06-03  qty 15  Unit price $6.00
 
Quantity on hand after the third purchase is 30. Whether this should be maintained as a total somewhere, or derived from totalling the receipts, is a matter for debate, but I think that it is not relevant for this discussion.
 
Then assume the following sales -
 
2016-06-11  qty 8
2016-06-12  qty 12
2016-06-13  qty 16
 
The first sale will succeed, and will record a ‘cost of  sale’ of (5 x $5.00) + (3 x $5.50).
The second sale will succeed, and will record a ‘cost of  sale’ of (7 x $5.50) + (5 x $6.00).
The third sale must be rejected, as there is insufficient stock.
 
This is how I propose to achieve this -
 
CREATE TABLE inv_rec
    (row_id SERIAL PRIMARY KEY,
    product_id INT REFERENCES inv_products,
    rec_date DATE,
    qty INT
    unit_price DEC(15, 2));
 
CREATE TABLE inv_alloc
    (row_id SERIAL PRIMARY KEY,
    rec_id INT REFERENCES inv_rec,
    qty INT);
 
INSERT INTO inv_rec (product_id, rec_date, qty, unit_price)
  VALUES (99, ‘2016-06-01’, 5, 5.00);
INSERT INTO inv_rec (product_id, rec_date, qty, unit_price)
  VALUES (99, ‘2016-06-02’, 10, 5.50);
INSERT INTO inv_rec (product_id, rec_date, qty, unit_price)
  VALUES (99, ‘2016-06-03’, 15, 6.00);
 
The sales will be handled at application level. Here is some pseudo code -
 
qty_to_allocate = sale_qty
cost_of_sale = 0
 
BEGIN TRANSACTION
 
SELECT a.row_id, a.unit_price,
      a.qty + COALESCE((SELECT SUM(b.qty) FROM inv_alloc b
      WHERE b.rec_id = a.row_id), 0) AS balance
FROM inv_rec a
WHERE a.product_id = 99
AND
      a.qty + COALESCE((SELECT SUM(b.qty) FROM inv_alloc b
      WHERE b.rec_id = a.row_id), 0)
  > 0
ORDER BY a.rec_date
FOR UPDATE
 
for row in rows:
    if row.balance >= qty_to_allocate:
        INSERT INTO inv_alloc (rec_id, qty)
            VALUES (row.row_id, –qty_to_allocate)
        cost_of_sale += (qty_to_allocate * unit_price)
        qty_to_allocate = 0
    else:
        INSERT INTO inv_alloc (rec_id, qty)
            VALUES (row.row_id, –row.balance)
        cost_of_sale += (row.balance * unit_price)
        qty_to_allocate –= row.balance
 
if qty_to_allocate:  # i.e. insufficient stock
    raise exception and ROLLBACK
else:
    COMMIT
 
My main concern is that this should be robust.
 
A secondary concern is that it should be reasonably efficient, but that is not a priority at this stage. If it became a problem, I would look at maintaining a ‘balance’ column on each ‘inv_rec’.
 
Comments welcome.
 
Frank
 

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Postgresql Jobs]     [Postgresql Admin]     [Postgresql Performance]     [Linux Clusters]     [PHP Home]     [PHP on Windows]     [Kernel Newbies]     [PHP Classes]     [PHP Books]     [PHP Databases]     [Postgresql & PHP]     [Yosemite]
  Powered by Linux