Passed
Push — master ( 8673f8...5c56ce )
by Aimeos
04:42
created

Standard::createService()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2024
6
 * @package MShop
7
 * @subpackage Order
8
 */
9
10
11
namespace Aimeos\MShop\Order\Manager;
12
13
14
/**
15
 * Default order manager implementation.
16
 *
17
 * @package MShop
18
 * @subpackage Order
19
 */
20
class Standard extends Base
21
	implements \Aimeos\MShop\Order\Manager\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
22
{
23
	use Session;
24
	use Update;
25
26
27
	/**
28
	 * Counts the number items that are available for the values of the given key.
29
	 *
30
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria
31
	 * @param array|string $key Search key or list of key to aggregate items for
32
	 * @param string|null $value Search key for aggregating the value column
33
	 * @param string|null $type Type of the aggregation, empty string for count or "sum" or "avg" (average)
34
	 * @return \Aimeos\Map List of the search keys as key and the number of counted items as value
35
	 */
36
	public function aggregate( \Aimeos\Base\Criteria\Iface $search, $key, string $value = null, string $type = null ) : \Aimeos\Map
37
	{
38
		/** mshop/order/manager/aggregate/mysql
39
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
40
		 *
41
		 * @see mshop/order/manager/aggregate/ansi
42
		 */
43
44
		/** mshop/order/manager/aggregate/ansi
45
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
46
		 *
47
		 * Groups all records by the values in the key column and counts their
48
		 * occurence. The matched records can be limited by the given criteria
49
		 * from the order database. The records must be from one of the sites
50
		 * that are configured via the context item. If the current site is part
51
		 * of a tree of sites, the statement can count all records from the
52
		 * current site and the complete sub-tree of sites.
53
		 *
54
		 * As the records can normally be limited by criteria from sub-managers,
55
		 * their tables must be joined in the SQL context. This is done by
56
		 * using the "internaldeps" property from the definition of the ID
57
		 * column of the sub-managers. These internal dependencies specify
58
		 * the JOIN between the tables and the used columns for joining. The
59
		 * ":joins" placeholder is then replaced by the JOIN strings from
60
		 * the sub-managers.
61
		 *
62
		 * To limit the records matched, conditions can be added to the given
63
		 * criteria object. It can contain comparisons like column names that
64
		 * must match specific values which can be combined by AND, OR or NOT
65
		 * operators. The resulting string of SQL conditions replaces the
66
		 * ":cond" placeholder before the statement is sent to the database
67
		 * server.
68
		 *
69
		 * This statement doesn't return any records. Instead, it returns pairs
70
		 * of the different values found in the key column together with the
71
		 * number of records that have been found for that key values.
72
		 *
73
		 * The SQL statement should conform to the ANSI standard to be
74
		 * compatible with most relational database systems. This also
75
		 * includes using double quotes for table and column names.
76
		 *
77
		 * @param string SQL statement for aggregating order items
78
		 * @since 2014.09
79
		 * @see mshop/order/manager/insert/ansi
80
		 * @see mshop/order/manager/update/ansi
81
		 * @see mshop/order/manager/newid/ansi
82
		 * @see mshop/order/manager/delete/ansi
83
		 * @see mshop/order/manager/search/ansi
84
		 * @see mshop/order/manager/count/ansi
85
		 */
86
87
		/** mshop/order/manager/aggregateavg/mysql
88
		 * Computes the average of all values grouped by the key column and matched by the given criteria
89
		 *
90
		 * @param string SQL statement for aggregating the order items and computing the average value
91
		 * @since 2017.10
92
		 * @see mshop/order/manager/aggregateavg/ansi
93
		 * @see mshop/order/manager/aggregate/mysql
94
		 */
95
96
		/** mshop/order/manager/aggregateavg/ansi
97
		 * Computes the average of all values grouped by the key column and matched by the given criteria
98
		 *
99
		 * @param string SQL statement for aggregating the order items and computing the average value
100
		 * @since 2017.10
101
		 * @see mshop/order/manager/aggregate/ansi
102
		 */
103
104
		/** mshop/order/manager/aggregatesum/mysql
105
		 * Computes the sum of all values grouped by the key column and matched by the given criteria
106
		 *
107
		 * @param string SQL statement for aggregating the order items and computing the sum
108
		 * @since 2017.10
109
		 * @see mshop/order/manager/aggregatesum/ansi
110
		 * @see mshop/order/manager/aggregate/mysql
111
		 */
112
113
		/** mshop/order/manager/aggregatesum/ansi
114
		 * Computes the sum of all values grouped by the key column and matched by the given criteria
115
		 *
116
		 * @param string SQL statement for aggregating the order items and computing the sum
117
		 * @since 2017.10
118
		 * @see mshop/order/manager/aggregate/ansi
119
		 */
120
121
		$cfgkey = 'mshop/order/manager/aggregate';
122
		return $this->aggregateBase( $search, $key, $cfgkey, ['order'], $value, $type );
123
	}
124
125
126
	/**
127
	 * Creates a new empty item instance
128
	 *
129
	 * @param array $values Values the item should be initialized with
130
	 * @return \Aimeos\MShop\Order\Item\Iface New order item object
131
	 */
132
	public function create( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
133
	{
134
		$context = $this->context();
135
		$locale = $context->locale();
136
137
		$values['.locale'] = $values['.locale'] ?? $locale;
138
		$values['.price'] = $values['.price'] ?? \Aimeos\MShop::create( $context, 'price' )->create();
139
		$values['order.siteid'] = $values['order.siteid'] ?? $locale->getSiteId();
140
141
		$item = new \Aimeos\MShop\Order\Item\Standard( 'order.', $values );
142
		\Aimeos\MShop::create( $context, 'plugin' )->register( $item, 'order' );
143
144
		return $item;
145
	}
146
147
148
	/**
149
	 * Creates a new address item instance
150
	 *
151
	 * @param array $values Values the item should be initialized with
152
	 * @return \Aimeos\MShop\Order\Item\Address\Iface New order address item object
153
	 */
154
	public function createAddress( array $values = [] ) : \Aimeos\MShop\Order\Item\Address\Iface
155
	{
156
		return $this->object()->getSubManager( 'address' )->create( $values );
157
	}
158
159
160
	/**
161
	 * Creates a new coupon item instance
162
	 *
163
	 * @param array $values Values the item should be initialized with
164
	 * @return \Aimeos\MShop\Order\Item\Coupon\Iface New order coupon item object
165
	 */
166
	public function createCoupon( array $values = [] ) : \Aimeos\MShop\Order\Item\Coupon\Iface
167
	{
168
		return $this->object()->getSubManager( 'coupon' )->create( $values );
169
	}
170
171
172
	/**
173
	 * Creates a new product item instance
174
	 *
175
	 * @param array $values Values the item should be initialized with
176
	 * @return \Aimeos\MShop\Order\Item\Product\Iface New order product item object
177
	 */
178
	public function createProduct( array $values = [] ) : \Aimeos\MShop\Order\Item\Product\Iface
179
	{
180
		return $this->object()->getSubManager( 'product' )->create( $values );
181
	}
182
183
184
	/**
185
	 * Creates a new product attribute item instance
186
	 *
187
	 * @param array $values Values the item should be initialized with
188
	 * @return \Aimeos\MShop\Order\Item\Product\Attribute\Iface New order product attribute item object
189
	 */
190
	public function createProductAttribute( array $values = [] ) : \Aimeos\MShop\Order\Item\Product\Attribute\Iface
191
	{
192
		return $this->object()->getSubManager( 'product' )->getSubManager( 'attribute' )->create( $values );
193
	}
194
195
196
	/**
197
	 * Creates a new service item instance
198
	 *
199
	 * @param array $values Values the item should be initialized with
200
	 * @return \Aimeos\MShop\Order\Item\Service\Iface New order service item object
201
	 */
202
	public function createService( array $values = [] ) : \Aimeos\MShop\Order\Item\Service\Iface
203
	{
204
		return $this->object()->getSubManager( 'service' )->create( $values );
205
	}
206
207
208
	/**
209
	 * Creates a new service attribute item instance
210
	 *
211
	 * @param array $values Values the item should be initialized with
212
	 * @return \Aimeos\MShop\Order\Item\Service\Attribute\Iface New order service attribute item object
213
	 */
214
	public function createServiceAttribute( array $values = [] ) : \Aimeos\MShop\Order\Item\Service\Attribute\Iface
215
	{
216
		return $this->object()->getSubManager( 'service' )->getSubManager( 'attribute' )->create( $values );
217
	}
218
219
220
	/**
221
	 * Creates a new service transaction item instance
222
	 *
223
	 * @param array $values Values the item should be initialized with
224
	 * @return \Aimeos\MShop\Order\Item\Service\Transaction\Iface New order service transaction item object
225
	 */
226
	public function createServiceTransaction( array $values = [] ) : \Aimeos\MShop\Order\Item\Service\Transaction\Iface
227
	{
228
		return $this->object()->getSubManager( 'service' )->getSubManager( 'transaction' )->create( $values );
229
	}
230
231
232
	/**
233
	 * Creates a new status item instance
234
	 *
235
	 * @param array $values Values the item should be initialized with
236
	 * @return \Aimeos\MShop\Order\Item\Status\Iface New order item object
237
	 */
238
	public function createStatus( array $values = [] ) : \Aimeos\MShop\Order\Item\Status\Iface
239
	{
240
		return $this->object()->getSubManager( 'status' )->create( $values );
241
	}
242
243
244
	/**
245
	 * Creates a search critera object
246
	 *
247
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
248
	 * @param bool $site TRUE to add site criteria to show orders with available products only
249
	 * @return \Aimeos\Base\Criteria\Iface New search criteria object
250
	 */
251
	public function filter( ?bool $default = false, bool $site = false ) : \Aimeos\Base\Criteria\Iface
252
	{
253
		$search = parent::filter( $default );
254
255
		if( $default !== false ) {
256
			$search->add( ['order.customerid' => $this->context()->user()] );
257
		}
258
259
		if( $site === true )
260
		{
261
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_SUBTREE;
262
			$search->add( $this->siteCondition( 'order.product.siteid', $level ) );
263
		}
264
265
		return $search;
266
	}
267
268
269
	/**
270
	 * Returns the additional column/search definitions
271
	 *
272
	 * @return array Associative list of column names as keys and items implementing \Aimeos\Base\Criteria\Attribute\Iface
273
	 */
274
	public function getSaveAttributes() : array
275
	{
276
		return $this->createAttributes( [
277
			'order.invoiceno' => [
278
				'label' => 'Invoice number',
279
				'internalcode' => 'invoiceno',
280
			],
281
			'order.relatedid' => [
282
				'label' => 'Related invoice ID',
283
				'internalcode' => 'relatedid',
284
			],
285
			'order.channel' => [
286
				'label' => 'Order channel',
287
				'internalcode' => 'channel',
288
			],
289
			'order.datepayment' => [
290
				'label' => 'Purchase date',
291
				'internalcode' => 'datepayment',
292
				'type' => 'datetime',
293
			],
294
			'order.datedelivery' => [
295
				'label' => 'Delivery date',
296
				'internalcode' => 'datedelivery',
297
				'type' => 'datetime',
298
			],
299
			'order.statusdelivery' => [
300
				'label' => 'Delivery status',
301
				'internalcode' => 'statusdelivery',
302
				'type' => 'int',
303
			],
304
			'order.statuspayment' => [
305
				'label' => 'Payment status',
306
				'internalcode' => 'statuspayment',
307
				'type' => 'int',
308
			],
309
			'order.customerid' => [
310
				'label' => 'Order customer ID',
311
				'internalcode' => 'customerid',
312
			],
313
			'order.customerref' => [
314
				'label' => 'Order customer reference',
315
				'internalcode' => 'customerref',
316
			],
317
			'order.comment' => [
318
				'label' => 'Order comment',
319
				'internalcode' => 'comment',
320
			],
321
		] );
322
	}
323
324
325
	/**
326
	 * Returns the attributes that can be used for searching.
327
	 *
328
	 * @param bool $withsub Return also attributes of sub-managers if true
329
	 * @return \Aimeos\Base\Criteria\Attribute\Iface[] List of search attribute items
330
	 */
331
	public function getSearchAttributes( bool $withsub = true ) : array
332
	{
333
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
334
		$level = $this->context()->config()->get( 'mshop/order/manager/sitemode', $level );
335
		$expr = $this->siteString( 'mordst_cs."siteid"', $level );
336
337
		return array_replace( parent::getSearchAttributes( $withsub ), $this->createAttributes( [
338
			'order.sitecode' => [
339
				'label' => 'Order site code',
340
				'internalcode' => 'sitecode',
341
				'public' => false,
342
			],
343
			'order.languageid' => [
344
				'label' => 'Order language code',
345
				'internalcode' => 'langid',
346
			],
347
			'order.currencyid' => [
348
				'label' => 'Order currencyid code',
349
				'internalcode' => 'currencyid',
350
			],
351
			'order.price' => [
352
				'label' => 'Order price amount',
353
				'internalcode' => 'price',
354
			],
355
			'order.costs' => [
356
				'label' => 'Order shipping amount',
357
				'internalcode' => 'costs',
358
			],
359
			'order.rebate' => [
360
				'label' => 'Order rebate amount',
361
				'internalcode' => 'rebate',
362
			],
363
			'order.taxvalue' => [
364
				'label' => 'Order tax amount',
365
				'internalcode' => 'tax',
366
			],
367
			'order.taxflag' => [
368
				'label' => 'Order tax flag (0=net, 1=gross)',
369
				'internalcode' => 'taxflag',
370
			],
371
			'order.cdate' => [
372
				'label' => 'Create date',
373
				'internalcode' => 'cdate',
374
			],
375
			'order.cmonth' => [
376
				'label' => 'Create month',
377
				'internalcode' => 'cmonth',
378
			],
379
			'order.cweek' => [
380
				'label' => 'Create week',
381
				'internalcode' => 'cweek',
382
			],
383
			'order.cwday' => [
384
				'label' => 'Create weekday',
385
				'internalcode' => 'cwday',
386
			],
387
			'order.chour' => [
388
				'label' => 'Create hour',
389
				'internalcode' => 'chour',
390
			],
391
			'order:status' => [
392
				'code' => 'order:status()',
393
				'internalcode' => '( SELECT COUNT(mordst_cs."parentid")
394
					FROM "mshop_order_status" AS mordst_cs
395
					WHERE mord."id" = mordst_cs."parentid" AND ' . $expr . '
396
					AND mordst_cs."type" = $1 AND mordst_cs."value" IN ( $2 ) )',
397
				'label' => 'Number of order status items, parameter(<type>,<value>)',
398
				'type' => 'int',
399
				'public' => false,
400
			],
401
		] ) );
402
	}
403
404
405
	/**
406
	 * Saves the dependent items of the item
407
	 *
408
	 * @param \Aimeos\MShop\Common\Item\Iface $item Item object
409
	 * @param bool $fetch True if the new ID should be returned in the item
410
	 * @return \Aimeos\MShop\Common\Item\Iface Updated item
411
	 */
412
	public function saveRefs( \Aimeos\MShop\Common\Item\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Common\Item\Iface
413
	{
414
		$this->addStatus( $item );
415
		$this->saveAddresses( $item );
416
		$this->saveServices( $item );
417
		$this->saveProducts( $item );
418
		$this->saveCoupons( $item );
419
		$this->saveStatuses( $item );
420
421
		return $item;
422
	}
423
424
425
	/**
426
	 * Merges the data from the given map and the referenced items
427
	 *
428
	 * @param array $entries Associative list of ID as key and the associative list of property key/value pairs as values
429
	 * @param array $ref List of referenced items to fetch and add to the entries
430
	 * @return array Associative list of ID as key and the updated entries as value
431
	 */
432
	public function searchRefs( array $entries, array $ref ) : array
433
	{
434
		$context = $this->context();
435
		$ids = array_keys( $entries );
436
		$addresses = $customers = $coupons = $products = $services = $statuses = [];
437
438
		$priceManager = \Aimeos\MShop::create( $context, 'price' );
439
		$localeManager = \Aimeos\MShop::create( $context, 'locale' );
440
441
		if( $this->hasRef( $ref, 'customer' ) && !( $cids = map( $entries )->col( 'order.customerid' )->filter() )->empty() )
442
		{
443
			$manager = \Aimeos\MShop::create( $this->context(), 'customer' );
444
			$search = $manager->filter()->slice( 0, 0x7fffffff )->add( ['customer.id' => $cids] );
445
			$customers = $manager->search( $search, $ref );
446
		}
447
448
		if( $this->hasRef( $ref, 'order/address' ) ) {
449
			$addresses = $this->getAddresses( $ids, $ref );
450
		}
451
452
		if( $this->hasRef( $ref, 'order/product' ) || $this->hasRef( $ref, 'order/coupon' ) ) {
453
			$products = $this->getProducts( $ids, $ref );
454
		}
455
456
		if( $this->hasRef( $ref, 'order/coupon' ) ) {
457
			$coupons = $this->getCoupons( $ids, $ref );
458
		}
459
460
		if( $this->hasRef( $ref, 'order/service' ) ) {
461
			$services = $this->getServices( $ids, $ref );
462
		}
463
464
		if( $this->hasRef( $ref, 'order/status' ) ) {
465
			$statuses = $this->getStatuses( $ids, $ref );
466
		}
467
468
		foreach( $entries as $id => $row )
469
		{
470
			$entries[$id]['.price'] = $priceManager->create( [
471
				'price.currencyid' => $row['order.currencyid'],
472
				'price.value' => $row['order.price'],
473
				'price.costs' => $row['order.costs'],
474
				'price.rebate' => $row['order.rebate'],
475
				'price.taxflag' => $row['order.taxflag'],
476
				'price.taxvalue' => $row['order.taxvalue'],
477
				'price.siteid' => $row['order.siteid'],
478
			] );
479
480
			// you may need the site object! take care!
481
			$entries[$id]['.locale'] = $localeManager->create( [
482
				'locale.currencyid' => $row['order.currencyid'],
483
				'locale.languageid' => $row['order.languageid'],
484
				'locale.siteid' => $row['order.siteid'],
485
			] );
486
487
			$entries[$id]['.customer'] = $customers[$row['order.customerid']] ?? null;
488
			$entries[$id]['.addresses'] = $addresses[$id] ?? [];
489
			$entries[$id]['.coupons'] = $coupons[$id] ?? [];
490
			$entries[$id]['.products'] = $products[$id] ?? [];
491
			$entries[$id]['.services'] = $services[$id] ?? [];
492
			$entries[$id]['.statuses'] = $statuses[$id] ?? [];
493
		}
494
495
		return $entries;
496
	}
497
498
499
	/**
500
	 * Adds the new payment and delivery values to the order status log.
501
	 *
502
	 * @param \Aimeos\MShop\Order\Item\Iface $item Order item object
503
	 * @return \Aimeos\MShop\Order\Manager\Iface Manager object for chaining method calls
504
	 */
505
	protected function addStatus( \Aimeos\MShop\Order\Item\Iface $item ) : \Aimeos\MShop\Order\Manager\Iface
506
	{
507
		$object = $this->object();
508
509
		if( ( $status = $item->get( '.statuspayment' ) ) !== null && $status != $item->getStatusPayment() ) {
510
			$item->addStatus( $object->createStatus()->setType( 'status-payment' )->setValue( $item->getStatusPayment() ) );
511
		}
512
513
		if( ( $status = $item->get( '.statusdelivery' ) ) !== null && $status != $item->getStatusDelivery() ) {
514
			$item->addStatus( $object->createStatus()->setType( 'status-delivery' )->setValue( $item->getStatusDelivery() ) );
515
		}
516
517
		return $this;
518
	}
519
520
521
	/**
522
	 * Binds additional values to the statement before execution.
523
	 *
524
	 * @param \Aimeos\MShop\Common\Item\Iface $item Item object
525
	 * @param \Aimeos\Base\DB\Statement\Iface $stmt Database statement object
526
	 * @param int $idx Current bind index
527
	 * @return \Aimeos\Base\DB\Statement\Iface Database statement object with bound values
528
	 */
529
	protected function bind( \Aimeos\MShop\Common\Item\Iface $item, \Aimeos\Base\DB\Statement\Iface $stmt, int &$idx ) : \Aimeos\Base\DB\Statement\Iface
530
	{
531
		$price = $item->getPrice();
532
		$context = $this->context();
533
534
		$stmt->bind( $idx++, $context->locale()->getSiteItem()->getCode() );
535
		$stmt->bind( $idx++, $item->locale()->getLanguageId() );
536
		$stmt->bind( $idx++, $price->getCurrencyId() );
537
		$stmt->bind( $idx++, $price->getValue() );
538
		$stmt->bind( $idx++, $price->getCosts() );
539
		$stmt->bind( $idx++, $price->getRebate() );
540
		$stmt->bind( $idx++, $price->getTaxValue() );
541
		$stmt->bind( $idx++, $price->getTaxFlag(), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
542
543
		if( $item->getId() === null )
544
		{
545
			$date = date_create_from_format( 'Y-m-d H:i:s', $context->datetime() );
546
			$stmt->bind( $idx++, $date->format( 'Y-m-d' ) ); // cdate
547
			$stmt->bind( $idx++, $date->format( 'Y-m' ) ); // cmonth
548
			$stmt->bind( $idx++, $date->format( 'Y-W' ) ); // cweek
549
			$stmt->bind( $idx++, $date->format( 'w' ) ); // cwday
550
			$stmt->bind( $idx++, $date->format( 'G' ) ); // chour
551
		}
552
553
		return $stmt;
554
	}
555
556
557
	/**
558
	 * Creates a new invoice number for the passed order and site.
559
	 *
560
	 * @param \Aimeos\MShop\Order\Item\Iface $item Order item with necessary values
561
	 * @return string Unique invoice number for the current site
562
	 */
563
	protected function createInvoiceNumber( \Aimeos\MShop\Order\Item\Iface $item ) : string
564
	{
565
		$context = $this->context();
566
		$siteId = $context->locale()->getSiteId();
567
		$conn = $context->db( 'db-locale', true );
568
569
		try
570
		{
571
			$conn->query( 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE' )->finish();
572
			$conn->query( 'START TRANSACTION' )->finish();
573
574
			$result = $conn->query( 'SELECT "invoiceno" FROM "mshop_locale_site" where "siteid" = ?', [$siteId] );
575
			$row = $result->fetch();
576
			$result->finish();
577
578
			$conn->create( 'UPDATE "mshop_locale_site" SET "invoiceno" = "invoiceno" + 1 WHERE "siteid" = ?' )
579
				->bind( 1, $siteId )->execute()->finish();
580
581
			$conn->query( 'COMMIT' )->finish();
582
		}
583
		catch( \Exception $e )
584
		{
585
			$conn->close();
586
			throw $e;
587
		}
588
589
		return $row['invoiceno'] ?? '';
590
	}
591
592
593
	/**
594
	 * Returns the prefix for the item properties and search keys.
595
	 *
596
	 * @return string Prefix for the item properties and search keys
597
	 */
598
	protected function prefix() : string
599
	{
600
		return 'order.';
601
	}
602
603
604
	/**
605
	 * Creates a one-time order in the storage from the given invoice object.
606
	 *
607
	 * @param \Aimeos\MShop\Common\Item\Iface $item Order item with necessary values
608
	 * @param bool $fetch True if the new ID should be returned in the item
609
	 * @return \Aimeos\MShop\Common\Item\Iface $item Updated item including the generated ID
610
	 */
611
	protected function saveBase( \Aimeos\MShop\Common\Item\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Common\Item\Iface
612
	{
613
		if( empty( $item->getInvoiceNumber() ) && $item->getStatusPayment() >= \Aimeos\MShop\Order\Item\Base::PAY_PENDING )
614
		{
615
			try {
616
				$item->setInvoiceNumber( $this->createInvoiceNumber( $item ) );
617
			} catch( \Exception $e ) { // redo on transaction deadlock
618
				$item->setInvoiceNumber( $this->createInvoiceNumber( $item ) );
619
			}
620
		}
621
622
		return parent::saveBase( $item, $fetch );
623
	}
624
625
626
	/** mshop/order/manager/name
627
	 * Class name of the used order manager implementation
628
	 *
629
	 * Each default manager can be replace by an alternative imlementation.
630
	 * To use this implementation, you have to set the last part of the class
631
	 * name as configuration value so the manager factory knows which class it
632
	 * has to instantiate.
633
	 *
634
	 * For example, if the name of the default class is
635
	 *
636
	 *  \Aimeos\MShop\Order\Manager\Standard
637
	 *
638
	 * and you want to replace it with your own version named
639
	 *
640
	 *  \Aimeos\MShop\Order\Manager\Mymanager
641
	 *
642
	 * then you have to set the this configuration option:
643
	 *
644
	 *  mshop/order/manager/name = Mymanager
645
	 *
646
	 * The value is the last part of your own class name and it's case sensitive,
647
	 * so take care that the configuration value is exactly named like the last
648
	 * part of the class name.
649
	 *
650
	 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
651
	 * characters are possible! You should always start the last part of the class
652
	 * name with an upper case character and continue only with lower case characters
653
	 * or numbers. Avoid chamel case names like "MyManager"!
654
	 *
655
	 * @param string Last part of the class name
656
	 * @since 2015.10
657
	 */
658
659
	/** mshop/order/manager/decorators/excludes
660
	 * Excludes decorators added by the "common" option from the order manager
661
	 *
662
	 * Decorators extend the functionality of a class by adding new aspects
663
	 * (e.g. log what is currently done), executing the methods of the underlying
664
	 * class only in certain conditions (e.g. only for logged in users) or
665
	 * modify what is returned to the caller.
666
	 *
667
	 * This option allows you to remove a decorator added via
668
	 * "mshop/common/manager/decorators/default" before they are wrapped
669
	 * around the order manager.
670
	 *
671
	 *  mshop/order/manager/decorators/excludes = array( 'decorator1' )
672
	 *
673
	 * This would remove the decorator named "decorator1" from the list of
674
	 * common decorators ("\Aimeos\MShop\Common\Manager\Decorator\*") added via
675
	 * "mshop/common/manager/decorators/default" for the order manager.
676
	 *
677
	 * @param array List of decorator names
678
	 * @since 2015.10
679
	 * @see mshop/common/manager/decorators/default
680
	 * @see mshop/order/manager/decorators/global
681
	 * @see mshop/order/manager/decorators/local
682
	 */
683
684
	/** mshop/order/manager/decorators/global
685
	 * Adds a list of globally available decorators only to the order manager
686
	 *
687
	 * Decorators extend the functionality of a class by adding new aspects
688
	 * (e.g. log what is currently done), executing the methods of the underlying
689
	 * class only in certain conditions (e.g. only for logged in users) or
690
	 * modify what is returned to the caller.
691
	 *
692
	 * This option allows you to wrap global decorators
693
	 * ("\Aimeos\MShop\Common\Manager\Decorator\*") around the order manager.
694
	 *
695
	 *  mshop/order/manager/decorators/global = array( 'decorator1' )
696
	 *
697
	 * This would add the decorator named "decorator1" defined by
698
	 * "\Aimeos\MShop\Common\Manager\Decorator\Decorator1" only to the order
699
	 * manager.
700
	 *
701
	 * @param array List of decorator names
702
	 * @since 2015.10
703
	 * @see mshop/common/manager/decorators/default
704
	 * @see mshop/order/manager/decorators/excludes
705
	 * @see mshop/order/manager/decorators/local
706
	 */
707
708
	/** mshop/order/manager/decorators/local
709
	 * Adds a list of local decorators only to the order manager
710
	 *
711
	 * Decorators extend the functionality of a class by adding new aspects
712
	 * (e.g. log what is currently done), executing the methods of the underlying
713
	 * class only in certain conditions (e.g. only for logged in users) or
714
	 * modify what is returned to the caller.
715
	 *
716
	 * This option allows you to wrap local decorators
717
	 * ("\Aimeos\MShop\Order\Manager\Decorator\*") around the order manager.
718
	 *
719
	 *  mshop/order/manager/decorators/local = array( 'decorator2' )
720
	 *
721
	 * This would add the decorator named "decorator2" defined by
722
	 * "\Aimeos\MShop\Order\Manager\Decorator\Decorator2" only to the order
723
	 * manager.
724
	 *
725
	 * @param array List of decorator names
726
	 * @since 2015.10
727
	 * @see mshop/common/manager/decorators/default
728
	 * @see mshop/order/manager/decorators/excludes
729
	 * @see mshop/order/manager/decorators/global
730
	 */
731
732
	/** mshop/order/manager/resource
733
	 * Name of the database connection resource to use
734
	 *
735
	 * You can configure a different database connection for each data domain
736
	 * and if no such connection name exists, the "db" connection will be used.
737
	 * It's also possible to use the same database connection for different
738
	 * data domains by configuring the same connection name using this setting.
739
	 *
740
	 * @param string Database connection name
741
	 * @since 2023.04
742
	 */
743
744
745
	/** mshop/order/manager/delete/mysql
746
	 * Deletes the items matched by the given IDs from the database
747
	 *
748
	 * @see mshop/order/manager/delete/ansi
749
	 */
750
751
	/** mshop/order/manager/delete/ansi
752
	 * Deletes the items matched by the given IDs from the database
753
	 *
754
	 * Removes the records specified by the given IDs from the order database.
755
	 * The records must be from the site that is configured via the
756
	 * context item.
757
	 *
758
	 * The ":cond" placeholder is replaced by the name of the ID column and
759
	 * the given ID or list of IDs while the site ID is bound to the question
760
	 * mark.
761
	 *
762
	 * The SQL statement should conform to the ANSI standard to be
763
	 * compatible with most relational database systems. This also
764
	 * includes using double quotes for table and column names.
765
	 *
766
	 * @param string SQL statement for deleting items
767
	 * @since 2015.10
768
	 * @see mshop/order/manager/insert/ansi
769
	 * @see mshop/order/manager/update/ansi
770
	 * @see mshop/order/manager/newid/ansi
771
	 * @see mshop/order/manager/search/ansi
772
	 * @see mshop/order/manager/count/ansi
773
	 */
774
775
	/** mshop/order/manager/submanagers
776
	 * List of manager names that can be instantiated by the order manager
777
	 *
778
	 * Managers provide a generic interface to the underlying storage.
779
	 * Each manager has or can have sub-managers caring about particular
780
	 * aspects. Each of these sub-managers can be instantiated by its
781
	 * parent manager using the getSubManager() method.
782
	 *
783
	 * The search keys from sub-managers can be normally used in the
784
	 * manager as well. It allows you to search for items of the manager
785
	 * using the search keys of the sub-managers to further limit the
786
	 * retrieved list of items.
787
	 *
788
	 * @param array List of sub-manager names
789
	 * @since 2015.10
790
	 */
791
792
	/** mshop/order/manager/insert/mysql
793
	 * Inserts a new order record into the database table
794
	 *
795
	 * @see mshop/order/manager/insert/ansi
796
	 */
797
798
	/** mshop/order/manager/insert/ansi
799
	 * Inserts a new order record into the database table
800
	 *
801
	 * Items with no ID yet (i.e. the ID is NULL) will be created in
802
	 * the database and the newly created ID retrieved afterwards
803
	 * using the "newid" SQL statement.
804
	 *
805
	 * The SQL statement must be a string suitable for being used as
806
	 * prepared statement. It must include question marks for binding
807
	 * the values from the order item to the statement before they are
808
	 * sent to the database server. The number of question marks must
809
	 * be the same as the number of columns listed in the INSERT
810
	 * statement. The catalog of the columns must correspond to the
811
	 * catalog in the save() method, so the correct values are
812
	 * bound to the columns.
813
	 *
814
	 * The SQL statement should conform to the ANSI standard to be
815
	 * compatible with most relational database systems. This also
816
	 * includes using double quotes for table and column names.
817
	 *
818
	 * @param string SQL statement for inserting records
819
	 * @since 2015.10
820
	 * @see mshop/order/manager/update/ansi
821
	 * @see mshop/order/manager/newid/ansi
822
	 * @see mshop/order/manager/delete/ansi
823
	 * @see mshop/order/manager/search/ansi
824
	 * @see mshop/order/manager/count/ansi
825
	 */
826
827
	/** mshop/order/manager/update/mysql
828
	 * Updates an existing order record in the database
829
	 *
830
	 * @see mshop/order/manager/update/ansi
831
	 */
832
833
	/** mshop/order/manager/update/ansi
834
	 * Updates an existing order record in the database
835
	 *
836
	 * Items which already have an ID (i.e. the ID is not NULL) will
837
	 * be updated in the database.
838
	 *
839
	 * The SQL statement must be a string suitable for being used as
840
	 * prepared statement. It must include question marks for binding
841
	 * the values from the order item to the statement before they are
842
	 * sent to the database server. The catalog of the columns must
843
	 * correspond to the catalog in the save() method, so the
844
	 * correct values are bound to the columns.
845
	 *
846
	 * The SQL statement should conform to the ANSI standard to be
847
	 * compatible with most relational database systems. This also
848
	 * includes using double quotes for table and column names.
849
	 *
850
	 * @param string SQL statement for updating records
851
	 * @since 2015.10
852
	 * @see mshop/order/manager/insert/ansi
853
	 * @see mshop/order/manager/newid/ansi
854
	 * @see mshop/order/manager/delete/ansi
855
	 * @see mshop/order/manager/search/ansi
856
	 * @see mshop/order/manager/count/ansi
857
	 */
858
859
	/** mshop/order/manager/newid/mysql
860
	 * Retrieves the ID generated by the database when inserting a new record
861
	 *
862
	 * @see mshop/order/manager/newid/ansi
863
	 */
864
865
	/** mshop/order/manager/newid/ansi
866
	 * Retrieves the ID generated by the database when inserting a new record
867
	 *
868
	 * As soon as a new record is inserted into the database table,
869
	 * the database server generates a new and unique identifier for
870
	 * that record. This ID can be used for retrieving, updating and
871
	 * deleting that specific record from the table again.
872
	 *
873
	 * For MySQL:
874
	 *  SELECT LAST_INSERT_ID()
875
	 * For PostgreSQL:
876
	 *  SELECT currval('seq_mrul_id')
877
	 * For SQL Server:
878
	 *  SELECT SCOPE_IDENTITY()
879
	 * For Oracle:
880
	 *  SELECT "seq_mrul_id".CURRVAL FROM DUAL
881
	 *
882
	 * There's no way to retrive the new ID by a SQL statements that
883
	 * fits for most database servers as they implement their own
884
	 * specific way.
885
	 *
886
	 * @param string SQL statement for retrieving the last inserted record ID
887
	 * @since 2015.10
888
	 * @see mshop/order/manager/insert/ansi
889
	 * @see mshop/order/manager/update/ansi
890
	 * @see mshop/order/manager/delete/ansi
891
	 * @see mshop/order/manager/search/ansi
892
	 * @see mshop/order/manager/count/ansi
893
	 */
894
895
	/** mshop/order/manager/sitemode
896
	 * Mode how items from levels below or above in the site tree are handled
897
	 *
898
	 * By default, only items from the current site are fetched from the
899
	 * storage. If the ai-sites extension is installed, you can create a
900
	 * tree of sites. Then, this setting allows you to define for the
901
	 * whole order domain if items from parent sites are inherited,
902
	 * sites from child sites are aggregated or both.
903
	 *
904
	 * Available constants for the site mode are:
905
	 * * 0 = only items from the current site
906
	 * * 1 = inherit items from parent sites
907
	 * * 2 = aggregate items from child sites
908
	 * * 3 = inherit and aggregate items at the same time
909
	 *
910
	 * You also need to set the mode in the locale manager
911
	 * (mshop/locale/manager/sitelevel) to one of the constants.
912
	 * If you set it to the same value, it will work as described but you
913
	 * can also use different modes. For example, if inheritance and
914
	 * aggregation is configured the locale manager but only inheritance
915
	 * in the domain manager because aggregating items makes no sense in
916
	 * this domain, then items wil be only inherited. Thus, you have full
917
	 * control over inheritance and aggregation in each domain.
918
	 *
919
	 * @param int Constant from Aimeos\MShop\Locale\Manager\Base class
920
	 * @since 2018.01
921
	 * @see mshop/locale/manager/sitelevel
922
	 */
923
924
	/** mshop/order/manager/search/mysql
925
	 * Retrieves the records matched by the given criteria in the database
926
	 *
927
	 * @see mshop/order/manager/search/ansi
928
	 */
929
930
	/** mshop/order/manager/search/ansi
931
	 * Retrieves the records matched by the given criteria in the database
932
	 *
933
	 * Fetches the records matched by the given criteria from the order
934
	 * database. The records must be from one of the sites that are
935
	 * configured via the context item. If the current site is part of
936
	 * a tree of sites, the SELECT statement can retrieve all records
937
	 * from the current site and the complete sub-tree of sites.
938
	 *
939
	 * As the records can normally be limited by criteria from sub-managers,
940
	 * their tables must be joined in the SQL context. This is done by
941
	 * using the "internaldeps" property from the definition of the ID
942
	 * column of the sub-managers. These internal dependencies specify
943
	 * the JOIN between the tables and the used columns for joining. The
944
	 * ":joins" placeholder is then replaced by the JOIN strings from
945
	 * the sub-managers.
946
	 *
947
	 * To limit the records matched, conditions can be added to the given
948
	 * criteria object. It can contain comparisons like column names that
949
	 * must match specific values which can be combined by AND, OR or NOT
950
	 * operators. The resulting string of SQL conditions replaces the
951
	 * ":cond" placeholder before the statement is sent to the database
952
	 * server.
953
	 *
954
	 * If the records that are retrieved should be cataloged by one or more
955
	 * columns, the generated string of column / sort direction pairs
956
	 * replaces the ":catalog" placeholder. In case no cataloging is required,
957
	 * the complete ORDER BY part including the "\/*-catalogby*\/...\/*catalogby-*\/"
958
	 * markers is removed to speed up retrieving the records. Columns of
959
	 * sub-managers can also be used for cataloging the result set but then
960
	 * no index can be used.
961
	 *
962
	 * The number of returned records can be limited and can start at any
963
	 * number between the begining and the end of the result set. For that
964
	 * the ":size" and ":start" placeholders are replaced by the
965
	 * corresponding values from the criteria object. The default values
966
	 * are 0 for the start and 100 for the size value.
967
	 *
968
	 * The SQL statement should conform to the ANSI standard to be
969
	 * compatible with most relational database systems. This also
970
	 * includes using double quotes for table and column names.
971
	 *
972
	 * @param string SQL statement for searching items
973
	 * @since 2015.10
974
	 * @see mshop/order/manager/insert/ansi
975
	 * @see mshop/order/manager/update/ansi
976
	 * @see mshop/order/manager/newid/ansi
977
	 * @see mshop/order/manager/delete/ansi
978
	 * @see mshop/order/manager/count/ansi
979
	 */
980
981
	/** mshop/order/manager/count/mysql
982
	 * Counts the number of records matched by the given criteria in the database
983
	 *
984
	 * @see mshop/order/manager/count/ansi
985
	 */
986
987
	/** mshop/order/manager/count/ansi
988
	 * Counts the number of records matched by the given criteria in the database
989
	 *
990
	 * Counts all records matched by the given criteria from the order
991
	 * database. The records must be from one of the sites that are
992
	 * configured via the context item. If the current site is part of
993
	 * a tree of sites, the statement can count all records from the
994
	 * current site and the complete sub-tree of sites.
995
	 *
996
	 * As the records can normally be limited by criteria from sub-managers,
997
	 * their tables must be joined in the SQL context. This is done by
998
	 * using the "internaldeps" property from the definition of the ID
999
	 * column of the sub-managers. These internal dependencies specify
1000
	 * the JOIN between the tables and the used columns for joining. The
1001
	 * ":joins" placeholder is then replaced by the JOIN strings from
1002
	 * the sub-managers.
1003
	 *
1004
	 * To limit the records matched, conditions can be added to the given
1005
	 * criteria object. It can contain comparisons like column names that
1006
	 * must match specific values which can be combined by AND, OR or NOT
1007
	 * operators. The resulting string of SQL conditions replaces the
1008
	 * ":cond" placeholder before the statement is sent to the database
1009
	 * server.
1010
	 *
1011
	 * Both, the strings for ":joins" and for ":cond" are the same as for
1012
	 * the "search" SQL statement.
1013
	 *
1014
	 * Contrary to the "search" statement, it doesn't return any records
1015
	 * but instead the number of records that have been found. As counting
1016
	 * thousands of records can be a long running task, the maximum number
1017
	 * of counted records is limited for performance reasons.
1018
	 *
1019
	 * The SQL statement should conform to the ANSI standard to be
1020
	 * compatible with most relational database systems. This also
1021
	 * includes using double quotes for table and column names.
1022
	 *
1023
	 * @param string SQL statement for counting items
1024
	 * @since 2015.10
1025
	 * @see mshop/order/manager/insert/ansi
1026
	 * @see mshop/order/manager/update/ansi
1027
	 * @see mshop/order/manager/newid/ansi
1028
	 * @see mshop/order/manager/delete/ansi
1029
	 * @see mshop/order/manager/search/ansi
1030
	 */
1031
}
1032