Completed
Push — master ( 137f58...aece78 )
by Aimeos
02:30
created

Base::calcPrice()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 29
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 29
rs 8.5806
cc 4
eloc 14
nc 6
nop 3
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2012
6
 * @copyright Aimeos (aimeos.org), 2015-2016
7
 * @package Controller
8
 * @subpackage Frontend
9
 */
10
11
12
namespace Aimeos\Controller\Frontend\Basket;
13
14
15
/**
16
 * Base class for the basket frontend controller
17
 *
18
 * @package Controller
19
 * @subpackage Frontend
20
 */
21
abstract class Base extends \Aimeos\Controller\Frontend\Base
22
{
23
	private $listTypeAttributes = array();
24
25
26
	/**
27
	 * Calculates and returns the current price for the given order product and product prices.
28
	 *
29
	 * @param \Aimeos\MShop\Order\Item\Base\Product\Iface $product Ordered product item
30
	 * @param \Aimeos\MShop\Price\Item\Iface[] $prices List of price items
31
	 * @param integer $quantity New product quantity
32
	 * @return \Aimeos\MShop\Price\Item\Iface Price item with calculated price
33
	 */
34
	protected function calcPrice( \Aimeos\MShop\Order\Item\Base\Product\Iface $product, array $prices, $quantity )
35
	{
36
		$context = $this->getContext();
37
38
		if( empty( $prices ) )
39
		{
40
			$parentItem = $this->getDomainItem( 'product', 'product.id', $product->getProductId(), array( 'price' ) );
41
			$prices = $parentItem->getRefItems( 'price', 'default' );
42
		}
43
44
		$priceManager = \Aimeos\MShop\Factory::createManager( $context, 'price' );
45
		$price = $priceManager->getLowestPrice( $prices, $quantity );
46
47
		foreach( $this->getAttributeItems( $product->getAttributes() ) as $attrItem )
48
		{
49
			$prices = $attrItem->getRefItems( 'price', 'default' );
50
51
			if( count( $prices ) > 0 )
52
			{
53
				$attrPrice = $priceManager->getLowestPrice( $prices, $quantity );
54
				$price->addItem( $attrPrice );
55
			}
56
		}
57
58
		// remove product rebate of original price in favor to rebates granted for the order
59
		$price->setRebate( '0.00' );
60
61
		return $price;
62
	}
63
64
65
	/**
66
	 * Checks if the IDs of the given items are really associated to the product.
67
	 *
68
	 * @param string $prodId Unique ID of the product
69
	 * @param string $domain Domain the references must be of
70
	 * @param integer $listTypeId ID of the list type the referenced items must be
71
	 * @param array $refIds List of IDs that must be associated to the product
72
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If one or more of the IDs are not associated
73
	 */
74
	protected function checkReferences( $prodId, $domain, $listTypeId, array $refIds )
75
	{
76
		$productManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'product' );
77
		$search = $productManager->createSearch( true );
78
79
		$expr = array(
80
			$search->compare( '==', 'product.id', $prodId ),
81
			$search->getConditions(),
82
		);
83
84
		if( count( $refIds ) > 0 )
85
		{
86
			foreach( $refIds as $key => $refId ) {
87
				$refIds[$key] = (string) $refId;
88
			}
89
90
			$param = array( $domain, $listTypeId, $refIds );
91
			$cmpfunc = $search->createFunction( 'product.contains', $param );
92
93
			$expr[] = $search->compare( '==', $cmpfunc, count( $refIds ) );
94
		}
95
96
		$search->setConditions( $search->combine( '&&', $expr ) );
97
98
		if( count( $productManager->searchItems( $search, array() ) ) === 0 )
99
		{
100
			$msg = sprintf( 'Invalid "%1$s" references for product with ID "%2$s"', $domain, $prodId );
101
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
102
		}
103
	}
