0

这是一个人为的问题,但对我们的行业来说是一个真实的问题。

我们客户租用的酒店房间,出于酒店会计的原因,房费每晚都计入账本,而不是一次全部计入。我一直在努力修复我们的统一费率计算模块,该模块已完全损坏。

问题的一个例子是 - 客户同意为 30 晚住宿支付 376 美元。但是,376 美元不是 1 次记入账簿,而是(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 * 税率 = 99.9983,四舍五入为 99.99。99.99 * 3 != 300。

有效的小计数组,[86.02, 86.02, 86.04] 2 * (86.02 * taxrate ) + (86.04 * taxrate) = total

4

3 回答 3

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
=======================

沙盒上的示例

于 2017-02-28T00:13:14.210 回答
0

税额和税额的区别不可避免的。甚至不要与其他不一致(价格)。

根据国家/地区的不同,有税收计算顺序的规则。我认为在大多数国家/地区,这两种方法都是允许的,并且两种方法都可以容忍舍入差异——无论如何你都需要检查。在这种情况下,它应该由您的客户甚至更好的会计部门决定(您可能会询问规则)。

如果您想精确计算总税额,则使用总和作为基数进行计算(也有两种可能的方法,因为您可以使用总和或净总和) - 只有计算基数才会具有其列的准确总和。

如果您想精确计算所有列的总和,那么您将失去总税率的准确性。

提示:

  • 如果您不使用任何“货币格式”库,则在内部将每个货币/价格值表示为整数(类型转换而不是四舍五入)并将其格式化以仅用于显示。
  • 不要用总/净和税的税率计算 - 使第二个值作为基值和计算值的减法/加法得出。
于 2017-02-28T03:31:40.963 回答
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

于 2017-02-28T04:11:55.880 回答