Completed
Push — master ( fbc717...7baaae )
by Aimeos
09:41
created

Autofill   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 322
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 322
rs 8.8
c 1
b 0
f 0
wmc 36
lcom 2
cbo 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A checkConfigBE() 0 6 1
A getConfigBE() 0 4 1
A register() 0 5 1
C update() 0 37 7
B getServiceItem() 0 36 4
A setAddresses() 0 16 4
B setServices() 0 21 5
A setAddressDefault() 0 20 4
C setServicesDefault() 0 23 9
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2014
6
 * @copyright Aimeos (aimeos.org), 2015-2017
7
 * @package MShop
8
 * @subpackage Plugin
9
 */
10
11
12
namespace Aimeos\MShop\Plugin\Provider\Order;
13
14
15
/**
16
 * Adds address and service items to the basket
17
 *
18
 * This plugins acts if a product is added to the basket or a delivery/payment
19
 * service is removed from the basket. It adds the a delivery/payment service
20
 * item and the customer address(es) to the basket.
21
 *
22
 * The following options are available:
23
 * - autofill.address: 1 (add billing address of the logged in customer to the basket)
24
 * - autofill.delivery: 1 (add the first delivery option to the basket)
25
 * - autofill.deliverycode: '...' and autofill.delivery: 1 (add specific delivery option to the basket)
26
 * - autofill.payment: 1 (add the first payment option to the basket)
27
 * - autofill.paymentcode: '...' and autofill.payment: 1 (add specific payment option to the basket)
28
 * - autofill.useorder: 1 (use last order of the customer to pre-fill addresses or services)
29
 * - autofill.orderservice: 1 (add delivery and payment services from the last order of the customer)
30
 * - autofill.orderaddress: 1 (add billing and delivery addresses from the last order of the customer)
31
 *
32
 * This plugin interacts with other plugins that add products or remove services!
33
 * Especially the "ServiceUpdate" plugin may remove a delivery/payment option
34
 * that isn't available any more based on the current basket content.
35
 *
36
 * To trace the execution and interaction of the plugins, set the log level to DEBUG:
37
 *	madmin/log/manager/standard/loglevel = 7
38
 *
39
 * @package MShop
40
 * @subpackage Plugin
41
 */
42
class Autofill
43
	extends \Aimeos\MShop\Plugin\Provider\Factory\Base
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces between "Base" and comma; 1 found
Loading history...
44
	implements \Aimeos\MShop\Plugin\Provider\Factory\Iface
