Calculating tax correctly using Mysql and php - php

I am trying to re-calculate tax on a sale after it is entered into a database. I can calculate the information correctly before storing in the database, but calculating it from the database for reporting purposes has proved troublesome due to rounding errors.
The application displays an interface that allows one to add items and view the subtotal, taxes and totals.
Example input:
Item 1: 9.95
Item 2: 2.95
Subtotal: 12.90
Taxes (could be different for each item, but now they are all the same for each item)
2.900%: 0.37
1.000%: 0.13
1.100%: 0.14
3.000%: 0.39
Tax Total: 1.03
Total: 13.93
The way I calculate taxes before storing into database:
foreach Item as $price
{
foreach Tax as $percent
{
$taxes[$percent]+=$price*($percent/100);
}
}
foreach($taxes as $percent => $tax_amount)
{
$taxes[$percent] = ROUND_TO_2_DECIMALS($tax_amount);
}
After calculating all this information I store the information in a database that can be retrieved later for reporting purposes.
Example Schema:
phppos_sales_items
- sale_id
- item_id
- price
phppos_sales_items_taxes
- sale_id
- item_id
- tax_percent
Sample Data for phppos_sales_items table
sale_id: 1, item_id: 1, item_unit_price: 9.95
sale_id: 1, item_id: 2, item_unit_price: 2.95
Sample Data for phppos_sales_items_taxes table
sale_id: 1, item_id: 1, percent: 2.90
sale_id: 1, item_id: 1, percent: 1.00
sale_id: 1, item_id: 1, percent: 1.10
sale_id: 1, item_id: 1, percent: 3.00
sale_id: 1, item_id: 2, percent: 2.90
sale_id: 1, item_id: 2, percent: 1.00
sale_id: 1, item_id: 2, percent: 1.10
sale_id: 1, item_id: 2, percent: 3.00
Sample Query to calculate tax for a sale
(SELECT SUM(tax) FROM (SELECT ROUND((item_unit_price)*(SUM(percent)/100),2) as tax
FROM phppos_sales_items
INNER JOIN phppos_sales_items_taxes USING (sale_id, item_id)
WHERE sale_id = 1
GROUP BY sale_id, item_id) as tax)
Output is 1.04 when I need to get 1.03. This query works in most cases, but it fails a lot to when there are rounding problems.
I need to be able to generate summary informatation accuratly using sql. I am open to any suggestions on how to calculate information before the sale and in the database.

OK, so, your question, as I understand it has not much to do with programming.
So, you have an item which is worth $1. Now, you have two taxes on it, one is 1.4% and another one is 1.3%. Now, this is what you are doing
tax1 = round($1*0.014, 2) = $0.01
tax2 = round($1*0.013, 2) = $0.01
totaltax = $0.02
So far so good... OK, but now let's do it other way. If we add up the taxes, we should pay the total of 1.4 + 1.3 = 2.7%, which means:
totaltax = round($1*0.027, 2) = $0.03
See what happened here?
Now, which of these ways is right? If you physically need to pay tax1 separately from tax2, then each of them is quantified in cents and the first way is right. But, I feel like you most likely only pay the final total tax, that is you add up percents first, calculate your tax and then pay it to the nearest cent.
In relation to your code, you should get 1.03, that's right, but your query is doing a mistake similar to the first variant by rounding the result for each item before adding them up (it is doing round(price1*tax1, 2) + round(price2*tax2, 2)). (Actually, just to be complete, you have three places to round: individual tax level, item level and on the total level.)
To round at the end instead of for each item, move out your ROUND:
(SELECT ROUND(SUM(tax),2) FROM (SELECT (item_unit_price)*(SUM(percent)/100) as tax
FROM phppos_sales_items
INNER JOIN phppos_sales_items_taxes USING (sale_id, item_id)
WHERE sale_id = 1
GROUP BY sale_id, item_id) as tax)

Sorry to put as an answer, since the comment can not display code properly.
How about
(SELECT ROUND(SUM(tax)/100,2) FROM (SELECT item_unit_price*SUM(percent) as tax
FROM phppos_sales_items
INNER JOIN phppos_sales_items_taxes USING (sale_id, item_id)
WHERE sale_id = 1
GROUP BY sale_id, item_id) as tax)
Can you export the tables as sql, so I can try on my local? If nothing is too confidential.

Related

substract with phpspreedshet

