I have an ecommerce site with 4 tables
(Users, Vendors, Products and Orders).
Vendor here means a shop.
A User is a person and can own many different Vendors.
These Vendors can have many different products (Vendor hasMany Product relationship and a Product belongsTo Vendor relationship).
The orders table saves all transaction information when a customer buys.
Now we make our money via commission for each sale that happens on the eCommerce site.
The commission is calculated for each product based on the percentage rate of the Vendor(Shop) it is found under. These rates are stored in the Vendors table.
In my products table I have
id
product_name
product_price
product_image
product_file
product_description
product_level
vendor_id
In my Vendors table I have:
id
user_name
vendor_email
vendor_name
rate
And in my orders table I have:
id
customer_name
customer_tel
product_name
product_price
product_id
vendor_email
transaction_id
payment_status
Key things to note: Vendor Email is unique. vendor_id in the products table is the id field in the vendors table etc.
Now, let's take a user John who has 3 Vendors (A, B and C) and 5 products each under these vendors.
For Vendor A, we have products A1, A2, A3 etc same for Vendor B (B1, B2 ..) and Vendor C (C1, C2 etc).
Let's say John has a 5% commission rate to shop A, a 10% rate to shop B and 0% rate to shop C.
Vendor A: rate = 5%.
Products = [A1, A2, A3, A4, A5]
Vendor B: rate = 10%.
Products = [B1, B2, B3, B4, B5]
Vendor C: rate = 0%.
Products = [C1, C2, C3, C4, C5]
This means if product A1 (found in Shop A) cost $10 and it is bought we get 5% of the sale:
5/100 * $10 = $0.5
and John gets 95% which is
$10 - $0.5 = 9.5$.
Let's take another product B2 (found in Shop B with a rate of 10%) and cost $100. this means we get %10 of each sale and John gets 90%.
that is: 10/100 * $100 = $10 for us and John gets $100 - $10 = $90.
Let's take a final product c1 (found in shop C with a rate of 0%) and cost $200. This means when this product is bought, John gets all of the money.
Now for this three products, if John was to calculate his total income, this will be:
$9.5 + $90 + $200 = $299.5
and our total commision will be:
$0.5 + $10 + $0 = $10.5
I want to be able to display this data to the user. So fat, I am able to get the collection using eloquent in my controller and display the data to the user.
In my controller I have:
public function index()
{
$myOrders = ['vendor_email' => Auth()->User()->email, 'payment_status' => 'SUCCESSFUL'];
$orders = Order::where($myOrders)->orderBy('created_at', 'desc')->paginate(10);
$products = Product::with('vendor')->get();
$totalOrders = Order::where($myOrders)->count();
return view('admin.orders', compact('orders', 'products', 'totalOrders'));
}
And in my view, to get the amount John makes for each product, I use a Foreach loop in my blade:
Total: {{totalOrders}}
#foreach($orders as $order)
#if(Auth()->User()->email == $order->vendor_email)
Product Name: {{$order->product_name}}
Product Price: {{$order->product_price}}
#foreach($products as $product)
#if($order->product_id == $product->id)
Rate: {{$product->vendor->rate}}
Income: {{$order->product_price * $product->vendor->rate}}
Vendor: {{$product->vendor->vendor_name}}
#endif
#endforeach
#endif
#endforeach
This works perfectly as it returns the correct figures for each product which has been bought. If we take our example above, it will return
Total: 3
Product | Price |Rate |Income |Vendor
A1 | $10 |5% |$9.5 |A
B2 | $100 |10% |$90 |B
C1 | $200 |0% |$200 |C
My biggest problem is how to dynamically get the total of the income from the controller and displaying it as a variable in the Blade View. I want to be able to get this value in the controller so I can do calculations on it, say the User wants to withdraw part of his income, I should be able to deduct the withdrawal amount from the total income and display what's left etc.
I've been stuck with this problem and I have done a lot of googling but can't seem to find any solution. I got the total count of the products of each user by:
$totalOrders = Order::where('vendor_email', Auth()->User()->email)->where('payment_status', 'SUCCESSFUL')->count();
From that I can display this total orders of each user by simply calling this variable in blade:
Total: {{$totalOrders}}
My question now is how do I calculate the total income of the user: that is taking the price of each product sold and multiplying it by the commision rate of the Vendor shop so I can have some variable such as:
Total Income: {{some Variable}}
I really need help with this.
Order Model
public function product(){
return $this->belongsTo(App\Product::class, 'product_id', 'id');
}
Product Model
public function orders(){
return $this->hasMany(App\Order::class, 'order_id', 'id');
}
public function vendor(){
return $this->belongsTo(App\Vendor::class, 'vendor_id', 'id');
}
Controller
// These are orders for the logged in user.
$orders = Order::with('product.vendor')->where([
'vendor_email' => Auth()->User()->email, 'payment_status' => 'SUCCESSFUL'])
->get();
// Their income should be (100 - rate)% since the rate is the commission you get // as you stated in your post.
$totalIncome = $orders->sum(function ($order) {
return ($order->product->product_price * (100 - $order->product->vendor->rate);
});
$totalOrders = sizeof($orders);
return view('admin.orders', compact('orders', 'totalIncome', 'totalOrders'));
Blade
Total Orders: {{totalOrders}}
#foreach($orders as $order)
Product Name: {{$order->product->name}}
Product Price: {{$order->product->product_price}}
Rate: {{$order->product->vendor->rate}}
Income: {{$order->product->product_price * (100 - $order->product->vendor->rate)}}
Vendor: {{$order->product->vendor->vendor_name}}
#endforeach
Total Income: {{totalIncome}}
Related
Here is the structure of 1st Table called Product.
PRODID PDESC PRICE CATEGORY DISCOUNT
101 BALL 10 SPORTS 5
102 SHIRT 20 APPAREL 10
Here is the structure of 2nd table called SaleDetail.
SALEID PRODID QUANTITY
1001 101 5
1001 101 2
1002 102 10
1002 102 5
I am trying to get total sales amount for each product by joining 2 tables. Here is the SQL i tried but its not giving correct result.
select a.prodid,
(sum((price - discount))),
sum(quantity),
(sum((price - discount))) * sum(quantity)
from product a
join saledetail b on a.prodid = b.prodid
group by a.prodid
2nd column of the query is giving incorrect final price. Please help me correct this SQL.
Please find an indicative answer to your question in the fiddle.
A problem stems from the aggregation of the difference of price. In case that the same product has two different prices, then these prices would be aggregated to one.
Moreover, you multiple the sums of the prices and quantities, while you need to perform the calculation on every sample. Look at the answer by #DanteTheSmith.
You might consider to use the SaleDetail table on the left side of your query.
SELECT SD.PRODID,
P.Price-P.Discount AS Final_Price,
SUM(SD.QUANTITY) AS Amount_Sold,
SUM((P.Price-P.Discount)*SD.QUANTITY) AS Sales_Amount
FROM SaleDetail AS SD
JOIN Product AS P
ON SD.PRODID = P.PRODID
GROUP BY SD.PRODID, P.Price-P.Discount
It would help if you built the example in SQL fiddle or gave the creates for the tables, but if I have to guess your problem is:
(sum((price - discount))) * sum(quantity)
needs to be:
sum((price - discount) * quantity)
(price - discount) * quantity is the function you wanna apply PER ROW of the joined table then you wanna add all those up with SUM() when grouping by prodid.
Furthermore, you can notice that (price - discount) needs to be done ONLY ONCE PER ROW so a quicker version would be to do:
(price-discount) * sum(quantity)
That would give you the total money earned for that product across all the sales you made, and I am guessing this is what you want?
I just notice you have a problem with 2nd column, dunno if that has been in question all along:
(sum((price - discount)))
Why are you summing? Do you want the money earned per product per unit of the product? Well guess what, your price is the same all the time, same as your discount so you can simply go with:
(price-discount) as PPP
NOTE: This assumes the discount is numerical (not percentage) and is applicable to all your sales, also the price is forever the same all which is not real life like.
Query need to find a client named Yossi Cohen, who purchased a number of items and the amount billed(sum) of purchase
For example: Yossi Cohen bought three items costing 40 shekels and we want to know the amount of the order.
example:
namecustomer namemodel quantity sum
Yossi Cohen iphone6 3 120
I try to write this:(not working)
SELECT nameCustomer,
(SELECT idCustomer,nameCustomer,nameModel,idOrders,Quantity,
SUM(price*Quantity)AS OrderTotal
FROM Customers,OrdersItems,Products GROUP BY idOrders)
FROM Customer
where Customer = 'Yossi Cohen';
This should do the work:
SELECT idCustomer,nameCustomer,nameModel,idOrders,Quantity
From Customer
Where sum=price*Quantity
AND Customer = 'Yossi Cohen';
Although I can get the joins to work, it doesn't return the lowest deal possible. The result should be lowest handset price possible with the lowest monthly cost. Currently I can get the lowest handset price, but not with the lowest monthly cost and similarly lowest monthly cost but not with the lowest priced handset.
I have 3 tables DEALS, TARIFFS and HANDSETS. When a customer comes to a manufacturer product listing page, it loads all products related to that manufacturer (in most instances around 40 products). What I would like is it to load the cheapest deal available for each product. Tariff table has over 4000 records. Handset table has over 3000 records. Deals table has over 1 million records.
There is a representation of the the 3 tables below with relevant columns:
Handset Table
==================================
id | manufacturer_id
-----------------------------------
100 1
Deals Table
==================================================
tariff_id | handset_id | handset_cost
--------------------------------------------------
15 100 44.99
20 100 114.99
Tariffs Table
==============================
id | monthly_cost
------------------------------
15 12.50
20 7.50
This is the query
$this->db->select('h.name, t.monthly_cost, d.handset_cost');
$this->db->from('aff_deals d');
$this->db->join('aff_tariffs t', 't.id = d.tariff_id', 'inner');
$this->db->join('aff_handsets h', 'h.id = d.handset_id', 'inner');
if (isset($manuid)) {
$this->db->where('h.manufacturer_id', $manuid);
}
$this->db->order_by('d.handset_cost ASC');
$this->db->order_by('t.monthly_cost ASC');
$this->db->group_by('h.name');
$query = $this->db->get();
$result = $query->result();
Unfortunately this returns handset cost of £114.99 and monthly cost of £7.50, when I need £44.99 and £12.50. The example above is a simple snap shot. I have tried MIN(d.handset_cost), sub-queries but cannot get the desired results. Somehow I need to be able to get the lowest price handsets, even if it's 0.00 (FREE), with its equivalent monthly cost. Any help would be most welcome.
According to your query you are misusing Mysql's GROUP BY extension without having any aggregate function this will lead your query to return indeterminate results for columns which are absent in group by taking your query as an example,columns t.monthly_cost, d.handset_cost values are indeterminate if you are specific to pick minimum row from deals table per handset then you can use below query
SELECT h.name,
t.monthly_cost,
d.handset_cost
FROM aff_deals d
INNER JOIN (SELECT handset_id,MIN(handset_cost) handset_cost
FROM aff_deals
GROUP BY handset_id) dd
ON d.handset_id = dd.handset_id AND d.handset_cost = dd.handset_cost
INNER JOIN aff_tariffs t ON t.id = d.tariff_id
INNER JOIN aff_handsets h ON h.id = d.handset_id
WHERE h.manufacturer_id =1
ORDER BY d.handset_cost ASC,t.monthly_cost ASC
See Demo
For active record query it will difficult to replicate above (subselect) query bu you can directly run this query through $this->db->query('your query')
Background:
We are setting up a promotions system to give away free products to registered customers. We're trying to design a database which is flexible enough to handle multiple products, and giveaways. The requirements are that products may be given away on a drip basis on a first come basis to qualified customers.
Example:
Apple wants to give away 1000 ipads in March.
They want to give away maximum of 1 per hour.
They want to give it to customers who are in California or New York.
They want to limit how many free ipads a customer can get (limit 1 per 15 days).
Data Structure:
Products - 1 entry per unique product. e.g. Apple iPad
ProductGiveAways
ProductID: AppleIpad
Quantity:1000
StartDate: 03/01/2014
End Date 03/31/2014
CustomerState: California,NewYork
PurchaseLimitDays: 15
Problem:
With the above structure we are able to do a query against our customers table and find out which are qualified for the promotion.
What I cannot figure out is the best way to:
Query customers in California or New York (is this a good use case for a join and another table?)
When a customer logs in to see what free items are not available to him, how can I exclude the Apple iPad if the customer has already gotten this freebie?
In other words:
Say amazon.com wants to show me DVDs which I have not already bought. What is the proper way to query that?
Is the right approach to first get a list of previously bought products and then Query with a NOT clause?
I'm assuming you'll have a table for what has been given away. In this table I would include a column for recipient id which can map back to the customer table. You can then create queries to find eligible recipients by searching for customers who have not met disqualifying conditions.
select customerid
from customer
where customerid not in (
select recipientid
from givenaway
where ..... and ....
)
Because there's not a definitive data structure defined, I'm going to use the following which you can tailor to whatever data structure you have designed yourself:
Product
ProductId - INTEGER (IDENTITY and PRIMARY KEY)
ProductName - VARCHAR
States
StateId - INTEGER (IDENTITY and PRIMARY KEY)
StateName - VARCHAR
Customer
CustomerId - INTEGER (IDENTITY and PRIMARY KEY)
StateId - INTEGER (FOREIGN KEY)
Promotion
PromotionId - INTEGER (IDENTITY and PRIMARY KEY)
ProductId - INTEGER (FOREIGN KEY)
Quantity - INTEGER
StartDate - DATETIME
End Date - DATETIME
PurchaseLimitDays - INTEGER
PromotionState
PromotionId - INTEGER (FOREIGN KEY)
StateId - INTEGER (FOREIGN KEY)
So in answer to your questions:
Query customers in California or New York (is this a good use case for a join and another table?)
Personally I would join to a centralized state table (PromotionState) in my above example, I'm sure there's a better way but you could do a condition such as:
WHERE
(SELECT COUNT * FROM PromotionState x WHERE x.PromotionId = p.PromotionId) = 0
OR NOT(ps.PromotionId IS NULL)
Alternatively you could do a GROUP BY and HAVING, using all the other columns as the items to GROUP BY and something like HAVING COUNT * = 0 OR HAVING SUM CASE WHEN (Conditions met) THEN 1 ELSE 0 END = 0
When a customer logs in to see what free items are not available to him, how can I exclude the Apple iPad if the customer has already gotten this freebie?
Say amazon.com wants to show me DVDs which I have not already bought. What is the proper way to query that?
As I've said you could use GROUP BY and HAVING to determine whether an item has been previously "won" by either using COUNT or SUM
Is the right approach to first get a list of previously bought products and then Query with a NOT clause?
There are probably better ways, sub queries can get very heavy and sluggish, I'd recommend trying some of the above techniques and then using a profiler to hopefully make it more efficient.
Some database design
First, when you set the CustomerState to California,NewYork you are violating the First Normal Form of database design.
So let's reorganize your domain model.
State - 1 Entry per unique state
...
Customer - 1 Entry per unique customer
StateId: (California|NewYork|...)
...
Product - 1 Entry per unique product
...
ProductGiveAways - Many entries per product
ProductID
Quantity
StartDate
End Date
PurchaseLimitDays
...
ProductGiveAways_State
ProductGiveAwaysId
StateId
...
Customer_Product - 1 Entry per bought product by customer
CustomerId
ProductId
PurchaseDate
...
Technical issues
When you want to query custoners in California or New York, all you have to do now is :
// This is just an example, you have to change the 'California', 'New York' with their ids
SELECT * FROM Customer WHERE StateId IN ('California', 'New York')
When a customer logs in to see what free items are available to him :
// It's not an accurate sql, just an example
SELECT Product.*
FROM Product
JOIN ProductGiveAways ON ProductId
JOIN ProductGiveAways_State ON ProductGiveAwaysId
WHERE ProductId NOT IN (
SELECT ProductId FROM Customer_Product JOIN ProductGiveAways ON ProductId
WHERE CustomerId = /* the customer id */
AND ( TO_DAYS(now()) - TO_DAYS(PurchaseDate) ) < PurchaseLimitDays
)
AND StateId = /* customer StateId */
AND StartDate < now() < End Date // Elligible ProductGiveAways
For Laravel We Use Something Like this, i hope you can relate to this query or you can use online laravel query converter for using it in mysql ( orator )
$user_id = auth()->user()->id;
Product::where('status', 'active')->whereNotIn('id', function($query) use ($user_id) { $query->select('product_id')->from(new OrderProduct->getTable())->where('user_id', $user_id)->where('status', 'delivered'); });
I have the following db schema (i'm only mentioning the columns for each table necessary for the query in mind):
bills
-------
id
amount (decimal)
collection_case_id
collection_case
------------------
id
payments
----------
id
bill_id
amount (decimal)
type (char(2)) - "CH" stands for check payment, field used for polymorphism
check_payments
---------------
payment_id
For a set of collection cases, I need to get the total interest earned from check payments.
I am currently doing these calculations in memory after retrieving all the bill records for my set of collection case criteria. For each bill I simply add the sum of checks received - bill amount if sum of checks received is greater than bill amount.
for instance my total received query looks like this (collection cases 1 & 2):
select sum(payment.amount) as received
from bills bill
cross join payments payment inner join check_payments checkpayment
on
payment.id=checkpayment.payment_id
where payment.type='CH'
and payment.bill_id=bill.id
and (bill.collection_case_id in (1 , 2))
Not really sure if this is what you meant.
select IF(sum(payment.amount) > bill.amount, sum(payment.amount)-bill.amount, 0) as interest
from bills bill
cross join payments payment inner join check_payments checkpayment
on
payment.id=checkpayment.payment_id
where payment.type='CH'
and payment.bill_id=bill.id
and (bill.collection_case_id in (1 , 2))