104
105
106
	/**
107
	 * Creates the order product attribute items from the given attribute IDs and updates the price item if necessary.
108
	 *
109
	 * @param \Aimeos\MShop\Price\Item\Iface $price Price item of the ordered product
110
	 * @param string $prodid Unique product ID where the given attributes must be attached to
111
	 * @param integer $quantity Number of products that should be added to the basket
112
	 * @param array $attributeIds List of attributes IDs of the given type
113
	 * @param string $type Attribute type
114
	 * @param array $attributeValues Associative list of attribute IDs as keys and their codes as values
115
	 * @return array List of items implementing \Aimeos\MShop\Order\Item\Product\Attribute\Iface
116
	 */
117
	protected function createOrderProductAttributes( \Aimeos\MShop\Price\Item\Iface $price, $prodid, $quantity,
118
			array $attributeIds, $type, array $attributeValues = array() )
119
	{
120
		if( empty( $attributeIds ) ) {
121
			return array();
122
		}
123
124
		$attrTypeId = $this->getProductListTypeItem( 'attribute', $type )->getId();
125
		$this->checkReferences( $prodid, 'attribute', $attrTypeId, $attributeIds );
126
127
		$list = array();
128
		$context = $this->getContext();
129
130
		$priceManager = \Aimeos\MShop\Factory::createManager( $context, 'price' );
131
		$orderProductAttributeManager = \Aimeos\MShop\Factory::createManager( $context, 'order/base/product/attribute' );
132
133
		foreach( $this->getAttributes( $attributeIds ) as $id => $attrItem )
134
		{
135
			$prices = $attrItem->getRefItems( 'price', 'default', 'default' );
136
137
			if( !empty( $prices ) ) {
138
				$price->addItem( $priceManager->getLowestPrice( $prices, $quantity ) );
139
			}
140
141
			$item = $orderProductAttributeManager->createItem();
142
			$item->copyFrom( $attrItem );
143
			$item->setType( $type );
144
145
			if( isset( $attributeValues[$id] ) ) {
146
				$item->setValue( $attributeValues[$id] );
147
			}
148
149
			$list[] = $item;
150
		}
151
152
		return $list;
153
	}
154
155
156
	/**
157
	 * Returns the attribute items for the given attribute IDs.
158
	 *
159
	 * @param array $attributeIds List of attribute IDs
160
	 * @param string[] $domains Names of the domain items that should be fetched too
161
	 * @return array List of items implementing \Aimeos\MShop\Attribute\Item\Iface
162
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If the actual attribute number doesn't match the expected one
163
	 */
164
	protected function getAttributes( array $attributeIds, array $domains = array( 'price', 'text' ) )
165
	{
166
		if( empty( $attributeIds ) ) {
167
			return array();
168
		}
169
170
		$attributeManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'attribute' );
171
172
		$search = $attributeManager->createSearch( true );
173
		$expr = array(
174
				$search->compare( '==', 'attribute.id', $attributeIds ),
175
				$search->getConditions(),
176
		);
177
		$search->setConditions( $search->combine( '&&', $expr ) );
178
		$search->setSlice( 0, 0x7fffffff );
179
180
		$attrItems = $attributeManager->searchItems( $search, $domains );
181
182
		if( count( $attrItems ) !== count( $attributeIds ) )
183
		{
184
			$expected = implode( ',', $attributeIds );
185
			$actual = implode( ',', array_keys( $attrItems ) );
186
			$msg = sprintf( 'Available attribute IDs "%1$s" do not match the given attribute IDs "%2$s"', $actual, $expected );
187
188
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
189
		}
190
191
		return $attrItems;
192
	}
193
194
195
	/**
196
	 * Returns the attribute items using the given order attribute items.
197
	 *
198
	 * @param \Aimeos\MShop\Order\Item\Base\Product\Attribute\Item[] $orderAttributes List of order product attribute items
199
	 * @return \Aimeos\MShop\Attribute\Item\Iface[] Associative list of attribute IDs as key and attribute items as values
200
	 */
201
	protected function getAttributeItems( array $orderAttributes )
202
	{
203
		if( empty( $orderAttributes ) ) {
204
			return array();
205
		}
206
207
		$attributeManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'attribute' );
208
		$search = $attributeManager->createSearch( true );
209
		$expr = array();
210
211
		foreach( $orderAttributes as $item )
212
		{
213
			$tmp = array(
214
				$search->compare( '==', 'attribute.domain', 'product' ),
215
				$search->compare( '==', 'attribute.code', $item->getValue() ),
216
				$search->compare( '==', 'attribute.type.domain', 'product' ),
217
				$search->compare( '==', 'attribute.type.code', $item->getCode() ),
218
				$search->compare( '>', 'attribute.type.status', 0 ),
219
				$search->getConditions(),
220
			);
221
			$expr[] = $search->combine( '&&', $tmp );
222
		}
223
224
		$search->setConditions( $search->combine( '||', $expr ) );
225
		return $attributeManager->searchItems( $search, array( 'price' ) );
226
	}
227
228
229
	/**
230
	 * Retrieves the domain item specified by the given key and value.
231
	 *
232
	 * @param string $domain Product manager search key
233
	 * @param string $key Domain manager search key
234
	 * @param string $value Unique domain identifier
235
	 * @param string[] $ref List of referenced items that should be fetched too
236
	 * @return \Aimeos\MShop\Common\Item\Iface Domain item object
237
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception
238
	 */
239
	protected function getDomainItem( $domain, $key, $value, array $ref )
240
	{
241
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $domain );
242
243
		$search = $manager->createSearch( true );
244
		$expr = array(
245
			$search->compare( '==', $key, $value ),
246
			$search->getConditions(),
247
		);
248
		$search->setConditions( $search->combine( '&&', $expr ) );
249
250
		$result = $manager->searchItems( $search, $ref );
251
252
		if( ( $item = reset( $result ) ) === false )
253
		{
254
			$msg = sprintf( 'No item for "%1$s" (%2$s) found', $value, $key );
255
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
256
		}
257
258
		return $item;
259
	}
260
261
262
	/**
263
	 * Returns the list type item for the given domain and code.
264
	 *
265
	 * @param string $domain Domain name of the list type
266
	 * @param string $code Code of the list type
267
	 * @return \Aimeos\MShop\Common\Item\Type\Iface List type item
268
	 */
269
	protected function getProductListTypeItem( $domain, $code )
270
	{
271
		if( !isset( $this->listTypeAttributes[$domain][$code] ) )
272
		{
273
			$listTypeManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'product/lists/type' );
274
275
			$listTypeSearch = $listTypeManager->createSearch( true );
276
			$expr = array(
277
				$listTypeSearch->compare( '==', 'product.lists.type.domain', $domain ),
278
				$listTypeSearch->compare( '==', 'product.lists.type.code', $code ),
279
				$listTypeSearch->getConditions(),
280
			);
281
			$listTypeSearch->setConditions( $listTypeSearch->combine( '&&', $expr ) );
282
283
			$listTypeItems = $listTypeManager->searchItems( $listTypeSearch );
284
285
			if( ( $listTypeItem = reset( $listTypeItems ) ) === false )
286
			{
287
				$msg = sprintf( 'List type for domain "%1$s" and code "%2$s" not found', $domain, $code );
288
				throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
289
			}
290
291
			$this->listTypeAttributes[$domain][$code] = $listTypeItem;
292
		}
293
294
		return $this->listTypeAttributes[$domain][$code];
295
	}
296
297
298
	/**
299
	 * Returns the product variants of a selection product that match the given attributes.
300
	 *
301
	 * @param \Aimeos\MShop\Product\Item\Iface $productItem Product item including sub-products
302
	 * @param array $variantAttributeIds IDs for the variant-building attributes
303
	 * @param array $domains Names of the domain items that should be fetched too
304
	 * @return array List of products matching the given attributes
305
	 */
306
	protected function getProductVariants( \Aimeos\MShop\Product\Item\Iface $productItem, array $variantAttributeIds,
307
			array $domains = array( 'attribute', 'media', 'price', 'text' ) )
308
	{
309
		$subProductIds = array();
310
		foreach( $productItem->getRefItems( 'product', 'default', 'default' ) as $item ) {
311
			$subProductIds[] = $item->getId();
312
		}
313
314
		if( count( $subProductIds ) === 0 ) {
315
			return array();
316
		}
317
318
		$productManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'product' );
