Merge pull request #13993 from eileenmcnaughton/recur_fixes
[civicrm-core.git] / CRM / Financial / Form / PaymentEdit.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2019
32 */
33 class CRM_Financial_Form_PaymentEdit extends CRM_Core_Form {
34
35 /**
36 * The id of the financial trxn.
37 *
38 * @var int
39 */
40 protected $_id;
41
42 /**
43 * The id of the related contribution ID
44 *
45 * @var int
46 */
47 protected $_contributionID;
48
49 /**
50 * The variable which holds the information of a financial transaction
51 *
52 * @var array
53 */
54 protected $_values;
55
56 /**
57 * Explicitly declare the form context.
58 */
59 public function getDefaultContext() {
60 return 'create';
61 }
62
63 /**
64 * Set variables up before form is built.
65 */
66 public function preProcess() {
67 $this->_action = CRM_Core_Action::UPDATE;
68 parent::preProcess();
69 $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this);
70 $this->assign('id', $this->_id);
71 $this->_contributionID = CRM_Utils_Request::retrieve('contribution_id', 'Positive', $this);
72
73 $this->_values = civicrm_api3('FinancialTrxn', 'getsingle', ['id' => $this->_id]);
74 if (!empty($this->_values['payment_processor_id'])) {
75 CRM_Core_Error::statusBounce(ts('You cannot update this payment as it is tied to a payment processor'));
76 }
77 }
78
79 /**
80 * Set default values.
81 *
82 * @return array
83 */
84 public function setDefaultValues() {
85 $defaults = $this->_values;
86 // Format money fields - localize for display
87 $moneyFields = ['total_amount', 'fee_amount', 'net_amount'];
88 foreach ($moneyFields as $field) {
89 $defaults[$field] = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($this->_values[$field]);
90 }
91 return $defaults;
92 }
93
94 /**
95 * Build quickForm.
96 */
97 public function buildQuickForm() {
98 CRM_Utils_System::setTitle(ts('Update Payment details'));
99
100 $paymentFields = $this->getPaymentFields();
101 $this->assign('paymentFields', $paymentFields);
102 foreach ($paymentFields as $name => $paymentField) {
103 if (!empty($paymentField['add_field'])) {
104 $attributes = [
105 'entity' => 'FinancialTrxn',
106 'name' => $name,
107 ];
108 $this->addField($name, $attributes, $paymentField['is_required']);
109 }
110 else {
111 $this->add($paymentField['htmlType'],
112 $name,
113 $paymentField['title'],
114 $paymentField['attributes'],
115 $paymentField['is_required']
116 );
117 }
118 }
119
120 $this->assign('currency', CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_Currency', $this->_values['currency'], 'symbol', 'name'));
121 $this->addFormRule([__CLASS__, 'formRule'], $this);
122
123 $this->addButtons([
124 [
125 'type' => 'submit',
126 'name' => ts('Update'),
127 'isDefault' => TRUE,
128 ],
129 [
130 'type' => 'cancel',
131 'name' => ts('Cancel'),
132 ],
133 ]);
134 }
135
136 /**
137 * Global form rule.
138 *
139 * @param array $fields
140 * The input form values.
141 * @param array $files
142 * The uploaded files if any.
143 * @param $self
144 *
145 * @return bool|array
146 * true if no errors, else array of errors
147 */
148 public static function formRule($fields, $files, $self) {
149 $errors = [];
150
151 // if Credit Card is chosen and pan_truncation is not NULL ensure that it's value is numeric else throw validation error
152 if (CRM_Core_PseudoConstant::getName('CRM_Financial_DAO_FinancialTrxn', 'payment_instrument_id', $fields['payment_instrument_id']) == 'Credit Card' &&
153 !empty($fields['pan_truncation']) &&
154 !CRM_Utils_Rule::numeric($fields['pan_truncation'])
155 ) {
156 $errors['pan_truncation'] = ts('Please enter a valid Card Number');
157 }
158
159 return $errors;
160 }
161
162 /**
163 * Process the form submission.
164 */
165 public function postProcess() {
166 $params = [
167 'id' => $this->_id,
168 'payment_instrument_id' => $this->_submitValues['payment_instrument_id'],
169 'trxn_id' => CRM_Utils_Array::value('trxn_id', $this->_submitValues),
170 'trxn_date' => CRM_Utils_Array::value('trxn_date', $this->_submitValues, date('YmdHis')),
171 ];
172
173 $paymentInstrumentName = CRM_Core_PseudoConstant::getName('CRM_Financial_DAO_FinancialTrxn', 'payment_instrument_id', $params['payment_instrument_id']);
174 if ($paymentInstrumentName == 'Credit Card') {
175 $params['card_type_id'] = CRM_Utils_Array::value('card_type_id', $this->_submitValues);
176 $params['pan_truncation'] = CRM_Utils_Array::value('pan_truncation', $this->_submitValues);
177 }
178 elseif ($paymentInstrumentName == 'Check') {
179 $params['check_number'] = CRM_Utils_Array::value('check_number', $this->_submitValues);
180 }
181
182 $this->submit($params);
183
184 CRM_Core_Session::singleton()->pushUserContext(CRM_Utils_System::url(CRM_Utils_System::currentPath()));
185 }
186
187 /**
188 * Wrapper function to process form submission
189 *
190 * @param array $submittedValues
191 *
192 */
193 protected function submit($submittedValues) {
194 // if payment instrument is changed then
195 // 1. Record a new reverse financial transaction with old payment instrument
196 // 2. Record a new financial transaction with new payment instrument
197 // 3. Add EntityFinancialTrxn records to relate with corresponding financial item and contribution
198 if ($submittedValues['payment_instrument_id'] != $this->_values['payment_instrument_id']) {
199 $previousFinanciaTrxn = $this->_values;
200 $newFinancialTrxn = $submittedValues;
201 unset($previousFinanciaTrxn['id'], $newFinancialTrxn['id']);
202 $previousFinanciaTrxn['trxn_date'] = CRM_Utils_Array::value('trxn_date', $submittedValues, date('YmdHis'));
203 $previousFinanciaTrxn['total_amount'] = -$previousFinanciaTrxn['total_amount'];
204 $previousFinanciaTrxn['net_amount'] = -$previousFinanciaTrxn['net_amount'];
205 $previousFinanciaTrxn['fee_amount'] = -$previousFinanciaTrxn['fee_amount'];
206 $previousFinanciaTrxn['contribution_id'] = $newFinancialTrxn['contribution_id'] = $this->_contributionID;
207
208 $newFinancialTrxn['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getInstrumentFinancialAccount($submittedValues['payment_instrument_id']);
209 foreach (['total_amount', 'fee_amount', 'net_amount', 'currency', 'is_payment', 'status_id'] as $fieldName) {
210 $newFinancialTrxn[$fieldName] = $this->_values[$fieldName];
211 }
212
213 foreach ([$previousFinanciaTrxn, $newFinancialTrxn] as $financialTrxnParams) {
214 $financialTrxn = civicrm_api3('FinancialTrxn', 'create', $financialTrxnParams);
215 $trxnParams = [
216 'total_amount' => $financialTrxnParams['total_amount'],
217 'contribution_id' => $this->_contributionID,
218 ];
219 $contributionTotalAmount = CRM_Core_DAO::getFieldValue('CRM_Contribute_BAO_Contribution', $this->_contributionID, 'total_amount');
220 CRM_Contribute_BAO_Contribution::assignProportionalLineItems($trxnParams, $financialTrxn['id'], $contributionTotalAmount);
221 }
222 }
223 else {
224 // simply update the financial trxn
225 civicrm_api3('FinancialTrxn', 'create', $submittedValues);
226 }
227
228 self::updateRelatedContribution($submittedValues, $this->_contributionID);
229 }
230
231 /**
232 * Wrapper for unit testing the post process submit function.
233 *
234 * @param array $params
235 */
236 public function testSubmit($params) {
237 $this->_id = $params['id'];
238 $this->_contributionID = $params['contribution_id'];
239 $this->_values = civicrm_api3('FinancialTrxn', 'getsingle', ['id' => $params['id']]);
240
241 $this->submit($params);
242 }
243
244 /**
245 * Function to update contribution's check_number and trxn_id by
246 * concatenating values from financial trxn's check_number and trxn_id respectively
247 *
248 * @param array $params
249 * @param int $contributionID
250 */
251 public static function updateRelatedContribution($params, $contributionID) {
252 $contributionDAO = new CRM_Contribute_DAO_Contribution();
253 $contributionDAO->id = $contributionID;
254 $contributionDAO->find(TRUE);
255
256 foreach (['trxn_id', 'check_number'] as $fieldName) {
257 if (!empty($params[$fieldName])) {
258 if (!empty($contributionDAO->$fieldName)) {
259 $values = explode(',', $contributionDAO->$fieldName);
260 // if submitted check_number or trxn_id value is
261 // already present then ignore else add to $values array
262 if (!in_array($params[$fieldName], $values)) {
263 $values[] = $params[$fieldName];
264 }
265 $contributionDAO->$fieldName = implode(',', $values);
266 }
267 }
268 }
269
270 $contributionDAO->save();
271 }
272
273 /**
274 * Get payment fields
275 */
276 public function getPaymentFields() {
277 $paymentFields = [
278 'payment_instrument_id' => [
279 'is_required' => TRUE,
280 'add_field' => TRUE,
281 ],
282 'check_number' => [
283 'is_required' => FALSE,
284 'add_field' => TRUE,
285 ],
286 // @TODO we need to show card type icon in place of select field
287 'card_type_id' => [
288 'is_required' => FALSE,
289 'add_field' => TRUE,
290 ],
291 'pan_truncation' => [
292 'is_required' => FALSE,
293 'add_field' => TRUE,
294 ],
295 'trxn_id' => [
296 'add_field' => TRUE,
297 'is_required' => FALSE,
298 ],
299 'trxn_date' => [
300 'htmlType' => 'datepicker',
301 'name' => 'trxn_date',
302 'title' => ts('Transaction Date'),
303 'is_required' => TRUE,
304 'attributes' => [
305 'date' => 'yyyy-mm-dd',
306 'time' => 24,
307 ],
308 ],
309 'total_amount' => [
310 'htmlType' => 'text',
311 'name' => 'total_amount',
312 'title' => ts('Total Amount'),
313 'is_required' => TRUE,
314 'attributes' => [
315 'readonly' => TRUE,
316 'size' => 6,
317 ],
318 ],
319 ];
320
321 return $paymentFields;
322 }
323
324 }