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