319
		$search = $productManager->createSearch( true );
320
321
		$expr = array(
322
				$search->compare( '==', 'product.id', $subProductIds ),
323
				$search->getConditions(),
324
		);
325
326
		if( count( $variantAttributeIds ) > 0 )
327
		{
328
			foreach( $variantAttributeIds as $key => $id ) {
329
				$variantAttributeIds[$key] = (string) $id;
330
			}
331
332
			$listTypeItem = $this->getProductListTypeItem( 'attribute', 'variant' );
333
334
			$param = array( 'attribute', $listTypeItem->getId(), $variantAttributeIds );
335
			$cmpfunc = $search->createFunction( 'product.contains', $param );
336
337
			$expr[] = $search->compare( '==', $cmpfunc, count( $variantAttributeIds ) );
338
		}
339
340
		$search->setConditions( $search->combine( '&&', $expr ) );
341
342
		return $productManager->searchItems( $search, $domains );
343
	}
344
345
346
	/**
347
	 * Returns the value of an array or the default value if it's not available.
348
	 *
349
	 * @param array $values Associative list of key/value pairs
350
	 * @param string $name Name of the key to return the value for
351
	 * @param mixed $default Default value if no value is available for the given name
352
	 * @return mixed Value from the array or default value
353
	 */
354
	protected function getValue( array $values, $name, $default = null )
355
	{
356
		if( isset( $values[$name] ) ) {
357
			return $values[$name];
358
		}
359
360
		return $default;
361
	}
362
363
364
	/**
365
	 * Checks if the product is part of at least one category in the product catalog.
366
	 *
367
	 * @param string $prodid Unique ID of the product
368
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If product is not associated to at least one category
369
	 * @deprecated 2016.05
370
	 */
371
	protected function checkCategory( $prodid )
372
	{
373
		$catalogListManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'catalog/lists' );
374
375
		$search = $catalogListManager->createSearch( true );
376
		$expr = array(
377
				$search->compare( '==', 'catalog.lists.refid', $prodid ),
378
				$search->getConditions()
379
		);
380
		$search->setConditions( $search->combine( '&&', $expr ) );
381
		$search->setSlice( 0, 1 );
382
383
		$result = $catalogListManager->searchItems( $search );
384
385
		if( reset( $result ) === false )
386
		{
387
			$msg = sprintf( 'Adding product with ID "%1$s" is not allowed', $prodid );
388
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
389
		}
390
	}
391
392
393
	/**
394
	 * Edits the changed product to the basket if it's in stock.
395
	 *
396
	 * @param \Aimeos\MShop\Order\Item\Base\Product\Iface $orderBaseProductItem Old order product from basket
397
	 * @param string $productId Unique ID of the product item that belongs to the order product
398
	 * @param integer $quantity Number of products to add to the basket
399
	 * @param array $options Associative list of options
400
	 * @param string $warehouse Warehouse code for retrieving the stock level
401
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If there's not enough stock available
402
	 * @deprecated 2016.05
403
	 */
404
	protected function addProductInStock( \Aimeos\MShop\Order\Item\Base\Product\Iface $orderBaseProductItem,
405
			$productId, $quantity, array $options, $warehouse )
406
	{
407
		$stocklevel = null;
408
		if( !isset( $options['stock'] ) || $options['stock'] != false ) {
409
			$stocklevel = $this->getStockLevel( $productId, $warehouse );
0 ignored issues
show
Deprecated Code introduced by
The method Aimeos\Controller\Fronte...t\Base::getStockLevel() has been deprecated with message: 2016.04 Use basket stock decorator instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
410
		}
411
412
		if( $stocklevel === null || $stocklevel > 0 )
413
		{
414
			$position = $this->get()->addProduct( $orderBaseProductItem );
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Aimeos\Controller\Frontend\Basket\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
415
			$orderBaseProductItem = clone $this->get()->getProduct( $position );
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Aimeos\Controller\Frontend\Basket\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
416
			$quantity = $orderBaseProductItem->getQuantity();
417
418
			if( $stocklevel > 0 && $stocklevel < $quantity )
419
			{
420
				$this->get()->deleteProduct( $position );
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Aimeos\Controller\Frontend\Basket\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
421
				$orderBaseProductItem->setQuantity( $stocklevel );
422
				$this->get()->addProduct( $orderBaseProductItem, $position );
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Aimeos\Controller\Frontend\Basket\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
423
			}
424
		}
425
426
		if( $stocklevel !== null && $stocklevel < $quantity )
427
		{
428
			$msg = sprintf( 'There are not enough products "%1$s" in stock', $orderBaseProductItem->getName() );
429
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
430
		}
431
	}
