-
-
Notifications
You must be signed in to change notification settings - Fork 825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CRM-19908 - Fundamental Fixes for TaxMath Calculations 4.7 #9711
Conversation
@@ -475,7 +475,8 @@ public static function getFirstLastDetails($contactID) { | |||
*/ | |||
public static function calculateTaxAmount($amount, $taxRate) { | |||
$taxAmount = array(); | |||
$taxAmount['tax_amount'] = round(($taxRate / 100) * CRM_Utils_Rule::cleanMoney($amount), 2); | |||
// There can not be any rounding at this stage - as this is prior to quantity multiplication | |||
$taxAmount['tax_amount'] = ($taxRate / 100) * CRM_Utils_Rule::cleanMoney($amount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fundamentally wrong to round here - as at this stage - in this basic Utils function quantity has not yet been taken into account. Rounding here causes incorrect Sales Tax calculations.
* @return int|void | ||
* tax rate | ||
*/ | ||
public static function calculateTaxRate($lineItemId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function needs to be removed - it is fundamentally wrong to calculate Tax Rate using Tax Amount - which was calculated using Tax Rate.
if ($field['html_type'] == 'Text') { | ||
$taxAmount = $field['options'][$optionValueId]['tax_amount'] * $lineItem[$optionValueId]['qty']; | ||
$taxAmount = round($field['options'][$optionValueId]['tax_amount'] * $lineItem[$optionValueId]['qty'], 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now we can round - we have now multiplied by quantity.
@@ -78,7 +78,7 @@ | |||
{if $getTaxDetails} | |||
<td class="right">{$line.line_total|crmMoney}</td> | |||
{if $line.tax_rate != "" || $line.tax_amount != ""} | |||
<td class="right">{$taxTerm} ({$line.tax_rate|string_format:"%.2f"}%)</td> | |||
<td class="right">{$taxTerm} ({$line.tax_rate|string_format:"%.3f"}%)</td> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some Tax Rates are 3 digits [like e.g. in QEO: 9.975%] - so let's not constrain them to 2 digits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's change {$line.tax_rate|string_format:"%.3f"} to {$line.tax_rate|rtrim:'0'} as per http://stackoverflow.com/questions/26565650/smarty-trim-zeros-from-the-end-of-string-float-value both here and in 4.6?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rtrim:'0' would that not make 10% ->1%?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should remove string_format and since it would be handled in php at https://github.com/civicrm/civicrm-core/pull/9711/files#r98392553
I'm not seeing an undefined index in the Drupal dblog when I generate the PDF invoice - but will dig in and figure out why the test says there is. Ok found it - retesting now. |
Failure in api call for CustomValue create: DB Error: syntax error |
Tangential to your problem but maybe relevant to the Jenkins comments:
|
jenkins test this please |
Jenkins re test this please |
@civicrm-builder test this please |
Sorry but these failures have nothing to do w/ my PR: |
I've posted the how to set up a Sales Tax etc on: the 4.6 PR: #9710 (comment) |
Jenkins re test this please |
@agileware have undertaken to review this next week - in time for 4.6.17 |
Yay! Thanks @agileware |
I've add a PHPUnit Test - and I've debug/tested the test - it will fail if pre-mature rounding is re-introduced |
/** | ||
* Test Tax Amount is calculated properly when using PriceSet with Field Type = Text/Numeric Quantity | ||
*/ | ||
public function testSubmitContributionPageWithPriceSetQuantity() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks good - I'm glad you found a way to actually test the page rather than just a true unit test as I think it's more reassuring
For reference, here's a link to my testing results on the 4.6-LTS version of this PR: #9710 (comment) |
I just want to note than in resolving this we should figure out how it interacts with #9685 #9684 #9683 #9682 #9589 #8884 - which are the other open PRS (in addition to #9710 ) @seamuslee001 @pradpnayak can you determine whether you think this supercedes your PRs or otherwise how your PRs relate. |
else { | ||
// There is no Tax Rate associated with this Financial Type | ||
$lineItems[$dao->id]['tax_rate'] = FALSE; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move line 301-309 to a function calculateTaxRate() ? So that we can reuse the function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not to a function called calculateTaxRate - part of the fundamental issue I addressed was that TaxRate should -never- be calculated [so I removed the existing function calculateTaxRate]. If should simply be retrieved. I can move this into a functional called retrieveTaxRate; That would be cleaner;
$taxRates = CRM_Core_PseudoConstant::getTaxRates(); | ||
if (isset($lineItems[$dao->id]['financial_type_id']) && array_key_exists($lineItems[$dao->id]['financial_type_id'], $taxRates)) { | ||
// We are close to output/display here - so apply some rounding at output/display level - to not show Tax Rate in all 8 decimals | ||
$lineItems[$dao->id]['tax_rate'] = round($taxRates[$lineItems[$dao->id]['financial_type_id']], 3); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
civicrm_financial_account.tax_rate allows 8 precision (number of digits after the decimal point), so we should not round this off to 3. If the concern is with tax rate like 10.40300000 or 10.0558000, then i think casting would fix the problem
eg.
$lineItems[$dao->id]['tax_rate'] = (float) $taxRates[$lineItems[$dao->id]['financial_type_id']];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok - let me try that. I have a couple scenarios here I can test that with.
'label' => 'Printing Rights', | ||
)); | ||
$lineItemId = $lineItem['id']; | ||
$lineItem_TaxAmount = round($lineItem['values'][$lineItemId]['tax_amount'], 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we round tax_amount? I guess its stored in 2 digit after decimal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - and some payment processors appear to only process amounts with 2 decimals.
$financialType = $this->createFinancialType(); | ||
$financialTypeId = $financialType['id']; | ||
// This function sets the Tax Rate at 10% - it currently has no way to pass Tax Rate into it - so let's work with 10% | ||
$financialAccount = $this->relationForFinancialTypeWithFinancialAccount($financialType['id'], 5); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
relationForFinancialTypeWithFinancialAccount() has only single arg. You will need to change in function to accept second arg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know - but this 10% will do for this test - that comment in the test makes it clear to anyone looking at the test that the TaxRate is 10% - otherwise the math/logic below is difficult to follow.
QA'd PR, looks perfect. The calculation for tax_rate should not be rounded to 3 precision as civicrm_financial_account.tax_rate allows precision till 8. I think we should also add some unit tests to check for tax_rate values. |
Again TaxRate is retrieved - so no need for a basic PHP Unit level test - but perhaps an invoice level test is feasible. Comparing the output on the PDF to the TaxRate that the admin had used to set up his/her Financial Accounts. |
Just a FYI. We're testing this today manually, using about ~13 test cases |
Review of CRM-19908 - Fundamental Fixes for TaxMath Calculations 4.7. #9711 The following tests were performed on a CiviCRM 4.7.15 installation before and after CRM-19908 patch is applied. Test Case: Donation, credit card paymentSet up:
Method 1:
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Donation, pending pay laterSet up:
Method 1:
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Event registration, credit card paymentSet up:
Method 1:
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Membership, credit card paymentSet up:
Method 1
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Event, pending pay laterTest based on bug report, #23973#note-12 and #23973#note-10 Set up:
Method 1:
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Membership, pending pay laterTest based on bug report, #23973#note-12 and #23973#note-10 Set up:
Method 1:
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Recording a contribution using credit cardMethod:
Result:
Test Case Result:
Test Case: Event registration credit card payment and price setSet up:
Method:
Result:
Test Case Result:
Test Case: Membership, credit card payment and price setSet up:
Method:
Result:
Test Case Result:
Test Case: Donation, credit card payment and price setSet up:
Method:
Result:
Test Case Result:
Test Case: Donation, pending pay later and price setSet up:
Method 1:
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Event, pending pay later and price setSet up:
Method 1:
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Membership, pending pay later and price setSet up:
Method 1:
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Back-end Membership, credit card paymentSet up:
Method:
Result:
Test Case Result:
Test Case: Back-end Membership, pending pay laterSet up:
Method 1:
Result 1:
Test Case Result:
Method 2:
Result 2:
Test Case Result:
Test Case: Add Membership from search result and override membership amountSet up:
Method:
Result:
Test Case Result:
Test Case: Renew Membership from search result and override membership amountSet up:
Method:
Result:
Test Case Result:
|
Thanks for drawing up such a thorough test plan |
Wow! Merci! So would it be correct to say that you've found (thus far) is that this PR fixes many cases -but not everything- known to TaxMath and it doesn't appear to break anything (that was previously working), right? |
The world as known by TaxMath.... |
"Tax rates are correct but each time you click edit and then save, the tax amount increments." I think you have misinterpreted this as an incorrect result - but from personal experience I can tell you this is exactly how the tax system works. |
@eileenmcnaughton thanks. It would be great to convert these to unit tests and automate. I think this would be very worthwhile, more auto-test coverage the better. |
Yes - more tax coverage would be better! Most of these cases have some test coverage - but generally not on the tax calculation - so in some cases it is just adding checks to existing tests |
@KarinG @eileenmcnaughton we are still finalising the test results and changing this report. So please treat it as draft at this stage. We had to transpose the wiki-text from Redmine (ours) to Github (here) and there were some errors. So still fixing that up. |
(also note Karin did add some tests in this PR) |
Correct, we haven't come across any new problems at this stage. We're still testing. |
@eileenmcnaughton we're done and the results of the testing is posted
I assume this is a poke at tax in general, a joke right? Because clicking the "Save" button and seeing the taxed amount increase dramatically each time is in itself quite a funny experience (as a developer). |
yes - it was intended as a joke. So - merge this & create a JIRA ticket for fairer taxation? |
If you merge this I'll hunt down the unfair double taxation upon edit/save. |
Deal. |
Ok I better get on with it then! |
OK started: created https://issues.civicrm.org/jira/browse/CRM-19966 - @agileware - could you please go through the open JIRA TaxMath issues and close which ones you believe are now fixed? I'll dig in on the Tax Fairness issue. |
@KarinG happy too, but our JIRA permissions are insufficient to do that. Would be great if we have permission to link JIRA issues to each other as well. It's a real bugger having to search each time for relateds. Alternately, if an EPIC could be set up then all the tax related issues could be grouped that way. |
Ah ok - just give me or @eileenmcnaughton the JIRA issue numbers that need closing - and we can do that. Perhaps Eileen knows how you'd go about getting additional JIRA permissions. |
This is really fantastic @KarinG & @agileware - we can go forward now with a lot more confidence in tax in Civi. I've added a couple of JIRA groups to your Agileware account - not sure if it is what required. @KarinG looks like you have the same JIRA permissions as me - I added some groups at this URL https://issues.civicrm.org/jira/secure/admin/user/UserBrowser.jspa |
CRM-19908 - Fundamental Fixes for TaxMath Calculations 4.7
Please see https://issues.civicrm.org/jira/browse/CRM-19908 for full description/rationale.
This PR addresses two fundamentally wrong operations:
Purpose of these edits - three fold: to make the minimal edits required to:
Note: I've not added a PHP Unit test for 1) to add a test would require edits to CiviCRM Core (contribution.php and confirm.php) itself [right now you can't test line_items w/ quantity within the TEST framework] - that in itself it not without risk. I strongly believe we need to get the fundamentals right first.
This is the BEFORE: note Tax Rate is 5.01% and Tax Amount on that lineItem is $15.30 - this is incorrect:
This is the AFTER: note Tax Rate is 5% and Tax Amount on that lineItem is $15.26 - this is correct: