这是一个人为的问题,但对我们这个行业来说却是一个真正的问题。
我们的客户租用酒店房间,出于酒店会计原因,每晚都会将房费加到对开页中,而不是一次性全部。我一直致力于修复我们的固定费率计算模块,该模块完全被破坏了。
问题的一个例子是 - 客户同意支付376美元的30晚住宿费用。但是,376美元没有被放在书上一次,它是每晚的住宿对开(<$ 376 /晚)。
因此,给定整个住宿期间的预期总数,一些晚数和税率 - 我们如何获得一个小计列表,当征税和累计时,将等于总计。我们现有的解决方案是需要酒店员工进行手动调整,这容易出错且繁琐。我们希望尽可能均匀地分配价格,以使会计报告尽可能接近准确。
现在它有点乱,我无法通过正确的夜数获得总计。我觉得应该有一个算法,我只是不知道它。
我目前正在研究的是这种尾递归函数,
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;
}
此处完整测试:http://sandbox.onlinephpfunctions.com/code/9eb43e95fad866ef7198a0e2bfa778edf326e091
3晚300美元,税率16.25%
从总数中找出小计并除以晚上的工作: 300 /(1 +税率)= 258.0645美元,四舍五入至258.06美元 $ 258.06 / 3 = 86.02
86.02 * taxrate = 99.9983,四舍五入至99.99。 99.99 * 3!= 300。
工作的小计数组,[86.02,86.02,86.04] 2 *(86.02 *税率)+(86.04 *税率)=总计
答案 0 :(得分:0)
我会做一些更简单的事情 计算功能完成工作。其余的只是一个考验。
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);
结果:
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
=======================
答案 1 :(得分:0)
税额与税额之和之间的差异是不可避免的。不要把它与其他不一致(价格)混在一起。
根据国家/地区有税收计算顺序的规则。我认为在大多数国家/地区都允许这两种方法,并且两种方式都可以容忍舍入差异 - 无论如何都需要检查。在这种情况下,它应该由您的客户决定,甚至更好地由他的会计部门决定(您可能会询问规则)。
如果您想要精确计算总税额,那么使用总和作为基数来计算它(也可以使用两种方法,因为您可以使用总和或净额) - 只有计算基数才能有其列的准确总和。 / p>
如果您希望精确归类所有列,那么您将失去总税率的准确性。
提示:
答案 2 :(得分:0)
好的,所以我觉得这太复杂了。我相信这是现在正确的方法,只需要一个简单的循环计算每晚,直到我们达到我们的金额。
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