432
433
434
	/**
435
	 * Edits the changed product to the basket if it's in stock.
436
	 *
437
	 * @param \Aimeos\MShop\Order\Item\Base\Product\Iface $product Old order product from basket
438
	 * @param \Aimeos\MShop\Product\Item\Iface $productItem Product item that belongs to the order product
439
	 * @param integer $quantity New product quantity
440
	 * @param integer $position Position of the old order product in the basket
441
	 * @param array Associative list of options
442
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If there's not enough stock available
443
	 * @deprecated 2016.05
444
	 */
445
	protected function editProductInStock( \Aimeos\MShop\Order\Item\Base\Product\Iface $product,
446
			\Aimeos\MShop\Product\Item\Iface $productItem, $quantity, $position, array $options )
447
	{
448
		$stocklevel = null;
449
		if( !isset( $options['stock'] ) || $options['stock'] != false ) {
450
			$stocklevel = $this->getStockLevel( $productItem->getId(), $product->getWarehouseCode() );
0 ignored issues
show
Deprecated Code introduced by
The method Aimeos\Controller\Fronte...t\Base::getStockLevel() has been deprecated with message: 2016.04 Use basket stock decorator instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
451
		}
452
453
		$product->setQuantity( ( $stocklevel !== null && $stocklevel > 0 ? min( $stocklevel, $quantity ) : $quantity ) );
454
455
		$this->get()->deleteProduct( $position );
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Aimeos\Controller\Frontend\Basket\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
456
457
		if( $stocklevel === null || $stocklevel > 0 ) {
458
			$this->get()->addProduct( $product, $position );
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Aimeos\Controller\Frontend\Basket\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
459
		}
460
461
		if( $stocklevel !== null && $stocklevel < $quantity )
462
		{
463
			$msg = sprintf( 'There are not enough products "%1$s" in stock', $productItem->getName() );
464
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
465
		}
466
	}
467
468
469
	/**
470
	 * Returns the highest stock level for the product.
471
	 *
472
	 * @param string $prodid Unique ID of the product
473
	 * @param string $warehouse Unique code of the warehouse
474
	 * @return integer|null Number of available items in stock (null for unlimited stock)
475
	 * @deprecated 2016.04 Use basket stock decorator instead
476
	 */
477
	protected function getStockLevel( $prodid, $warehouse )
478
	{
479
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'product/stock' );
480
481
		$search = $manager->createSearch( true );
482
		$expr = array(
483
				$search->compare( '==', 'product.stock.parentid', $prodid ),
484
				$search->getConditions(),
485
				$search->compare( '==', 'product.stock.warehouse.code', $warehouse ),
486
		);
487
		$search->setConditions( $search->combine( '&&', $expr ) );
488
489
		$result = $manager->searchItems( $search );
490
491
		if( empty( $result ) )
492
		{
493
			$msg = sprintf( 'No stock for product ID "%1$s" and warehouse "%2$s" available', $prodid, $warehouse );
494
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
495
		}
496
497
		$stocklevel = null;
498
499
		foreach( $result as $item )
500
		{
501
			if( ( $stock = $item->getStockLevel() ) === null ) {
502
				return null;
503
			}
504
505
			$stocklevel = max( (int) $stocklevel, $item->getStockLevel() );
506
		}
507
508
		return $stocklevel;
509
	}
510
}
511