I have a table called expenses. There are numerous columns but the ones involved in my php page are date, spplierinv, amount. I have created a page that lists all the expenses in a given month and totals it at the end. However, each row has a value, but many rows might be on the same supplier invoice.This means adding each row with the same supplierinv to get a total as per my bank statement. Is there anyway I can get a total for the rows based on the supplierinv. I mean say I have 10 rows. 5 on supplierinv 4, two on supplierinv 5 and 3 on supplierinv 12, how can a get 3 figures (inv 4, 5 and 12) and the grand total at the bottom. Many thanks
Do you need to show the individual expense rows as well as all the totals? If not, you can group by the supplierinv column and sum the amounts:
select supplierinv, sum(amount) as invoice_amount
from expenses group by supplierinv
then you can simply add all the invoice_amount values client-side to get the grand total.
if you do need each expense row, you can do the invoice aggregation client-side fairly easily:
$invtotals = array();
$grand_total = 0;
foreach (getexpenses() as $row) {
$supplierinv = $row['supplierinv'];
if (!array_key_exists($supplierinv, $invtotals)) {
$invtotals[$supplierinv] = 0;
}
$invtotals[$supplierinv] += $row['amount'];
$grand_total += $row['amount'];
}
another approach, if you want to show the table split up into invoices, is to just ensure the results are ordered by supplierinv and then:
$grand_total = 0;
$invoice_total = 0;
$current_invoice = -1;
foreach (getexpenses() as $row) {
if ($current_invoice > 0 && $current_invoice != $row['supplierinv']) {
show_invoice_total($current_invoice, $invoice_total);
$grand_total += $invoice_amount;
$current_invoice = $row['supplierinv'];
$invoice_total = 0;
}
$invoice_total += $row['amount'];
show_expense($row);
}
if ($current_invoice > 0) {
show_invoice_total($current_invoice, $invoice_total);
$grand_total += $invoice_amount;
}
show_grand_total($grand_total);
You can have the database do the aggregation for each invoice and the grand total like so:
select date, supplierinv, amount,
sum(amount) over(partition by supplierinv) as invoice_amount,
sum(amount) over()
from expenses
although you still have to do some post-processing to display this sensibly, so this doesn't really gain anything imho.
Related
I have a problem on breaking down the amount of the given payment, I think i was missing something on my code. This is my table below.
If i have payment of 5,000, it will deduct on the first row and the remaining sum will send to the 2nd row.
//this is the payment
$payment = 5000;
$temp = '';
//the loop of function
$data = payment_breakdown();
foreach ($data as $row) {
$amount = $row['AMOUNT'] - $payment."\n";
}
//handle the difference of every loop of the function
$temp = $amount;
//result of the amount
echo $amount;
//this is my function to get the data from the table
function payment_breakdown(){
$db = database2();
$query = "SELECT AMOUNT FROM TABLE1";
$cmd = $db->prepare($query);
$cmd->execute();
$rows = $cmd->fetchAll();
$db = null;
return $rows;
}
supposedly if the amount on the table is this
Data From Table | The Payment
3000 | 3000
3000 | 2000
You could do it in SQL:
SELECT
amount,
paid,
sum(amount-paid) open
FROM
payment
GROUP BY amount, paid
WITH ROLLUP
HAVING
amount IS NULL
OR paid IS NOT NULL
;
Based on the data
INSERT INTO payment (amount, paid)
VALUES
(3000,3000),
(3000,2000),
(4000,1000)
;
you get
amount;paid; open
3000 ;2000; 1000
3000 ;3000; 0
4000 ;1000; 3000
NULL ;NULL; 4000
Or you might want a simple substraction from a deposit:
SET #credit := 5000;
SELECT
amount,
#credit := #credit-amount credit
FROM payment
;
SELECT #credit;
However, in the latter case you should rather store all payments and amounts to pay into database tables and sum up all together. Consider refactoring your database structure design.
I'm trying to create a quick-fix script in order to delete some records and get the data back on track.
Originally I had sales records inserting and using a quantity value to create an equal number of records. So I if I inserted a record with a quantity value of 12, then I would insert 12 of that record. This gave each SKU it's own record so that individual ones could have dates updated.
However, this has changed and I now need to do it based on store locations rather than quantity so I have too many records in some cases.
If I run the script with the current query (for dealer number 145) it selects all skus they have ordered this year, as expected. I need to now run a delete statement that takes a row count of each sku and compares it to the storefront value from the join.
So, if storefronts = 10 and there are 25 records for that sku/dealer combo, then I need to delete the oldest 15. The logic here is for each sku/dealer combo where curdate() < expire_date there should only be x number of rows equal to storefronts
I'm trying to test this with dealer 145, who has 10 stores. So for all records this year where curdate() is before the expiration, there should be no more than 10 records for each sku, if that makes sense.
$selectRecords = "
SELECT p.sku_id, p.dealer_id, p.start_date, p.expire_date, p.quantity, s.storefronts, s.dealer_id
FROM placements p
inner join storefronts s on p.dealer_id = s.dealer_id
WHERE p.dealer_id = 145
and start_date > '2018-01-01'
order by start_date asc;
";
try{
$currentRecords = $MysqlConn->prepare($selectRecords);
$currentResult = $currentRecords->execute();
$currentCount = $currentRecords->rowCount();
echo "Open Orders: " . $currentCount . "\n"; //returns correct count
}catch(PDOException $ex){
echo "QUERY FAILED!: " .$ex->getMessage();
}
$currentArray = [];
while($row = $selectRecords->fetch(PDO::FETCH_ASSOC)){
//pseudo code
foreach(sku_id){
if(rowCount > storefronts){
delete starting with oldest start_date
until row count == storefronts
}
}
}
I feel like it shouldn't be too complicated but I'm really stuck on how to execute this exactly.
Here's a fiddle with data examples:
http://sqlfiddle.com/#!9/c0dd6
I have a script which returns the ID's of the warehouses (4,1,2,10,9) in order how close they are to the customer.
$warehouse_rank = array('0'=>4,'1'=>1, '2'=>2, '3'=>10, '4'=>9);
When I lookup a product from the database, I return a breakdown of which warehouses have it in stock and the quantity. Like so:
$product_breakdown = array(
'storage'=>array(
'10001'=>array(
'total_stock'=>89,
'breakdown'=>array(
'4'=>0,
'1'=>89,
'2'=>0,
'10'=>0,
'9'=>0
)
)
)
);
10001 is the product ID in this case.
I made this loop to determine which warehouse has the desired quantity, so then I can order it:
foreach ($warehouse_rank as $key => $warehouse_id){
if($product_breakdown['storage'][$product_id['output']]['breakdown'][$warehouse_id] >= $posted->order->quantity) {
}
}
However the problem is this will only detect when the requested quantity is available as whole in the warehouse.
I cannot wrap my head around how to go about when the requested quantity is spread out in multiple warehouses.
For example:
They request 20 pieces.
And the distribution is as follows '4'=>5pc, '1'=>5pc, '2'=>1pc, '10'=>8pc, '9'=>10pc.
So ideally the warehouses will be assigned something like this: WID:4=5pc, WID:1=5pc, WID:9=10pc.
There are two factors, how close the warehouse is; but also to accomplish the allocation with the least warehouses possible.
Any ideas, suggestions how to approach this?
The number of warehouses is dynamic, there could be more or less warehouses. And I'd like to take out as much quantity as possible from the closest warehouse. Thats why I have $warehouse_rank.
PS. I'm not looking for help on how to make the actual order. Just the quantity allocation per warehouse.
Alter your database query so it adds a generated column. Have it calculate the distance from each warehouse to the customer. Add a flat rate weighting value to this total to compensate for each extra warehouse added to the supply chain.
Divide the result by the amount of stock to get a cost per unit figure of supplying from each warehouse. Order your database query on this column pick from the top until you have enough stock for the order.
So you have a table like this:
CREATE TABLE IF NOT EXISTS `stock` (
`warehouse` char(11),
`stock` int(10),
`distance` int(10)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `stock` (`warehouse`, `stock`,
`distance`)
VALUES
('A', 4, 3),
('B', 6, 8),
('C', 10, 10);
And you query it like this, where the 15 in stock + 15 is the weighting value you want to add:
SELECT *, ((stock + 15) / distance) AS per_unit
FROM stock
ORDER BY stock DESC, per_unit ASC
Heres a demo sqlfiddle
Try this.
It will first check to see if there is enough quantity. Else echo not enough.
But you have to deal with that somehow.
Then it will foreach loop with key being the warehouse and breakdown being the quantity in that house.
Subtract the amount possible or what is left of order and store the order in variable order.
$orderqt = 25;
if($orderqt <= $product_breakdown['storage']['10001']['total_stock']){
foreach($product_breakdown['storage']['10001']['breakdown'] as $key => &$breakdown){
if($orderqt>0){
$possibleWarehouses = array_filter(
$product_breakdown['storage']['10001']['breakdown'],
function ($value) use($orderqt) {
return ($value >= $orderqt);
}
);
if(count($possibleWarehouses) != 0){
$house = key($possibleWarehouses);
$order[$house] = $orderqt;
$product_breakdown['storage']['10001']['breakdown'][$house] -= $orderqt;
$orderqt = 0;
}elseif($orderqt >= $breakdown){
$orderqt -= $breakdown;
$order[$key] = $breakdown;
$breakdown = 0;
}else{
$order[$key] = $orderqt;
$orderqt = 0;
$breakdown -= $orderqt;
}
}else{
break;
}
}
}else{
echo "not enough";
}
var_dump($order,$product_breakdown);
Added array_filter to see if any warehouse has the more than the orderd quantity.
$possibleWarehouses is an array that will hold warehouses that can fullfill the rest of the quantity that is ordered.
In this case it will be NULL, NULL and then warehouse 9 with 10 in quantity.
https://3v4l.org/ZeUrt
I have "price offer" items under certain processes;
PROCESS 1
index no 0 price offer a count=1 ($selected_price_offers_cnt)
PROCESS 2
index no 1 price offer b count=3 (3+1=4)
index no 2 price offer c count=3 (3+1=4)
index no 3 price offer d count=3 (3+1=4) 3=selected_price_offers_cnt, 1=$previous, 4=$next
PROCESS 3
index no 4 price offer e count=2 (2+4=6)
index no 5 price offer f count=2 (2+4=6) 2=selected_price_offers_cnt, 4=$previous, 6=$next
What I need to do is to print "scale sub total" only below the LAST price offer item of EACH PROCESSES. In other words, below a, d and f.
In order to do that I try to compare the latest count of the price offers from the beginning (1, 4 and 6) with the current row index no + 1.
If we arrive at the last price offer of a PROCESS X, its count will be == index no + 1 (like at process offer d count 4 = index no 3 + 1).
So I need to keep $previous variable updated with $next for the following PROCESS Y.
My code takes unnaturally long time to finish execution and at last it prints only one sale sub total line.
(As this code is used inside Drupal views global php field, SQL queries are arranged to work there and they return correct values, no problem there.)
Any approaches other than while...loop is also welcomed.
<?php
$process_nid = $row->nid_3;
$current_index_no = $view->row_index; //the number of the row that is being processed
$selected_price_offers = db_query("SELECT entity_id FROM drupal_field_data_field_process WHERE bundle = 'price_offer' AND deleted = '0' AND field_process_nid = {$process_nid} AND entity_id IN (SELECT content_id FROM drupal_flag_counts WHERE fid='18' AND content_type='node' AND COUNT = '1')");
$selected_price_offers_cnt = db_query("SELECT COUNT(entity_id) FROM drupal_field_data_field_process WHERE bundle = 'price_offer' AND deleted = '0' AND field_process_nid = {$process_nid} AND entity_id IN (SELECT content_id FROM drupal_flag_counts WHERE fid='18' AND content_type='node' AND COUNT = '1')")->fetchField();
$previous = 0;
while($current_index_no) {
$next = $selected_price_offers_cnt + $previous; // add the previous to the current amount
if ($next == ($current_index_no+1)) {
$previous = $next; // save the new price offer count to the $previous in order to use it at the next row
// this part below works as expected
$sum1 = 0;
foreach($selected_price_offers as $item) {
$po_item= $item->entity_id;
$scale1 = db_query("SELECT field_sub_total_scale_1_value FROM drupal_field_data_field_sub_total_scale_1 WHERE bundle = 'price_offer' AND deleted = '0' AND entity_id = {$po_item}")->fetchField();
$sum1+= $scale1;
}
print "scale sub total: ".number_format($sum1,2,",","."). " TL";
}
else {
echo "";
}
}
?>
I have to calculate a price based on a rate structure along these lines:
$303.00 fixed price up to 500 units
$0.023 additional per unit from 501-10,000 units
$0.022 additional per unit from 10,001-25,000 units
$0.021 additional per unit from 25,001-50,000 units
I'm a little lost on setting up a database structure and algorithm (the larger sticking point) for calculating this. Has anyone done this? Is there a nice, elegant way of calculating this sort of thing?
edit: As an example, a 25,100 unit run would cost $303.00 for the first 500 units, $218.50 for the next 9,500 units, $330.00 for the next 15,000 units, and $2.10 for the next 100 units, for a total of $853.60.
It wouldn't be a simple 25,100 * $0.021 calculation - I'm well aware of how to select and calculate that.
Similar to the way income tax is assessed - on a marginal basis.
I assume you want something flexible, otherwise it would be trivial to hardcode it.
You could use a pricing table:
ID MAX FIX UNIT
1 500 303 0
2 9500 0 .23
3 15000 0 .22
4 25000 0 .21
Then you could calculate as follows:
$items = ?;
$cost = 0;
$rows = get_rows("select max, fix, unit from pricing order by id asc");
foreach ($rows as $r)
{
if ($items <= 0)
break;
$cost += $r['fix'] + min($r['max'], $items) * $r['unit'];
$items -= $r['max'];
}
I have assumed that you want the algorithm in PHP.
Python
from collections import namedtuple
RateRule= namedtuple( 'RateRule', ['qty_band','fixed','per_unit'] )
rate_table = [
RateRule(500, 303, None),
RateRule(9500, None, 0.023),
RateRule(15000, None, 0.022),
RateRule(25000, None, 0.021)
]
def total_price( units, rate_table ):
# Base
total = rate_table[0].fixed
units_purchased_so_far = rate_table[0].qty_band
# Whole Price Bands
rule = 1
while units > units_purchased_so_far + rate_table[rule].qty_band:
total += rate_table[rule].qty_band * rate_table[rule].per_unit
units_purchased_so_far += rate_table[rule].qty_band
rule += 1
# Units within the top Price Band
if units > units_purchased_so_far:
total += (units - units_purchased_so_far) * rate_table[rule].per_unit
return total
Something like this:
Product
-------
[PK] ProductID
Price
-----
[PK] PriceID
[FK] ProductID
Price
QtyMin
QtyMax
So effectively a 1-many relationship between product and price. You could use a sentinel value for the maximum if you require a flat rate regardless of quantity.
SELECT
CASE is_fixed_price
WHEN 1
THEN unit_price / ?
ELSE
unit_price
END
FROM rate_structure
WHERE ? BETWEEN min_qty AND max_qty
Where ? is the quantity your customer wants to order. Syntax off the top of my head, for mysql 5.x. The side effect of this is potential rounding error accumulation.
What I wound up doing:
size units fixed per
1 500 303.000 0.000
1 10000 0.000 0.023
1 25000 0.000 0.022
1 50000 0.000 0.021
function calculate_price($size, $quantity) {
global $db;
$price = 0;
$count = 0;
// fetch rates from the database
// note: $size is already sanitised by the calling function
$query = "SELECT units, flat, per FROM rates WHERE size={$size} ORDER BY units ASC";
$result = $db->query($query);
// step through the rates
while($rate = $result->fetch_object()) {
// figure out how many of our units fall within this tier
$tier_count = max(0, min($quantity - $count, $rate->units - $count));
// calculate the price for this tier, including any flat rate
$tier_price = $rate->flat + ($rate->per * $tier_count);
// add tier price and count to the totals
$price += $tier_price;
$count += $tier_count;
// store the last, largest number of units rate for any leftovers outside our tiers
$last_rate = $rate;
}
// if some of our units fall outside our defined tiers, use the last tier's values for them
if($count < $quantity) {
$tier_count = $quantity - $count;
$tier_price = $last_rate->flat + ($last_rate->per * $tier_count);
$price += $tier_price;
$count += $tier_count;
}
return $price;
}