45
{
46
	private $beConfig = array(
47
		'autofill.address' => array(
48
			'code' => 'autofill.address',
49
			'internalcode'=> 'autofill.address',
50
			'label'=> 'Add customer address automatically',
51
			'type'=> 'boolean',
52
			'internaltype'=> 'boolean',
53
			'default'=> '',
54
			'required'=> false,
55
		),
56
		'autofill.delivery' => array(
57
			'code' => 'autofill.delivery',
58
			'internalcode'=> 'autofill.delivery',
59
			'label'=> 'Add delivery option automatically',
60
			'type'=> 'boolean',
61
			'internaltype'=> 'boolean',
62
			'default'=> '',
63
			'required'=> false,
64
		),
65
		'autofill.deliverycode' => array(
66
			'code' => 'autofill.deliverycode',
67
			'internalcode'=> 'autofill.deliverycode',
68
			'label'=> 'Add delivery by code',
69
			'type'=> 'string',
70
			'internaltype'=> 'string',
71
			'default'=> '',
72
			'required'=> false,
73
		),
74
		'autofill.payment' => array(
75
			'code' => 'autofill.payment',
76
			'internalcode'=> 'autofill.payment',
77
			'label'=> 'Add payment option automatically',
78
			'type'=> 'boolean',
79
			'internaltype'=> 'boolean',
80
			'default'=> '',
81
			'required'=> false,
82
		),
83
		'autofill.paymentcode' => array(
84
			'code' => 'autofill.paymentcode',
85
			'internalcode'=> 'autofill.paymentcode',
86
			'label'=> 'Add payment by code',
87
			'type'=> 'string',
88
			'internaltype'=> 'string',
89
			'default'=> '',
90
			'required'=> false,
91
		),
92
		'autofill.useorder' => array(
93
			'code' => 'autofill.useorder',
94
			'internalcode'=> 'autofill.useorder',
95
			'label'=> 'Add from last order',
96
			'type'=> 'boolean',
97
			'internaltype'=> 'boolean',
98
			'default'=> '',
99
			'required'=> false,
100
		),
101
		'autofill.orderaddress' => array(
102
			'code' => 'autofill.orderaddress',
103
			'internalcode'=> 'autofill.orderaddress',
104
			'label'=> 'Add address from last order',
105
			'type'=> 'boolean',
106
			'internaltype'=> 'boolean',
107
			'default'=> '',
108
			'required'=> false,
109
		),
110
		'autofill.orderservice' => array(
111
			'code' => 'autofill.orderservice',
112
			'internalcode'=> 'autofill.orderservice',
113
			'label'=> 'Add delivery/payment from last order',
114
			'type'=> 'boolean',
115
			'internaltype'=> 'boolean',
116
			'default'=> '',
117
			'required'=> false,
118
		),
119
	);
120
121
122
	/**
123
	 * Checks the backend configuration attributes for validity.
124
	 *
125
	 * @param array $attributes Attributes added by the shop owner in the administraton interface
126
	 * @return array An array with the attribute keys as key and an error message as values for all attributes that are
127
	 * 	known by the provider but aren't valid
128
	 */
129
	public function checkConfigBE( array $attributes )
130
	{
131
		$errors = parent::checkConfigBE( $attributes );
132
133
		return array_merge( $errors, $this->checkConfig( $this->beConfig, $attributes ) );
134
	}
135
136
137
	/**
138
	 * Returns the configuration attribute definitions of the provider to generate a list of available fields and
139
	 * rules for the value of each field in the administration interface.
140
	 *
141
	 * @return array List of attribute definitions implementing \Aimeos\MW\Common\Critera\Attribute\Iface
142
	 */
143
	public function getConfigBE()
144
	{
145
		return $this->getConfigItems( $this->beConfig );
146
	}
147
148
149
	/**
150
	 * Subscribes itself to a publisher
151
	 *
152
	 * @param \Aimeos\MW\Observer\Publisher\Iface $p Object implementing publisher interface
153
	 */
154
	public function register( \Aimeos\MW\Observer\Publisher\Iface $p )
155
	{
156
		$p->addListener( $this->getObject(), 'addProduct.after' );
157
		$p->addListener( $this->getObject(), 'deleteService.after' );
158
	}
159
160
161
	/**
162
	 * Receives a notification from a publisher object
163
	 *
164
	 * @param \Aimeos\MW\Observer\Publisher\Iface $order Shop basket instance implementing publisher interface
165
	 * @param string $action Name of the action to listen for
166
	 * @param mixed $value Object or value changed in publisher
167
	 * @throws \Aimeos\MShop\Plugin\Provider\Exception if an error occurs
168
	 * @return bool true if subsequent plugins should be processed
169
	 */
170
	public function update( \Aimeos\MW\Observer\Publisher\Iface $order, $action, $value = null )
171
	{
172
		if( !( $order instanceof \Aimeos\MShop\Order\Item\Base\Iface ) )
173
		{
174
			$msg = $this->getContext()->getI18n()->dt( 'mshop', 'Object is not of required type "%1$s"' );
175
			throw new \Aimeos\MShop\Plugin\Exception( sprintf( $msg, '\Aimeos\MShop\Order\Item\Base\Iface' ) );
176
		}
177
178
		$context = $this->getContext();
179
		$services = $order->getServices();
180
		$addresses = $order->getAddresses();
181
182
		if( ( $userid = $context->getUserId() ) !== null
183
			&& (bool) $this->getConfigValue( 'autofill.useorder', false ) === true
184
			&& ( empty( $addresses ) || empty( $services ) )
185
		) {
186
			$orderManager = \Aimeos\MShop\Factory::createManager( $context, 'order' );
187
188
			$search = $orderManager->createSearch();
189
			$search->setConditions( $search->compare( '==', 'order.base.customerid', $userid ) );
190
			$search->setSortations( array( $search->sort( '-', 'order.ctime' ) ) );
191
			$search->setSlice( 0, 1 );
192
193
			$result = $orderManager->searchItems( $search );
194
195
			if( ( $item = reset( $result ) ) !== false )
196
			{
197
				$this->setAddresses( $order, $item );
198
				$this->setServices( $order, $item );
199
			}
200
		}
201
202
		$this->setAddressDefault( $order );
203
		$this->setServicesDefault( $order );
204
205
		return true;
206
	}
207
208
209
	/**
210
	 * Returns the order service item for the given type and code if available.
211
	 *
212
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket of the customer
213
	 * @param string $type Service type constant from \Aimeos\MShop\Order\Item\Base\Service\Base
214
	 * @param string|null $code Service item code
215
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface|null Order service item if available or null otherwise
216
	 */
217
	protected function getServiceItem( \Aimeos\MShop\Order\Item\Base\Iface $order, $type, $code = null )
218
	{
219
		$context = $this->getContext();
220
		$serviceManager = \Aimeos\MShop\Factory::createManager( $context, 'service' );
221
222
		$search = $serviceManager->createSearch( true );
223
224
		$expr = [];
225
226
		if( $code !== null ) {
227
			$expr[] = $search->compare( '==', 'service.code', $code );
228
		}
229
230
		$expr[] = $search->compare( '==', 'service.type.code', $type );
231
		$expr[] = $search->getConditions();
232
233
		$search->setConditions( $search->combine( '&&', $expr ) );
234
		$search->setSortations( array( $search->sort( '+', 'service.position' ) ) );
235
236
		$result = $serviceManager->searchItems( $search, array( 'media', 'price', 'text' ) );
237
238
		foreach( $result as $item )
239
		{
240
			$provider = $serviceManager->getProvider( $item );
241
242
			if( $provider->isAvailable( $order ) === true )
243
			{
244
				$orderServiceManager = \Aimeos\MShop\Factory::createManager( $context, 'order/base/service' );
245
				$orderServiceItem = $orderServiceManager->createItem();
246
				$orderServiceItem->copyFrom( $item );
247
				$orderServiceItem->setPrice( $provider->calcPrice( $order ) );
248
249
				return $orderServiceItem;
250
			}
251
		}
252
	}
253
254
255
	/**
256
	 * Adds the addresses from the given order item to the basket.
257
	 *
258
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket object
259
	 * @param \Aimeos\MShop\Order\Item\Iface $item Existing order to fetch the addresses from
260
	 */
261
	protected function setAddresses( \Aimeos\MShop\Order\Item\Base\Iface $order, \Aimeos\MShop\Order\Item\Iface $item )
262
	{
263
		$addresses = $order->getAddresses();
264
265
		if( empty( $addresses ) && (bool) $this->getConfigValue( 'autofill.orderaddress', true ) === true )
266
		{
267
			$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'order/base/address' );
268
			$search = $manager->createSearch();
269
			$search->setConditions( $search->compare( '==', 'order.base.address.baseid', $item->getBaseId() ) );
270
			$addresses = $manager->searchItems( $search );
271
272
			foreach( $addresses as $address ) {
273
				$order->setAddress( $address, $address->getType() );
274
			}
275
		}
276
	}
