[Ref] Make functions private to protect them
[civicrm-core.git] / CRM / Contribute / BAO / FinancialProcessor.php
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 */
33 private static function getFinancialAccountForStatusChangeTrxn($params, $default): int {
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
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 private 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 private 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
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
244 }