Passed
Pull Request — master (#48)
by
unknown
02:31
created

WC_PG_Notification_Handler   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 535
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 218
c 0
b 0
f 0
dl 0
loc 535
rs 4.08
wmc 59

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getPagantisOrder() 0 8 2
A getPagantisOrderId() 0 10 2
A updateBdInfo() 0 13 1
A getMerchantOrder() 0 7 2
A validateAmount() 0 6 2
A checkMerchantOrderStatus() 0 24 2
A getOrigin() 0 3 1
A checkPagantisStatus() 0 19 5
A checkDbTable() 0 12 2
A blockConcurrency() 0 27 5
A saveOrder() 0 25 5
B processInformation() 0 41 8
A setOrigin() 0 3 1
A checkOrderStatus() 0 15 4
A checkConcurrency() 0 9 2
A buildResponse() 0 18 3
A confirmPagantisOrder() 0 16 3
A processMerchantOrder() 0 4 1
A insertLog() 0 14 2
A rollbackMerchantOrder() 0 3 1
A unblockConcurrency() 0 12 3
A checkDbLogTable() 0 14 2

How to fix   Complexity   

Complex Class

Complex classes like WC_PG_Notification_Handler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WC_PG_Notification_Handler, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
use Pagantis\OrdersApiClient\Client;
4
use Pagantis\ModuleUtils\Exception\ConcurrencyException;
5
use Pagantis\ModuleUtils\Exception\AlreadyProcessedException;
6
use Pagantis\ModuleUtils\Exception\AmountMismatchException;
7
use Pagantis\ModuleUtils\Exception\MerchantOrderNotFoundException;
8
use Pagantis\ModuleUtils\Exception\NoIdentificationException;
9
use Pagantis\ModuleUtils\Exception\OrderNotFoundException;
10
use Pagantis\ModuleUtils\Exception\QuoteNotFoundException;
11
use Pagantis\ModuleUtils\Exception\UnknownException;
12
use Pagantis\ModuleUtils\Exception\WrongStatusException;
13
use Pagantis\ModuleUtils\Model\Response\JsonSuccessResponse;
14
use Pagantis\ModuleUtils\Model\Response\JsonExceptionResponse;
15
use Pagantis\ModuleUtils\Model\Log\LogEntry;
16
use Pagantis\OrdersApiClient\Model\Order;
17
18
if (!defined('ABSPATH')) {
19
    exit;
20
}
21
22
class WC_PG_Notification_Handler extends WC_Pagantis_Gateway
23
{
24
    /**
25
    * Concurrency table name
26
    */
27
    const CONCURRENCY_TABLE = 'pagantis_concurrency';
28
29
    /**
30
    * Seconds to expire a locked request
31
    */
32
    const CONCURRENCY_TIMEOUT = 5;
33
34
    /**
35
     * Pagantis Order Number
36
     *
37
    * @var mixed $pagantisOrder
38
    */
39
    protected $pagantisOrder;
40
41
    /**
42
     *  Origin of Order
43
     *
44
    * @var $string $origin
0 ignored issues
show
Documentation Bug introduced by
The doc comment $string at position 0 could not be parsed: Unknown type name '$string' at position 0 in $string.
Loading history...
45
    */
46
    public $origin;
47
48
    /**
49
     * WC_Order object
50
     *
51
    * @var $string
0 ignored issues
show
Documentation Bug introduced by
The doc comment $string at position 0 could not be parsed: Unknown type name '$string' at position 0 in $string.
Loading history...
52
    */
53
    public $order;
54
55
    /**
56
    * @var mixed $woocommerceOrderId
57
    */
58
    protected $woocommerceOrderId = '';
59
60
    /**
61
    * @var mixed $cfg
62
    */
63
    protected $cfg;
64
65
    /**
66
 * @var Client $orderClient
67
*/
68
    protected $orderClient;
69
70
    /**
71
 * @var WC_Order $woocommerceOrder
0 ignored issues
show
Bug introduced by
The type WC_Order was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
72
*/
73
    protected $woocommerceOrder;
74
75
    /**
76
 * @var mixed $pagantisOrderId
77
*/
78
    protected $pagantisOrderId = '';
79
80
    /**
81
     * Validation vs PagantisClient
82
     *
83
     * @return JsonExceptionResponse|JsonSuccessResponse
84
     * @throws ConcurrencyException
85
     */
86
    public function processInformation()
87
    {
88
        require_once dirname(__FILE__) . '/../includes/class-wc-pagantis-logger.php';
89
        require_once dirname(__FILE__) . '/../includes/functions.php';
90
        try {
91
            require_once(__ROOT__.'/vendor/autoload.php');
92
            try {
93
                if ($_SERVER['REQUEST_METHOD'] === 'GET' && $_GET['origin'] === 'notification') {
94
                    return $this->buildResponse();
95
                }
96
                $this->checkConcurrency();
97
                $this->getMerchantOrder();
98
                $this->getPagantisOrderId();
99
                $this->getPagantisOrder();
100
                $checkAlreadyProcessed = $this->checkOrderStatus();
101
                if ($checkAlreadyProcessed) {
102
                    return $this->buildResponse();
103
                }
104
                $this->validateAmount();
105
                if ($this->checkMerchantOrderStatus()) {
106
                    $this->processMerchantOrder();
107
                }
108
            } catch (\Exception $exception) {
109
                WC_Pagantis_Logger::insert_log_entry_in_wpdb($exception);
110
111
                return $this->buildResponse($exception);
112
            }
113
114
            try {
115
                $this->confirmPagantisOrder();
116
117
                return $this->buildResponse();
118
            } catch (\Exception $exception) {
119
                $this->rollbackMerchantOrder();
120
                WC_Pagantis_Logger::insert_log_entry_in_wpdb($exception);
121
122
                return $this->buildResponse($exception);
123
            }
124
        } catch (\Exception $exception) {
125
            WC_Pagantis_Logger::insert_log_entry_in_wpdb($exception);
126
            return $this->buildResponse($exception);
127
        }
128
    }
129
130
    /**
131
     * COMMON FUNCTIONS
132
     */
133
134
    /**
135
     * @throws ConcurrencyException
136
     * @throws QuoteNotFoundException
137
     */
138
    private function checkConcurrency()
139
    {
140
        $this->woocommerceOrderId = $_GET['order-received'];
141
        if ($this->woocommerceOrderId === '') {
142
            throw new QuoteNotFoundException();
143
        }
144
145
        $this->unblockConcurrency();
146
        $this->blockConcurrency($this->woocommerceOrderId);
147
    }
148
149
    /**
150
     * @throws MerchantOrderNotFoundException
151
     */
152
    private function getMerchantOrder()
153
    {
154
        try {
155
            $this->woocommerceOrder = new WC_Order($this->woocommerceOrderId);
156
            $this->woocommerceOrder->set_payment_method_title(Ucfirst(PAGANTIS_PLUGIN_ID));
157
        } catch (\Exception $e) {
158
            throw new MerchantOrderNotFoundException();
159
        }
160
    }
161
162
    /**
163
     * @throws NoIdentificationException
164
     */
165
    private function getPagantisOrderId()
166
    {
167
        global $wpdb;
168
        $this->checkDbTable();
169
        $tableName             = $wpdb->prefix.PAGANTIS_WC_ORDERS_TABLE;
170
        $queryResult           = $wpdb->get_row("select order_id from $tableName where id='".$this->woocommerceOrderId."'");
171
        $this->pagantisOrderId = $queryResult->order_id;
172
173
        if ($this->pagantisOrderId === '') {
174
            throw new NoIdentificationException();
175
        }
176
    }
177
178
    /**
179
     * @throws OrderNotFoundException
180
     */
181
    private function getPagantisOrder()
182
    {
183
        try {
184
            $this->cfg           = get_option('woocommerce_pagantis_settings');
0 ignored issues
show
Bug introduced by
The function get_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

184
            $this->cfg           = /** @scrutinizer ignore-call */ get_option('woocommerce_pagantis_settings');
Loading history...
185
            $this->orderClient   = new Client($this->cfg['pagantis_public_key'], $this->cfg['pagantis_private_key']);
186
            $this->pagantisOrder = $this->orderClient->getOrder($this->pagantisOrderId);
187
        } catch (\Exception $e) {
188
            throw new OrderNotFoundException();
189
        }
190
    }
191
192
    /**
193
     * @return bool
194
     * @throws WrongStatusException
195
     */
196
    private function checkOrderStatus()
197
    {
198
        try {
199
            $this->checkPagantisStatus(array('AUTHORIZED'));
200
        } catch (\Exception $e) {
201
            if ($this->pagantisOrder instanceof Order) {
202
                $status = $this->pagantisOrder->getStatus();
203
            } else {
204
                $status = '-';
205
            }
206
207
            if ($status === Order::STATUS_CONFIRMED) {
208
                return true;
209
            }
210
            throw new WrongStatusException($status);
211
        }
212
    }
213
214
    /**
215
     * @return bool
216
     */
217
    private function checkMerchantOrderStatus()
218
    {
219
        //Order status reference => https://docs.woocommerce.com/document/managing-orders/
220
        $validStatus   = array('on-hold', 'pending', 'failed', 'processing', 'completed');
221
        $isValidStatus = apply_filters(
0 ignored issues
show
Bug introduced by
The function apply_filters was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

221
        $isValidStatus = /** @scrutinizer ignore-call */ apply_filters(
Loading history...
222
            'woocommerce_valid_order_statuses_for_payment_complete',
223
            $validStatus,
224
            $this
225
        );
226
227
        if (!$this->woocommerceOrder->has_status($isValidStatus)) { // TO CONFIRM
228
            $logMessage = 'WARNING checkMerchantOrderStatus.' .
229
                          ' Merchant order id:'.$this->woocommerceOrder->get_id().
230
                          ' Merchant order status:'.$this->woocommerceOrder->get_status().
231
                          ' Pagantis order id:'.$this->pagantisOrder->getStatus().
232
                          ' Pagantis order status:'.$this->pagantisOrder->getId();
233
234
            WC_Pagantis_Logger::insert_log_entry_in_wpdb(null, $logMessage);
235
            $this->woocommerceOrder->add_order_note($logMessage);
236
            $this->woocommerceOrder->save();
237
            return false;
238
        }
239
240
        return true; //TO SAVE
241
    }
242
243
    /**
244
     * @throws AmountMismatchException
245
     */
246
    private function validateAmount()
247
    {
248
        $pagantisAmount = $this->pagantisOrder->getShoppingCart()->getTotalAmount();
249
        $wcAmount       = intval(strval(100 * $this->woocommerceOrder->get_total()));
250
        if ($pagantisAmount != $wcAmount) {
251
            throw new AmountMismatchException($pagantisAmount, $wcAmount);
252
        }
253
    }
254
255
    /**
256
     * @throws Exception
257
     */
258
    private function processMerchantOrder()
259
    {
260
        $this->saveOrder();
261
        $this->updateBdInfo();
262
    }
263
264
    /**
265
     * @return false|string
266
     * @throws UnknownException
267
     */
268
    private function confirmPagantisOrder()
269
    {
270
        try {
271
            $this->pagantisOrder = $this->orderClient->confirmOrder($this->pagantisOrderId);
272
        } catch (\Exception $e) {
273
            $this->pagantisOrder = $this->orderClient->getOrder($this->pagantisOrderId);
274
            if ($this->pagantisOrder->getStatus() !== Order::STATUS_CONFIRMED) {
275
                throw new UnknownException($e->getMessage());
276
            } else {
277
                $logMessage = 'Concurrency issue: Order_id '.$this->pagantisOrderId.' was confirmed by other process';
278
                WC_Pagantis_Logger::insert_log_entry_in_wpdb(null, $logMessage);
279
            }
280
        }
281
282
        $jsonResponse = new JsonSuccessResponse();
283
        return $jsonResponse->toJson();
284
    }
285
286
    /**
287
     * UTILS FUNCTIONS
288
     */
289
    /**
290
 * STEP 1 CC - Check concurrency
291
*/
292
    /**
293
     * Check if orders table exists
294
     */
295
    private function checkDbTable()
296
    {
297
        global $wpdb;
298
        $tableName = $wpdb->prefix.PAGANTIS_WC_ORDERS_TABLE;
299
300
        if ($wpdb->get_var("SHOW TABLES LIKE '$tableName'") != $tableName) {
301
            $charset_collate = $wpdb->get_charset_collate();
302
            $sql             = "CREATE TABLE $tableName (id int, order_id varchar(50), wc_order_id varchar(50), 
303
                  UNIQUE KEY id (id)) $charset_collate";
304
305
            require_once(ABSPATH.'wp-admin/includes/upgrade.php');
0 ignored issues
show
Bug introduced by
The constant ABSPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
306
            dbDelta($sql);
0 ignored issues
show
Bug introduced by
The function dbDelta was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

306
            /** @scrutinizer ignore-call */ 
307
            dbDelta($sql);
Loading history...
307
        }
308
    }
309
310
    /**
311
     * Check if logs table exists
312
     */
313
    private function checkDbLogTable()
0 ignored issues
show
Unused Code introduced by
The method checkDbLogTable() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
314
    {
315
        global $wpdb;
316
        $tableName = $wpdb->prefix.PAGANTIS_LOGS_TABLE;
317
318
        if ($wpdb->get_var("SHOW TABLES LIKE '$tableName'") != $tableName) {
319
            $charset_collate = $wpdb->get_charset_collate();
320
            $sql             = "CREATE TABLE $tableName ( id int NOT NULL AUTO_INCREMENT, log text NOT NULL, 
321
                    createdAt timestamp DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY id (id)) $charset_collate";
322
323
            require_once(ABSPATH.'wp-admin/includes/upgrade.php');
0 ignored issues
show
Bug introduced by
The constant ABSPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
324
            dbDelta($sql);
0 ignored issues
show
Bug introduced by
The function dbDelta was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

324
            /** @scrutinizer ignore-call */ 
325
            dbDelta($sql);
Loading history...
325
        }
326
        return;
327
    }
328
329
    /**
330
 * STEP 2 GMO - Get Merchant Order
331
*/
332
    /**
333
 * STEP 3 GPOI - Get Pagantis OrderId
334
*/
335
    /**
336
 * STEP 4 GPO - Get Pagantis Order
337
*/
338
    /**
339
 * STEP 5 COS - Check Order Status
340
*/
341
342
    /**
343
     * @param $statusArray
344
     *
345
     * @throws \Exception
346
     */
347
    private function checkPagantisStatus($statusArray)
348
    {
349
        $pagantisStatus = array();
350
        foreach ($statusArray as $status) {
351
            $pagantisStatus[] = constant("\Pagantis\OrdersApiClient\Model\Order::STATUS_$status");
352
        }
353
354
        if ($this->pagantisOrder instanceof Order) {
355
            $payed = in_array($this->pagantisOrder->getStatus(), $pagantisStatus);
356
            if (!$payed) {
357
                if ($this->pagantisOrder instanceof Order) {
0 ignored issues
show
introduced by
$this->pagantisOrder is always a sub-type of Pagantis\OrdersApiClient\Model\Order.
Loading history...
358
                    $status = $this->pagantisOrder->getStatus();
359
                } else {
360
                    $status = '-';
361
                }
362
                throw new WrongStatusException($status);
363
            }
364
        } else {
365
            throw new OrderNotFoundException();
366
        }
367
    }
368
369
    /**
370
 * STEP 6 CMOS - Check Merchant Order Status
371
*/
372
    /**
373
 * STEP 7 VA - Validate Amount
374
*/
375
    /**
376
 * STEP 8 PMO - Process Merchant Order
377
*/
378
    /**
379
     * @throws \Exception
380
     */
381
    private function saveOrder()
382
    {
383
        global $woocommerce;
384
        $paymentResult = $this->woocommerceOrder->payment_complete();
385
        if ($paymentResult) {
386
            $metadataOrder = $this->pagantisOrder->getMetadata();
387
            $metadataInfo  = null;
388
            foreach ($metadataOrder as $metadataKey => $metadataValue) {
389
                if ($metadataKey === 'promotedProduct') {
390
                    $metadataInfo .= "/Producto promocionado = $metadataValue";
391
                }
392
            }
393
394
            if ($metadataInfo != null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $metadataInfo of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
395
                $this->woocommerceOrder->add_order_note($metadataInfo);
396
            }
397
398
            $this->woocommerceOrder->add_order_note("Notification received via $this->origin");
399
            $this->woocommerceOrder->reduce_order_stock();
400
            $this->woocommerceOrder->save();
401
402
            $woocommerce->cart->empty_cart();
403
            sleep(3);
404
        } else {
405
            throw new UnknownException('Order can not be saved');
406
        }
407
    }
408
409
    /**
410
     * Save the merchant order_id with the related identification
411
     */
412
    private function updateBdInfo()
413
    {
414
        global $wpdb;
415
416
        $this->checkDbTable();
417
        $tableName = $wpdb->prefix.PAGANTIS_WC_ORDERS_TABLE;
418
419
        $wpdb->update(
420
            $tableName,
421
            array('wc_order_id'=>$this->woocommerceOrderId),
422
            array('id' => $this->woocommerceOrderId),
423
            array('%s'),
424
            array('%d')
425
        );
426
    }
427
428
    /**
429
    * STEP 9 CPO - Confirmation Pagantis Order
430
    */
431
    private function rollbackMerchantOrder()
432
    {
433
        $this->woocommerceOrder->update_status('pending', __('Pending payment', 'woocommerce')); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
0 ignored issues
show
Bug introduced by
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

433
        $this->woocommerceOrder->update_status('pending', /** @scrutinizer ignore-call */ __('Pending payment', 'woocommerce')); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
Loading history...
434
    }
435
436
    /**
437
     * @param null $exception
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $exception is correct as it would always require null to be passed?
Loading history...
438
     * @param null $message
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $message is correct as it would always require null to be passed?
Loading history...
439
     */
440
    private function insertLog($exception = null, $message = null)
0 ignored issues
show
Unused Code introduced by
The method insertLog() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
441
    {
442
        global $wpdb;
443
444
        pg_wc_check_db_log_table();
445
        $logEntry = new LogEntry();
446
        if ($exception instanceof \Exception) {
447
            $logEntry = $logEntry->error($exception);
448
        } else {
449
            $logEntry = $logEntry->info($message);
450
        }
451
452
        $tableName = $wpdb->prefix.PAGANTIS_LOGS_TABLE;
453
        $wpdb->insert($tableName, array('log' => $logEntry->toJson()));
454
    }
455
456
    /**
457
     * @param null $orderId
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $orderId is correct as it would always require null to be passed?
Loading history...
458
     *
459
     * @throws ConcurrencyException
460
     */
461
    private function unblockConcurrency($orderId = null)
462
    {
463
        global $wpdb;
464
        $tableName = $wpdb->prefix.PAGANTIS_CONCURRENCY_TABLE;
465
        if ($orderId === null) {
0 ignored issues
show
introduced by
The condition $orderId === null is always true.
Loading history...
466
            $query = "DELETE FROM $tableName WHERE createdAt<(NOW()- INTERVAL ".self::CONCURRENCY_TIMEOUT.' SECOND)';
467
        } else {
468
            $query = "DELETE FROM $tableName WHERE order_id = $orderId";
469
        }
470
        $resultDelete = $wpdb->query($query);
471
        if ($resultDelete === false) {
472
            throw new ConcurrencyException();
473
        }
474
    }
475
476
    /**
477
     * @param $orderId
478
     *
479
     * @throws ConcurrencyException
480
     */
481
    private function blockConcurrency($orderId)
482
    {
483
        global $wpdb;
484
        $tableName    = $wpdb->prefix.PAGANTIS_CONCURRENCY_TABLE;
485
        $insertResult = $wpdb->insert($tableName, array('order_id' => $orderId));
486
        if ($insertResult === false) {
487
            if ($this->getOrigin() === 'Notify') {
488
                throw new ConcurrencyException();
489
            } else {
490
                $query           = sprintf(
491
                    'SELECT TIMESTAMPDIFF(SECOND,NOW()-INTERVAL %s SECOND, createdAt) as rest FROM %s WHERE %s',
492
                    self::CONCURRENCY_TIMEOUT,
493
                    $tableName,
494
                    "order_id=$orderId"
495
                );
496
                $resultSeconds   = $wpdb->get_row($query);
497
                $restSeconds     = isset($resultSeconds) ? ($resultSeconds->rest) : 0;
498
                $secondsToExpire = ($restSeconds>self::CONCURRENCY_TIMEOUT) ? self::CONCURRENCY_TIMEOUT : $restSeconds;
499
                sleep($secondsToExpire+1);
500
501
                $logMessage = sprintf(
502
                    'User waiting %s seconds, default seconds %s, bd time to expire %s seconds',
503
                    $secondsToExpire,
504
                    self::CONCURRENCY_TIMEOUT,
505
                    $restSeconds
506
                );
507
                WC_Pagantis_Logger::insert_log_entry_in_wpdb(null, $logMessage);
508
            }
509
        }
510
    }
511
512
    /**
513
     * @param null $exception
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $exception is correct as it would always require null to be passed?
Loading history...
514
     *
515
     * @return JsonExceptionResponse|JsonSuccessResponse
516
     * @throws ConcurrencyException
517
     */
518
    private function buildResponse($exception = null)
519
    {
520
        $this->unblockConcurrency($this->woocommerceOrderId);
521
522
        if ($exception === null) {
0 ignored issues
show
introduced by
The condition $exception === null is always true.
Loading history...
523
            $jsonResponse = new JsonSuccessResponse();
524
        } else {
525
            $jsonResponse = new JsonExceptionResponse();
526
            $jsonResponse->setException($exception);
527
        }
528
529
        $jsonResponse->setMerchantOrderId($this->woocommerceOrderId);
530
        $jsonResponse->setPagantisOrderId($this->pagantisOrderId);
531
532
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
533
            $jsonResponse->printResponse();
534
        } else {
535
            return $jsonResponse;
536
        }
537
    }
538
539
    /**
540
     * GETTERS & SETTERS
541
     */
542
543
    /**
544
     * @return mixed
545
     */
546
    public function getOrigin()
547
    {
548
        return $this->origin;
549
    }
550
551
    /**
552
     * @param mixed $origin
553
     */
554
    public function setOrigin($origin)
555
    {
556
        $this->origin = $origin;
557
    }
558
}
559