277
278
279
	/**
280
	 * Adds the services from the given order item to the basket.
281
	 *
282
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket object
283
	 * @param \Aimeos\MShop\Order\Item\Iface $item Existing order to fetch the services from
284
	 */
285
	protected function setServices( \Aimeos\MShop\Order\Item\Base\Iface $order, \Aimeos\MShop\Order\Item\Iface $item )
286
	{
287
		$services = $order->getServices();
288
289
		if( empty( $services ) && $this->getConfigValue( 'autofill.orderservice', true ) == true )
290
		{
291
			$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'order/base/service' );
292
			$search = $manager->createSearch();
293
			$search->setConditions( $search->compare( '==', 'order.base.service.baseid', $item->getBaseId() ) );
294
			$services = $manager->searchItems( $search );
295
296
			foreach( $services as $service )
297
			{
298
				$type = $service->getType();
299
300
				if( ( $item = $this->getServiceItem( $order, $type, $service->getCode() ) ) !== null ) {
301
					$order->setService( $item, $type );
302
				}
303
			}
304
		}
305
	}
306
307
308
	/**
309
	 * Adds the default addresses to the basket if they are not available.
310
	 *
311
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket object
312
	 */
313
	protected function setAddressDefault( \Aimeos\MShop\Order\Item\Base\Iface $order )
314
	{
315
		$context = $this->getContext();
316
		$addresses = $order->getAddresses();
317
		$type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_PAYMENT;
318
319
		if( $context->getUserId() !== null && !isset( $addresses[$type] )
320
			&& (bool) $this->getConfigValue( 'autofill.address', false ) === true
321
		) {
322
			$customerManager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );
323
			$orderAddressManager = \Aimeos\MShop\Factory::createManager( $context, 'order/base/address' );
324
325
			$address = $customerManager->getItem( $context->getUserId() )->getPaymentAddress();
326
327
			$orderAddressItem = $orderAddressManager->createItem();
328
			$orderAddressItem->copyFrom( $address );
329
330
			$order->setAddress( $orderAddressItem, $type );
331
		}
332
	}
333
334
335
	/**
336
	 * Adds the default services to the basket if they are not available.
337
	 *
338
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket object
339
	 */
340
	protected function setServicesDefault( \Aimeos\MShop\Order\Item\Base\Iface $order )
341
	{
342
		$services = $order->getServices();
343
344
		$type = \Aimeos\MShop\Order\Item\Base\Service\Base::TYPE_DELIVERY;
345
346
		if( !isset( $services[$type] ) && (bool) $this->getConfigValue( 'autofill.delivery', false ) === true
347
			&& ( ( $item = $this->getServiceItem( $order, $type, $this->getConfigValue( 'autofill.deliverycode' ) ) ) !== null
348
			|| ( $item = $this->getServiceItem( $order, $type ) ) !== null )
349
		) {
350
			$order->setService( $item, $type );
351
		}
352
353
354
		$type = \Aimeos\MShop\Order\Item\Base\Service\Base::TYPE_PAYMENT;
355
356
		if( !isset( $services[$type] ) && (bool) $this->getConfigValue( 'autofill.payment', false ) === true
357
			&& ( ( $item = $this->getServiceItem( $order, $type, $this->getConfigValue( 'autofill.paymentcode' ) ) ) !== null
358
			|| ( $item = $this->getServiceItem( $order, $type ) ) !== null )
359
		) {
360
			$order->setService( $item, $type );
361
		}
362
	}
363
}
364