Fix button breakage on viewContribution
[civicrm-core.git] / CRM / Contribute / Form / ContributionView.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 use Civi\Api4\Contribution;
13
14 /**
15 *
16 * @package CRM
17 * @copyright CiviCRM LLC https://civicrm.org/licensing
18 */
19
20 /**
21 * This class generates form components for Payment-Instrument.
22 */
23 class CRM_Contribute_Form_ContributionView extends CRM_Core_Form {
24
25 /**
26 * Set variables up before form is built.
27 *
28 * @throws \CRM_Core_Exception
29 * @throws \API_Exception
30 */
31 public function preProcess() {
32 $id = $this->getID();
33
34 // Check permission for action.
35 if (!CRM_Core_Permission::checkActionPermission('CiviContribute', $this->_action)) {
36 CRM_Core_Error::statusBounce(ts('You do not have permission to access this page.'));
37 }
38 $params = ['id' => $id];
39 $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this);
40 $this->assign('context', $context);
41
42 // Note than this get could be restricted by ACLs in an extension
43 $contribution = Contribution::get(TRUE)->addWhere('id', '=', $id)->addSelect('*')->execute()->first();
44 if (empty($contribution)) {
45 CRM_Core_Error::statusBounce(ts('Access to contribution not permitted'));
46 }
47 // We just cast here because it was traditionally an array called values - would be better
48 // just to use 'contribution'.
49 $values = (array) $contribution;
50 $contributionStatus = CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $values['contribution_status_id']);
51
52 $this->addExpectedSmartyVariables([
53 'hookDiscount',
54 'pricesetFieldsCount',
55 'pcp_id',
56 'getTaxDetails',
57 // currencySymbol maybe doesn't make sense but is probably old?
58 'currencySymbol',
59 ]);
60
61 // @todo - it might have been better to create a new form that extends this
62 // for template contributions rather than overloading this form.
63 $force_create_template = CRM_Utils_Request::retrieve('force_create_template', 'Boolean', $this, FALSE, FALSE);
64 if ($force_create_template && !empty($values['contribution_recur_id']) && empty($values['is_template'])) {
65 // Create a template contribution.
66 $templateContributionId = CRM_Contribute_BAO_ContributionRecur::ensureTemplateContributionExists($values['contribution_recur_id']);
67 if (!empty($templateContributionId)) {
68 $id = $templateContributionId;
69 $params = ['id' => $id];
70 $values = CRM_Contribute_BAO_Contribution::getValuesWithMappings($params);
71 }
72 }
73 $this->assign('is_template', $values['is_template']);
74
75 CRM_Contribute_BAO_Contribution::resolveDefaults($values);
76
77 $values['contribution_page_title'] = '';
78 if (!empty($values['contribution_page_id'])) {
79 $contribPages = CRM_Contribute_PseudoConstant::contributionPage(NULL, TRUE);
80 $values['contribution_page_title'] = CRM_Utils_Array::value(CRM_Utils_Array::value('contribution_page_id', $values), $contribPages);
81 }
82
83 // get received into i.e to_financial_account_id from last trxn
84 $financialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($this->getID(), 'DESC');
85 $values['to_financial_account'] = '';
86 $values['payment_processor_name'] = '';
87 if (!empty($financialTrxnId['financialTrxnId'])) {
88 $values['to_financial_account_id'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialTrxn', $financialTrxnId['financialTrxnId'], 'to_financial_account_id');
89 if ($values['to_financial_account_id']) {
90 $values['to_financial_account'] = CRM_Contribute_PseudoConstant::financialAccount($values['to_financial_account_id']);
91 }
92 $values['payment_processor_id'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialTrxn', $financialTrxnId['financialTrxnId'], 'payment_processor_id');
93 if ($values['payment_processor_id']) {
94 $values['payment_processor_name'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessor', $values['payment_processor_id'], 'name');
95 }
96 }
97
98 if (!empty($values['contribution_recur_id'])) {
99 $sql = "SELECT installments, frequency_interval, frequency_unit FROM civicrm_contribution_recur WHERE id = %1";
100 $params = [1 => [$values['contribution_recur_id'], 'Integer']];
101 $dao = CRM_Core_DAO::executeQuery($sql, $params);
102 if ($dao->fetch()) {
103 $values['recur_installments'] = $dao->installments;
104 $values['recur_frequency_unit'] = $dao->frequency_unit;
105 $values['recur_frequency_interval'] = $dao->frequency_interval;
106 }
107 }
108
109 try {
110 $participantLineItems = \Civi\Api4\LineItem::get()
111 ->addSelect('entity_id', 'participant.role_id:label', 'participant.fee_level', 'participant.contact_id', 'contact.display_name')
112 ->addJoin('Participant AS participant', 'LEFT', ['participant.id', '=', 'entity_id'])
113 ->addJoin('Contact AS contact', 'LEFT', ['contact.id', '=', 'participant.contact_id'])
114 ->addWhere('entity_table', '=', 'civicrm_participant')
115 ->addWhere('contribution_id', '=', $id)
116 ->execute();
117 }
118 catch (API_Exception $e) {
119 // likely don't have permission for events/participants
120 $participantLineItems = [];
121 }
122
123 $associatedParticipants = FALSE;
124 foreach ($participantLineItems as $participant) {
125 $associatedParticipants[] = [
126 'participantLink' => CRM_Utils_System::url('civicrm/contact/view/participant',
127 "action=view&reset=1&id={$participant['entity_id']}&cid={$participant['participant.contact_id']}&context=home"
128 ),
129 'participantName' => $participant['contact.display_name'],
130 'fee' => implode(', ', $participant['participant.fee_level']),
131 'role' => implode(', ', $participant['participant.role_id:label']),
132 ];
133 }
134 $this->assign('associatedParticipants', $associatedParticipants);
135
136 $groupTree = CRM_Core_BAO_CustomGroup::getTree('Contribution', NULL, $id, 0, $values['financial_type_id'] ?? NULL,
137 NULL, TRUE, NULL, FALSE, CRM_Core_Permission::VIEW);
138 CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, FALSE, NULL, NULL, NULL, $id);
139
140 $premiumId = NULL;
141 $dao = new CRM_Contribute_DAO_ContributionProduct();
142 $dao->contribution_id = $id;
143 if ($dao->find(TRUE)) {
144 $premiumId = $dao->id;
145 $productID = $dao->product_id;
146 }
147
148 $this->assign('premium', '');
149 if ($premiumId) {
150 $productDAO = new CRM_Contribute_DAO_Product();
151 $productDAO->id = $productID;
152 $productDAO->find(TRUE);
153
154 $this->assign('premium', $productDAO->name);
155 $this->assign('option', $dao->product_option);
156 $this->assign('fulfilled', $dao->fulfilled_date);
157 }
158
159 // Get Note
160 $noteValue = CRM_Core_BAO_Note::getNote(CRM_Utils_Array::value('id', $values), 'civicrm_contribution');
161 $values['note'] = array_values($noteValue);
162
163 // show billing address location details, if exists
164 $values['billing_address'] = '';
165 if (!empty($values['address_id'])) {
166 $addressParams = ['id' => $values['address_id']];
167 $addressDetails = CRM_Core_BAO_Address::getValues($addressParams, FALSE, 'id');
168 $addressDetails = array_values($addressDetails);
169 $values['billing_address'] = $addressDetails[0]['display'];
170 }
171
172 //assign soft credit record if exists.
173 $SCRecords = CRM_Contribute_BAO_ContributionSoft::getSoftContribution($this->getID(), TRUE);
174 $this->assign('softContributions', empty($SCRecords['soft_credit']) ? NULL : $SCRecords['soft_credit']);
175 // unset doesn't complain if array member missing
176 unset($SCRecords['soft_credit']);
177 foreach ($SCRecords as $name => $value) {
178 $this->assign($name, $value);
179 }
180
181 $lineItems = [CRM_Price_BAO_LineItem::getLineItemsByContributionID(($id))];
182 $this->assign('lineItem', $lineItems);
183 $values['totalAmount'] = $values['total_amount'];
184 $this->assign('displayLineItemFinancialType', TRUE);
185
186 //do check for campaigns
187 $values['campaign'] = '';
188 if ($campaignId = CRM_Utils_Array::value('campaign_id', $values)) {
189 $campaigns = CRM_Campaign_BAO_Campaign::getCampaigns($campaignId);
190 $values['campaign'] = $campaigns[$campaignId];
191 }
192 if ($contributionStatus === 'Refunded') {
193 $this->assign('refund_trxn_id', CRM_Core_BAO_FinancialTrxn::getRefundTransactionTrxnID($id));
194 }
195
196 // assign values to the template
197 $this->assignVariables($values, array_keys($values));
198 $invoicing = CRM_Invoicing_Utils::isInvoicingEnabled();
199 $this->assign('invoicing', $invoicing);
200 $this->assign('isDeferred', Civi::settings()->get('deferred_revenue_enabled'));
201 if ($invoicing && isset($values['tax_amount'])) {
202 $this->assign('totalTaxAmount', $values['tax_amount']);
203 }
204
205 // omitting contactImage from title for now since the summary overlay css doesn't work outside of our crm-container
206 $displayName = CRM_Contact_BAO_Contact::displayName($values['contact_id']);
207 $this->assign('displayName', $displayName);
208 // Check if this is default domain contact CRM-10482
209 if (CRM_Contact_BAO_Contact::checkDomainContact($values['contact_id'])) {
210 $displayName .= ' (' . ts('default organization') . ')';
211 }
212
213 if (empty($values['is_template'])) {
214 $this->setTitle(ts('View Contribution from') . ' ' . $displayName);
215 }
216 else {
217 $this->setTitle(ts('View Template Contribution from') . ' ' . $displayName);
218 }
219
220 // add viewed contribution to recent items list
221 $url = CRM_Utils_System::url('civicrm/contact/view/contribution',
222 "action=view&reset=1&id={$values['id']}&cid={$values['contact_id']}&context=home"
223 );
224
225 $title = $displayName . ' - (' . CRM_Utils_Money::format($values['total_amount'], $values['currency']) . ' ' . ' - ' . $values['financial_type'] . ')';
226
227 $recentOther = [];
228 if (CRM_Core_Permission::checkActionPermission('CiviContribute', CRM_Core_Action::UPDATE)) {
229 $recentOther['editUrl'] = CRM_Utils_System::url('civicrm/contact/view/contribution',
230 "action=update&reset=1&id={$values['id']}&cid={$values['contact_id']}&context=home"
231 );
232 }
233 if (CRM_Core_Permission::checkActionPermission('CiviContribute', CRM_Core_Action::DELETE)) {
234 $recentOther['deleteUrl'] = CRM_Utils_System::url('civicrm/contact/view/contribution',
235 "action=delete&reset=1&id={$values['id']}&cid={$values['contact_id']}&context=home"
236 );
237 }
238 CRM_Utils_Recent::add($title,
239 $url,
240 $values['id'],
241 'Contribution',
242 $values['contact_id'],
243 NULL,
244 $recentOther
245 );
246 $statusOptionValueNames = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
247 $contributionStatus = $statusOptionValueNames[$values['contribution_status_id']];
248 $this->assign('addRecordPayment', in_array($contributionStatus, ['Partially paid', 'Pending refund', 'Pending']));
249 $this->assignPaymentInfoBlock($id);
250
251 $searchKey = NULL;
252 if ($this->controller->_key) {
253 $searchKey = $this->controller->_key;
254 }
255
256 if ($this->isHasAccess('update')) {
257 $urlParams = "reset=1&id={$id}&cid={$values['contact_id']}&action=update&context={$context}";
258 if (($context === 'fulltext' || $context === 'search') && $searchKey) {
259 $urlParams = "reset=1&id={$id}&cid={$values['contact_id']}&action=update&context={$context}&key={$searchKey}";
260 }
261 if (!$contribution['is_template']) {
262 foreach (CRM_Contribute_BAO_Contribution::getContributionPaymentLinks($this->getID(), $contributionStatus) as $paymentButton) {
263 $paymentButton['icon'] = 'fa-plus-circle';
264 $linkButtons[] = $paymentButton;
265 }
266 }
267 $linkButtons[] = [
268 'title' => ts('Edit'),
269 'url' => 'civicrm/contact/view/contribution',
270 'qs' => $urlParams,
271 'icon' => 'fa-pencil',
272 'accessKey' => 'e',
273 'ref' => '',
274 'name' => '',
275 'extra' => '',
276 ];
277 }
278
279 if ($this->isHasAccess('delete')) {
280 $urlParams = "reset=1&id={$id}&cid={$values['contact_id']}&action=delete&context={$context}";
281 if (($context === 'fulltext' || $context === 'search') && $searchKey) {
282 $urlParams = "reset=1&id={$id}&cid={$values['contact_id']}&action=delete&context={$context}&key={$searchKey}";
283 }
284 $linkButtons[] = [
285 'title' => ts('Delete'),
286 'url' => 'civicrm/contact/view/contribution',
287 'qs' => $urlParams,
288 'icon' => 'fa-trash',
289 'accessKey' => '',
290 'ref' => '',
291 'name' => '',
292 'extra' => '',
293 ];
294 }
295
296 $pdfUrlParams = "reset=1&id={$id}&cid={$values['contact_id']}";
297 $emailUrlParams = "reset=1&id={$id}&cid={$values['contact_id']}&select=email";
298 if (Civi::settings()->get('invoicing') && !$contribution['is_template']) {
299 if (($values['contribution_status'] !== 'Refunded') && ($values['contribution_status'] !== 'Cancelled')) {
300 $invoiceButtonText = ts('Download Invoice');
301 }
302 else {
303 $invoiceButtonText = ts('Download Invoice and Credit Note');
304 }
305 $linkButtons[] = [
306 'title' => $invoiceButtonText,
307 'url' => 'civicrm/contribute/invoice',
308 'qs' => $pdfUrlParams,
309 'icon' => 'fa-download',
310 ];
311 $linkButtons[] = [
312 'title' => ts('Email Invoice'),
313 'url' => 'civicrm/contribute/invoice/email',
314 'qs' => $emailUrlParams,
315 'icon' => 'fa-paper-plane',
316 ];
317 }
318 $this->assign('linkButtons', $linkButtons ?? []);
319 // These next 3 parameters are used to construct a url in PaymentInfo.tpl
320 $this->assign('contactId', $values['contact_id']);
321 $this->assign('componentId', $id);
322 $this->assign('component', 'contribution');
323 $this->assignPaymentInfoBlock($id);
324 }
325
326 /**
327 * Build the form object.
328 */
329 public function buildQuickForm() {
330 $this->addButtons([
331 [
332 'type' => 'cancel',
333 'name' => ts('Done'),
334 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
335 'isDefault' => TRUE,
336 ],
337 ]);
338 }
339
340 /**
341 * Assign the values to build the payment info block.
342 *
343 * @todo - this is a bit too much copy & paste from AbstractEditPayment
344 * (justifying on the basis it's 'pretty short' and in a different inheritance
345 * tree. I feel like traits are probably the longer term answer).
346 *
347 * @param int $id
348 *
349 * @return string
350 * Block title.
351 */
352 protected function assignPaymentInfoBlock($id) {
353 // component is used in getPaymentInfo primarily to retrieve the contribution id, we
354 // already have that.
355 $paymentInfo = CRM_Contribute_BAO_Contribution::getPaymentInfo($id, 'contribution', TRUE);
356 $title = ts('View Payment');
357 $this->assign('transaction', TRUE);
358 // Used in paymentInfoBlock.tpl
359 $this->assign('payments', $paymentInfo['transaction']);
360 $this->assign('paymentLinks', $paymentInfo['payment_links']);
361 return $title;
362 }
363
364 /**
365 * @param string $action
366 *
367 * @return bool
368 */
369 private function isHasAccess(string $action): bool {
370 try {
371 return Contribution::checkAccess()
372 ->setAction($action)
373 ->addValue('id', $this->getID())
374 ->execute()->first()['access'];
375 }
376 catch (API_Exception $e) {
377 return FALSE;
378 }
379 }
380
381 /**
382 * Get the contribution ID.
383 *
384 * @return int
385 */
386 private function getID(): int {
387 $id = $this->get('id');
388 if (empty($id)) {
389 CRM_Core_Error::statusBounce('Contribution ID is required');
390 }
391 return $id;
392 }
393
394 }