I am trying to calculate a late daily fee of 0.02%, but when the second installment is reached the late fee adds the second installment plus total fee from first installment and now calculate daily fee with new accrued figure.
Example
First installment 5 days late before reaching second instalment:
fee=(arrears*5*0.002)
Second instalment is reached and loan overdue for another 5 days:
fee2=((instalment2+fee)*5*0.002))
Usually the interest use Compound Interest the formula (arrears*(1+0.002)^5), not Simple Interest formula (arrears*5*0.002).
Anyway, I made a piece of code to show how can you do it.
<?php
function CalculateLoan($Value,$Days,$Fee){
//Simple Interest
return $Value + $Value*$Days*$Fee;
//Compound interest
//return $Value * pow(1+$Fee,$Days);
}
// Assuming 6 Instalments of $1000 every 5 days.
$ArrayInstalments = array();
$ArrayInstalments[0]= array("Value"=>1000,"PayDay"=>0);
$ArrayInstalments[1]= array("Value"=>1000,"PayDay"=>5);
$ArrayInstalments[2]= array("Value"=>1000,"PayDay"=>10);
$ArrayInstalments[3]= array("Value"=>1000,"PayDay"=>15);
$ArrayInstalments[4]= array("Value"=>1000,"PayDay"=>20);
$ArrayInstalments[5]= array("Value"=>1000,"PayDay"=>25);
// 30 days passed
$CurrentDay = 30;
// 0.2% fee/day
$Fee = 0.002;
// Initial Debit
$Sum = 0;
foreach ($ArrayInstalments as $key=>$Instalment){
$ThisDebit = CalculateLoan($Instalment['Value'],max($CurrentDay-$Instalment['PayDay'],0),$Fee);
echo 'Instalment:'.$key.' Debit:' . $ThisDebit . '<br/>';
$Sum += $ThisDebit;
}
echo 'Total:' .$Sum. '<br/>';
?>
Related
I need to apply discounts to our billing services and am having a devil of a time trying to figure out how to determine the used percent of an applied discount. I have scrapped almost 10 versions of code and think I am just to close to the problem and am not able to see this clearly.
I am trying to make universal function that will use the first/last day of the billing cycle, plus the startDiscount and endDiscount dates to determine what percent to apply to the actual discount; regardless if the discount is a % discount or an actual $ value.
Example: Bill month March 2023 (31 day bill cycle), invoiced services from Mar 1st - Mar 31st. But a 50% discount was applied starting on March 10th - the discount is for 90 days.
This means that roughly 66% of the 50% discount (IE: 33%) needs to be applied to the march invoice, the full 50% of April and May...and then 34% of the 50% discount (IE: 17%) needs to be applied to Junes bill.
So for the applied discounts:
March = 66% of the 50% discount = 33% discount applied to charges
April = 100% of the 50% discount = 50% discount applied to the charges
May = = 100% of the 50% discount = 50% discount applied to the charges
June = 33% of the 50% discount = 17% discount applied to charges
So as each invoice is processed, the first/last day of the invoice range is passed in as well as the start/finish dates of the discount
$discPercent = applyDiscount("2023-03-01","2023-03-31","2023-03-10","2023-06-10") ;
$discPercent = applyDiscount("2023-04-01","2023-04-30","2023-03-10","2023-06-10") ;
$discPercent = applyDiscount("2023-05-01","2023-05-31","2023-03-10","2023-06-10") ;
$discPercent = applyDiscount("2023-06-01","2023-06-30","2023-03-10","2023-06-10") ;
function applyDiscount($firstDay,$lastDay,$startDisc,$endDisc) {
...
...
return $applyThisPercent ;
}
Then $discPercent can be applied an existing 90 day 50% discount OR or can be applied to $100 credit per invoice cycle for 90s
You need to calculate the number of days overlapping the billing period and the discount period and divide it by number of days in the billing period.
With the help of the overlap function in this answer, you get this:
function datesOverlap($start_one, $end_one, $start_two, $end_two) {
if($start_one <= $end_two && $end_one >= $start_two) { //If the dates overlap
return datesDays(min($end_one, $end_two), max($start_two, $start_one));
}
return 0; //Return 0 if there is no overlap
}
function datesDays($start, $end) {
return $start->diff($end)->days+1;
}
function applyDiscount($firstDay, $lastDay, $startDisc, $endDisc) {
$firstDay = new DateTime($firstDay);
$lastDay = new DateTime($lastDay);
$startDisc = new DateTime($startDisc);
$endDisc = new DateTime($endDisc);
$all_days = datesDays($firstDay, $lastDay);
$used_days = datesOverlap($firstDay, $lastDay, $startDisc, $endDisc);
return round(100*$used_days/$all_days);
}
echo applyDiscount("2023-03-01","2023-03-31","2023-03-10","2023-06-10")."\n";
echo applyDiscount("2023-04-01","2023-04-30","2023-03-10","2023-06-10")."\n";
echo applyDiscount("2023-05-01","2023-05-31","2023-03-10","2023-06-10")."\n";
echo applyDiscount("2023-06-01","2023-06-30","2023-03-10","2023-06-10")."\n";
This results in:
71
100
100
33
I want to display to a user an amount per day and increment it until the next 124 days assumed to be four months.
I have a system where a user invest and a total ernable profit is calculated with the percentage ROI for such stock. Assuming someone invested in a stockA that has 20% ROI and matures in 4 months; assuming a user purchased that stockA that is sold $100 and he bought 4 units meaning he spent $400 and will earn
as follows:
$units = 4;
$cost = 100;
$roi = 20/100;
$total_invested = $units * $cost;
$profit = $total_invest * $roi;
// $profit will be $80
My problem is I want to display value of $profit/124 that is the displaying a fraction of total earning to the user daily until maturity of 4 months. I can't figure out how to do that daily not just with loop of 124 iterations.
that is if the user total earning is $80/124 giving 0.65 on the first day and increment it with same value the next day until it reached the end date which is 4 months from now
/**
*
* display and increment earning every day for 4 months or 124 days
*
*/
function display_earning() {
$profit = $profit_roi;
$dateBegin = now();
$dateEnd = "date of 4 month from now";
$earning = 0;
for ($i = 0; $i < 124; $i++) {
$earning += $profit / 124;
return $earning;
}
}
I think your problem is to update and save the updated earning of user for 4 months. To do that you need to write a server level cron-job that run on every day and a script in which you update and save the earning of user and check it has reached 4 months for that user since you started or not.
I hope you want this and that will help you. Happy Learning.
It's not clear what you want your code to do. Do you want it to give the total earned cumulatively by day x or give the total earned on any one day? If it is any one day and you don't want to take compound interest into account the earning will be the same every single day. In which case all your function has to do is return
function return_earnings($profit, $totalDays) {
return $profit / $totalDays;
}
as the amount earned each day will be the same as any other day. If you do want compound interest then you'll need to code that it. I assume you don't as there is no sign of it in the code you supplied.
I get the feeling what you are looking for instead is a list of how much was earned each day cumulatively. If that's the case, the code you've written is more appropriate but needs some modification. At the moment your code is returning $earning after the first iteration of the loop. Moving return to the end of the function should fix that.
function display_earning() {
$profit = $profit_roi;
$dateBegin = now();
$dateEnd = "date of 4 month from now";
$earning = 0;
$totalDays = 124; // You might want to change '124' in future
// and having $totalDays in the for loop makes it more
// obvious what your code is trying to do.
for ($i = 0; $i < $totalDays; $i++) {
$earning += $profit / $totalDays;
}
return $earning;
}
However now because of the line $earning += you will return the sum of earning at the end of $totalDays, ie the $profit, which you already know! You may want the cumulative earnings by a given day to be an item in an array, in which case:
function display_earning() {
$profit = $profit_roi;
$dateBegin = now();
$dateEnd = "date of 4 month from now";
$totalEarning = 0; //renamed to $totalEarning
$earningsByDay = array();
$totalDays = 124;
for ($i = 0; $i < $totalDays; $i++) {
$totalEarning += $profit / $totalDays;
$earningsByDay[$i] = $totalEarning;
}
return $earningsByDay;
}
This will now return an array with each element of the array amounting to the sum earned by that day. For example, $earningsByDay[0] will be the first day, $earningsByDay[11] will be the 10th day etc. If this is what you are looking for, you can use php's native range() function to make your life easier:
function display_earning() {
$profit = $profit_roi;
$dateBegin = now();
$dateEnd = "date of 4 month from now";
$totalEarning = 0; //renamed to $totalEarning
$earningsByDay = array();
$totalDays = 124;
return range(0, $profit, $profit/$totalDays)
}
A few final thoughts:
You say you want to display the amount earned each day. You function does not display anything it just calculates a value and returns it. That's good practise. Have your functions do one thing. Some other function can display the data from this one, or perhaps multiple functions can, each formatting it in a way that is appropriate for your current need. I can't help you with the display as I have no way of knowing what format you want. $dateEnd probably doesn't need to be in the function.
On a related note, another function name might lead to less confusion about what the function does.
I'm not sure what $dateBegin = now(); adds, unless as Moeez Saiyam suggests, you are trying to automate this somehow.
You've defined $profit = $profit_roi; without declaring what $profit_roi is in the scope of your function. I know it was in your introductory notes, but the function won't know that.
Is the function the right place to define $profit and $totalDays? They are probably passed to the function form elsewhere.
Combining these thoughts, this gives us:
function getIncrementalEarnings($profit, $totalDays) { //function renamed to clarify its purpose
return range(0, $profit, $profit/$totalDays)
}
This is a bit of a contrived problem, but a real one for our industry.
Our customer's rent hotel rooms and for hotel accounting reasons, room charges are added to the folio each night, not all at once. I've been working on fixing our flat rate calculation module, which is completely broken.
An example of the problem is - Customer agrees to pay $376 for a 30 night stay. However, $376 is not put on the books 1 time, it's ($376/nights) put on the folio for each night's stay.
So, given a desired Total for the entire stay, a number of nights, and a tax rate - how would we get a list of subtotals, when taxed and rolled up, that will equal the Total. Our existing solution has been to require a manual adjustment by hotel staff, which is error prone and burdensome. We'd like to have the prices distributed as evenly as possible, to keep accounting reports as close to accurate as possible.
It's kind of messy right now, and I can't get it to get the Total with the correct number of nights. I feel like there should be an algorithm for this, and I'm just not aware of it.
What I'm currently working on involves this tail-recursive function,
function iterationRate($amount, $days, $tax, $ary=array()){
$amount_left = $amount;
// we have subtotals already, subtract these from the desired total
if(count($ary) > 0) {
$amount_left = bcsub($amount, sumWithTaxes($ary, $tax));
}
// make sure it's a valid currency amount
$amount_left = bcround($amount_left, 2);
$tonights_rate = bcdiv($amount_left/$tax, $days);
// prevent negative charges / credit
if ($tonights_rate >= 0) {
array_push($ary, bcround($tonights_rate, 2));
}
else {
// remove an item from the array to give us more space
array_shift($ary);
$days = $days + 1;
}
if($days > 1) {
return iterationRate($amount, $days - 1, $tax, $ary);
}
$test_subtotals = sumWithTaxes($ary, $tax);
$diff = bcsub($test_subtotals, $amount);
// if we don't have our amount, remove the diff from an item and try again
if(abs($diff) > 0) {
$firstnight = array_shift($ary);
array_push($ary, bcsub($firstnight, $diff));
return iterationRate($amount, $days, $tax, $ary);
}
return $ary;
}
entire test here: http://sandbox.onlinephpfunctions.com/code/9eb43e95fad866ef7198a0e2bfa778edf326e091
Example
$300 for 3 nights, tax rate of 16.25%
Finding a subtotal from the total and dividing by nights won't work:
300/(1 + taxrate) = $258.0645, rounded to $258.06
$258.06 / 3 = 86.02
86.02 * taxrate = 99.9983, rounded to 99.99.
99.99 * 3 != 300.
Subtotal array that does work, [86.02, 86.02, 86.04]
2 * (86.02 * taxrate) + (86.04 * taxrate) = total
Difference between tax of sum and sum of taxes is inevitable. Don't even it up with other inconsistencies (price).
There are rules for order of tax calculation depending on country. I think that in most countries both methods are allowed and rounding diffrences are tolerated both ways - you need to check anyway. In this case it should be decided by your client or even better his accounting department (you might ask about the rules).
If you want to be precise in total tax then calculate it using sum as base (also there are two methods possible since you can use gross or net sum for it) - only calculation base will have accurate sum of its column.
If you want to be precise in sum of all columns then you'll lose accuracy of total tax rate.
Tips:
if you don't use any "money format" library then internally represent every money/price value as an integer (typecast instead of rounding) and format it for display only.
Don't calculate with tax rate both gross/net and tax - make second value derive as subtraction/addition of base and calculated value.
Ok, so I was making this far too complicated. I believe this is now the correct way to do it, just a simple loop counting up each night until we hit our amount.
function iterationRate($amount, $days, $tax, $ary=array())
{
$nightly_base = floor(bcdiv(bcdiv($amount, $tax), $days));
$rates = [];
for ($i = 0; $i < $days; $i++) {
array_push($rates, $nightly_base);
}
$idx = 0;
do {
$rates[$idx] = $rates[$idx] + 0.01;
$idx = ( ($idx >= count($rates) - 1) ? 0 : ( $idx + 1 ) );
} while ($amount > sumWithTaxes($rates, $tax));
return $rates;
}
http://sandbox.onlinephpfunctions.com/code/735f05c2fa0cfa416533f674bee1b02b247f9dd6
I would do something more simple.
The calculate function does the job. The rest is just a test.
function calculate($amount, $days, $taxes){
$per_day = round($amount/$days,2);
$last_day = $amount-$per_day*($days-1);
$total_taxes = $amount-$amount/$taxes;
$per_day_taxes = round($per_day-$per_day/$taxes,2);
$last_day_taxes = $total_taxes-$per_day_taxes*($days-1);
return (object) array("per_day"=>$per_day,
"last_day"=>$last_day,
"per_day_taxes"=>$per_day_taxes,
"last_day_taxes"=>$last_day_taxes,
"total_taxes"=>$total_taxes);
}
function runTests($tests) {
foreach($tests as $idx => $test) {
$values = calculate($test->amount,$test->days,$test->taxes);
printf("Test %d: Amount(%.02f) Days(%d) Tax(%.02f)\n", $idx, $test->amount, $test->days, $test->taxes);
printf("Rates the first %d days (incl. taxes):| %.02f, where taxes are:| %.02f\n", $test->days-1, $values->per_day, $values->per_day_taxes);
printf("Rate the last day (incl. taxes): | %.02f, where taxes is :| %.02f\n", $values->last_day, $values->last_day_taxes);
printf("Rates the first %d days (excl. taxes):| %.02f\n", $test->days-1, $values->per_day-$values->per_day_taxes);
printf("Rate the last day (excl. taxes): | %.02f\n", $values->last_day-$values->last_day_taxes);
printf("Total (excl. without) taxes: | %.02f\n", $test->amount-$values->total_taxes);
printf("Total taxes: | %.02f\n", $values->total_taxes);
echo "=======================\n";
echo "\n";
}
}
$tests = [
(object) array("amount"=>376, "days"=>22, "taxes"=>1),
(object) array("amount"=>376, "days"=>22, "taxes"=>1.1625),
(object) array("amount"=>1505, "days"=>25, "taxes"=>1.09),
(object) array("amount"=>380, "days"=>22, "taxes"=>1.1),
];
runTests($tests);
Result:
Test 0: Amount(376.00) Days(22) Tax(1.00)
Rates the first 21 days (incl. taxes):| 17.09, where taxes are:| 0.00
Rate the last day (incl. taxes): | 17.11, where taxes is :| 0.00
Rates the first 21 days (excl. taxes):| 17.09
Rate the last day (excl. taxes): | 17.11
Total (excl. without) taxes: | 376.00
Total taxes: | 0.00
=======================
Test 1: Amount(376.00) Days(22) Tax(1.16)
Rates the first 21 days (incl. taxes):| 17.09, where taxes are:| 2.39
Rate the last day (incl. taxes): | 17.11, where taxes is :| 2.37
Rates the first 21 days (excl. taxes):| 14.70
Rate the last day (excl. taxes): | 14.74
Total (excl. without) taxes: | 323.44
Total taxes: | 52.56
=======================
Test 2: Amount(1505.00) Days(25) Tax(1.09)
Rates the first 24 days (incl. taxes):| 60.20, where taxes are:| 4.97
Rate the last day (incl. taxes): | 60.20, where taxes is :| 4.99
Rates the first 24 days (excl. taxes):| 55.23
Rate the last day (excl. taxes): | 55.21
Total (excl. without) taxes: | 1380.73
Total taxes: | 124.27
=======================
Test 3: Amount(380.00) Days(22) Tax(1.10)
Rates the first 21 days (incl. taxes):| 17.27, where taxes are:| 1.57
Rate the last day (incl. taxes): | 17.33, where taxes is :| 1.58
Rates the first 21 days (excl. taxes):| 15.70
Rate the last day (excl. taxes): | 15.75
Total (excl. without) taxes: | 345.45
Total taxes: | 34.55
=======================
example on sandbox
I have a booking system where users can book a room at any time, and for any number of continuous days. The booking is charged depending on the number of minutes the room is in use.
In the booking system, the start and end time is represented by two timestamps. e.g.
start_time = 1397124000
end_time = 1397129400
From this I can calculate the number of seconds booked, and therefore calculate a charge.
The problem I have is that I'd like to calculate a 50% discount on any bookings made out of peak times - before 8am, and after 6pm. Bookings can be made across these times (i.e. 7am-9am), and so the appropriate proportion should be discounted (50% discount on the 7-8am portion of the 7-9am booking).
My code for calculating this is getting extremely confusing and complicated, as there are several scenarios:
The booking starts and ends during a discount period (e.g. 3am-7am - 4 hours discounted)
The booking starts during a discount, but ends afterwards (e.g. 7am-9am - 1 hour discounted)
The booking starts and ends during a period of non-discount (10am-5pm - no discount)
The booking starts during a period of non-discount, but ends afterwards (5pm-10pm - 1 hour discounted)
The booking spans an entire before-during-after discount period (2am-10pm - 10 hours discounted) or even more complicated (2am on Day 1 to 10pm on Day 5 - 50 hours discounted).
At the moment trying to work out what proportion of a booking is during these pre-8am, post-6pm discount period when only provided with a start and end timestamp is very difficult.
My code is a very large series of if and else statements testing against start and end times, but it is not robust against bookings that span more than one day and is otherwise very messy.
I'm looking for a method that can easily calculate what proportion of a booking is within a discounted time, that accounts for all possible scenarios. A difficult problem!
After pondering many options, the following solution eventually occurred to me.
I wrote a function to move through the booking start and end times in 30 minute intervals, check to see if each time was within a discounted period:
function discount_period($timestamp) {
$lowerTime = mktime (8,0,0,date("n", $timestamp), date("j", $timestamp), date("Y", $timestamp));
$upperTime = mktime (18,0,0,date("n", $timestamp), date("j", $timestamp), date("Y", $timestamp));
if (($timestamp < $lowerTime) || ($timestamp >= $upperTime)) {
return true;
}
else {
return false;
}
}
$discountmins = 0;
$nondiscountmins = 0;
for ($i = strtotime($billingResult->start_time); $i < strtotime($billingResult->end_time); $i += 1800) {
if (discount_period($i)) {
// This is within a discount period of at least 30 minutes
$discountmins += 30;
}
else {
$nondiscountmins +=30;
}
}
$discountedcost = ((($discountmins / 60) * $billingResult->cost_per_hr) * 0.5) / 100;
$nondiscountcost = (($nondiscountmins / 60) * $billingResult->cost_per_hr) / 100;
So it essentially checks to see if the next 30 minute window is within a discount period - if it is, it increments the discounted minute counter, or the non-discounted minute counter if not. At the end it them merely calculates the costs based on the number of minutes.
Heres the code I have at the moment, its all working as intended, however, the cumulative total isn't working, and I'm positive I'm doing something absolutely stupid.
assume period = 20
assume inflation = 3
assume nightlycost = 100
assume nights = 7
$yearlycost = $nightlycost*$nights;
while ($period > 0) {
$period = $period-1;
$yearlyincrease = ($inflation / 100) * $yearlycost;
$nightlyincrease = ($inflation / 100) * $nightlycost;
$nightlycost = $nightlycost + $nightlyincrease;
$yearlycost = ($yearlycost + $yearlyincrease) + $yearlycost;
}
Result:
Nightly Hotel Rate in 20 years: $180.61 - <?php echo round($nightlycost, 2); ?> correct
Weekly Hotel Rate in 20 years: $1264.27 - <?php echo round($nightlycost, 2) * 7; ?> correct
Total cost to you over 20 years: $988595884.74 - <?php echo round($yearlycost, 2); ?> incorrect
Everything outputs correctly and as expected, except for the yearly cumulative cost. It should take the previous yearly cost and add that years cost+inflation.
Example: first year is 700, so second year should be 700 + 700 + 21 (21 is 3%, the inflation for that year). Second year cumulative total is thus: 1421. Third year will be 1421 + 721 (last years total) + 3% of 721.
Hopefully this is clear enough for you to see where I'm going wrong. Thanks!
I find it hard to understand where your code goes wrong, but my intuition is that the last line in your loop body should have a multiplication.
Basically, you have a base cost for period 0. Then you want to calculate the cumulative cost given inflation after X years. That cost is (pseudocode)
base = nightlycost + nights
infl = 1.03
cumulative = base + base*infl + base*infl^2 + base*infl^3 + ... + base*infl^periods
The last expression can be simplified to
cumulative = base*((1-infl^periods)/(1-infl))
(This holds according to Eq. 4 here: http://mathworld.wolfram.com/ExponentialSumFormulas.html)
Example:
$base = 100*7;
$infl = 1.03; // 3% of inflation/year
$periods = 2;
$cumulative = $base * (1-pow($infl, $periods))/(1-$infl);
print "Cumulative cost after $periods is $cumulative\n";
// Let's try with three periods.
$periods = 3;
$cumulative = $base * (1-pow($infl, $periods))/(1-$infl);
print "Cumulative cost after $periods is $cumulative\n";
Output:
Cumulative cost after 2 is 1421
Cumulative cost after 3 is 2163.63