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 |
|
|
|
|
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, $item->getType() ); |
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->addService( $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->addService( $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->addService( $item, $type ); |
361
|
|
|
} |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
|