Commit | Line | Data |
---|---|---|
538e521c EM |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
4 | | Copyright CiviCRM LLC. All rights reserved. | | |
5 | | | | |
6 | | This work is published under the GNU AGPLv3 license with some | | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
9 | +--------------------------------------------------------------------+ | |
10 | */ | |
11 | ||
12 | /** | |
13 | * Class for handling processing of financial records. | |
14 | * | |
15 | * This is a place to extract the financial record processing code to | |
16 | * in order to clean it up. | |
17 | * | |
18 | * @internal core use only. | |
19 | * | |
20 | * @package CRM | |
21 | * @copyright CiviCRM LLC https://civicrm.org/licensing | |
22 | */ | |
23 | class CRM_Contribute_BAO_FinancialProcessor { | |
24 | ||
25 | /** | |
26 | * Get the financial account for the item associated with the new transaction. | |
27 | * | |
28 | * @param array $params | |
29 | * @param int $default | |
30 | * | |
31 | * @return int | |
32 | */ | |
c259e5f3 | 33 | private static function getFinancialAccountForStatusChangeTrxn($params, $default): int { |
538e521c EM |
34 | if (!empty($params['financial_account_id'])) { |
35 | return $params['financial_account_id']; | |
36 | } | |
37 | ||
38 | $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus($params['contribution_status_id'], 'name'); | |
39 | $preferredAccountsRelationships = [ | |
40 | 'Refunded' => 'Credit/Contra Revenue Account is', | |
41 | 'Chargeback' => 'Chargeback Account is', | |
42 | ]; | |
43 | ||
44 | if (array_key_exists($contributionStatus, $preferredAccountsRelationships)) { | |
45 | $financialTypeID = !empty($params['financial_type_id']) ? $params['financial_type_id'] : $params['prevContribution']->financial_type_id; | |
46 | return CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship( | |
47 | $financialTypeID, | |
48 | $preferredAccountsRelationships[$contributionStatus] | |
49 | ); | |
50 | } | |
51 | return $default; | |
52 | } | |
53 | ||
c259e5f3 EM |
54 | /** |
55 | * Create the financial items for the line. | |
56 | * | |
57 | * @param array $params | |
58 | * @param string $context | |
59 | * @param array $fields | |
60 | * @param array $previousLineItems | |
61 | * @param array $inputParams | |
62 | * @param bool $isARefund | |
63 | * @param array $trxnIds | |
64 | * @param int $fieldId | |
65 | * | |
66 | * @internal | |
67 | * | |
68 | * @return array | |
69 | */ | |
70 | public static function createFinancialItemsForLine($params, $context, $fields, array $previousLineItems, array $inputParams, bool $isARefund, $trxnIds, $fieldId): array { | |
71 | foreach ($fields as $fieldValueId => $lineItemDetails) { | |
72 | $prevFinancialItem = CRM_Financial_BAO_FinancialItem::getPreviousFinancialItem($lineItemDetails['id']); | |
73 | $receiveDate = CRM_Utils_Date::isoToMysql($params['prevContribution']->receive_date); | |
74 | if ($params['contribution']->receive_date) { | |
75 | $receiveDate = CRM_Utils_Date::isoToMysql($params['contribution']->receive_date); | |
76 | } | |
77 | ||
78 | $financialAccount = CRM_Contribute_BAO_FinancialProcessor::getFinancialAccountForStatusChangeTrxn($params, CRM_Utils_Array::value('financial_account_id', $prevFinancialItem)); | |
79 | ||
80 | $currency = $params['prevContribution']->currency; | |
81 | if ($params['contribution']->currency) { | |
82 | $currency = $params['contribution']->currency; | |
83 | } | |
84 | $previousLineItemTotal = CRM_Utils_Array::value('line_total', CRM_Utils_Array::value($fieldValueId, $previousLineItems), 0); | |
85 | $itemParams = [ | |
86 | 'transaction_date' => $receiveDate, | |
87 | 'contact_id' => $params['prevContribution']->contact_id, | |
88 | 'currency' => $currency, | |
89 | 'amount' => self::getFinancialItemAmountFromParams($inputParams, $context, $lineItemDetails, $isARefund, $previousLineItemTotal), | |
90 | 'description' => $prevFinancialItem['description'] ?? NULL, | |
91 | 'status_id' => $prevFinancialItem['status_id'], | |
92 | 'financial_account_id' => $financialAccount, | |
93 | 'entity_table' => 'civicrm_line_item', | |
94 | 'entity_id' => $lineItemDetails['id'], | |
95 | ]; | |
96 | $financialItem = CRM_Financial_BAO_FinancialItem::create($itemParams, NULL, $trxnIds); | |
97 | $params['line_item'][$fieldId][$fieldValueId]['deferred_line_total'] = $itemParams['amount']; | |
98 | $params['line_item'][$fieldId][$fieldValueId]['financial_item_id'] = $financialItem->id; | |
99 | ||
100 | if (($lineItemDetails['tax_amount'] && $lineItemDetails['tax_amount'] !== 'null') || ($context === 'changeFinancialType')) { | |
101 | $taxAmount = (float) $lineItemDetails['tax_amount']; | |
102 | if ($context === 'changeFinancialType' && $lineItemDetails['tax_amount'] === 'null') { | |
103 | // reverse the Sale Tax amount if there is no tax rate associated with new Financial Type | |
104 | $taxAmount = CRM_Utils_Array::value('tax_amount', CRM_Utils_Array::value($fieldValueId, $previousLineItems), 0); | |
105 | } | |
106 | elseif ($previousLineItemTotal != $lineItemDetails['line_total']) { | |
107 | $taxAmount -= CRM_Utils_Array::value('tax_amount', CRM_Utils_Array::value($fieldValueId, $previousLineItems), 0); | |
108 | } | |
109 | if ($taxAmount != 0) { | |
110 | $itemParams['amount'] = CRM_Contribute_BAO_FinancialProcessor::getMultiplier($params['contribution']->contribution_status_id, $context) * $taxAmount; | |
111 | $itemParams['description'] = CRM_Invoicing_Utils::getTaxTerm(); | |
112 | if ($lineItemDetails['financial_type_id']) { | |
113 | $itemParams['financial_account_id'] = CRM_Financial_BAO_FinancialAccount::getSalesTaxFinancialAccount($lineItemDetails['financial_type_id']); | |
114 | } | |
115 | CRM_Financial_BAO_FinancialItem::create($itemParams, NULL, $trxnIds); | |
116 | } | |
117 | } | |
118 | } | |
119 | return $params; | |
120 | } | |
121 | ||
122 | /** | |
123 | * Get the multiplier for adjusting rows. | |
124 | * | |
125 | * If we are dealing with a refund or cancellation then it will be a negative | |
126 | * amount to reflect the negative transaction. | |
127 | * | |
128 | * If we are changing Financial Type it will be a negative amount to | |
129 | * adjust down the old type. | |
130 | * | |
131 | * @param int $contribution_status_id | |
132 | * @param string $context | |
133 | * | |
134 | * @return int | |
135 | */ | |
136 | public static function getMultiplier($contribution_status_id, $context) { | |
137 | if ($context === 'changeFinancialType' || CRM_Contribute_BAO_Contribution::isContributionStatusNegative($contribution_status_id)) { | |
138 | return -1; | |
139 | } | |
140 | return 1; | |
141 | } | |
142 | ||
143 | /** | |
144 | * Get the amount for the financial item row. | |
145 | * | |
146 | * Helper function to start to break down recordFinancialTransactions for readability. | |
147 | * | |
148 | * The logic is more historical than .. logical. Paths other than the deprecated one are tested. | |
149 | * | |
150 | * Codewise, several somewhat disimmilar things have been squished into recordFinancialAccounts | |
151 | * for historical reasons. Going forwards we can hope to add tests & improve readibility | |
152 | * of that function | |
153 | * | |
154 | * @param array $params | |
155 | * Params as passed to contribution.create | |
156 | * | |
157 | * @param string $context | |
158 | * changeFinancialType| changedAmount | |
159 | * @param array $lineItemDetails | |
160 | * Line items. | |
161 | * @param bool $isARefund | |
162 | * Is this a refund / negative transaction. | |
163 | * @param int $previousLineItemTotal | |
164 | * | |
165 | * @return float | |
166 | * @todo move recordFinancialAccounts & helper functions to their own class? | |
167 | * | |
168 | */ | |
169 | protected static function getFinancialItemAmountFromParams($params, $context, $lineItemDetails, $isARefund, $previousLineItemTotal) { | |
170 | if ($context == 'changedAmount') { | |
171 | $lineTotal = $lineItemDetails['line_total']; | |
172 | if ($lineTotal != $previousLineItemTotal) { | |
173 | $lineTotal -= $previousLineItemTotal; | |
174 | } | |
175 | return $lineTotal; | |
176 | } | |
177 | elseif ($context == 'changeFinancialType') { | |
178 | return -$lineItemDetails['line_total']; | |
179 | } | |
180 | elseif ($context == 'changedStatus') { | |
181 | $cancelledTaxAmount = 0; | |
182 | if ($isARefund) { | |
183 | $cancelledTaxAmount = CRM_Utils_Array::value('tax_amount', $lineItemDetails, '0.00'); | |
184 | } | |
185 | return CRM_Contribute_BAO_FinancialProcessor::getMultiplier($params['contribution']->contribution_status_id, $context) * ((float) $lineItemDetails['line_total'] + (float) $cancelledTaxAmount); | |
186 | } | |
187 | elseif ($context === NULL) { | |
188 | // erm, yes because? but, hey, it's tested. | |
189 | return $lineItemDetails['line_total']; | |
190 | } | |
191 | else { | |
192 | return CRM_Contribute_BAO_FinancialProcessor::getMultiplier($params['contribution']->contribution_status_id, $context) * ((float) $lineItemDetails['line_total']); | |
193 | } | |
194 | } | |
195 | ||
6a1dbda6 EM |
196 | /** |
197 | * Update all financial accounts entry. | |
198 | * | |
199 | * @param array $params | |
200 | * Contribution object, line item array and params for trxn. | |
201 | * | |
202 | * @param string $context | |
203 | * Update scenarios. | |
204 | * | |
205 | * @todo stop passing $params by reference. It is unclear the purpose of doing this & | |
206 | * adds unpredictability. | |
207 | * | |
208 | */ | |
209 | public static function updateFinancialAccounts(&$params, $context = NULL) { | |
210 | $inputParams = $params; | |
211 | $isARefund = self::isContributionUpdateARefund($params['prevContribution']->contribution_status_id, $params['contribution']->contribution_status_id); | |
212 | ||
213 | if ($context === 'changedAmount' || $context === 'changeFinancialType') { | |
214 | // @todo we should stop passing $params by reference - splitting this out would be a step towards that. | |
215 | $params['trxnParams']['total_amount'] = $params['trxnParams']['net_amount'] = ($params['total_amount'] - $params['prevContribution']->total_amount); | |
216 | } | |
217 | ||
218 | $trxn = CRM_Core_BAO_FinancialTrxn::create($params['trxnParams']); | |
219 | // @todo we should stop passing $params by reference - splitting this out would be a step towards that. | |
220 | $params['entity_id'] = $trxn->id; | |
221 | ||
222 | $trxnIds['id'] = $params['entity_id']; | |
223 | $previousLineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($params['contribution']->id); | |
224 | foreach ($params['line_item'] as $fieldId => $fields) { | |
225 | $params = CRM_Contribute_BAO_FinancialProcessor::createFinancialItemsForLine($params, $context, $fields, $previousLineItems, $inputParams, $isARefund, $trxnIds, $fieldId); | |
226 | } | |
227 | } | |
228 | ||
229 | /** | |
230 | * Does this contribution status update represent a refund. | |
231 | * | |
232 | * @param int $previousContributionStatusID | |
233 | * @param int $currentContributionStatusID | |
234 | * | |
235 | * @return bool | |
236 | */ | |
237 | public static function isContributionUpdateARefund($previousContributionStatusID, $currentContributionStatusID): bool { | |
238 | if ('Completed' !== CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $previousContributionStatusID)) { | |
239 | return FALSE; | |
240 | } | |
241 | return CRM_Contribute_BAO_Contribution::isContributionStatusNegative($currentContributionStatusID); | |
242 | } | |
243 | ||
538e521c | 244 | } |