Merge pull request #22152 from eileenmcnaughton/n1
[civicrm-core.git] / CRM / Financial / BAO / FinancialItem.php
CommitLineData
6a488035 1<?php
6a488035
TO
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035 16 */
6a488035
TO
17class CRM_Financial_BAO_FinancialItem extends CRM_Financial_DAO_FinancialItem {
18
19 /**
fe482240 20 * Class constructor.
6a488035 21 */
045f52a3 22 public function __construct() {
481a74f4 23 parent::__construct();
6a488035
TO
24 }
25
26 /**
fe482240 27 * Fetch object based on array of properties.
6a488035 28 *
ed5dd492
TO
29 * @param array $params
30 * (reference ) an assoc array of name/value pairs.
31 * @param array $defaults
32 * (reference ) an assoc array to hold the flattened values.
6a488035 33 *
5b541553 34 * @return CRM_Financial_DAO_FinancialItem
6a488035 35 */
00be9182 36 public static function retrieve(&$params, &$defaults) {
6a488035
TO
37 $financialItem = new CRM_Financial_DAO_FinancialItem();
38 $financialItem->copyValues($params);
39 if ($financialItem->find(TRUE)) {
40 CRM_Core_DAO::storeValues($financialItem, $defaults);
41 return $financialItem;
42 }
43 return NULL;
44 }
45
46 /**
fe482240 47 * Add the financial items and financial trxn.
6a488035 48 *
ed5dd492
TO
49 * @param object $lineItem
50 * Line item object.
51 * @param object $contribution
52 * Contribution object.
53 * @param bool $taxTrxnID
03e04002 54 *
54957108 55 * @param int $trxnId
56 *
57 * @return CRM_Financial_DAO_FinancialItem
6a488035 58 */
a191ff3d 59 public static function add($lineItem, $contribution, $taxTrxnID = FALSE, $trxnId = NULL) {
6a488035 60 $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
7611ae71 61 $financialItemStatus = CRM_Core_PseudoConstant::get('CRM_Financial_DAO_FinancialItem', 'status_id');
b81ee58c 62 $itemStatus = NULL;
0bdf3091 63 if ($contribution->contribution_status_id == array_search('Completed', $contributionStatuses)
353ffa53
TO
64 || $contribution->contribution_status_id == array_search('Pending refund', $contributionStatuses)
65 ) {
6a488035 66 $itemStatus = array_search('Paid', $financialItemStatus);
03e04002 67 }
f6bae84f 68 elseif ($contribution->contribution_status_id == array_search('Pending', $contributionStatuses)
353ffa53
TO
69 || $contribution->contribution_status_id == array_search('In Progress', $contributionStatuses)
70 ) {
6a488035 71 $itemStatus = array_search('Unpaid', $financialItemStatus);
03e04002 72 }
f8325309
PJ
73 elseif ($contribution->contribution_status_id == array_search('Partially paid', $contributionStatuses)) {
74 $itemStatus = array_search('Partially paid', $financialItemStatus);
75 }
be2fb01f 76 $params = [
f1518315 77 'transaction_date' => $contribution->receive_date,
353ffa53
TO
78 'contact_id' => $contribution->contact_id,
79 'amount' => $lineItem->line_total,
80 'currency' => $contribution->currency,
81 'entity_table' => 'civicrm_line_item',
82 'entity_id' => $lineItem->id,
cf28d075 83 'description' => ($lineItem->qty != 1 ? $lineItem->qty . ' of ' : '') . $lineItem->label,
353ffa53 84 'status_id' => $itemStatus,
be2fb01f 85 ];
03e04002 86
0b7bd9dd 87 if ($taxTrxnID) {
88 $params['amount'] = $lineItem->tax_amount;
530f1e12 89 $params['description'] = Civi::settings()->get('tax_term');
53d294ba 90 $accountRelName = 'Sales Tax Account is';
0b7bd9dd 91 }
92 else {
8cf6bd83
PN
93 $accountRelName = 'Income Account is';
94 if (property_exists($contribution, 'revenue_recognition_date') && !CRM_Utils_System::isNull($contribution->revenue_recognition_date)) {
95 $accountRelName = 'Deferred Revenue Account is';
96 }
0b7bd9dd 97 }
6a488035 98 if ($lineItem->financial_type_id) {
928a340b 99 $params['financial_account_id'] = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship(
53d294ba
PN
100 $lineItem->financial_type_id,
101 $accountRelName
6a488035 102 );
6a488035 103 }
a191ff3d 104 if (empty($trxnId)) {
85726d00 105 if (empty($trxnId['id'])) {
9c472292
PN
106 $trxn = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution->id, 'ASC', TRUE);
107 $trxnId['id'] = $trxn['financialTrxnId'];
108 }
a191ff3d 109 }
8cf6bd83
PN
110 $financialItem = self::create($params, NULL, $trxnId);
111 return $financialItem;
03e04002 112 }
6a488035
TO
113
114 /**
54957108 115 * Create the financial Items and financial entity trxn.
6a488035 116 *
ed5dd492
TO
117 * @param array $params
118 * Associated array to create financial items.
119 * @param array $ids
120 * Financial item ids.
121 * @param array $trxnIds
122 * Financial item ids.
03e04002 123 *
54957108 124 * @return CRM_Financial_DAO_FinancialItem
6a488035 125 */
00be9182 126 public static function create(&$params, $ids = NULL, $trxnIds = NULL) {
6a488035 127 $financialItem = new CRM_Financial_DAO_FinancialItem();
045f52a3 128
a9ed1dc0
PN
129 if (!empty($ids['id'])) {
130 CRM_Utils_Hook::pre('edit', 'FinancialItem', $ids['id'], $params);
131 }
132 else {
133 CRM_Utils_Hook::pre('create', 'FinancialItem', NULL, $params);
134 }
045f52a3 135
6a488035 136 $financialItem->copyValues($params);
a7488080 137 if (!empty($ids['id'])) {
03e04002 138 $financialItem->id = $ids['id'];
6a488035
TO
139 }
140
141 $financialItem->save();
9c1bc317 142 $financialtrxnIDS = $trxnIds['id'] ?? NULL;
9c472292
PN
143 if (!empty($financialtrxnIDS)) {
144 if (!is_array($financialtrxnIDS)) {
be2fb01f 145 $financialtrxnIDS = [$financialtrxnIDS];
9c472292
PN
146 }
147 foreach ($financialtrxnIDS as $tID) {
be2fb01f 148 $entity_financial_trxn_params = [
9c472292
PN
149 'entity_table' => "civicrm_financial_item",
150 'entity_id' => $financialItem->id,
151 'financial_trxn_id' => $tID,
152 'amount' => $params['amount'],
be2fb01f 153 ];
9c472292
PN
154 if (!empty($ids['entityFinancialTrxnId'])) {
155 $entity_financial_trxn_params['id'] = $ids['entityFinancialTrxnId'];
156 }
157 self::createEntityTrxn($entity_financial_trxn_params);
6a488035 158 }
6a488035 159 }
a9ed1dc0
PN
160 if (!empty($ids['id'])) {
161 CRM_Utils_Hook::post('edit', 'FinancialItem', $financialItem->id, $financialItem);
162 }
045f52a3 163 else {
a9ed1dc0
PN
164 CRM_Utils_Hook::post('create', 'FinancialItem', $financialItem->id, $financialItem);
165 }
6a488035 166 return $financialItem;
03e04002 167 }
6a488035
TO
168
169 /**
fe482240 170 * Takes an associative array and creates a entity financial transaction object.
6a488035 171 *
ed5dd492 172 * @param array $params
5b541553 173 * an assoc array of name/value pairs.
6a488035 174 *
5b541553 175 * @return CRM_Financial_DAO_EntityFinancialTrxn
6a488035 176 */
00be9182 177 public static function createEntityTrxn($params) {
6a488035
TO
178 $entity_trxn = new CRM_Financial_DAO_EntityFinancialTrxn();
179 $entity_trxn->copyValues($params);
180 $entity_trxn->save();
181 return $entity_trxn;
182 }
183
184 /**
f044623d
EM
185 * Retrieve entity financial trxn details.
186 *
187 * @deprecated - only called by tests - to be replaced with
188 * $trxn = (array) EntityFinancialTrxn::get()
189 * ->addWhere('id', '=', $contributionID)
190 * ->addWhere('entity_table', '=', 'civicrm_contribution')
191 * ->addSelect('*')->execute();
6a488035 192 *
ed5dd492 193 * @param array $params
5b541553 194 * an assoc array of name/value pairs.
ed5dd492 195 * @param bool $maxId
5b541553 196 * To retrieve max id.
6a488035
TO
197 *
198 * @return array
6a488035 199 */
00be9182 200 public static function retrieveEntityFinancialTrxn($params, $maxId = FALSE) {
6a488035
TO
201 $financialItem = new CRM_Financial_DAO_EntityFinancialTrxn();
202 $financialItem->copyValues($params);
cded2ebf 203 // retrieve last entry from civicrm_entity_financial_trxn
6a488035
TO
204 if ($maxId) {
205 $financialItem->orderBy('id DESC');
206 $financialItem->limit(1);
207 }
208 $financialItem->find();
209 while ($financialItem->fetch()) {
be2fb01f 210 $financialItems[$financialItem->id] = [
353ffa53
TO
211 'id' => $financialItem->id,
212 'entity_table' => $financialItem->entity_table,
213 'entity_id' => $financialItem->entity_id,
6a488035 214 'financial_trxn_id' => $financialItem->financial_trxn_id,
353ffa53 215 'amount' => $financialItem->amount,
be2fb01f 216 ];
03e04002 217 }
6a488035
TO
218 if (!empty($financialItems)) {
219 return $financialItems;
220 }
221 else {
045f52a3 222 return NULL;
6a488035
TO
223 }
224 }
2efcf0c2 225
f182074e 226 /**
fe482240 227 * Check if contact is present in financial_item table.
f182074e 228 *
0e480632 229 * @see https://issues.civicrm.org/jira/browse/CRM-12929
f182074e 230 *
ed5dd492
TO
231 * @param array $contactIds
232 * An array contact id's.
f182074e 233 *
ed5dd492
TO
234 * @param array $error
235 * Error to display.
f182074e 236 *
a2fb4683 237 * @return array|bool
f182074e 238 */
00be9182 239 public static function checkContactPresent($contactIds, &$error) {
f182074e
PN
240 if (empty($contactIds)) {
241 return FALSE;
242 }
2efcf0c2 243
aaffa79f 244 $allowPermDelete = Civi::settings()->get('allowPermDeleteFinancial');
f182074e
PN
245
246 if (!$allowPermDelete) {
247 $sql = 'SELECT DISTINCT(cc.id), cc.display_name FROM civicrm_contact cc
248INNER JOIN civicrm_contribution con ON con.contact_id = cc.id
045f52a3 249WHERE cc.id IN (' . implode(',', $contactIds) . ') AND con.is_test = 0';
f182074e
PN
250 $dao = CRM_Core_DAO::executeQuery($sql);
251 if ($dao->N) {
252 while ($dao->fetch()) {
253 $url = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid=$dao->id");
254 $not_deleted[$dao->id] = "<a href='$url'>$dao->display_name</a>";
255 }
2efcf0c2 256
f182074e
PN
257 $errorStatus = '';
258 if (is_array($error)) {
259 $errorStatus = '<ul><li>' . implode('</li><li>', $not_deleted) . '</li></ul>';
260 }
2efcf0c2 261
86bfa4f6 262 $error['_qf_default'] = $errorStatus . ts('This contact(s) can not be permanently deleted because the contact record is linked to one or more live financial transactions. Deleting this contact would result in the loss of financial data.');
2efcf0c2 263 return $error;
f182074e
PN
264 }
265 }
266 return FALSE;
267 }
96025800 268
00b4d571 269 /**
cf28d075 270 * Get most relevant previous financial item relating to the line item.
00b4d571 271 *
cf28d075 272 * This function specifically excludes sales tax.
00b4d571 273 *
cf28d075 274 * @param int $entityId
00b4d571 275 *
81716ddb 276 * @return array
00b4d571 277 */
cf28d075 278 public static function getPreviousFinancialItem($entityId) {
be2fb01f 279 $params = [
cf28d075 280 'entity_id' => $entityId,
281 'entity_table' => 'civicrm_line_item',
be2fb01f
CW
282 'options' => ['limit' => 1, 'sort' => 'id DESC'],
283 ];
284 $salesTaxFinancialAccounts = civicrm_api3('FinancialAccount', 'get', ['is_tax' => 1]);
cf28d075 285 if ($salesTaxFinancialAccounts['count']) {
be2fb01f 286 $params['financial_account_id'] = ['NOT IN' => array_keys($salesTaxFinancialAccounts['values'])];
cf28d075 287 }
288 return civicrm_api3('FinancialItem', 'getsingle', $params);
00b4d571
PN
289 }
290
6844be64
MD
291 /**
292 * Whitelist of possible values for the entity_table field
293 *
294 * @return array
295 */
296 public static function entityTables(): array {
297 return [
298 'civicrm_line_item' => ts('Line Item'),
299 'civicrm_financial_trxn' => ts('Financial Trxn'),
300 ];
301 }
302
6a488035 303}