Skip to content
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

[WIP]CRM-19585, Sales tax fixes #9340

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 95 additions & 18 deletions CRM/Contribute/BAO/Contribution.php
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@ public static function calculateMissingAmountParams(&$params, $contributionID) {
$params['fee_amount'] = 0;
}
}
if (!isset($params['net_amount'])) {
// recalculate net amount if it is not set or if tax amount is set
if (!isset($params['net_amount']) || isset($params['tax_amount'])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we torture our poor users by asking them to enter net_amount rather than always calculating it from fee amount, tax amount etc - has anyone else felt the love of altering total & then failing to save because net amount fails validation.

Erm that was a tangent

if (!$contributionID) {
$params['net_amount'] = $params['total_amount'] - $params['fee_amount'];
}
Expand Down Expand Up @@ -3218,7 +3219,7 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues =
$params['trxnParams'] = $trxnParams;

if (!empty($params['prevContribution'])) {
$updated = FALSE;
$updated = $ignoreChangeAmount = FALSE;
$params['trxnParams']['total_amount'] = $trxnParams['total_amount'] = $params['total_amount'] = $params['prevContribution']->total_amount;
$params['trxnParams']['fee_amount'] = $params['prevContribution']->fee_amount;
$params['trxnParams']['net_amount'] = $params['prevContribution']->net_amount;
Expand Down Expand Up @@ -3263,9 +3264,9 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues =
}
}
self::updateFinancialAccounts($params, 'changeFinancialType');
/* $params['trxnParams']['to_financial_account_id'] = $trxnParams['to_financial_account_id']; */
$params['financial_account_id'] = $newFinancialAccount;
$params['total_amount'] = $params['trxnParams']['total_amount'] = $params['trxnParams']['net_amount'] = $trxnParams['total_amount'];
list($changeFTAmount, $ignoreChangeAmount) = self::calcluateFTChangeAmount($params, $trxnParams['total_amount']);
$params['total_amount'] = $params['trxnParams']['total_amount'] = $params['trxnParams']['net_amount'] = $changeFTAmount;
self::updateFinancialAccounts($params);
$params['trxnParams']['to_financial_account_id'] = $trxnParams['to_financial_account_id'];
$updated = TRUE;
Expand Down Expand Up @@ -3341,7 +3342,7 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues =
$params['trxnParams']['net_amount'] = CRM_Utils_Array::value('net_amount', $params);
$params['trxnParams']['total_amount'] = $trxnParams['total_amount'] = $params['total_amount'] = $totalAmount;
$params['trxnParams']['trxn_id'] = $params['contribution']->trxn_id;
if (isset($totalAmount) &&
if (!$ignoreChangeAmount && isset($totalAmount) &&
$totalAmount != $params['prevContribution']->total_amount
) {
//Update Financial Records
Expand Down Expand Up @@ -3444,6 +3445,12 @@ public static function updateFinancialAccounts(&$params, $context = NULL, $skipT
}
if ($context == 'changedAmount' || $context == 'changeFinancialType') {
$itemAmount = $params['trxnParams']['total_amount'] = $params['trxnParams']['net_amount'] = $params['total_amount'] - $params['prevContribution']->total_amount;
if (isset($params['prevContribution']->tax_amount)) {
Copy link
Member

@omarabuhussein omarabuhussein Nov 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about the logic here since I don't have enough context .. but shouldn't this be :

if (isset($params['tax_amount'])) {

Copy link
Contributor

@JoeMurray JoeMurray Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pradpnayak @Edzelopez could you reply to each comment explaining how it is addressed or why you don't think it should be? It appears the same changes have been resubmitted as a new PR but the commented code may still appear, eg https://github.com/civicrm/civicrm-core/pull/9682/files#diff-7a5b0e2d131dabc49178460fc63328c0R3453

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @omarabuhussein

The logic here (and below) is intentional. This code here handles the case where a contribution with a taxable financial type is edited to change the financial type to another taxable/non-taxable one. In such a case, previously, the taxes were not subtracted/added to the total amount. The reason why the conditions look reversed is that the taxes usually have reversed mathematical signs. So for example, if the previous total amount was $110 with a contribution of $100 and $10 tax, on changing this to a non-taxable financial type, we would need to subtract the $10. For this reason, we have checked if there was a previous tax applied to a contribution and subtracted it from the new total amount. Similarly, for the next condition, if we have changed the financial type to a new taxable financial type, say amounting to 20% tax, we will need to first subtract the previous tax amount from the contribution to avoid calculating 20% on $110 rather than $100.

HTH,
Edsel

$itemAmount -= CRM_Utils_Array::value('tax_amount', $params, 0);
}
if (isset($params['tax_amount'])) {
Copy link
Member

@omarabuhussein omarabuhussein Nov 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here .. shouldn't this be

if (isset($params['prevContribution']->tax_amount))

so basically flip the if condition with the one above it ... or am I missing something ?

$itemAmount += $params['prevContribution']->tax_amount;
}
}
if ($context == 'changedStatus') {
//get all the statuses
Expand Down Expand Up @@ -3546,6 +3553,7 @@ public static function updateFinancialAccounts(&$params, $context = NULL, $skipT
}
}
$trxn = CRM_Core_BAO_FinancialTrxn::create($params['trxnParams']);
$previousLineItem = CRM_Price_BAO_LineItem::getLineItemsByContributionID($params['prevContribution']->id);
$params['entity_id'] = $trxn->id;
if ($context != 'changePaymentInstrument') {
$itemParams['entity_table'] = 'civicrm_line_item';
Expand All @@ -3568,15 +3576,15 @@ public static function updateFinancialAccounts(&$params, $context = NULL, $skipT
if ($context == 'changeFinancialType' || self::isContributionStatusNegative($params['contribution']->contribution_status_id)) {
$diff = -1;
}
if (!empty($params['is_quick_config'])) {
$amount = $itemAmount;
if (!$amount) {
$amount = $params['total_amount'];
}
}
else {
$amount = $diff * $fieldValues['line_total'];
}

// calculate financial item amount
$amountParams = array(
'diff' => $diff,
'line_total' => $fieldValues['line_total'],
'previous_line_total' => CRM_Utils_Array::value('line_total', CRM_Utils_Array::value($fieldValues['id'], $previousLineItem)),
'item_amount' => $itemAmount,
);
$amount = self::calcluateFinancialItemAmount($params, $amountParams, $context);

$itemParams = array(
'transaction_date' => $receiveDate,
Expand All @@ -3593,13 +3601,25 @@ public static function updateFinancialAccounts(&$params, $context = NULL, $skipT
$params['line_item'][$fieldId][$fieldValueId]['deferred_line_total'] = $amount;
$params['line_item'][$fieldId][$fieldValueId]['financial_item_id'] = $financialItem->id;

if ($fieldValues['tax_amount']) {
$taxAmount = CRM_Utils_Array::value('tax_amount', $fieldValues);
$taxFinancialType = $fieldValues['financial_type_id'];
if ($context == 'changeFinancialType') {
$taxAmount = $previousLineItem[$fieldValues['id']]['tax_amount'];
$taxFinancialType = $previousLineItem[$fieldValues['id']]['financial_type_id'];
}

if ($taxAmount) {
$invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
$taxTerm = CRM_Utils_Array::value('tax_term', $invoiceSettings);
$itemParams['amount'] = $diff * $fieldValues['tax_amount'];
if ($context == 'changedAmount') {
$itemParams['amount'] = $diff * ($fieldValues['tax_amount'] - $previousLineItem[$fieldValues['id']]['tax_amount']);
}
else {
$itemParams['amount'] = $diff * $taxAmount;
}
$itemParams['description'] = $taxTerm;
if ($fieldValues['financial_type_id']) {
$itemParams['financial_account_id'] = self::getFinancialAccountId($fieldValues['financial_type_id']);
if ($taxFinancialType) {
$itemParams['financial_account_id'] = self::getFinancialAccountId($taxFinancialType);
}
CRM_Financial_BAO_FinancialItem::create($itemParams, NULL, $trxnIds);
}
Expand Down Expand Up @@ -5336,4 +5356,61 @@ public static function recordAlwaysAccountsReceivable(&$trxnParams, $contributio
$trxnParams['from_financial_account_id'] = $params['to_financial_account_id'];
}

/**
* Calculate amount when Financial type for contribution is changed.
*
* @param array $params
* contribution params
* @param float $totalAmount
* financial trxn total amount
*
* @return array
*/
public static function calcluateFTChangeAmount($params, $totalAmount) {
$changeFTAmount = $totalAmount;
$ignoreChangeAmount = FALSE;
if (isset($params['prevContribution']->total_amount) || isset($params['tax_amount'])) {
$taxAmount = CRM_Utils_Array::value('tax_amount', $params, 0);
$changesinTaxAmount = $totalAmount - $params['prevContribution']->total_amount + $params['prevContribution']->tax_amount - $taxAmount;

if ($changesinTaxAmount == 0) {
$ignoreChangeAmount = TRUE;
$changeFTAmount = $totalAmount;
}
}

return array($changeFTAmount, $ignoreChangeAmount);
}

/**
* Calculate financial item amount when contribution is updated.
*
* @param array $params
* contribution params
* @param array $amountParams
*
* @param string $context
*
* @return float
*/
public static function calcluateFinancialItemAmount($params, $amountParams, $context) {
if (!empty($params['is_quick_config'])) {
$amount = $amountParams['item_amount'];
if (!$amount) {
$amount = $params['total_amount'];
if ($context === NULL) {
$amount -= CRM_Utils_Array::value('tax_amount', $params, 0);
}
}
}
else {
$amount = $amountParams['line_total'];
if ($context == 'changedAmount') {
$amount -= $amountParams['previous_line_total'];
}
$amount *= $amountParams['diff'];
}
return $amount;
}

}
13 changes: 2 additions & 11 deletions CRM/Contribute/Form/Contribution.php
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ public function setDefaultValues() {
}

if (isset($defaults['net_amount'])) {
$defaults['net_amount'] = CRM_Utils_Money::format($defaults['net_amount'], NULL, '%a');
$defaults['net_amount'] = CRM_Utils_Money::format($defaults['net_amount'] - $defaults['tax_amount'], NULL, '%a');
}

if ($this->_contributionType) {
Expand Down Expand Up @@ -970,16 +970,7 @@ public static function formRule($fields, $files, $self) {

if (!empty($fields['total_amount']) && (!empty($fields['net_amount']) || !empty($fields['fee_amount']))) {
$sum = CRM_Utils_Rule::cleanMoney($fields['net_amount']) + CRM_Utils_Rule::cleanMoney($fields['fee_amount']);
// For taxable contribution we need to deduct taxable amount from
// (net amount + fee amount) before comparing it with total amount
if (!empty($self->_values['tax_amount'])) {
$componentDetails = CRM_Contribute_BAO_Contribution::getComponentDetails($self->_id);
if (!(CRM_Utils_Array::value('membership', $componentDetails) ||
CRM_Utils_Array::value('participant', $componentDetails))
) {
$sum = CRM_Utils_Money::format($sum - $self->_values['tax_amount'], NULL, '%a');
}
}

if (CRM_Utils_Rule::cleanMoney($fields['total_amount']) != $sum) {
$errors['total_amount'] = ts('The sum of fee amount and net amount must be equal to total amount');
}
Expand Down
21 changes: 18 additions & 3 deletions CRM/Financial/BAO/FinancialItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,28 @@ public static function checkContactPresent($contactIds, &$error) {
* @return object CRM_Core_DAO
*/
public static function getPreviousFinancialItem($entityId, $entityTable = 'civicrm_line_item') {
$liabilityAccountType = CRM_Core_OptionGroup::getValue('financial_account_type', 'Liability', 'name');
$queryParams = array(
1 => array($entityId, 'Integer'),
2 => array($entityTable, 'String'),
3 => array($liabilityAccountType, 'Integer'),
);
$query = 'SELECT id, description, status_id, financial_account_id
FROM civicrm_financial_item
WHERE entity_id = %1 AND entity_table = %2 ORDER BY id DESC LIMIT 1';
$query = 'SELECT cfi.id, cfi.description, cfi.amount, cfi.financial_account_id, cfi.status_id
FROM `civicrm_financial_item` cfi
INNER JOIN civicrm_entity_financial_trxn ceft
ON ceft.entity_id = cfi.id AND ceft.entity_table = "civicrm_financial_item"
INNER JOIN civicrm_financial_trxn cft ON cft.id = ceft.financial_trxn_id
INNER JOIN civicrm_financial_account cfa ON cfa.id = cfi.financial_account_id
WHERE cfi.entity_table = %2 AND cfi.entity_id = %1 AND
CASE
Copy link
Member

@omarabuhussein omarabuhussein Nov 20, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this query complete ? , because I don't see any meaning for this case statement .. whenever any case get satisfied it will end up either as ( AND 1 ) or (AND 0) and either way it has no meaning since you are not comparing or using it in anything .

WHEN cfa.financial_account_type_id = %3
AND cfi.financial_account_id = from_financial_account_id
THEN 1
WHEN cfa.financial_account_type_id = %3
THEN 0
ELSE 1
END
ORDER BY cfi.id DESC LIMIT 1';
$prevFinancialItem = CRM_Core_DAO::executeQuery($query, $queryParams);
$prevFinancialItem->fetch();
return $prevFinancialItem;
Expand Down
6 changes: 4 additions & 2 deletions CRM/Price/BAO/PriceField.php
Original file line number Diff line number Diff line change
Expand Up @@ -590,11 +590,13 @@ public static function addQuickFormElement(
* Include inactive options.
* @param bool $reset
* Discard stored values.
* @param bool $isDefaultContributionPriceSet
* Discard tax amount calculation for price set = default_contribution_amount.
*
* @return array
* array of options
*/
public static function getOptions($fieldId, $inactiveNeeded = FALSE, $reset = FALSE) {
public static function getOptions($fieldId, $inactiveNeeded = FALSE, $reset = FALSE, $isDefaultContributionPriceSet = FALSE) {
static $options = array();
if ($reset) {
$options = array();
Expand All @@ -613,7 +615,7 @@ public static function getOptions($fieldId, $inactiveNeeded = FALSE, $reset = FA
// ToDo - Code for Hook Invoke

foreach ($options[$fieldId] as $priceFieldId => $priceFieldValues) {
if (isset($priceFieldValues['financial_type_id']) && array_key_exists($priceFieldValues['financial_type_id'], $taxRates)) {
if (isset($priceFieldValues['financial_type_id']) && array_key_exists($priceFieldValues['financial_type_id'], $taxRates) && !$isDefaultContributionPriceSet) {
$options[$fieldId][$priceFieldId]['tax_rate'] = $taxRates[$priceFieldValues['financial_type_id']];
$taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($priceFieldValues['amount'], $options[$fieldId][$priceFieldId]['tax_rate']);
$options[$fieldId][$priceFieldId]['tax_amount'] = $taxAmount['tax_amount'];
Expand Down
38 changes: 20 additions & 18 deletions CRM/Price/BAO/PriceSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,11 @@ public static function getSetDetail($setID, $required = TRUE, $validOnly = FALSE

$dao = CRM_Core_DAO::executeQuery($sql, $params);

$isDefaultContributionPriceSet = FALSE;
if ('default_contribution_amount' == CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $setID)) {
$isDefaultContributionPriceSet = TRUE;
}

$visibility = CRM_Core_PseudoConstant::visibility('name');
while ($dao->fetch()) {
$fieldID = $dao->id;
Expand All @@ -587,7 +592,7 @@ public static function getSetDetail($setID, $required = TRUE, $validOnly = FALSE
}
$setTree[$setID]['fields'][$fieldID][$field] = $dao->$field;
}
$setTree[$setID]['fields'][$fieldID]['options'] = CRM_Price_BAO_PriceField::getOptions($fieldID, FALSE);
$setTree[$setID]['fields'][$fieldID]['options'] = CRM_Price_BAO_PriceField::getOptions($fieldID, FALSE, FALSE, $isDefaultContributionPriceSet);
}

// also get the pre and post help from this price set
Expand Down Expand Up @@ -798,20 +803,19 @@ public static function processAmount($fields, &$params, &$lineItem, $component =
$firstOption = reset($field['options']);
$params["price_{$id}"] = array($firstOption['id'] => $params["price_{$id}"]);
CRM_Price_BAO_LineItem::format($id, $params, $field, $lineItem);
if (CRM_Utils_Array::value('tax_rate', $field['options'][key($field['options'])])) {
$lineItem = self::setLineItem($field, $lineItem, key($field['options']));
$totalTax += $field['options'][key($field['options'])]['tax_amount'] * $lineItem[key($field['options'])]['qty'];
}
if (CRM_Utils_Array::value('name', $field['options'][key($field['options'])]) == 'contribution_amount') {
$optionValueId = key($field['options']);

if (CRM_Utils_Array::value('name', $field['options'][$optionValueId]) == 'contribution_amount') {
$taxRates = CRM_Core_PseudoConstant::getTaxRates();
if (array_key_exists($params['financial_type_id'], $taxRates)) {
$field['options'][key($field['options'])]['tax_rate'] = $taxRates[$params['financial_type_id']];
$taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($field['options'][key($field['options'])]['amount'], $field['options'][key($field['options'])]['tax_rate']);
$field['options'][key($field['options'])]['tax_amount'] = round($taxAmount['tax_amount'], 2);
$lineItem = self::setLineItem($field, $lineItem, key($field['options']));
$totalTax += $field['options'][key($field['options'])]['tax_amount'] * $lineItem[key($field['options'])]['qty'];
$taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($field['options'][$optionValueId]['amount'], $field['options'][$optionValueId]['tax_rate']);
$field['options'][$optionValueId]['tax_amount'] = round($taxAmount['tax_amount'], 2);
}
}
if (CRM_Utils_Array::value('tax_rate', $field['options'][$optionValueId])) {
$lineItem = self::setLineItem($field, $lineItem, $optionValueId, $totalTax);
}
$totalPrice += $lineItem[$firstOption['id']]['line_total'] + CRM_Utils_Array::value('tax_amount', $lineItem[key($field['options'])]);
break;

Expand All @@ -825,8 +829,7 @@ public static function processAmount($fields, &$params, &$lineItem, $component =

CRM_Price_BAO_LineItem::format($id, $params, $field, $lineItem, $amount_override);
if (CRM_Utils_Array::value('tax_rate', $field['options'][$optionValueId])) {
$lineItem = self::setLineItem($field, $lineItem, $optionValueId);
$totalTax += $field['options'][$optionValueId]['tax_amount'];
$lineItem = self::setLineItem($field, $lineItem, $optionValueId, $totalTax);
if (CRM_Utils_Array::value('field_title', $lineItem[$optionValueId]) == 'Membership Amount') {
$lineItem[$optionValueId]['line_total'] = $lineItem[$optionValueId]['unit_price'] = CRM_Utils_Rule::cleanMoney($lineItem[$optionValueId]['line_total'] - $lineItem[$optionValueId]['tax_amount']);
}
Expand All @@ -849,8 +852,7 @@ public static function processAmount($fields, &$params, &$lineItem, $component =

CRM_Price_BAO_LineItem::format($id, $params, $field, $lineItem);
if (CRM_Utils_Array::value('tax_rate', $field['options'][$optionValueId])) {
$lineItem = self::setLineItem($field, $lineItem, $optionValueId);
$totalTax += $field['options'][$optionValueId]['tax_amount'];
$lineItem = self::setLineItem($field, $lineItem, $optionValueId, $totalTax);
}
$totalPrice += $lineItem[$optionValueId]['line_total'] + CRM_Utils_Array::value('tax_amount', $lineItem[$optionValueId]);
if (
Expand All @@ -867,8 +869,7 @@ public static function processAmount($fields, &$params, &$lineItem, $component =
CRM_Price_BAO_LineItem::format($id, $params, $field, $lineItem);
foreach ($params["price_{$id}"] as $optionId => $option) {
if (CRM_Utils_Array::value('tax_rate', $field['options'][$optionId])) {
$lineItem = self::setLineItem($field, $lineItem, $optionId);
$totalTax += $field['options'][$optionId]['tax_amount'];
$lineItem = self::setLineItem($field, $lineItem, $optionId, $totalTax);
}
$totalPrice += $lineItem[$optionId]['line_total'] + CRM_Utils_Array::value('tax_amount', $lineItem[$optionId]);
if (
Expand Down Expand Up @@ -1660,10 +1661,11 @@ public static function copyPriceSet($baoName, $id, $newId) {
* @param array $field
* @param array $lineItem
* @param int $optionValueId
* @param float $totalTax
*
* @return array
*/
public static function setLineItem($field, $lineItem, $optionValueId) {
public static function setLineItem($field, $lineItem, $optionValueId, &$totalTax) {
if ($field['html_type'] == 'Text') {
$taxAmount = $field['options'][$optionValueId]['tax_amount'] * $lineItem[$optionValueId]['qty'];
}
Expand All @@ -1673,7 +1675,7 @@ public static function setLineItem($field, $lineItem, $optionValueId) {
$taxRate = $field['options'][$optionValueId]['tax_rate'];
$lineItem[$optionValueId]['tax_amount'] = $taxAmount;
$lineItem[$optionValueId]['tax_rate'] = $taxRate;

$totalTax += $taxAmount;
return $lineItem;
}

Expand Down
Loading