从总计生成小计数组的最佳方法

时间:2017-02-27 22:35:21

标签: php algorithm tax

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

我们的客户租用酒店房间,出于酒店会计原因,每晚都会将房费加到对开页中,而不是一次性全部。我一直致力于修复我们的固定费率计算模块,该模块完全被破坏了。

问题的一个例子是 - 客户同意支付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 *税率)=总计

3 个答案:

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

example on sandbox

答案 1 :(得分:0)

税额税额之和之间的差异是不可避免的。不要把它与其他不一致(价格)混在一起。

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

如果您想要精确计算总税额,那么使用总和作为基数来计算它(也可以使用两种方法,因为您可以使用总和或净额) - 只有计算基数才能有其列的准确总和。 / p>

如果您希望精确归类所有列,那么您将失去总税率的准确性。

提示:

  • 如果您不使用任何&#34;货币格式&#34;然后,库在内部将每个货币/价格值表示为整数(类型转换而不是舍入)并将其格式化以仅显示。
  • 不要用税率计算毛/净和税 - 将第二个值作为基数和计算值的减法/加法得出。

答案 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