I have a php front end for a database with expenses on a project.
I use phpspreadsheet to create reports from this database and until now everything worked great.
In the excel table I export three columns from the database table: type of expense, amount, date of expense.
The total amount for each type of expenses is entered in the table as a positive number and the quantities that are subtracted are entered as negative numbers - example: category of expense: travel costs, amount: 100, date | travel costs, amount: -10, date | travel costs, amount: -25, date, etc. In the excel template that I use with phpspreadsheet I created a fourth column 'balance'. In this column I substract the negative initial values from the starting amount that is positive and I end up with a excel table having four columns: travel costs, amount: 100, date, balance:100 | travel costs, amount: -10, date, balance:90 | travel costs, amount: -25, date, balance: 65 etc.
Until now it was simple because I knew the types of expenses - they were entered by me in the database so the users can only select between these predefined values and I got a sheet with multiple tables for each type of expense:
$sql="SELECT type_expense, , date, amount FROM expenses where acronym = '" . $_SESSION['idproject'] . "' ORDER BY type_expense";
$rsSql=db_query($sql,$conn);
$baseRow = 17;
$r=0;
$sum=0;
while ($data2 = db_fetch_array($rsSql)){
$row = $baseRow + $r;
$spreadsheet->getActiveSheet()->insertNewRowBefore($row, 1);
if($data2['type_expense']=='travel costs'){
$spreadsheet->getActiveSheet()->setCellValue('A' . $row, $r + 1)
->setCellValue('B' . $row, $data2['type_expense'])
->setCellValue('C' . $row, $data2['amount'])
->setCellValue('D' . $row, $data2['date']);
$sum=$sum+$data2['amount'];
$spreadsheet->getActiveSheet()->setCellValue('E'. $row , $sum);
$r= $r+1;
}
}
$spreadsheet->getActiveSheet()->removeRow($baseRow - 1, 1);
I use the same code for populating tables in the predefined sheet for the other two type of expenses: personnel cost and other expenses.
The problem is that now users need to define their own types of expenses so the code above could not work because I don't know how they define the type expense so I can use it in the if conditional inside the loop.
Also the sheet will be overcrowded with tables so I need to create a single table that define type of expense, date, amount and balance for all type of expenses - type of expense: travel costs, date, amount: 100, balance: 100 | type of expense: travel costs, date, amount: -10, balance: 90 | type of expense: personnel costs, amount: 10, date, balance:10 | type of expense: personnel costs, date, amount: -5, balance:5, etc
I tried with a conditional for all type of expenses as positive (this is the initial amount from where all the values are subtracted) but I get 0 in all the rows form the balance column.
I really need a suggestion where to go from here in order to have a single table with subtraction on each type of expenses.

MySQL/PHP storing multiple data points

I am relatively new to programming and databases.
I have a MySQL database with a "sales" table. This table lists all sales in a state (i.e. each record is a particular sale), with fields for sellername, and buyerzip. I would like to have another table "seller" that would include sellername, and also include fields to define the sellers market area by zip code - say 50+ zip codes define a market, and the seller could define multiple markets.
These market areas would be used for future queries: showing all sales in a particular market area.
Where do I start in terms of thinking how to store that "market area" data, and then use it for future queries?
Thanks
Table structure:
Sales table: ID, sellerID, buyerZipcode, amount
Seller table: ID, sellerName
Market table: ID, marketName
SellerMarketLink table: ID, sellerID, marketID
MarketZipCode table: ID, marketID, zipCode
Example data
Encoding the information in the first comment to your question
Market table
1, "Store A, first market"
2, "Store A, second market"
3, "Store B market"
MarketZipCode table
1, 1, 1
2, 1, 2
...
7, 1, 7
8, 1, 8
9, 2, 4
10, 2, 5
...
14, 2, 9
15, 2, 10
16, 3, 1
17, 3, 2
18, 3, 3
19, 3, 8
20, 3, 9
21, 3, 10
22, 3, 11
23, 3, 12
Query
Total sales in a market for each seller. Note that since one zip code can be part of several markets, the total of all values in "Total Sales" can be larger than the sum of all 'amount' values in the Sales table.
select Market.marketName, Seller.sellerName, sum(Sales.amount) as "Total Sales"
from Sales join Seller join SellerMarketLink join Market join MarketZipCode
on (Sales.sellerID = Seller.ID AND Seller.ID = SellerMarketLink.sellerID
AND SellerMarketLink.marketID = Market.ID
AND Market.ID = MarketZipCode.marketID
AND MarketZipCode.zipCode = Sales.buyerZipCode)
group by Market.ID, Market.marketName, Seller.ID, Seller.sellerName
order by Market.marketName, Seller.sellerName
I'm going to make the following assumptions here:
sellers have many markets
A market can span many zipcodes
Only one seller is responsible for a market
Markets can't overlap
Leading to the following structure...
sales table
seller | buyerzip
markets table
market | market_zipcodes_id
zipcodes table
market_id | zipcode
Hope that helps...

