Passed
Push — master ( 782d1a...bdda1f )
by Aimeos
24:22 queued 15:04
created

Standard   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 571
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 127
dl 0
loc 571
rs 9.2
c 1
b 0
f 0
wmc 40

15 Methods

Rating   Name   Duplication   Size   Complexity  
A getName() 0 3 1
A getDescription() 0 3 1
B addBasketServices() 0 40 6
A addBasketProducts() 0 15 4
A names() 0 17 1
A createContext() 0 25 2
B process() 0 45 6
A addBasketCoupons() 0 29 4
A domains() 0 23 1
A createOrder() 0 14 1
A addBasketAddresses() 0 31 5
A ends() 0 17 1
A createPayment() 0 6 2
A run() 0 34 4
A max() 0 17 1

How to fix   Complexity   

Complex Class

Complex classes like Standard 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 Standard, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2018-2022
6
 * @package Controller
7
 * @subpackage Jobs
8
 */
9
10
11
namespace Aimeos\Controller\Jobs\Subscription\Process\Renew;
12
13
14
/**
15
 * Job controller for subscription processs renew.
16
 *
17
 * @package Controller
18
 * @subpackage Jobs
19
 */
20
class Standard
21
	extends \Aimeos\Controller\Jobs\Subscription\Process\Base
22
	implements \Aimeos\Controller\Jobs\Iface
