I am trying to generate a report which calculates the margin from the below database. The problem is that the cost (existing in purchase_order_products table) of the product may change.
The cost of product with id 4022 on 2017-06-08 is 1110, however its cost is 1094 on 2017-07-25. This is confusing. I am unable to get the exact cost for each product sold.
I wrote a PHP algorithm which loops through all orders and purchase orders and used the oldest cost to newest cost. but the algorithm has a very high time complexity. Can this be done just using mysql query?
Please check below scenario:
Company created a purchase order for product X: quantity 3, cost 10. on day 1
Customers bought 2 product X sell price: 12 on day 1 (still have 1 item in inventory with cost 10)
Company created a purchase order for product X : quantity 4, cost 9. on day 2
Customers bought 3 product X sell price: 12 on day 2
Customers bought 2 product X sell price: 12 on day 3
Company created a purchase order for product X : quantity 2, cost 11. on day 3
Customers bought 2 product X sell price: 12 on day 3
The report:
day 1:
sold 2 product X for 12 , cost 10 , profit: 2 * (12 - 10)
day 2:
sold 3 product X for 12 , 1 item has a cost of 10, 2 items have a cost of 9 ,
profit: 1 * (12 - 10) + 2 * (12 - 9)
day 3:
sold 2 product X for 12 , cost 9 , profit: 2 * (12 - 9)
sold 2 product X for 12 , cost 11 , profit: 2 * (12 - 11)
Therefor the profit of newly sold products is calculated using the their corresponding cost. Hope you got my point.
Databse Structure:
4 Products From Database
Products Purchase orders for the above products
Sold Products
Dump File Attached here
Why don't you just take it easy and add a profit column to the orders table that is calculated in real time when a customer buys a product.This way you can calculate your marging solely from the sales orders given the fact that this is actualy already calculated somehow in order to generate the selling price. Of course this will work only for future sales but you can use your existing code with little modification to populate the profit column for old records and you will run this code only one time for the old transactions before the update.
To elaborate more:
Alter the table "sales_order" adding "profit" column . This way you can calculate the sum using the other related columns (total_paid, total_refund, total_due, grand_total) because you may want have more control over the report by including those monetary fields as needed in your calculation for example generating a report using total_payed only excluding tota_due or encluding it for different type of reports, in other words you can generate multiple reports types only from this table without overwhelming the DB system by adding this one column only.
Edit:
You can also add a cost column to this table for fast retrieving purpose and minimize joins and queries to other tables and if you want to take it a step further you can add a dedicated table for reports and it will be very helpful for example to generate a missing report from last month and checking old order status.
Some disclaimers:
this is an attempt to assist with the logic, so it's rough code(open to SQL injection attacks, so don't copy and paste this)
I can't test this query so there's probably mistakes in it, just trying to get you on the right track (and/or will make follow up edits)
This won't work if you need profit per order, only for profit per product. You could probably get a date range with a BETWEEN clause if needed.
That being said, I think something like this should work for you:
$productsIds = array('4022', '4023', '4160', '4548', '4601');
foreach($productIds as $pid){
$sql = "SELECT (soi.revenue - sum(pop.cost)) AS profit, sum(pop.cost) AS total_cost, sum(pop.quantity) AS total_purchased, soi.revenue, soi.total_sold
FROM purchase_order_products pop
JOIN (SELECT sum(price) AS revenue, sum(quantity_ordred) AS total_sold FROM sales_order_item WHERE product_id = ".$pid.") AS soi ON soi.product_id = pop.product_id
WHERE pop.product_id = ".$pid." GROUP BY pop.product_id HAVING sum(pop.quantity) < soi.total_sold ORDER BY pop.created_at ASC;";
$conn->query($sql);
//do what you want with results
}
The key thing here is using the HAVING clause after GROUP BY to determine where you cut off finding the sum of the purchase costs. You can sum them all as long as they're within that range, and you get the right dates ordering by created_at.
Again, I can't test this, and I wouldn't recommend using this code as is, just hoping this helps from a "here's a general idea of how to make this happen".
If I had time to recreate your databases I would, or if you provide sql dump files with example data, I could try to get you a working example.
The price of an object on a given time is given by the formula : "total price of the stock / total number in the stock".
To get this, you have two queries to execute :
The first one to know the amount of sold items (total price and quantities) :
sql:
SELECT SUM(row_total) sale_total_cost, SUM(quantity_ordered) sale_total_number
FROM sales_order_item soi
JOIN sales_order so ON soi.sales_order_id=so.id
WHERE so.purchase_date<'2017-06-07 15:03:30'
AND soi.product_id=4160;
the second one to know how much you have bought the products
sql:
SELECT SUM(pop.cost * pop.quantity) purchase_total_price, SUM(pop.quantity) purchase_total_number
FROM purchase_order_products pop
JOIN purchase_order po ON pop.purchase_order_id=po.id
WHERE po.created_at<'2017-06-07 15:03:30'
AND pop.product_id=4160;
The price of the product 4160 at 2017-02-01 14:23:35 is:
(purchase_total_price - sale_total_cost) / (purchase_total_number - sale_total_number)
The problem is that your "sales_order" table start on 2017-02-01 14:23:35, while your "purchase_order" table start on 2017-06-07 08:55:48. So the result will be incoherent as long as you can't track all your purchases from the start.
EDIT:
If you can modify you table structure and are only interested in future sells.
Adding the number of items sold in the purchase_order_products table
You have to modify purchase_order_products to have the consumption for each product:
ALTER TABLE `purchase_order_products` ADD COLUMN sold_items INT DEFAULT 0;
Initializing the data
In order for it to work, you have to make the sold_items column reflect your real stock
You should initialize your table with the following request
UPDATE `purchase_order_products` SET sold_items=quantity;
and then manually update the table with your exact stock for each product (which means that quantity_ordered-sold_items must reflect your real stock.
This has to be done only once.
adding the purchase price to the sales_order_item table
ALTER TABLE sales_order_item ADD total_purchase_price INT DEFAULT NULL
entering new sales order
When you enter new sales order, you will have to get the oldest purchase order with remaining items using the following command:
SELECT * FROM `purchase_order_products` WHERE quantity!=sold_items where product_id=4160 ORDER BY `purchase_order_id` LIMIT 1;
You will have then to increment the sold_items value, and calculate the total purchase price (sum) to fill the total_purchase_price column.
calculating margin
The margin will be easily calculated with the difference between row_total and total_purchase_price in the sales_order_item table
I appreciate you are using the FIFO method for the management of stock. However, this does not mean you need to use FIFO to calculate margins. The article https://en.wikipedia.org/wiki/Inventory_valuation gives an overview of the options. (Regulations in your country may exclude some options.)
I believe a repeatable solution for FIFO margin calculation of an individual sale is complex. There are complexities of opening balances, returns, partial deliveries, partial shipments, out-of-order processing, stock-take adjustments, damaged goods etc.
These issues do not seem to be addressed by the database structures in your question.
Typically, these issues are addressed by computing the margin/profit of a period (day, month etc) by calculating the change in value of the inventory over the period.
If you can use the average cost method, you can calculate the margin with pure SQL. I believe other methods would seem to require some iteration as there is no inherent order in SQL. (Your could improve performance by creating a new table and storing the previous period values.)
I would not be too worried about putting the whole solution in SQL as this would not appear to reduce the computational complexity of the problem. Still there might be speed advantages in doing as much of the calculation in the database engine, particularly if the data-set is large.
You might find this article interesting: Set-based Speed Phreakery: The FIFO Stock Inventory SQL Problem. (There are some clever people out there!)
I have table named tb_product with three columns ProductID, Quantity and Price
if for example Product with ID 5 has a quantity of 20 and somebody bought one item out of it it should be remaining 19 items from the table. That is anytime an order is made product quantity should be subtracted by the number ordered till the product reaches 0 or finishes from the product table.
I have no idea about the it, please somebody help me.
update tb_product set quantity=quantity-1 where productid=productid
My question was partially answered by Raphul Chauham, because his answer does not give me exactly what i was looking for but subtracted the quantity column by 1 instead of by the variable representative from the order. Below is what I wanted exactly.
<?php
$updateProduct=mysql_query("UPDATE tb_products SET quantity=quantity-'$quantity' WHERE productID='$product_id' and quantity>0");
?>
The answer I considered favorable was only subtracting quantity by one whether order quantity is 20 but with this, the subtraction vary as user order varies by the variable $quantity
i met with situation when need to import values with quantity that will be spended many times by some qty.
Eg. i have table with product material
ID TYPE QUANTITY SPENT
1 1 10 0
2 1 100 0
Now i spent 20 qty of material ONCE and need to update field SPENT to be like this
ID TYPE QUANTITY SPENT
1 1 10 10 <--- 0 left
2 1 100 10 <--- 90 left
Or i spend 8 qty first then another 20 qty, so table needs to be like this
First - spent 8
ID TYPE QUANTITY SPENT
1 1 10 8 <--- 2 left
2 1 100 0 <--- 100 left
Second - spent 20
ID TYPE QUANTITY SPENT
1 1 10 10 <--- 0 left
2 1 100 18 <--- 72 left
So i need to separate spended material quantity by avaible and reduce till limit then reduce other one with avaible qty.
What is the best way for this with php?
Please any help will be appreciated, thank you!
UPDATE `tablename` SET `SPENT` = 10, `QUANTITY` = `QUANTITY` - `SPENT` WHERE `ID` = 1
Note that the ID and SPENT fields fields have example values (10 and 1 can be changed, depends on you)
UPDATE tablename SET SPENT = your_variable, QUANTITY = QUANTITY - your_variable WHERE ID = your_row_id
I would do this something like this (REQUESTED is number of items requested):
Start database transaction (to prevent changing spent values by other processes)
Select one or more rows (you can select all rows if you know there wont be a lot of them. also select only those where QUANTITY > SPENT).
In PHP start adding SPENT and reduce REQUESTED row by row (if you didn't get all rows previously you need to get new rows as they are needed).
If REQUESTED is 0 it means you can save all rows do DB and commit transaction, else if REQUESTED > 0 and there no more rows where QUANTITY > SPENT then rollback transaction and inform user that he requested more than its available.
i had created opencart ecommerce checkout system...
i had added a extra field for each products, so when i checkout products i want Subtract my total downpayment amount from total amount of cart....
example for my cart
product1 10, downpayment 5 (number means US dollar)
product1 15, downpayment 8
my total amount is 25
total Downpayment is 13
i want Subtract downpayment from total amount and echo this value below total amount...
if got results it will be like this:
total amount = 25
downpayment = 13
remaining balance = 12
anybody please help me.??
sory my english not perfect... i am new in PHP...
i had already created product table from my database..
given below, its a uploaded product fields...
===Database q8marketz
== Table structure for table product
|------
|Field|Type|Null|Default
|------
|//**product_id**//|int(11)|No|
|model|varchar(64)|No|
|downpayment|varchar(32)|No|
|sku|varchar(64)|No|
|upc|varchar(12)|No|
|ean|varchar(14)|No|
|jan|varchar(13)|No|
|isbn|varchar(13)|No|
|mpn|varchar(64)|No|
|location|varchar(128)|No|
|quantity|int(4)|No|0
|stock_status_id|int(11)|No|
|image|varchar(255)|Yes|NULL
|manufacturer_id|int(11)|No|
|shipping|tinyint(1)|No|1
|price|decimal(15,4)|No|0.0000
|points|int(8)|No|0
|tax_class_id|int(11)|No|
|date_available|date|No|
|weight|decimal(15,8)|No|0.00000000
|weight_class_id|int(11)|No|0
|length|decimal(15,8)|No|0.00000000
|width|decimal(15,8)|No|0.00000000
|height|decimal(15,8)|No|0.00000000
|length_class_id|int(11)|No|0
|subtract|tinyint(1)|No|1
|minimum|int(11)|No|1
|sort_order|int(11)|No|0
|status|tinyint(1)|No|0
|date_added|datetime|No|0000-00-00 00:00:00
|date_modified|datetime|No|0000-00-00 00:00:00
|viewed|int(5)|No|0
== Dumping data for table product
|886|BA-HANDBAGS-001ss|8| | | | | | |aaaa|95|7|data/hand-bags.jpg|7|0|25.0000|25|0|2012-09-03|0.00000000|1|0.00000000|0.00000000|0.00000000|1|1|1|1|1|2012-09-25 13:00:18|2012-09-25 14:15:09|0
This could probably be done quite easily in the PHP code for the cart as like:
$total=25;
$downpayment=13;
$remainingBalance = $total-$downpayment;
echo $remainingBalance;
Based on this and your previous question though, I would really suggest having a look through some basic PHP and SQL tutorials. Something like this will probably go a long way to help you get through these sort of issues you are coming up against. Now, I am not saying don't ask, I am thinking though that you might learn a heck of a lot on the way.
I'm building a web site for marketing company. As per their requirement, when a customer makes a booking. A certain amount of bonus is distributed between employees based on
their hierarchy. The distribution starts from 60 days after booking and bonus is given
for 24 months.
The tables are
bookings
bid book_date
1 2012-05-09
2 2012-05-10
bonus
bid empid amount
1 1 300
1 2 400
2 2 300
2 3 400
Is it possible to write mysql views that generates monthly bonus an employee gets
for every month. I didn't find solution on how to make update with mysql view. Any hint
will of great help.
Instead of view, I would suggest is write mysql function which will return the bonus by accepting the employee ID.
Using mysql function you will have more room to write logic and PL/SQL.
Inner join on bid and filter to only include eligible bonuses by comparing the book date to today's date. If today's date is less than 60 days after or more than 24 months plus 60 days after the original book date, exclude it. (You can go to mySQL.com to learn more about how to manipulate dates in mySQL. I forget...)
You will be left with multiple rows containing only emp id and amount. In the second round, use a "select sum(amount) from (...put your other query here...) group by empid" to get the aggregate bonus per employee.
This approach (and I think any solution) requires a nested SQL statement, and so if you're not comfortable with that syntax you can use that term to explore in google or SO. Cheers!