mysql union or join just one query

I need to find a better way to get the discount for each article price in our web shop depending on which pricelist or pricelist/discount list a customer has. I think this is possible to do in just one query instead of the 5 I have today, but I really do not know where to start.
All our customers have a pricelist and some have both pricelist and one extra discount list. Today we have about 25 different pricelists and about 100 extra discount lists.
All the pricelists are structured in the same manner; they have a price group and a discount in percent.
For example pricelist 01 could look like
A 20
B 35
C 20
The extra discount list is structured in a different manner and can have a fixed price or percentage. It also has three different priority levels: discount based on the article code (has priority 1), based on category (has priority 2) and based on price group (has priority 3).
Discount list 0013 could look like:
In the article tab
PL-2344-444 40 (%)
P-0455-23 200 (SEK)
In the category tab
C12 50 (%)
N12 35 (%)
Today I have three different queries to see if I get a hit in the discount list:
First I check to see if I get a hit in priority 1: (FIXED returns f and PERCENTAGE r)
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE ARTCODE = 'JO-23455' AND DISCOUNTLIST = '0013'
If the above returns 0, I do the second query, priority 2:
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE CATEGORY = 'C15' AND DISCOUNTLIST = '0013'
And the last one priority 3:
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE PRICEGROUP = 'F' AND DISCOUNTLIST = '0013'
If none of the extra discount lists returns 0 I get the discount from the pricelist
SELECT DISCOUNT FROM PUPRIREG WHERE PRICELIST = '01' AND PRICEGROUP = 'F'
I call the function like follows
$discount = discount($articlecode, $category, $pricegroup);
function discount($articlecode, $category, $pricegroup){
$articlecode = sanitizingData($articlecode);
$category = sanitizingData($category);
$pricegroup = sanitizingData($pricegroup);
// do priority 1
// prio 2
// prio 3
// pricelist
return $discount;
}
I would be so happy if someone could show me how to do this. I am using mysqli and php.
Many thanks
Best regards linda
You can do the queries with a union:
(SELECT DISCOUNT, FIXED, PERCENTAGE, 1 priority FROM PUPRIREG
WHERE ARTCODE = 'JO-23455' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, FIXED, PERCENTAGE, 2 priority FROM PUPRIREG
WHERE CATEGORY = 'C15' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, FIXED, PERCENTAGE, 3 priority FROM PUPRIREG
WHERE PRICEGROUP = 'F' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, 0 fixed, 0 percentage, 4 priority FROM PUPRIREG
WHERE PRICELIST = '01' AND PRICEGROUP = 'F')
order by priority;
The additional artificial priority column and order by ensures, that you get the discounts properly sorted.

PHP MySQL inventory likeness percentage