23
{
24
	/** controller/jobs/subscription/process/renew/name
25
	 * Class name of the used subscription suggestions scheduler controller implementation
26
	 *
27
	 * Each default job controller can be replace by an alternative imlementation.
28
	 * To use this implementation, you have to set the last part of the class
29
	 * name as configuration value so the controller factory knows which class it
30
	 * has to instantiate.
31
	 *
32
	 * For example, if the name of the default class is
33
	 *
34
	 *  \Aimeos\Controller\Jobs\Subscription\Process\Renew\Standard
35
	 *
36
	 * and you want to replace it with your own version named
37
	 *
38
	 *  \Aimeos\Controller\Jobs\Subscription\Process\Renew\Myrenew
39
	 *
40
	 * then you have to set the this configuration option:
41
	 *
42
	 *  controller/jobs/subscription/process/renew/name = Myrenew
43
	 *
44
	 * The value is the last part of your own class name and it's case sensitive,
45
	 * so take care that the configuration value is exactly named like the last
46
	 * part of the class name.
47
	 *
48
	 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
49
	 * characters are possible! You should always start the last part of the class
50
	 * name with an upper case character and continue only with lower case characters
51
	 * or numbers. Avoid chamel case names like "MyRenew"!
52
	 *
53
	 * @param string Last part of the class name
54
	 * @since 2018.04
55
	 */
56
57
	/** controller/jobs/subscription/process/renew/decorators/excludes
58
	 * Excludes decorators added by the "common" option from the subscription process CSV job controller
59
	 *
60
	 * Decorators extrenew the functionality of a class by adding new aspects
61
	 * (e.g. log what is currently done), executing the methods of the underlying
62
	 * class only in certain conditions (e.g. only for logged in users) or
63
	 * modify what is returned to the caller.
64
	 *
65
	 * This option allows you to remove a decorator added via
66
	 * "controller/jobs/common/decorators/default" before they are wrapped
67
	 * around the job controller.
68
	 *
69
	 *  controller/jobs/subscription/process/renew/decorators/excludes = array( 'decorator1' )
70
	 *
71
	 * This would remove the decorator named "decorator1" from the list of
72
	 * common decorators ("\Aimeos\Controller\Jobs\Common\Decorator\*") added via
73
	 * "controller/jobs/common/decorators/default" to the job controller.
74
	 *
75
	 * @param array List of decorator names
76
	 * @since 2018.04
77
	 * @see controller/jobs/common/decorators/default
78
	 * @see controller/jobs/subscription/process/renew/decorators/global
79
	 * @see controller/jobs/subscription/process/renew/decorators/local
80
	 */
81
82
	/** controller/jobs/subscription/process/renew/decorators/global
83
	 * Adds a list of globally available decorators only to the subscription process CSV job controller
84
	 *
85
	 * Decorators extrenew the functionality of a class by adding new aspects
86
	 * (e.g. log what is currently done), executing the methods of the underlying
87
	 * class only in certain conditions (e.g. only for logged in users) or
88
	 * modify what is returned to the caller.
89
	 *
90
	 * This option allows you to wrap global decorators
91
	 * ("\Aimeos\Controller\Jobs\Common\Decorator\*") around the job controller.
92
	 *
93
	 *  controller/jobs/subscription/process/renew/decorators/global = array( 'decorator1' )
94
	 *
95
	 * This would add the decorator named "decorator1" defined by
96
	 * "\Aimeos\Controller\Jobs\Common\Decorator\Decorator1" only to the job controller.
97
	 *
98
	 * @param array List of decorator names
99
	 * @since 2018.04
100
	 * @see controller/jobs/common/decorators/default
101
	 * @see controller/jobs/subscription/process/renew/decorators/excludes
102
	 * @see controller/jobs/subscription/process/renew/decorators/local
103
	 */
104
105
	/** controller/jobs/subscription/process/renew/decorators/local
106
	 * Adds a list of local decorators only to the subscription process CSV job controller
107
	 *
108
	 * Decorators extrenew the functionality of a class by adding new aspects
109
	 * (e.g. log what is currently done), executing the methods of the underlying
110
	 * class only in certain conditions (e.g. only for logged in users) or
111
	 * modify what is returned to the caller.
112
	 *
113
	 * This option allows you to wrap local decorators
114
	 * ("\Aimeos\Controller\Jobs\Subscription\Process\Renew\Decorator\*") around the job
115
	 * controller.
116
	 *
117
	 *  controller/jobs/subscription/process/renew/decorators/local = array( 'decorator2' )
118
	 *
119
	 * This would add the decorator named "decorator2" defined by
120
	 * "\Aimeos\Controller\Jobs\Subscription\Process\Renew\Decorator\Decorator2"
121
	 * only to the job controller.
122
	 *
123
	 * @param array List of decorator names
124
	 * @since 2018.04
125
	 * @see controller/jobs/common/decorators/default
126
	 * @see controller/jobs/subscription/process/renew/decorators/excludes
127
	 * @see controller/jobs/subscription/process/renew/decorators/global
128
	 */
129
130
131
	/**
132
	 * Returns the localized name of the job.
133
	 *
134
	 * @return string Name of the job
135
	 */
136
	public function getName() : string
137
	{
138
		return $this->context()->translate( 'controller/jobs', 'Subscription process renew' );
139
	}
140
141
142
	/**
143
	 * Returns the localized description of the job.
144
	 *
145
	 * @return string Description of the job
146
	 */
147
	public function getDescription() : string
148
	{
149
		return $this->context()->translate( 'controller/jobs', 'Renews subscriptions at next date' );
150
	}
151
152
153
	/**
154
	 * Executes the job.
155
	 *
156
	 * @throws \Aimeos\Controller\Jobs\Exception If an error occurs
157
	 */
158
	public function run()
159
	{
160
		$date = date( 'Y-m-d' );
161
		$context = $this->context();
162
		$domains = $this->domains();
163
164
		$processors = $this->getProcessors( $this->names() );
165
		$manager = \Aimeos\MShop::create( $context, 'subscription' );
166
167
		$search = $manager->filter( true )->add( 'subscription.datenext', '<=', $date )->slice( 0, $this->max() );
168
		$search->add( $search->or( [
169
			$search->compare( '==', 'subscription.dateend', null ),
170
			$search->compare( '>', 'subscription.dateend', $date ),
171
		] ) );
172
		$cursor = $manager->cursor( $search );
173
174
		while( $items = $manager->iterate( $cursor, $domains ) )
175
		{
176
			foreach( $items as $item )
177
			{
178
				$manager->begin();
179
180
				try
181
				{
182
					$manager->save( $this->process( $item, $processors ) );
183
					$manager->commit();
184
				}
185
				catch( \Exception $e )
186
				{
187
					$manager->rollback();
188
189
					$str = 'Unable to renew subscription with ID "%1$s": %2$s';
190
					$msg = sprintf( $str, $item->getId(), $e->getMessage() . "\n" . $e->getTraceAsString() );
191
					$context->logger()->error( $msg, 'subscription/process/renew' );
192
				}
193
			}
194
		}
195
	}
196
197
198
	/**
199
	 * Adds the given addresses to the order
200
	 *
201
	 * @param \Aimeos\MShop\ContextIface Context object
0 ignored issues
show
Bug introduced by
The type Aimeos\Controller\Jobs\S...n\Process\Renew\Context 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...
202
	 * @param \Aimeos\MShop\Order\Item\Iface $newOrder Order object to add the addresses to
203
	 * @param \Aimeos\Map $addresses List of type as key and address object implementing \Aimeos\MShop\Order\Item\Address\Iface as value
204
	 * @return \Aimeos\MShop\Order\Item\Iface Order with addresses added
205
	 */
206
	protected function addBasketAddresses( \Aimeos\MShop\ContextIface $context,
207
		\Aimeos\MShop\Order\Item\Iface $newOrder, \Aimeos\Map $addresses ) : \Aimeos\MShop\Order\Item\Iface
208
	{
209
		foreach( $addresses as $type => $orderAddresses )
210
		{
211
			$idx = 0;
212
213
			foreach( $orderAddresses as $orderAddress ) {
214
				$newOrder->addAddress( $orderAddress->setId( null ), $type, $idx );
215
			}
216
		}
217
218
		if( !$newOrder->getCustomerId() ) {
219
			return $newOrder;
220
		}
221
222
		try
223
		{
224
			$customer = \Aimeos\MShop::create( $context, 'customer' )->get( $newOrder->getCustomerId() );
225
			$address = \Aimeos\MShop::create( $context, 'order/address' )->create();
226
227
			$type = \Aimeos\MShop\Order\Item\Address\Base::TYPE_PAYMENT;
228
			$newOrder->addAddress( $address->copyFrom( $customer->getPaymentAddress() ), $type, 0 );
229
		}
230
		catch( \Exception $e )
231
		{
232
			$msg = sprintf( 'Unable to add current address for customer with ID "%1$s"', $newOrder->getCustomerId() );
233
			$context->logger()->info( $msg, 'subscription/process/renew' );
234
		}
235
236
		return $newOrder;
237
	}
238
239
240
	/**
241
	 * Adds the given coupon codes to the order if enabled
242
	 *
243
	 * @param \Aimeos\MShop\ContextIface Context object
244
	 * @param \Aimeos\MShop\Order\Item\Iface $newOrder Order including product and addresses
245
	 * @param \Aimeos\Map $codes List of coupon codes that should be added to the given order
246
	 * @return \Aimeos\MShop\Order\Item\Iface Basket, maybe with coupons added
247
	 */
248
	protected function addBasketCoupons( \Aimeos\MShop\ContextIface $context,
249
		\Aimeos\MShop\Order\Item\Iface $newOrder, \Aimeos\Map $codes ) : \Aimeos\MShop\Order\Item\Iface
250
	{
251
		/** controller/jobs/subscription/process/renew/use-coupons
252
		 * Applies the coupons of the previous order also to the new one
253
		 *
254
		 * Reuse coupon codes added to the order by the customer the first time
255
		 * again in new subscription orders. If they have any effect depends on
256
		 * the codes still being active (status, time frame and count) and the
257
		 * decorators added to the coupon providers in the admin interface.
258
		 *
259
		 * @param boolean True to reuse coupon codes, false to remove coupons
260
		 * @category Developer
261
		 * @category User
262
		 * @since 2018.10
263
		 */
264
		if( $context->config()->get( 'controller/jobs/subscription/process/renew/use-coupons', false ) )
265
		{
266
			foreach( $codes as $code )
267
			{
268
				try {
269
					$newOrder->addCoupon( $code );
270
				} catch( \Aimeos\MShop\Plugin\Provider\Exception | \Aimeos\MShop\Coupon\Exception $e ) {
271
					$newOrder->deleteCoupon( $code );
272
				}
273
			}
274
		}
275
276
		return $newOrder;
277
	}
278
279
280
	/**
281
	 * Adds the given products to the order
282
	 *
283
	 * @param \Aimeos\MShop\ContextIface Context object
284
	 * @param \Aimeos\MShop\Order\Item\Iface $order Order to add the products to
285
	 * @param \Aimeos\Map $orderProducts List of product items Implementing \Aimeos\MShop\Order\Item\Product\Iface
286
	 * @param string $orderProductId Unique ID of the ordered subscription product
287
	 * @return \Aimeos\MShop\Order\Item\Iface Order with products added
288
	 */
289
	protected function addBasketProducts( \Aimeos\MShop\ContextIface $context,
290
		\Aimeos\MShop\Order\Item\Iface $newOrder, \Aimeos\Map $orderProducts, $orderProductId ) : \Aimeos\MShop\Order\Item\Iface
291
	{
292
		foreach( $orderProducts as $orderProduct )
293
		{
294
			if( $orderProduct->getId() == $orderProductId )
295
			{
296
				foreach( $orderProduct->getAttributeItems() as $attrItem ) {
297
					$attrItem->setId( null );
298
				}
299
				$newOrder->addProduct( $orderProduct->setId( null ) );
300
			}
301
		}
302
303
		return $newOrder;
304
	}
305
306
307
	/**
308
	 * Adds a matching delivery and payment service to the order
309
	 *
310
	 * @param \Aimeos\MShop\ContextIface Context object
311
	 * @param \Aimeos\MShop\Order\Item\Iface $order Order to add the services to
312
	 * @param \Aimeos\Map $services Associative list of type as key and list of service objects implementing \Aimeos\MShop\Order\Item\Service\Iface as values
313
	 * @return \Aimeos\MShop\Order\Item\Iface Order with delivery and payment service added
314
	 */
315
	protected function addBasketServices( \Aimeos\MShop\ContextIface $context,
316
		\Aimeos\MShop\Order\Item\Iface $newOrder, \Aimeos\Map $services ) : \Aimeos\MShop\Order\Item\Iface
317
	{
318
		$type = \Aimeos\MShop\Order\Item\Service\Base::TYPE_PAYMENT;
319
320
		if( isset( $services[$type] ) )
321
		{
322
			$idx = 0;
323
324
			foreach( $services[$type] as $orderService )
325
			{
326
				foreach( $orderService->getAttributeItems() as $attrItem ) {
327
					$attrItem->setId( null );
328
				}
329
				$newOrder->addService( $orderService->setId( null ), $type, $idx++ );
330
			}
331
		}
332
333
		$idx = 0;
334
		$type = \Aimeos\MShop\Order\Item\Service\Base::TYPE_DELIVERY;
335
336
		$serviceManager = \Aimeos\MShop::create( $context, 'service' );
337
		$orderServiceManager = \Aimeos\MShop::create( $context, 'order/service' );
338
339
		$search = $serviceManager->filter( true );
340
		$search->setSortations( [$search->sort( '+', 'service.position' )] );
341
		$search->setConditions( $search->compare( '==', 'service.type', $type ) );
342
343
		foreach( $serviceManager->search( $search, ['media', 'price', 'text'] ) as $item )
344
		{
345
			$provider = $serviceManager->getProvider( $item, $item->getType() );
346
347
			if( $provider->isAvailable( $newOrder ) === true )
348
			{
349
				$orderServiceItem = $orderServiceManager->create()->copyFrom( $item );
350
				return $newOrder->addService( $orderServiceItem, $type, $idx++ );
351
			}
352
		}
353
354
		return $newOrder;
355
	}
356
357
358
	/**
359
	 * Creates a new context based on the order and the customer the subscription belongs to
360
	 *
361
	 * @param \Aimeos\MShop\Subscription\Item\Iface $order Subscription item with associated order
362
	 * @return \Aimeos\MShop\ContextIface New context object
363
	 */
364
	protected function createContext( \Aimeos\MShop\Subscription\Item\Iface $subscription ) : \Aimeos\MShop\ContextIface
365
	{
366
		$context = clone $this->context();
367
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
368
369
		$order = $subscription->getOrderItem();
370
		$sitecode = $order->getSiteCode();
371
		$locale = $order->locale();
372
373
		$manager = \Aimeos\MShop::create( $context, 'locale' );
374
		$locale = $manager->bootstrap( $sitecode, $locale->getLanguageId(), $locale->getCurrencyId(), false, $level );
375
376
		$context->setLocale( $locale );
377
378
		try
379
		{
380
			$manager = \Aimeos\MShop::create( $context, 'customer' );
381
			$customerItem = $manager->get( $order->getCustomerId(), ['customer/group'] );
382
383
			$context->setUserId( $order->getCustomerId() );
384
			$context->setGroupIds( $customerItem->getGroups() );
385
		}
386
		catch( \Exception $e ) {} // Subscription without account
387
388
		return $context;
389
	}
390
391
392
	/**
393
	 * Creates and stores a new order from the given subscription
394
	 *
395
	 * @param \Aimeos\MShop\ContextIface Context object
396
	 * @param \Aimeos\MShop\Subscription\Item\Iface $subscription Subscription item with associated order
397
	 * @return \Aimeos\MShop\Order\Item\Iface New order item including addresses, coupons, products and services
398
	 */
399
	protected function createOrder( \Aimeos\MShop\ContextIface $context,
400
		\Aimeos\MShop\Subscription\Item\Iface $subscription ) : \Aimeos\MShop\Order\Item\Iface
401
	{
402
		$order = $subscription->getOrderItem();
403
404
		$manager = \Aimeos\MShop::create( $context, 'order' );
405
		$newOrder = $manager->create()->setCustomerId( $order->getCustomerId() )->setChannel( 'subscription' );
406
407
		$newOrder = $this->addBasketAddresses( $context, $newOrder, $order->getAddresses() );
408
		$newOrder = $this->addBasketProducts( $context, $newOrder, $order->getProducts(), $subscription->getOrderProductId() );
409
		$newOrder = $this->addBasketServices( $context, $newOrder, $order->getServices() );
410
		$newOrder = $this->addBasketCoupons( $context, $newOrder, $order->getCoupons()->keys() );
411
412
		return $newOrder->check();
413
	}
414
415
416
	/**
417
	 * Creates a new payment for the given order and invoice
418
	 *
419
	 * @param \Aimeos\MShop\ContextIface Context object
420
	 * @param \Aimeos\MShop\Order\Item\Iface $order Complete order with product, addresses and services
421
	 * @param \Aimeos\MShop\Order\Item\Iface New invoice item associated to the order
0 ignored issues
show
Bug introduced by
The type Aimeos\Controller\Jobs\S...ption\Process\Renew\New 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...
422
	 */
423
	protected function createPayment( \Aimeos\MShop\ContextIface $context, \Aimeos\MShop\Order\Item\Iface $order )
424
	{
425
		$manager = \Aimeos\MShop::create( $context, 'service' );
426
427
		foreach( $order->getService( \Aimeos\MShop\Order\Item\Service\Base::TYPE_PAYMENT ) as $service ) {
428
			$manager->getProvider( $manager->get( $service->getServiceId() ), 'payment' )->repay( $order );
429
		}
430
	}
431
432
433
	/**
434
	 * Returns the domains that should be fetched together with the order data
435
	 *
436
	 * @return array List of domain names
437
	 */
438
	protected function domains() : array
439
	{
440
		/** controller/jobs/subscription/process/domains
441
		 * Associated items that should be available too in the subscription
442
		 *
443
		 * Orders consist of address, coupons, products and services. They can be
444
		 * fetched together with the subscription items and passed to the processor.
445
		 * Available domains for those items are:
446
		 *
447
		 * - order
448
		 * - order/address
449
		 * - order/coupon
450
		 * - order/product
451
		 * - order/service
452
		 *
453
		 * @param array Referenced domain names
454
		 * @since 2022.04
455
		 * @see controller/common/subscription/process/processors
456
		 * @see controller/common/subscription/process/payment-days
457
		 * @see controller/common/subscription/process/payment-status
458
		 */
459
		$domains = ['order', 'order/address', 'order/coupon', 'order/product', 'order/service'];
460
		return $this->context()->config()->get( 'controller/jobs/subscription/process/domains', $domains );
461
	}
462
463
464
	/**
465
	 * Returns if subscriptions should end if payment couldn't be captured
466
	 *
467
	 * @return bool TRUE if subscription should end, FALSE if not
468
	 */
469
	protected function ends() : bool
470
	{
471
		/** controller/common/subscription/process/payment-ends
472
		 * Subscriptions ends if payment couldn't be captured
473
		 *
474
		 * By default, a subscription ends automatically if the next payment couldn't
475
		 * be captured. When setting this configuration to FALSE, the subscription job
476
		 * controller will try to capture the payment at the next run again until the
477
		 * subscription is deactivated manually.
478
		 *
479
		 * @param bool TRUE if payment failures ends the subscriptions, FALSE if not
480
		 * @since 2019.10
481
		 * @see controller/common/subscription/process/processors
482
		 * @see controller/common/subscription/process/payment-days
483
		 * @see controller/common/subscription/process/payment-status
484
		 */
485
		return (bool) $this->context()->config()->get( 'controller/common/subscription/process/payment-ends', true );
486
	}
487
488
489
	/**
490
	 * Returns the maximum number of orders processed at once
491
	 *
492
	 * @return int Maximum number of items
493
	 */
494
	protected function max() : int
495
	{
496
		/** controller/jobs/subscription/process/batch-max
497
		 * Maximum number of subscriptions processed at once by the subscription process job
498
		 *
499
		 * This setting configures the maximum number of subscriptions including
500
		 * orders that will be processed at once. Bigger batches an improve the
501
		 * performance but requires more memory.
502
		 *
503
		 * @param integer Number of subscriptions
504
		 * @since 2023.04
505
		 * @see controller/jobs/subscription/process/domains
506
		 * @see controller/jobs/subscription/process/names
507
		 * @see controller/jobs/subscription/process/payment-days
508
		 * @see controller/jobs/subscription/process/payment-status
509
		 */
510
		return $this->context()->config()->get( 'controller/jobs/subscription/process/batch-max', 100 );
511
	}
512
513
514
	/**
515
	 * Returns the names of the subscription processors
516
	 *
517
	 * @return array List of processor names
518
	 */
519
	protected function names() : array
520
	{
521
		/** controller/jobs/subscription/process/processors
522
		 * List of processor names that should be executed for subscriptions
523
		 *
524
		 * For each subscription a number of processors for different tasks can be executed.
525
		 * They can for example add a group to the customers' account during the customer
526
		 * has an active subscribtion.
527
		 *
528
		 * @param array List of processor names
529
		 * @since 2018.04
530
		 * @see controller/jobs/subscription/process/domains
531
		 * @see controller/jobs/subscription/process/max
532
		 * @see controller/jobs/subscription/process/payment-days
533
		 * @see controller/jobs/subscription/process/payment-status
534
		 */
535
		return (array) $this->context()->config()->get( 'controller/jobs/subscription/process/processors', [] );
536
	}
537
538
539
	/**
540
	 * Runs the subscription processors for the passed item
541
	 *
542
	 * @param \Aimeos\MShop\Subscription\Item\Iface $item Subscription item
543
	 * @param iterable $processors List of processor objects to run on the item
544
	 * @return \Aimeos\MShop\Subscription\Item\Iface Updated subscription item
545
	 */
546
	protected function process( \Aimeos\MShop\Subscription\Item\Iface $item, iterable $processors
547
		) : \Aimeos\MShop\Subscription\Item\Iface
548
	{
549
		$context = $this->context();
550
		$orderManager = \Aimeos\MShop::create( $context, 'order' );
551
552
		$context = $this->createContext( $item );
553
		$newOrder = $this->createOrder( $context, $item );
554
555
		foreach( $processors as $processor ) {
556
			$processor->renewBefore( $item, $newOrder );
557
		}
558
559
		$newOrder = $orderManager->save( $newOrder->check() );
560
561
		try
562
		{
563
			$this->createPayment( $context, $newOrder );
0 ignored issues
show
Bug introduced by
It seems like $newOrder can also be of type Aimeos\Map; however, parameter $order of Aimeos\Controller\Jobs\S...andard::createPayment() does only seem to accept Aimeos\MShop\Order\Item\Iface, maybe add an additional type check? ( Ignorable by Annotation )

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

563
			$this->createPayment( $context, /** @scrutinizer ignore-type */ $newOrder );
Loading history...
564
565
			$interval = new \DateInterval( $item->getInterval() );
566
			$date = date_create( (string) $item->getDateNext() )->add( $interval )->format( 'Y-m-d' );
567
568
			$item->setDateNext( $date )->setPeriod( $item->getPeriod() + 1 )->setReason( null );
569
		}
570
		catch( \Exception $e )
571
		{
572
			if( $e->getCode() < 1 ) // not a soft error
573
			{
574
				$item->setReason( \Aimeos\MShop\Subscription\Item\Iface::REASON_PAYMENT );
575
576
				if( $this->ends() ) {
577
					$item->setDateEnd( date_create()->format( 'Y-m-d' ) );
578
				}
579
			}
580
581
			throw $e;
582
		}
583
		finally // will be always executed, even if exception is rethrown in catch()
584
		{
585
			foreach( $processors as $processor ) {
586
				$processor->renewAfter( $item, $newOrder );
587
			}
588
		}
589
590
		return $item;
591
	}
592
}
593