I am working on yii2
. I want to calculate electricity bill slab wise. For this, I have already inserted slabs and their respective rates in a table. Now I want to calculate it.
My units are 128.82
and below is my slab and their rates.
I am able to carry out the slabs by using the following query
$mData = \Yii::$app->db->createCommand("SELECT s.`slab_start`, s.`slab_end`, s.`rate`, r.`cust_id`
FROM `mdc_tariff_slabs` s
INNER JOIN `mdc_meter_cust_rel` r ON s.`t_id` = r.`tariff_id`
WHERE r.`cust_id` = $consumer_no")->queryAll();
array(3) { [0]=> array(3) { ["slab_start"]=> string(1) "1" ["slab_end"]=> string(3) "100" ["rate"]=> string(2) "10" } [1]=> array(3) { ["slab_start"]=> string(3) "101" ["slab_end"]=> string(3) "150" ["rate"]=> string(2) "12" } [2]=> array(3) { ["slab_start"]=> string(3) "151" ["slab_end"]=> NULL ["rate"]=> string(2) "14" } }
Note: The slabs may differ in future
I have tried the following code, set the slabs according to my requirements and tried to get the bill
public static function calcBill($units)
{
if($units < 100){
$bill = $units*10;
}elseif($units > 100 && $units <=150){
$temp = 100*10;
$remaining_units = $units - 100;
$bill = $temp + ($remaining_units *12);
}else{
$temp = (100*10)+ (100*12);
$remaining_units = $units - 150;
$bill = $temp + ($remaining_units *14);
}
echo $bill;
die();
}
The bill is 1345.84
.
Now what I am trying to do is to pass the slabs as well and not to hard code them. I am finding it a bit difficult.
Update 1
I have 3 slabs/units limit. Each slab has its own rate. Rate is used to calculate the bill. Below is the scenario.
In my array, I have all the three values except for the last one which has only slab_start
and rate
.
By hard coding the slabs and rate I can calculate the bill but I want to calculate while using the array.
So the code may be look likes
if($units < slab_end){
$bill = $units*rate;
}elseif($units > slab_start && $units <=slab_end){<=slab_end
$temp = prev_slab_start *rate;
$remaining_units = $units - prev_slab;
$bill = $temp + ($remaining_units *rate);
}else{
$temp = (prev_slab*10)+ (prev_slab*12);
$remaining_units = $units - slab_start;
$bill = $temp + ($remaining_units *14);
}
I don't know the above logic is correct or not.
Note: Slab range and quantity may be changed. So I want to have a generic function that will do the needful
Update 2
Following the solution mentioned.
$array = ArrayHelper::map($mData, 'slab_end', 'rate');
var_dump($array);
die();
The above gives me
array(3) { [100]=> string(2) "10" [150]=> string(2) "12" [""]=> string(2) "14" }
Then I have added a function
public function billCalc($input, $slabs)
{
$result = [];
$bill = 0;
$previous_slab = 0;
foreach($slabs as $slab => $rate)
{
// calculate distance between current and previous slab
$slab_distance = $slab - $previous_slab;
// if current remainder of input value is >= distance, add distance to result,
// and subtract distance from remainder of input
if( $input >= $slab_distance )
{
$result[] = $slab_distance;
$bill += $slab_distance * $rate;
$input -= $slab_distance;
}// otherwise, add remainder as last item of result, and break out of the loop here
else
{
$result[] = $input;
$bill += $input * $rate;
break;
}
$previous_slab = $slab;
}
var_dump($result, $bill);
exit();
}
Now I am facing an issue. When the input is less than 150
the result is perfectly ok, but when it's greater or equal to 150
I am getting below error
A non-numeric value encountered
It's giving me this error at
$slab_distance = $slab - $previous_slab;
Any help would be highly appreciated.
So you simply want to multiply the amount covered by each slab (“units”) with the rate then - so I would create an array that has the slab end as key, and the rate as value - then you can simply loop over that with the extended foreach syntax, and have access to the rate corresponding to the slab at all times.
Expanding on my answer to the previous question, this would look like this then:
$input = '128.82';
$slabs = [100 => 10, 150 => 12, PHP_INT_MAX => 14];
$result = [];
$bill = 0;
$previous_slab = 0;
foreach($slabs as $slab => $rate) {
// calculate distance between current and previous slab
$slab_distance = $slab - $previous_slab;
// if current remainder of input value is >= distance, add distance to result,
// and subtract distance from remainder of input
if( $input >= $slab_distance ) {
$result[] = $slab_distance;
$bill += $slab_distance * $rate;
$input -= $slab_distance;
}
// otherwise, add remainder as last item of result, and break out of the loop here
else {
$result[] = $input;
$bill += $input * $rate;
break;
}
$previous_slab = $slab;
}
var_dump($result, $bill);
Result:
array (size=2)
0 => int 100
1 => float 28.82
float 1345.84
My approach again uses only the upper slab boundaries here - you don’t have one in your last record from the database, so I am substituting PHP_INT_MAX here for that NULL value. Taking that into account, when you create that array from your database content, should be not challenge, I hope.
(If you only need the 1345.84
as result here, and not the array that shows the distribution accross the indivudual slabs, then you can just remove all lines that contain $result
anywhere.)