Edit:
I removed the confusing stuff and I wanted to simplify what I want to accomplish:
Let's say John had 2 baskets and 1 of them contained 1 stone, 2 marbles and 2 sticks, the other one contained 2 stones and 2 sticks.
Eric had 1 basket which contained 3 bottles, 2 caps and 1 stone.
Meanwhile Jack had 1 basket which contained 1 stick. Also, Jack has a stone somewhere but it's not inside his basket yet.
For the purpose of this query, let's say we're focusing on Jack's items. So we would go over everybody's baskets and see which basket looks pretty much the same as all the items he has overall (a) without thinking whether Jack's items are inside a basket or not and (b) only compare to Jack's items with items that are inside other people's baskets, not outside.
This would be the ideal end result when searching which baskets Jack can match the easiest with all his items regardless of where his items are currently:
Fact:
Jack has in total 1 stick and 1 stone.
Result:
1st: John's second basket containing 2 stones and 2 sticks
2nd: John's first basket containing 1 stone, 2 marbles and 2 sticks
3rd: Eric's basket containing 3 bottles, 2 caps and 1 stone
This will do what you asked for a single basket:
SELECT SUM(item.qty/(SELECT sum(qty) total FROM basket WHERE bid = xxx)
* 1/all_item.qty) likeness, basket.bid, all_basket.bid all_bid
FROM basket JOIN item USING (bid)
LEFT JOIN
(basket all_basket JOIN item all_item USING (bid))
USING (iid)
WHERE basket.bid = xxx
GROUP BY basket.bid, all_basket.bid
ORDER BY likeness DESC
It's probably possible to join this to the users table to do all his baskets at once. Then wrap it in yet another query to find the best match.
Try this first, and let me know if it works. If it does I'll see if I can do that part tomorrow.
Whilst I'm not sure that there's a simple way to calculate basket likeness as a percentage using SQL, I assume you are simply trying to product product recommendations.
You could formulate a query to produce product recommendations based on the current basket. You can do this by:
Finding any basket that contains at least one item from the current customer's basket.
Limiting the results to products that are in the same category.
You can fetch the appropriate information with the following query:
SELECT b1.basket_id, b1.product_id, b1.quantity, p1.product_category FROM baskets b1
INNER JOIN products p1 ON p1.product_id = b1.product_id
WHERE b1.product_id IN
(
SELECT b2.product_id FROM baskets b2
INNER JOIN products p2 ON p2.product_id = b2.product_id
WHERE b2.basket_id = $basket
AND p2.product_category = p1.product_category
)
AND b1.basket_id != $basket;
This will return the basket ID, product ID, quantity and product category ID for all items in all other baskets that share at least one product with your selected basket. It also filters the results so that the suggested products are always in the same category as the source product.
Here's a sample:
baskets table:
basket_id | product_id | quantity
1, 2, 1
1, 1, 2
1, 3, 5
2, 5, 1
2, 6, 1
3, 1, 1
3, 2, 1
3, 4, 1
products table:
product_id | product_name | product_price | product_category
1, 'cat1 prod1', 14, 1
2, 'cat1 prod2', 1.5 1
3, 'cat1 prod3', 2, 1
4, 'cat2 prod1', 22, 2
5, 'cat2 prod2', 6, 2
6, 'cat2 prod3', 45, 2
7, 'cat3 prod1', 24, 3
8, 'cat3 prod2', 55.4, 3
9, 'cat3 prod3', 22, 3
result of query:
basket_id | product_id | quantity | product_category
3, 1, 1, 1
3, 2, 1, 1
Hopefully that's of some use to you.

showing a grandtotal of values in a field

good pm. i was thinking is it possible to show the summation or grand total of a selected field in the table and with relation to date:
for example is i want to know the total beer consumption of my hotel every month.
i have here my table on services:
[services_id[pk],
customer_id[fk],
date_in,date_out,room_type,room_number,
extra_ref,
extra_bed,extra_snack,
extra_beer,extra_softdrinks,
extra_pillows,extra_breakfast,
extra_snack_q,
extra_beer_q,
extra_softdrinks_q,
extra_pillows_q,
extra_breakfast_q]
can you give some advice on how can i get it.
thanks in advance:
-renz
SELECT SUM(beer_amount) as monthly_beer_amount
FROM [DATABASE].[TABLE]
WHERE beer_sold_date BETWEEN '20110201' AND '20110228'
[EXTRA INFO]
Also I believe that the best way to organize this table is to separate out this table into a few other tables. Store customer info in the first table such as
[customer_id, customer_name, date_in, date_out, room_type, room_number].
1, Bob, 20110101, 20110110, big, 200
2, Joe, 20110101, 20110110, small, 202
....
And have another table named something like room_items which would have the following,
[id, item_name]
1, BEER
2, BED
3, SNACK
...
And then another table named room_purchases which will have the following,
[customer_id, purchase_id, amount, date....]
1, 1, 10, 20110101
2, 3, 5, 20110101
3, 1, 9, 20110101
....
This would help you to do a join on all three tables and they would be more normalized in this way.
SELECT SUM(t2.amount) as beer_amount
FROM customers as t1
LEFT JOIN room_purchases as t2 ON t1.customer_id = t2.customer_id
WHERE t2.purchase_id = 1 AND t2.date BETWEEN 20110101 AND 20110131
SELECT DATE_FORMAT(date_in,"%m-%Y") AS month,SUM(extra_beer) AS beers
FROM your_table
GROUP BY month
this will return something like this:
02-2011 | 43
03-2011 | 52
if you want to limit the query depending on the date just add a WHERE constraint before the GROUP BY

Categories